summaryrefslogtreecommitdiff
path: root/src/ui/sidebarwidget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/sidebarwidget.c')
-rw-r--r--src/ui/sidebarwidget.c170
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
113iDefineObjectConstructionArgs(SidebarWidget, (enum iSidebarSide side), side) 114iDefineObjectConstructionArgs(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
119static int cmpTree_Bookmark_(const iBookmark **a, const iBookmark **b) { 120iBookmark *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
128iBool 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
139int 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
148int 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
141static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const char *label, 183static 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
256static 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
214static void updateItems_SidebarWidget_(iSidebarWidget *d) { 266static 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
755void deinit_SidebarWidget(iSidebarWidget *d) { 821void deinit_SidebarWidget(iSidebarWidget *d) {
756 deinit_String(&d->cmdPrefix); 822 deinit_String(&d->cmdPrefix);
823 deinit_IntSet(&d->closedFolders);
757} 824}
758 825
759iBool setButtonFont_SidebarWidget(iSidebarWidget *d, int font) { 826iBool 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
1105static 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
1022static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { 1114static 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,