diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 20 | ||||
-rw-r--r-- | src/bookmarks.c | 30 | ||||
-rw-r--r-- | src/bookmarks.h | 13 | ||||
-rw-r--r-- | src/defs.h | 2 | ||||
-rw-r--r-- | src/ui/keys.c | 1 | ||||
-rw-r--r-- | src/ui/listwidget.c | 30 | ||||
-rw-r--r-- | src/ui/lookupwidget.c | 3 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 170 | ||||
-rw-r--r-- | src/ui/util.c | 8 | ||||
-rw-r--r-- | src/ui/window.c | 3 |
10 files changed, 222 insertions, 58 deletions
@@ -2033,6 +2033,7 @@ static void resetFonts_App_(iApp *d) { | |||
2033 | iBool handleCommand_App(const char *cmd) { | 2033 | iBool handleCommand_App(const char *cmd) { |
2034 | iApp *d = &app_; | 2034 | iApp *d = &app_; |
2035 | const iBool isFrozen = !d->window || d->window->isDrawFrozen; | 2035 | const iBool isFrozen = !d->window || d->window->isDrawFrozen; |
2036 | /* TODO: Maybe break this up a little bit? There's a very long list of ifs here. */ | ||
2036 | if (equal_Command(cmd, "config.error")) { | 2037 | if (equal_Command(cmd, "config.error")) { |
2037 | makeSimpleMessage_Widget(uiTextCaution_ColorEscape "CONFIG ERROR", | 2038 | makeSimpleMessage_Widget(uiTextCaution_ColorEscape "CONFIG ERROR", |
2038 | format_CStr("Error in config file: %s\n" | 2039 | format_CStr("Error in config file: %s\n" |
@@ -2764,6 +2765,25 @@ iBool handleCommand_App(const char *cmd) { | |||
2764 | makeFeedSettings_Widget(findUrl_Bookmarks(d->bookmarks, url)); | 2765 | makeFeedSettings_Widget(findUrl_Bookmarks(d->bookmarks, url)); |
2765 | return iTrue; | 2766 | return iTrue; |
2766 | } | 2767 | } |
2768 | else if (equal_Command(cmd, "bookmarks.addfolder")) { | ||
2769 | if (suffixPtr_Command(cmd, "value")) { | ||
2770 | add_Bookmarks(d->bookmarks, NULL, collect_String(suffix_Command(cmd, "value")), NULL, 0); | ||
2771 | postCommand_App("bookmarks.changed"); | ||
2772 | } | ||
2773 | else { | ||
2774 | iWidget *dlg = makeValueInput_Widget(get_Root()->widget, | ||
2775 | collectNewCStr_String(cstr_Lang("dlg.addfolder.defaulttitle")), | ||
2776 | uiHeading_ColorEscape "${heading.addfolder}", "${dlg.addfolder.prompt}", | ||
2777 | uiTextAction_ColorEscape "${dlg.addfolder}", "bookmarks.addfolder"); | ||
2778 | setSelectAllOnFocus_InputWidget(findChild_Widget(dlg, "input"), iTrue); | ||
2779 | } | ||
2780 | return iTrue; | ||
2781 | } | ||
2782 | else if (equal_Command(cmd, "bookmarks.sort")) { | ||
2783 | sort_Bookmarks(d->bookmarks, arg_Command(cmd), cmpTitleAscending_Bookmark); | ||
2784 | postCommand_App("bookmarks.changed"); | ||
2785 | return iTrue; | ||
2786 | } | ||
2767 | else if (equal_Command(cmd, "bookmarks.reload.remote")) { | 2787 | else if (equal_Command(cmd, "bookmarks.reload.remote")) { |
2768 | fetchRemote_Bookmarks(bookmarks_App()); | 2788 | fetchRemote_Bookmarks(bookmarks_App()); |
2769 | return iTrue; | 2789 | return iTrue; |
diff --git a/src/bookmarks.c b/src/bookmarks.c index 616e4632..f7691655 100644 --- a/src/bookmarks.c +++ b/src/bookmarks.c | |||
@@ -79,7 +79,7 @@ static int cmpTimeDescending_Bookmark_(const iBookmark **a, const iBookmark **b) | |||
79 | return iCmp(seconds_Time(&(*b)->when), seconds_Time(&(*a)->when)); | 79 | return iCmp(seconds_Time(&(*b)->when), seconds_Time(&(*a)->when)); |
80 | } | 80 | } |
81 | 81 | ||
82 | static int cmpTitleAscending_Bookmark_(const iBookmark **a, const iBookmark **b) { | 82 | int cmpTitleAscending_Bookmark(const iBookmark **a, const iBookmark **b) { |
83 | return cmpStringCase_String(&(*a)->title, &(*b)->title); | 83 | return cmpStringCase_String(&(*a)->title, &(*b)->title); |
84 | } | 84 | } |
85 | 85 | ||
@@ -250,7 +250,20 @@ static void load_BookmarkLoader(iBookmarkLoader *d, iFile *file) { | |||
250 | iDefineTypeConstructionArgs(BookmarkLoader, (iBookmarks *b), b) | 250 | iDefineTypeConstructionArgs(BookmarkLoader, (iBookmarks *b), b) |
251 | 251 | ||
252 | /*----------------------------------------------------------------------------------------------*/ | 252 | /*----------------------------------------------------------------------------------------------*/ |
253 | 253 | ||
254 | static iBool isMatchingParent_Bookmark_(void *context, const iBookmark *bm) { | ||
255 | return bm->parentId == *(const uint32_t *) context; | ||
256 | } | ||
257 | |||
258 | void sort_Bookmarks(iBookmarks *d, uint32_t parentId, iBookmarksCompareFunc cmp) { | ||
259 | lock_Mutex(d->mtx); | ||
260 | iConstForEach(PtrArray, i, list_Bookmarks(d, cmp, isMatchingParent_Bookmark_, &parentId)) { | ||
261 | iBookmark *bm = i.ptr; | ||
262 | bm->order = index_PtrArrayConstIterator(&i) + 1; | ||
263 | } | ||
264 | unlock_Mutex(d->mtx); | ||
265 | } | ||
266 | |||
254 | void load_Bookmarks(iBookmarks *d, const char *dirPath) { | 267 | void load_Bookmarks(iBookmarks *d, const char *dirPath) { |
255 | clear_Bookmarks(d); | 268 | clear_Bookmarks(d); |
256 | /* Load new .ini bookmarks, if present. */ | 269 | /* Load new .ini bookmarks, if present. */ |
@@ -258,11 +271,8 @@ void load_Bookmarks(iBookmarks *d, const char *dirPath) { | |||
258 | if (!open_File(f, readOnly_FileMode | text_FileMode)) { | 271 | if (!open_File(f, readOnly_FileMode | text_FileMode)) { |
259 | /* As a fallback, try loading the v1.6 bookmarks file. */ | 272 | /* As a fallback, try loading the v1.6 bookmarks file. */ |
260 | loadOldFormat_Bookmarks(d, dirPath); | 273 | loadOldFormat_Bookmarks(d, dirPath); |
261 | /* Set ordering based on titles. */ | 274 | /* Old format has an implicit alphabetic sort order. */ |
262 | iConstForEach(PtrArray, i, list_Bookmarks(d, cmpTitleAscending_Bookmark_, NULL, NULL)) { | 275 | sort_Bookmarks(d, 0, cmpTitleAscending_Bookmark); |
263 | iBookmark *bm = i.ptr; | ||
264 | bm->order = index_PtrArrayConstIterator(&i) + 1; | ||
265 | } | ||
266 | return; | 276 | return; |
267 | } | 277 | } |
268 | iBookmarkLoader loader; | 278 | iBookmarkLoader loader; |
@@ -317,7 +327,9 @@ uint32_t add_Bookmarks(iBookmarks *d, const iString *url, const iString *title, | |||
317 | iChar icon) { | 327 | iChar icon) { |
318 | lock_Mutex(d->mtx); | 328 | lock_Mutex(d->mtx); |
319 | iBookmark *bm = new_Bookmark(); | 329 | iBookmark *bm = new_Bookmark(); |
320 | set_String(&bm->url, canonicalUrl_String(url)); | 330 | if (url) { |
331 | set_String(&bm->url, canonicalUrl_String(url)); | ||
332 | } | ||
321 | set_String(&bm->title, title); | 333 | set_String(&bm->title, title); |
322 | if (tags) { | 334 | if (tags) { |
323 | set_String(&bm->tags, tags); | 335 | set_String(&bm->tags, tags); |
@@ -471,7 +483,7 @@ const iString *bookmarkListPage_Bookmarks(const iBookmarks *d, enum iBookmarkLis | |||
471 | const iPtrArray *bmList = list_Bookmarks(d, | 483 | const iPtrArray *bmList = list_Bookmarks(d, |
472 | listType == listByCreationTime_BookmarkListType | 484 | listType == listByCreationTime_BookmarkListType |
473 | ? cmpTimeDescending_Bookmark_ | 485 | ? cmpTimeDescending_Bookmark_ |
474 | : cmpTitleAscending_Bookmark_, | 486 | : cmpTitleAscending_Bookmark, |
475 | NULL, | 487 | NULL, |
476 | NULL); | 488 | NULL); |
477 | iConstForEach(PtrArray, i, bmList) { | 489 | iConstForEach(PtrArray, i, bmList) { |
diff --git a/src/bookmarks.h b/src/bookmarks.h index 0de930d7..40170062 100644 --- a/src/bookmarks.h +++ b/src/bookmarks.h | |||
@@ -53,7 +53,8 @@ struct Impl_Bookmark { | |||
53 | int order; /* sort order */ | 53 | int order; /* sort order */ |
54 | }; | 54 | }; |
55 | 55 | ||
56 | iLocalDef uint32_t id_Bookmark (const iBookmark *d) { return d->node.key; } | 56 | iLocalDef uint32_t id_Bookmark (const iBookmark *d) { return d->node.key; } |
57 | iLocalDef iBool isFolder_Bookmark (const iBookmark *d) { return isEmpty_String(&d->url); } | ||
57 | 58 | ||
58 | iBool hasTag_Bookmark (const iBookmark *, const char *tag); | 59 | iBool hasTag_Bookmark (const iBookmark *, const char *tag); |
59 | void addTag_Bookmark (iBookmark *, const char *tag); | 60 | void addTag_Bookmark (iBookmark *, const char *tag); |
@@ -73,11 +74,17 @@ iLocalDef void addOrRemoveTag_Bookmark(iBookmark *d, const char *tag, iBool add) | |||
73 | } | 74 | } |
74 | } | 75 | } |
75 | 76 | ||
77 | int cmpTitleAscending_Bookmark (const iBookmark **, const iBookmark **); | ||
78 | int cmpTree_Bookmark (const iBookmark **, const iBookmark **); | ||
79 | |||
76 | /*----------------------------------------------------------------------------------------------*/ | 80 | /*----------------------------------------------------------------------------------------------*/ |
77 | 81 | ||
78 | iDeclareType(Bookmarks) | 82 | iDeclareType(Bookmarks) |
79 | iDeclareTypeConstruction(Bookmarks) | 83 | iDeclareTypeConstruction(Bookmarks) |
80 | 84 | ||
85 | typedef iBool (*iBookmarksFilterFunc) (void *context, const iBookmark *); | ||
86 | typedef int (*iBookmarksCompareFunc) (const iBookmark **, const iBookmark **); | ||
87 | |||
81 | void clear_Bookmarks (iBookmarks *); | 88 | void clear_Bookmarks (iBookmarks *); |
82 | void load_Bookmarks (iBookmarks *, const char *dirPath); | 89 | void load_Bookmarks (iBookmarks *, const char *dirPath); |
83 | void save_Bookmarks (const iBookmarks *, const char *dirPath); | 90 | void save_Bookmarks (const iBookmarks *, const char *dirPath); |
@@ -88,15 +95,13 @@ iBool remove_Bookmarks (iBookmarks *, uint32_t id); | |||
88 | iBookmark * get_Bookmarks (iBookmarks *, uint32_t id); | 95 | iBookmark * get_Bookmarks (iBookmarks *, uint32_t id); |
89 | void reorder_Bookmarks (iBookmarks *, uint32_t id, int newOrder); | 96 | void reorder_Bookmarks (iBookmarks *, uint32_t id, int newOrder); |
90 | iBool updateBookmarkIcon_Bookmarks(iBookmarks *, const iString *url, iChar icon); | 97 | iBool updateBookmarkIcon_Bookmarks(iBookmarks *, const iString *url, iChar icon); |
98 | void sort_Bookmarks (iBookmarks *, uint32_t parentId, iBookmarksCompareFunc cmp); | ||
91 | void fetchRemote_Bookmarks (iBookmarks *); | 99 | void fetchRemote_Bookmarks (iBookmarks *); |
92 | void requestFinished_Bookmarks (iBookmarks *, iGmRequest *req); | 100 | void requestFinished_Bookmarks (iBookmarks *, iGmRequest *req); |
93 | 101 | ||
94 | iChar siteIcon_Bookmarks (const iBookmarks *, const iString *url); | 102 | iChar siteIcon_Bookmarks (const iBookmarks *, const iString *url); |
95 | uint32_t findUrl_Bookmarks (const iBookmarks *, const iString *url); /* O(n) */ | 103 | uint32_t findUrl_Bookmarks (const iBookmarks *, const iString *url); /* O(n) */ |
96 | 104 | ||
97 | typedef iBool (*iBookmarksFilterFunc) (void *context, const iBookmark *); | ||
98 | typedef int (*iBookmarksCompareFunc)(const iBookmark **, const iBookmark **); | ||
99 | |||
100 | iBool filterTagsRegExp_Bookmarks (void *regExp, const iBookmark *); | 105 | iBool filterTagsRegExp_Bookmarks (void *regExp, const iBookmark *); |
101 | 106 | ||
102 | /** | 107 | /** |
@@ -107,6 +107,7 @@ iLocalDef int acceptKeyMod_ReturnKeyBehavior(int behavior) { | |||
107 | #define rightArrow_Icon "\u279e" | 107 | #define rightArrow_Icon "\u279e" |
108 | #define barLeftArrow_Icon "\u21a4" | 108 | #define barLeftArrow_Icon "\u21a4" |
109 | #define barRightArrow_Icon "\u21a6" | 109 | #define barRightArrow_Icon "\u21a6" |
110 | #define upDownArrow_Icon "\u21c5" | ||
110 | #define clock_Icon "\U0001f553" | 111 | #define clock_Icon "\U0001f553" |
111 | #define pin_Icon "\U0001f588" | 112 | #define pin_Icon "\U0001f588" |
112 | #define star_Icon "\u2605" | 113 | #define star_Icon "\u2605" |
@@ -155,6 +156,7 @@ iLocalDef int acceptKeyMod_ReturnKeyBehavior(int behavior) { | |||
155 | #define return_Icon "\u23ce" | 156 | #define return_Icon "\u23ce" |
156 | #define undo_Icon "\u23ea" | 157 | #define undo_Icon "\u23ea" |
157 | #define select_Icon "\u2b1a" | 158 | #define select_Icon "\u2b1a" |
159 | #define downAngle_Icon "\ufe40" | ||
158 | 160 | ||
159 | #if defined (iPlatformApple) | 161 | #if defined (iPlatformApple) |
160 | # define shift_Icon "\u21e7" | 162 | # define shift_Icon "\u21e7" |
diff --git a/src/ui/keys.c b/src/ui/keys.c index 6de30f57..30072572 100644 --- a/src/ui/keys.c +++ b/src/ui/keys.c | |||
@@ -213,6 +213,7 @@ static const struct { int id; iMenuItem bind; int flags; } defaultBindings_[] = | |||
213 | { 46, { "${keys.link.homerow.hover}", 'h', 0, "document.linkkeys arg:1 hover:1" }, 0 }, | 213 | { 46, { "${keys.link.homerow.hover}", 'h', 0, "document.linkkeys arg:1 hover:1" }, 0 }, |
214 | { 47, { "${keys.link.homerow.next}", '.', 0, "document.linkkeys more:1" }, 0 }, | 214 | { 47, { "${keys.link.homerow.next}", '.', 0, "document.linkkeys more:1" }, 0 }, |
215 | { 50, { "${keys.bookmark.add}", 'd', KMOD_PRIMARY, "bookmark.add" }, 0 }, | 215 | { 50, { "${keys.bookmark.add}", 'd', KMOD_PRIMARY, "bookmark.add" }, 0 }, |
216 | { 51, { "${keys.bookmark.addfolder}", 'n', KMOD_SHIFT, "bookmarks.addfolder" }, 0 }, | ||
216 | { 55, { "${keys.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" }, 0 }, | 217 | { 55, { "${keys.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" }, 0 }, |
217 | { 60, { "${keys.findtext}", 'f', KMOD_PRIMARY, "focus.set id:find.input" }, 0 }, | 218 | { 60, { "${keys.findtext}", 'f', KMOD_PRIMARY, "focus.set id:find.input" }, 0 }, |
218 | { 70, { "${keys.zoom.in}", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" }, 0 }, | 219 | { 70, { "${keys.zoom.in}", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" }, 0 }, |
diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c index a34f3d03..f896b493 100644 --- a/src/ui/listwidget.c +++ b/src/ui/listwidget.c | |||
@@ -331,8 +331,8 @@ static size_t resolveDragDestination_ListWidget_(const iListWidget *d, iInt2 dst | |||
331 | const iRect rect = itemRect_ListWidget(d, index); | 331 | const iRect rect = itemRect_ListWidget(d, index); |
332 | const iRangei span = ySpan_Rect(rect); | 332 | const iRangei span = ySpan_Rect(rect); |
333 | if (item->isDropTarget) { | 333 | if (item->isDropTarget) { |
334 | const int pad = size_Range(&span) / 4; | 334 | const int pad = size_Range(&span) / 3; |
335 | if (dstPos.y >= span.start + pad && dstPos.y < span.end) { | 335 | if (dstPos.y >= span.start + pad && dstPos.y < span.end - pad) { |
336 | *isOnto = iTrue; | 336 | *isOnto = iTrue; |
337 | return index; | 337 | return index; |
338 | } | 338 | } |
@@ -352,11 +352,13 @@ static iBool endDrag_ListWidget_(iListWidget *d, iInt2 endPos) { | |||
352 | stop_Anim(&d->scrollY.pos); | 352 | stop_Anim(&d->scrollY.pos); |
353 | iBool isOnto; | 353 | iBool isOnto; |
354 | const size_t index = resolveDragDestination_ListWidget_(d, endPos, &isOnto); | 354 | const size_t index = resolveDragDestination_ListWidget_(d, endPos, &isOnto); |
355 | if (isOnto) { | 355 | if (index != d->dragItem) { |
356 | postCommand_Widget(d, "list.dragged arg:%zu onto:%zu", d->dragItem, index); | 356 | if (isOnto) { |
357 | } | 357 | postCommand_Widget(d, "list.dragged arg:%zu onto:%zu", d->dragItem, index); |
358 | else if (index != d->dragItem) { | 358 | } |
359 | postCommand_Widget(d, "list.dragged arg:%zu before:%zu", d->dragItem, index); | 359 | else { |
360 | postCommand_Widget(d, "list.dragged arg:%zu before:%zu", d->dragItem, index); | ||
361 | } | ||
360 | } | 362 | } |
361 | invalidateItem_ListWidget(d, d->dragItem); | 363 | invalidateItem_ListWidget(d, d->dragItem); |
362 | d->dragItem = iInvalidPos; | 364 | d->dragItem = iInvalidPos; |
@@ -394,7 +396,7 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { | |||
394 | } | 396 | } |
395 | else if (d->dragItem != iInvalidPos) { | 397 | else if (d->dragItem != iInvalidPos) { |
396 | /* Start scrolling if near the ends. */ | 398 | /* Start scrolling if near the ends. */ |
397 | const int zone = d->itemHeight; | 399 | const int zone = 2 * d->itemHeight; |
398 | const iRect bounds = bounds_Widget(w); | 400 | const iRect bounds = bounds_Widget(w); |
399 | float scrollSpeed = 0.0f; | 401 | float scrollSpeed = 0.0f; |
400 | if (mousePos.y > bottom_Rect(bounds) - zone) { | 402 | if (mousePos.y > bottom_Rect(bounds) - zone) { |
@@ -410,7 +412,7 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { | |||
410 | } | 412 | } |
411 | else { | 413 | else { |
412 | setValueSpeed_Anim(&d->scrollY.pos, scrollSpeed < 0 ? 0 : scrollMax_ListWidget_(d), | 414 | setValueSpeed_Anim(&d->scrollY.pos, scrollSpeed < 0 ? 0 : scrollMax_ListWidget_(d), |
413 | iAbs(scrollSpeed * gap_UI * 100)); | 415 | scrollSpeed * scrollSpeed * gap_UI * 400); |
414 | refreshWhileScrolling_ListWidget_(d); | 416 | refreshWhileScrolling_ListWidget_(d); |
415 | } | 417 | } |
416 | } | 418 | } |
@@ -451,7 +453,7 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { | |||
451 | ((const iListItem *) item_ListWidget(d, over))->isDraggable) { | 453 | ((const iListItem *) item_ListWidget(d, over))->isDraggable) { |
452 | d->dragItem = over; | 454 | d->dragItem = over; |
453 | d->dragOrigin = sub_I2(topLeft_Rect(itemRect_ListWidget(d, over)), | 455 | d->dragOrigin = sub_I2(topLeft_Rect(itemRect_ListWidget(d, over)), |
454 | pos_Click(&d->click)); | 456 | d->click.startPos); |
455 | invalidateItem_ListWidget(d, d->dragItem); | 457 | invalidateItem_ListWidget(d, d->dragItem); |
456 | } | 458 | } |
457 | } | 459 | } |
@@ -569,20 +571,22 @@ static void draw_ListWidget_(const iListWidget *d) { | |||
569 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | 571 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); |
570 | iBool dstOnto; | 572 | iBool dstOnto; |
571 | const size_t dstIndex = resolveDragDestination_ListWidget_(d, mousePos, &dstOnto); | 573 | const size_t dstIndex = resolveDragDestination_ListWidget_(d, mousePos, &dstOnto); |
572 | if (dstIndex != d->dragItem && dstIndex != d->dragItem + 1) { | 574 | if (dstIndex != d->dragItem) { |
573 | const iRect dstRect = itemRect_ListWidget(d, dstIndex); | 575 | const iRect dstRect = itemRect_ListWidget(d, dstIndex); |
574 | p.alpha = 0xff; | 576 | p.alpha = 0xff; |
575 | if (dstOnto) { | 577 | if (dstOnto) { |
576 | fillRect_Paint(&p, dstRect, uiTextAction_ColorId); | 578 | drawRectThickness_Paint(&p, dstRect, gap_UI / 2, uiTextAction_ColorId); |
577 | } | 579 | } |
578 | else { | 580 | else if (dstIndex != d->dragItem + 1) { |
579 | fillRect_Paint(&p, (iRect){ addY_I2(dstRect.pos, -gap_UI / 4), | 581 | fillRect_Paint(&p, (iRect){ addY_I2(dstRect.pos, -gap_UI / 4), |
580 | init_I2(width_Rect(dstRect), gap_UI / 2) }, | 582 | init_I2(width_Rect(dstRect), gap_UI / 2) }, |
581 | uiTextAction_ColorId); | 583 | uiTextAction_ColorId); |
582 | } | 584 | } |
583 | } | 585 | } |
584 | p.alpha = 0x80; | 586 | p.alpha = 0x80; |
587 | setOpacity_Text(0.5f); | ||
585 | class_ListItem(item)->draw(item, &p, itemRect, d); | 588 | class_ListItem(item)->draw(item, &p, itemRect, d); |
589 | setOpacity_Text(1.0f); | ||
586 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | 590 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); |
587 | } | 591 | } |
588 | unsetClip_Paint(&p); | 592 | unsetClip_Paint(&p); |
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c index 85217336..ab649eee 100644 --- a/src/ui/lookupwidget.c +++ b/src/ui/lookupwidget.c | |||
@@ -171,6 +171,9 @@ static float scoreMatch_(const iRegExp *pattern, iRangecc text) { | |||
171 | } | 171 | } |
172 | 172 | ||
173 | static float bookmarkRelevance_LookupJob_(const iLookupJob *d, const iBookmark *bm) { | 173 | static float bookmarkRelevance_LookupJob_(const iLookupJob *d, const iBookmark *bm) { |
174 | if (isFolder_Bookmark(bm)) { | ||
175 | return 0.0f; | ||
176 | } | ||
174 | iUrl parts; | 177 | iUrl parts; |
175 | init_Url(&parts, &bm->url); | 178 | init_Url(&parts, &bm->url); |
176 | const float t = scoreMatch_(d->term, range_String(&bm->title)); | 179 | const float t = scoreMatch_(d->term, range_String(&bm->title)); |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 20e43153..6c2934ec 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -108,6 +108,7 @@ struct Impl_SidebarWidget { | |||
108 | iWidget * menu; | 108 | iWidget * menu; |
109 | iSidebarItem * contextItem; /* list item accessed in the context menu */ | 109 | iSidebarItem * contextItem; /* list item accessed in the context menu */ |
110 | size_t contextIndex; /* index of list item accessed in the context menu */ | 110 | size_t contextIndex; /* index of list item accessed in the context menu */ |
111 | iIntSet closedFolders; /* otherwise open */ | ||
111 | }; | 112 | }; |
112 | 113 | ||
113 | iDefineObjectConstructionArgs(SidebarWidget, (enum iSidebarSide side), side) | 114 | iDefineObjectConstructionArgs(SidebarWidget, (enum iSidebarSide side), side) |
@@ -116,26 +117,67 @@ static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { | |||
116 | return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; | 117 | return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; |
117 | } | 118 | } |
118 | 119 | ||
119 | static int cmpTree_Bookmark_(const iBookmark **a, const iBookmark **b) { | 120 | iBookmark *parent_Bookmark(const iBookmark *d) { |
121 | /* TODO: Parent pointers should be prefetched! */ | ||
122 | if (d->parentId) { | ||
123 | return get_Bookmarks(bookmarks_App(), d->parentId); | ||
124 | } | ||
125 | return NULL; | ||
126 | } | ||
127 | |||
128 | iBool hasParent_Bookmark(const iBookmark *d, uint32_t parentId) { | ||
129 | /* TODO: Parent pointers should be prefetched! */ | ||
130 | while (d->parentId) { | ||
131 | if (d->parentId == parentId) { | ||
132 | return iTrue; | ||
133 | } | ||
134 | d = get_Bookmarks(bookmarks_App(), d->parentId); | ||
135 | } | ||
136 | return iFalse; | ||
137 | } | ||
138 | |||
139 | int depth_Bookmark(const iBookmark *d) { | ||
140 | /* TODO: Precalculate this! */ | ||
141 | int depth = 0; | ||
142 | for (; d->parentId; depth++) { | ||
143 | d = get_Bookmarks(bookmarks_App(), d->parentId); | ||
144 | } | ||
145 | return depth; | ||
146 | } | ||
147 | |||
148 | int cmpTree_Bookmark(const iBookmark **a, const iBookmark **b) { | ||
120 | const iBookmark *bm1 = *a, *bm2 = *b; | 149 | const iBookmark *bm1 = *a, *bm2 = *b; |
121 | if (bm2->parentId == id_Bookmark(bm1)) { | 150 | /* Contents of a parent come after it. */ |
151 | if (hasParent_Bookmark(bm2, id_Bookmark(bm1))) { | ||
122 | return -1; | 152 | return -1; |
123 | } | 153 | } |
124 | if (bm1->parentId == id_Bookmark(bm2)) { | 154 | if (hasParent_Bookmark(bm1, id_Bookmark(bm2))) { |
125 | return 1; | 155 | return 1; |
126 | } | 156 | } |
127 | if (bm1->parentId == bm2->parentId) { | 157 | /* Comparisons are only valid inside the same parent. */ |
128 | //return cmpStringCase_String(&bm1->title, &bm2->title); | 158 | while (bm1->parentId != bm2->parentId) { |
129 | return iCmp(bm1->order, bm2->order); | 159 | int depth1 = depth_Bookmark(bm1); |
130 | } | 160 | int depth2 = depth_Bookmark(bm2); |
131 | if (bm1->parentId) { | 161 | if (depth1 != depth2) { |
132 | bm1 = get_Bookmarks(bookmarks_App(), bm1->parentId); | 162 | /* Equalize the depth. */ |
133 | } | 163 | while (depth1 > depth2) { |
134 | if (bm2->parentId) { | 164 | bm1 = parent_Bookmark(bm1); |
135 | bm2 = get_Bookmarks(bookmarks_App(), bm2->parentId); | 165 | depth1--; |
166 | } | ||
167 | while (depth2 > depth1) { | ||
168 | bm2 = parent_Bookmark(bm2); | ||
169 | depth2--; | ||
170 | } | ||
171 | continue; | ||
172 | } | ||
173 | bm1 = parent_Bookmark(bm1); | ||
174 | depth1--; | ||
175 | bm2 = parent_Bookmark(bm2); | ||
176 | depth2--; | ||
136 | } | 177 | } |
137 | // return cmpStringCase_String(&bm1->title, &bm2->title); | 178 | const int cmp = iCmp(bm1->order, bm2->order); |
138 | return iCmp(bm1->order, bm2->order); | 179 | if (cmp) return cmp; |
180 | return cmpStringCase_String(&bm1->title, &bm2->title); | ||
139 | } | 181 | } |
140 | 182 | ||
141 | static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const char *label, | 183 | static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const char *label, |
@@ -211,6 +253,16 @@ static void updateContextMenu_SidebarWidget_(iSidebarWidget *d) { | |||
211 | d->menu = makeMenu_Widget(as_Widget(d), data_Array(items), size_Array(items)); | 253 | d->menu = makeMenu_Widget(as_Widget(d), data_Array(items), size_Array(items)); |
212 | } | 254 | } |
213 | 255 | ||
256 | static iBool isBookmarkFolded_SidebarWidget_(const iSidebarWidget *d, const iBookmark *bm) { | ||
257 | while (bm->parentId) { | ||
258 | if (contains_IntSet(&d->closedFolders, bm->parentId)) { | ||
259 | return iTrue; | ||
260 | } | ||
261 | bm = get_Bookmarks(bookmarks_App(), bm->parentId); | ||
262 | } | ||
263 | return iFalse; | ||
264 | } | ||
265 | |||
214 | static void updateItems_SidebarWidget_(iSidebarWidget *d) { | 266 | static void updateItems_SidebarWidget_(iSidebarWidget *d) { |
215 | clear_ListWidget(d->list); | 267 | clear_ListWidget(d->list); |
216 | releaseChildren_Widget(d->blank); | 268 | releaseChildren_Widget(d->blank); |
@@ -334,12 +386,22 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
334 | iRegExp *remoteSourceTag = iClob(new_RegExp("\\b" remoteSource_BookmarkTag "\\b", caseSensitive_RegExpOption)); | 386 | iRegExp *remoteSourceTag = iClob(new_RegExp("\\b" remoteSource_BookmarkTag "\\b", caseSensitive_RegExpOption)); |
335 | iRegExp *remoteTag = iClob(new_RegExp("\\b" remote_BookmarkTag "\\b", caseSensitive_RegExpOption)); | 387 | iRegExp *remoteTag = iClob(new_RegExp("\\b" remote_BookmarkTag "\\b", caseSensitive_RegExpOption)); |
336 | iRegExp *linkSplitTag = iClob(new_RegExp("\\b" linkSplit_BookmarkTag "\\b", caseSensitive_RegExpOption)); | 388 | iRegExp *linkSplitTag = iClob(new_RegExp("\\b" linkSplit_BookmarkTag "\\b", caseSensitive_RegExpOption)); |
337 | iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), cmpTree_Bookmark_, NULL, NULL)) { | 389 | iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), cmpTree_Bookmark, NULL, NULL)) { |
338 | const iBookmark *bm = i.ptr; | 390 | const iBookmark *bm = i.ptr; |
391 | if (isBookmarkFolded_SidebarWidget_(d, bm)) { | ||
392 | continue; /* inside a closed folder */ | ||
393 | } | ||
339 | iSidebarItem *item = new_SidebarItem(); | 394 | iSidebarItem *item = new_SidebarItem(); |
340 | item->listItem.isDraggable = iTrue; | 395 | item->listItem.isDraggable = iTrue; |
396 | item->isBold = item->listItem.isDropTarget = isFolder_Bookmark(bm); | ||
341 | item->id = id_Bookmark(bm); | 397 | item->id = id_Bookmark(bm); |
342 | item->icon = bm->icon; | 398 | item->indent = depth_Bookmark(bm); |
399 | if (isFolder_Bookmark(bm)) { | ||
400 | item->icon = contains_IntSet(&d->closedFolders, item->id) ? 0x27e9 : 0xfe40; | ||
401 | } | ||
402 | else { | ||
403 | item->icon = bm->icon; | ||
404 | } | ||
343 | set_String(&item->url, &bm->url); | 405 | set_String(&item->url, &bm->url); |
344 | set_String(&item->label, &bm->title); | 406 | set_String(&item->label, &bm->title); |
345 | /* Icons for special tags. */ { | 407 | /* Icons for special tags. */ { |
@@ -384,8 +446,11 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
384 | { "---", 0, 0, NULL }, | 446 | { "---", 0, 0, NULL }, |
385 | { delete_Icon " " uiTextCaution_ColorEscape "${bookmark.delete}", 0, 0, "bookmark.delete" }, | 447 | { delete_Icon " " uiTextCaution_ColorEscape "${bookmark.delete}", 0, 0, "bookmark.delete" }, |
386 | { "---", 0, 0, NULL }, | 448 | { "---", 0, 0, NULL }, |
387 | { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } }, | 449 | { add_Icon " ${menu.newfolder}", 0, 0, "bookmarks.addfolder" }, |
388 | 14); | 450 | { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" }, |
451 | { "---", 0, 0, NULL }, | ||
452 | { upDownArrow_Icon " ${menu.sort.alpha}", 0, 0, "bookmark.sortfolder" } }, | ||
453 | 17); | ||
389 | break; | 454 | break; |
390 | } | 455 | } |
391 | case history_SidebarMode: { | 456 | case history_SidebarMode: { |
@@ -671,6 +736,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
671 | d->resizer = NULL; | 736 | d->resizer = NULL; |
672 | d->list = NULL; | 737 | d->list = NULL; |
673 | d->actions = NULL; | 738 | d->actions = NULL; |
739 | init_IntSet(&d->closedFolders); | ||
674 | /* On a phone, the right sidebar is used exclusively for Identities. */ | 740 | /* On a phone, the right sidebar is used exclusively for Identities. */ |
675 | const iBool isPhone = deviceType_App() == phone_AppDeviceType; | 741 | const iBool isPhone = deviceType_App() == phone_AppDeviceType; |
676 | if (!isPhone || d->side == left_SidebarSide) { | 742 | if (!isPhone || d->side == left_SidebarSide) { |
@@ -754,6 +820,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
754 | 820 | ||
755 | void deinit_SidebarWidget(iSidebarWidget *d) { | 821 | void deinit_SidebarWidget(iSidebarWidget *d) { |
756 | deinit_String(&d->cmdPrefix); | 822 | deinit_String(&d->cmdPrefix); |
823 | deinit_IntSet(&d->closedFolders); | ||
757 | } | 824 | } |
758 | 825 | ||
759 | iBool setButtonFont_SidebarWidget(iSidebarWidget *d, int font) { | 826 | iBool setButtonFont_SidebarWidget(iSidebarWidget *d, int font) { |
@@ -801,6 +868,17 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
801 | break; | 868 | break; |
802 | } | 869 | } |
803 | case bookmarks_SidebarMode: | 870 | case bookmarks_SidebarMode: |
871 | if (isEmpty_String(&item->url)) /* a folder */ { | ||
872 | if (contains_IntSet(&d->closedFolders, item->id)) { | ||
873 | remove_IntSet(&d->closedFolders, item->id); | ||
874 | } | ||
875 | else { | ||
876 | insert_IntSet(&d->closedFolders, item->id); | ||
877 | } | ||
878 | updateItems_SidebarWidget_(d); | ||
879 | break; | ||
880 | } | ||
881 | /* fall through */ | ||
804 | case history_SidebarMode: { | 882 | case history_SidebarMode: { |
805 | if (!isEmpty_String(&item->url)) { | 883 | if (!isEmpty_String(&item->url)) { |
806 | postCommandf_Root(get_Root(), "open fromsidebar:1 newtab:%d url:%s", | 884 | postCommandf_Root(get_Root(), "open fromsidebar:1 newtab:%d url:%s", |
@@ -1013,10 +1091,24 @@ static void bookmarkMoved_SidebarWidget_(iSidebarWidget *d, size_t index, size_t | |||
1013 | isLast ? numItems_ListWidget(d->list) - 1 | 1091 | isLast ? numItems_ListWidget(d->list) - 1 |
1014 | : beforeIndex); | 1092 | : beforeIndex); |
1015 | const iBookmark *dst = get_Bookmarks(bookmarks_App(), dstItem->id); | 1093 | const iBookmark *dst = get_Bookmarks(bookmarks_App(), dstItem->id); |
1094 | if (hasParent_Bookmark(dst, movingItem->id)) { | ||
1095 | return; | ||
1096 | } | ||
1016 | reorder_Bookmarks(bookmarks_App(), movingItem->id, dst->order + (isLast ? 1 : 0)); | 1097 | reorder_Bookmarks(bookmarks_App(), movingItem->id, dst->order + (isLast ? 1 : 0)); |
1098 | get_Bookmarks(bookmarks_App(), movingItem->id)->parentId = dst->parentId; | ||
1017 | updateItems_SidebarWidget_(d); | 1099 | updateItems_SidebarWidget_(d); |
1018 | /* Don't confuse the user: keep the dragged item in hover state. */ | 1100 | /* Don't confuse the user: keep the dragged item in hover state. */ |
1019 | setHoverItem_ListWidget(d->list, index < beforeIndex ? beforeIndex - 1 : beforeIndex); | 1101 | setHoverItem_ListWidget(d->list, index < beforeIndex ? beforeIndex - 1 : beforeIndex); |
1102 | postCommandf_App("bookmarks.changed nosidebar:%p", d); /* skip this sidebar since we updated already */ | ||
1103 | } | ||
1104 | |||
1105 | static void bookmarkMovedOntoFolder_SidebarWidget_(iSidebarWidget *d, size_t index, | ||
1106 | size_t folderIndex) { | ||
1107 | const iSidebarItem *movingItem = item_ListWidget(d->list, index); | ||
1108 | const iSidebarItem *dstItem = item_ListWidget(d->list, folderIndex); | ||
1109 | iBookmark *bm = get_Bookmarks(bookmarks_App(), movingItem->id); | ||
1110 | bm->parentId = dstItem->id; | ||
1111 | postCommand_App("bookmarks.changed"); | ||
1020 | } | 1112 | } |
1021 | 1113 | ||
1022 | static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { | 1114 | static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { |
@@ -1070,7 +1162,9 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1070 | } | 1162 | } |
1071 | else if (equal_Command(cmd, "bookmarks.changed") && (d->mode == bookmarks_SidebarMode || | 1163 | else if (equal_Command(cmd, "bookmarks.changed") && (d->mode == bookmarks_SidebarMode || |
1072 | d->mode == feeds_SidebarMode)) { | 1164 | d->mode == feeds_SidebarMode)) { |
1073 | updateItems_SidebarWidget_(d); | 1165 | if (pointerLabel_Command(cmd, "nosidebar") != d) { |
1166 | updateItems_SidebarWidget_(d); | ||
1167 | } | ||
1074 | } | 1168 | } |
1075 | else if (equal_Command(cmd, "idents.changed") && d->mode == identities_SidebarMode) { | 1169 | else if (equal_Command(cmd, "idents.changed") && d->mode == identities_SidebarMode) { |
1076 | updateItems_SidebarWidget_(d); | 1170 | updateItems_SidebarWidget_(d); |
@@ -1135,6 +1229,9 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1135 | } | 1229 | } |
1136 | else { | 1230 | else { |
1137 | /* Dragged onto a folder. */ | 1231 | /* Dragged onto a folder. */ |
1232 | bookmarkMovedOntoFolder_SidebarWidget_(d, | ||
1233 | argU32Label_Command(cmd, "arg"), | ||
1234 | argU32Label_Command(cmd, "onto")); | ||
1138 | } | 1235 | } |
1139 | return iTrue; | 1236 | return iTrue; |
1140 | } | 1237 | } |
@@ -1170,15 +1267,12 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1170 | setText_InputWidget(findChild_Widget(dlg, "bmed.icon"), | 1267 | setText_InputWidget(findChild_Widget(dlg, "bmed.icon"), |
1171 | collect_String(newUnicodeN_String(&bm->icon, 1))); | 1268 | collect_String(newUnicodeN_String(&bm->icon, 1))); |
1172 | } | 1269 | } |
1173 | setFlags_Widget(findChild_Widget(dlg, "bmed.tag.home"), | 1270 | setToggle_Widget(findChild_Widget(dlg, "bmed.tag.home"), |
1174 | selected_WidgetFlag, | 1271 | hasTag_Bookmark(bm, homepage_BookmarkTag)); |
1175 | hasTag_Bookmark(bm, homepage_BookmarkTag)); | 1272 | setToggle_Widget(findChild_Widget(dlg, "bmed.tag.remote"), |
1176 | setFlags_Widget(findChild_Widget(dlg, "bmed.tag.remote"), | 1273 | hasTag_Bookmark(bm, remoteSource_BookmarkTag)); |
1177 | selected_WidgetFlag, | 1274 | setToggle_Widget(findChild_Widget(dlg, "bmed.tag.linksplit"), |
1178 | hasTag_Bookmark(bm, remoteSource_BookmarkTag)); | 1275 | hasTag_Bookmark(bm, linkSplit_BookmarkTag)); |
1179 | setFlags_Widget(findChild_Widget(dlg, "bmed.tag.linksplit"), | ||
1180 | selected_WidgetFlag, | ||
1181 | hasTag_Bookmark(bm, linkSplit_BookmarkTag)); | ||
1182 | setCommandHandler_Widget(dlg, handleBookmarkEditorCommands_SidebarWidget_); | 1276 | setCommandHandler_Widget(dlg, handleBookmarkEditorCommands_SidebarWidget_); |
1183 | setFocus_Widget(findChild_Widget(dlg, "bmed.title")); | 1277 | setFocus_Widget(findChild_Widget(dlg, "bmed.title")); |
1184 | } | 1278 | } |
@@ -1225,6 +1319,16 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1225 | } | 1319 | } |
1226 | return iTrue; | 1320 | return iTrue; |
1227 | } | 1321 | } |
1322 | else if (isCommand_Widget(w, ev, "bookmark.sortfolder")) { | ||
1323 | const iSidebarItem *item = d->contextItem; | ||
1324 | if (d->mode == bookmarks_SidebarMode && item) { | ||
1325 | postCommandf_App("bookmarks.sort arg:%zu", | ||
1326 | item->listItem.isDropTarget | ||
1327 | ? item->id | ||
1328 | : get_Bookmarks(bookmarks_App(), item->id)->parentId); | ||
1329 | } | ||
1330 | return iTrue; | ||
1331 | } | ||
1228 | else if (equal_Command(cmd, "feeds.update.finished")) { | 1332 | else if (equal_Command(cmd, "feeds.update.finished")) { |
1229 | d->numUnreadEntries = argLabel_Command(cmd, "unread"); | 1333 | d->numUnreadEntries = argLabel_Command(cmd, "unread"); |
1230 | checkModeButtonLayout_SidebarWidget_(d); | 1334 | checkModeButtonLayout_SidebarWidget_(d); |
@@ -1638,7 +1742,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
1638 | fillRect_Paint(p, itemRect, bg); | 1742 | fillRect_Paint(p, itemRect, bg); |
1639 | } | 1743 | } |
1640 | else if (sidebar->mode == bookmarks_SidebarMode) { | 1744 | else if (sidebar->mode == bookmarks_SidebarMode) { |
1641 | if (d->icon == 0x2913) { /* TODO: Remote icon; meaning: is this in a folder? */ | 1745 | if (d->indent) /* remote icon */ { |
1642 | bg = uiBackgroundFolder_ColorId; | 1746 | bg = uiBackgroundFolder_ColorId; |
1643 | fillRect_Paint(p, itemRect, bg); | 1747 | fillRect_Paint(p, itemRect, bg); |
1644 | } | 1748 | } |
@@ -1740,11 +1844,13 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
1740 | } | 1844 | } |
1741 | else if (sidebar->mode == bookmarks_SidebarMode) { | 1845 | else if (sidebar->mode == bookmarks_SidebarMode) { |
1742 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | 1846 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) |
1743 | : uiText_ColorId; | 1847 | : d->listItem.isDropTarget ? uiHeading_ColorId : uiText_ColorId; |
1848 | /* The icon. */ | ||
1744 | iString str; | 1849 | iString str; |
1745 | init_String(&str); | 1850 | init_String(&str); |
1746 | appendChar_String(&str, d->icon ? d->icon : 0x1f588); | 1851 | appendChar_String(&str, d->icon ? d->icon : 0x1f588); |
1747 | const iRect iconArea = { addX_I2(pos, gap_UI), | 1852 | const int leftIndent = d->indent * gap_UI * 4; |
1853 | const iRect iconArea = { addX_I2(pos, gap_UI + leftIndent), | ||
1748 | init_I2(1.75f * lineHeight_Text(font), itemHeight) }; | 1854 | init_I2(1.75f * lineHeight_Text(font), itemHeight) }; |
1749 | drawCentered_Text(font, | 1855 | drawCentered_Text(font, |
1750 | iconArea, | 1856 | iconArea, |
diff --git a/src/ui/util.c b/src/ui/util.c index 7fa5d675..5b9f15a9 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -816,6 +816,14 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | |||
816 | setUserData_Object(menu, deepCopyMenuItems_(menu, items, n)); | 816 | setUserData_Object(menu, deepCopyMenuItems_(menu, items, n)); |
817 | addChild_Widget(parent, menu); | 817 | addChild_Widget(parent, menu); |
818 | iRelease(menu); /* owned by parent now */ | 818 | iRelease(menu); /* owned by parent now */ |
819 | /* Keyboard shortcuts still need to triggerable via the menu, although the items don't exist. */ { | ||
820 | for (size_t i = 0; i < n; i++) { | ||
821 | const iMenuItem *item = &items[i]; | ||
822 | if (item->key) { | ||
823 | addAction_Widget(menu, item->key, item->kmods, item->command); | ||
824 | } | ||
825 | } | ||
826 | } | ||
819 | #else | 827 | #else |
820 | /* Non-native custom popup menu. This may still be displayed inside a separate window. */ | 828 | /* Non-native custom popup menu. This may still be displayed inside a separate window. */ |
821 | setDrawBufferEnabled_Widget(menu, iTrue); | 829 | setDrawBufferEnabled_Widget(menu, iTrue); |
diff --git a/src/ui/window.c b/src/ui/window.c index 0863aa47..5941ef5f 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -120,6 +120,7 @@ static const iMenuItem viewMenuItems_[] = { | |||
120 | static iMenuItem bookmarksMenuItems_[] = { | 120 | static iMenuItem bookmarksMenuItems_[] = { |
121 | { "${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, | 121 | { "${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, |
122 | { "${menu.page.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" }, | 122 | { "${menu.page.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" }, |
123 | { "${menu.newfolder}", 0, 0, "bookmarks.addfolder" }, | ||
123 | { "---", 0, 0, NULL }, | 124 | { "---", 0, 0, NULL }, |
124 | { "${menu.import.links}", 0, 0, "bookmark.links confirm:1" }, | 125 | { "${menu.import.links}", 0, 0, "bookmark.links confirm:1" }, |
125 | { "---", 0, 0, NULL }, | 126 | { "---", 0, 0, NULL }, |
@@ -128,6 +129,8 @@ static iMenuItem bookmarksMenuItems_[] = { | |||
128 | { "${macos.menu.bookmarks.bytime}", 0, 0, "open url:about:bookmarks?created" }, | 129 | { "${macos.menu.bookmarks.bytime}", 0, 0, "open url:about:bookmarks?created" }, |
129 | { "${menu.feeds.entrylist}", 0, 0, "open url:about:feeds" }, | 130 | { "${menu.feeds.entrylist}", 0, 0, "open url:about:feeds" }, |
130 | { "---", 0, 0, NULL }, | 131 | { "---", 0, 0, NULL }, |
132 | { "${menu.sort.alpha}", 0, 0, "bookmarks.sort" }, | ||
133 | { "---", 0, 0, NULL }, | ||
131 | { "${menu.bookmarks.refresh}", 0, 0, "bookmarks.reload.remote" }, | 134 | { "${menu.bookmarks.refresh}", 0, 0, "bookmarks.reload.remote" }, |
132 | { "${menu.feeds.refresh}", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" }, | 135 | { "${menu.feeds.refresh}", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" }, |
133 | }; | 136 | }; |