diff options
Diffstat (limited to 'src/ui/sidebarwidget.c')
-rw-r--r-- | src/ui/sidebarwidget.c | 170 |
1 files changed, 138 insertions, 32 deletions
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, |