summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-09-07 10:46:07 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-09-07 10:46:07 +0300
commit8ddbf90ecd69da7e24237081413f0620641708dc (patch)
treef890d6850917d50dae20317f3d284d702002a752 /src
parent77d172e3db30cdbecc79024da091903cfa7f349b (diff)
LookupWidget: Show results for visited URLs
Diffstat (limited to 'src')
-rw-r--r--src/ui/lookupwidget.c69
-rw-r--r--src/visited.c50
2 files changed, 95 insertions, 24 deletions
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c
index c1c52708..74f394e6 100644
--- a/src/ui/lookupwidget.c
+++ b/src/ui/lookupwidget.c
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
27#include "util.h" 27#include "util.h"
28#include "command.h" 28#include "command.h"
29#include "bookmarks.h" 29#include "bookmarks.h"
30#include "visited.h"
30#include "gmutil.h" 31#include "gmutil.h"
31#include "app.h" 32#include "app.h"
32 33
@@ -38,11 +39,13 @@ iDeclareType(LookupJob)
38 39
39struct Impl_LookupJob { 40struct Impl_LookupJob {
40 iRegExp *term; 41 iRegExp *term;
42 iTime now;
41 iPtrArray results; 43 iPtrArray results;
42}; 44};
43 45
44static void init_LookupJob(iLookupJob *d) { 46static void init_LookupJob(iLookupJob *d) {
45 d->term = NULL; 47 d->term = NULL;
48 initCurrent_Time(&d->now);
46 init_PtrArray(&d->results); 49 init_PtrArray(&d->results);
47} 50}
48 51
@@ -102,7 +105,7 @@ static void draw_LookupItem_(iLookupItem *d, iPaint *p, iRect rect, const iListW
102 const iInt2 size = measure_Text(d->font, cstr_String(&d->text)); 105 const iInt2 size = measure_Text(d->font, cstr_String(&d->text));
103 iInt2 pos = init_I2(left_Rect(rect) + 3 * gap_UI, mid_Rect(rect).y - size.y / 2); 106 iInt2 pos = init_I2(left_Rect(rect) + 3 * gap_UI, mid_Rect(rect).y - size.y / 2);
104 if (d->listItem.isSeparator) { 107 if (d->listItem.isSeparator) {
105 pos.y = bottom_Rect(rect) - lineHeight_Text(d->font) - gap_UI; 108 pos.y = bottom_Rect(rect) - lineHeight_Text(d->font);
106 } 109 }
107 drawRange_Text(d->font, pos, fg, range_String(&d->text)); 110 drawRange_Text(d->font, pos, fg, range_String(&d->text));
108} 111}
@@ -147,18 +150,31 @@ static float bookmarkRelevance_LookupJob_(const iLookupJob *d, const iBookmark *
147 return h + iMax(p, t) + 2 * g; /* extra weight for tags */ 150 return h + iMax(p, t) + 2 * g; /* extra weight for tags */
148} 151}
149 152
153static float visitedRelevance_LookupJob_(const iLookupJob *d, const iVisitedUrl *vis) {
154 iUrl parts;
155 init_Url(&parts, &vis->url);
156 const float h = scoreMatch_(d->term, parts.host);
157 const float p = scoreMatch_(d->term, parts.path);
158 const double age = secondsSince_Time(&d->now, &vis->when) / 3600.0 / 24.0; /* days */
159 return iMax(h, p) / (age + 1); /* extra weight for recency */
160}
161
150static iBool matchBookmark_LookupJob_(void *context, const iBookmark *bm) { 162static iBool matchBookmark_LookupJob_(void *context, const iBookmark *bm) {
151 return bookmarkRelevance_LookupJob_(context, bm) > 0; 163 return bookmarkRelevance_LookupJob_(context, bm) > 0;
152} 164}
153 165
154static void searchBookmarks_LookupJob_(iLookupJob *d) { 166static void searchBookmarks_LookupJob_(iLookupJob *d) {
155 /* Note: Called in a background thread. */ 167 /* Note: Called in a background thread. */
168 /* TODO: Thread safety! What if a bookmark gets deleted while its being accessed here? */
156 iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), NULL, matchBookmark_LookupJob_, d)) { 169 iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), NULL, matchBookmark_LookupJob_, d)) {
157 const iBookmark *bm = i.ptr; 170 const iBookmark *bm = i.ptr;
158 iLookupResult * res = new_LookupResult(); 171 iLookupResult * res = new_LookupResult();
159 res->type = bookmark_LookupResultType; 172 res->type = bookmark_LookupResultType;
160 res->relevance = bookmarkRelevance_LookupJob_(d, bm); 173 res->relevance = bookmarkRelevance_LookupJob_(d, bm);
161 set_String(&res->label, &bm->title); 174 appendChar_String(&res->label, bm->icon);
175 appendChar_String(&res->label, ' ');
176 append_String(&res->label, &bm->title);
177// set_String(&res->label, &bm->title);
162// appendFormat_String(&res->label, " (%f)", res->relevance); 178// appendFormat_String(&res->label, " (%f)", res->relevance);
163 set_String(&res->url, &bm->url); 179 set_String(&res->url, &bm->url);
164 res->when = bm->when; 180 res->when = bm->when;
@@ -166,6 +182,24 @@ static void searchBookmarks_LookupJob_(iLookupJob *d) {
166 } 182 }
167} 183}
168 184
185static void searchVisited_LookupJob_(iLookupJob *d) {
186 /* Note: Called in a background thread. */
187 /* TODO: Thread safety! Visited URLs may be deleted while being accessed here. */
188 iConstForEach(PtrArray, i, list_Visited(visited_App(), 0)) {
189 const iVisitedUrl *vis = i.ptr;
190 const float relevance = visitedRelevance_LookupJob_(d, vis);
191 if (relevance > 0) {
192 iLookupResult *res = new_LookupResult();
193 res->type = history_LookupResultType;
194 res->relevance = relevance;
195 set_String(&res->label, &vis->url);
196 set_String(&res->url, &vis->url);
197 res->when = vis->when;
198 pushBack_PtrArray(&d->results, res);
199 }
200 }
201}
202
169static iThreadResult worker_LookupWidget_(iThread *thread) { 203static iThreadResult worker_LookupWidget_(iThread *thread) {
170 iLookupWidget *d = userData_Thread(thread); 204 iLookupWidget *d = userData_Thread(thread);
171 printf("[LookupWidget] worker is running\n"); fflush(stdout); 205 printf("[LookupWidget] worker is running\n"); fflush(stdout);
@@ -193,7 +227,6 @@ static iThreadResult worker_LookupWidget_(iThread *thread) {
193 isFirst = iFalse; 227 isFirst = iFalse;
194 } 228 }
195 iAssert(!isEmpty_String(pattern)); 229 iAssert(!isEmpty_String(pattern));
196// printf("{%s}\n", cstr_String(pattern));
197 job->term = new_RegExp(cstr_String(pattern), caseInsensitive_RegExpOption); 230 job->term = new_RegExp(cstr_String(pattern), caseInsensitive_RegExpOption);
198 delete_String(pattern); 231 delete_String(pattern);
199 } 232 }
@@ -201,6 +234,7 @@ static iThreadResult worker_LookupWidget_(iThread *thread) {
201 unlock_Mutex(d->mtx); 234 unlock_Mutex(d->mtx);
202 /* Do the lookup. */ { 235 /* Do the lookup. */ {
203 searchBookmarks_LookupJob_(job); 236 searchBookmarks_LookupJob_(job);
237 searchVisited_LookupJob_(job);
204 } 238 }
205 /* Submit the result. */ 239 /* Submit the result. */
206 lock_Mutex(d->mtx); 240 lock_Mutex(d->mtx);
@@ -226,7 +260,7 @@ void init_LookupWidget(iLookupWidget *d) {
226 setId_Widget(w, "lookup"); 260 setId_Widget(w, "lookup");
227 setFlags_Widget(w, focusable_WidgetFlag | resizeChildren_WidgetFlag, iTrue); 261 setFlags_Widget(w, focusable_WidgetFlag | resizeChildren_WidgetFlag, iTrue);
228 d->list = addChild_Widget(w, iClob(new_ListWidget())); 262 d->list = addChild_Widget(w, iClob(new_ListWidget()));
229 setItemHeight_ListWidget(d->list, lineHeight_Text(default_FontId) * 2); 263 setItemHeight_ListWidget(d->list, lineHeight_Text(uiContent_FontId) * 1.25f);
230 d->cursor = iInvalidPos; 264 d->cursor = iInvalidPos;
231 d->work = new_Thread(worker_LookupWidget_); 265 d->work = new_Thread(worker_LookupWidget_);
232 setUserData_Thread(d->work, d); 266 setUserData_Thread(d->work, d);
@@ -321,23 +355,34 @@ static void presentResults_LookupWidget_(iLookupWidget *d) {
321 iLookupItem *item = new_LookupItem(NULL); 355 iLookupItem *item = new_LookupItem(NULL);
322 item->listItem.isSeparator = iTrue; 356 item->listItem.isSeparator = iTrue;
323 item->fg = uiHeading_ColorId; 357 item->fg = uiHeading_ColorId;
324 item->font = default_FontId; 358 item->font = uiLabel_FontId;
325 format_String(&item->text, "%s", cstr_LookupResultType(res->type)); 359 format_String(&item->text, "%s", cstr_LookupResultType(res->type));
326 addItem_ListWidget(d->list, item); 360 addItem_ListWidget(d->list, item);
327 iRelease(item); 361 iRelease(item);
328 lastType = res->type; 362 lastType = res->type;
329 } 363 }
330 iLookupItem *item = new_LookupItem(res); 364 iLookupItem *item = new_LookupItem(res);
365 const char *url = cstr_String(&res->url);
366 if (startsWithCase_String(&res->url, "gemini://")) {
367 url += 9;
368 }
331 switch (res->type) { 369 switch (res->type) {
332 case bookmark_LookupResultType: { 370 case bookmark_LookupResultType: {
333 item->fg = uiTextStrong_ColorId; 371 item->fg = uiTextStrong_ColorId;
334 item->font = default_FontId; 372 item->font = uiLabel_FontId;
335 const char *url = cstr_String(&res->url); 373 format_String(&item->text,
336 if (startsWithCase_String(&res->url, "gemini://")) { 374 "%s %s\u2014 %s",
337 url += 9; 375 cstr_String(&res->label),
338 } 376 uiText_ColorEscape,
339 format_String(&item->text, "%s\n%s Open %s", cstr_String(&res->label), 377 url);
340 uiText_ColorEscape, url); 378 format_String(&item->command, "open url:%s", cstr_String(&res->url));
379 break;
380 }
381 case history_LookupResultType: {
382 item->fg = uiText_ColorId;
383 item->font = uiContent_FontId;
384 format_String(&item->text, "%s \u2014 ", url);
385 append_String(&item->text, collect_String(format_Time(&res->when, "%b %d, %Y")));
341 format_String(&item->command, "open url:%s", cstr_String(&res->url)); 386 format_String(&item->command, "open url:%s", cstr_String(&res->url));
342 break; 387 break;
343 } 388 }
diff --git a/src/visited.c b/src/visited.c
index 912a6318..b0b12beb 100644
--- a/src/visited.c
+++ b/src/visited.c
@@ -24,6 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
24#include "app.h" 24#include "app.h"
25 25
26#include <the_Foundation/file.h> 26#include <the_Foundation/file.h>
27#include <the_Foundation/mutex.h>
27#include <the_Foundation/path.h> 28#include <the_Foundation/path.h>
28#include <the_Foundation/ptrarray.h> 29#include <the_Foundation/ptrarray.h>
29#include <the_Foundation/sortedarray.h> 30#include <the_Foundation/sortedarray.h>
@@ -51,24 +52,30 @@ static int cmpNewer_VisitedUrl_(const void *insert, const void *existing) {
51/*----------------------------------------------------------------------------------------------*/ 52/*----------------------------------------------------------------------------------------------*/
52 53
53struct Impl_Visited { 54struct Impl_Visited {
55 iMutex *mtx;
54 iSortedArray visited; 56 iSortedArray visited;
55}; 57};
56 58
57iDefineTypeConstruction(Visited) 59iDefineTypeConstruction(Visited)
58 60
59void init_Visited(iVisited *d) { 61void init_Visited(iVisited *d) {
62 d->mtx = new_Mutex();
60 init_SortedArray(&d->visited, sizeof(iVisitedUrl), cmpUrl_VisitedUrl_); 63 init_SortedArray(&d->visited, sizeof(iVisitedUrl), cmpUrl_VisitedUrl_);
61} 64}
62 65
63void deinit_Visited(iVisited *d) { 66void deinit_Visited(iVisited *d) {
64 clear_Visited(d); 67 iGuardMutex(d->mtx, {
65 deinit_SortedArray(&d->visited); 68 clear_Visited(d);
69 deinit_SortedArray(&d->visited);
70 });
71 delete_Mutex(d->mtx);
66} 72}
67 73
68void save_Visited(const iVisited *d, const char *dirPath) { 74void save_Visited(const iVisited *d, const char *dirPath) {
69 iString *line = new_String(); 75 iString *line = new_String();
70 iFile *f = newCStr_File(concatPath_CStr(dirPath, "visited.txt")); 76 iFile *f = newCStr_File(concatPath_CStr(dirPath, "visited.txt"));
71 if (open_File(f, writeOnly_FileMode | text_FileMode)) { 77 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
78 lock_Mutex(d->mtx);
72 iConstForEach(Array, i, &d->visited.values) { 79 iConstForEach(Array, i, &d->visited.values) {
73 const iVisitedUrl *item = i.value; 80 const iVisitedUrl *item = i.value;
74 iDate date; 81 iDate date;
@@ -84,6 +91,7 @@ void save_Visited(const iVisited *d, const char *dirPath) {
84 cstr_String(&item->url)); 91 cstr_String(&item->url));
85 writeData_File(f, cstr_String(line), size_String(line)); 92 writeData_File(f, cstr_String(line), size_String(line));
86 } 93 }
94 unlock_Mutex(d->mtx);
87 } 95 }
88 iRelease(f); 96 iRelease(f);
89 delete_String(line); 97 delete_String(line);
@@ -92,6 +100,7 @@ void save_Visited(const iVisited *d, const char *dirPath) {
92void load_Visited(iVisited *d, const char *dirPath) { 100void load_Visited(iVisited *d, const char *dirPath) {
93 iFile *f = newCStr_File(concatPath_CStr(dirPath, "visited.txt")); 101 iFile *f = newCStr_File(concatPath_CStr(dirPath, "visited.txt"));
94 if (open_File(f, readOnly_FileMode | text_FileMode)) { 102 if (open_File(f, readOnly_FileMode | text_FileMode)) {
103 lock_Mutex(d->mtx);
95 const iRangecc src = range_Block(collect_Block(readAll_File(f))); 104 const iRangecc src = range_Block(collect_Block(readAll_File(f)));
96 iRangecc line = iNullRange; 105 iRangecc line = iNullRange;
97 iTime now; 106 iTime now;
@@ -111,15 +120,18 @@ void load_Visited(iVisited *d, const char *dirPath) {
111 initRange_String(&item.url, (iRangecc){ line.start + 20, line.end }); 120 initRange_String(&item.url, (iRangecc){ line.start + 20, line.end });
112 insert_SortedArray(&d->visited, &item); 121 insert_SortedArray(&d->visited, &item);
113 } 122 }
123 unlock_Mutex(d->mtx);
114 } 124 }
115 iRelease(f); 125 iRelease(f);
116} 126}
117 127
118void clear_Visited(iVisited *d) { 128void clear_Visited(iVisited *d) {
129 lock_Mutex(d->mtx);
119 iForEach(Array, v, &d->visited.values) { 130 iForEach(Array, v, &d->visited.values) {
120 deinit_VisitedUrl(v.value); 131 deinit_VisitedUrl(v.value);
121 } 132 }
122 clear_SortedArray(&d->visited); 133 clear_SortedArray(&d->visited);
134 unlock_Mutex(d->mtx);
123} 135}
124 136
125static size_t find_Visited_(const iVisited *d, const iString *url) { 137static size_t find_Visited_(const iVisited *d, const iString *url) {
@@ -127,8 +139,10 @@ static size_t find_Visited_(const iVisited *d, const iString *url) {
127 init_VisitedUrl(&visit); 139 init_VisitedUrl(&visit);
128 set_String(&visit.url, url); 140 set_String(&visit.url, url);
129 size_t pos = iInvalidPos; 141 size_t pos = iInvalidPos;
130 locate_SortedArray(&d->visited, &visit, &pos); 142 iGuardMutex(d->mtx, {
131 deinit_VisitedUrl(&visit); 143 locate_SortedArray(&d->visited, &visit, &pos);
144 deinit_VisitedUrl(&visit);
145 });
132 return pos; 146 return pos;
133} 147}
134 148
@@ -137,23 +151,28 @@ void visitUrl_Visited(iVisited *d, const iString *url) {
137 init_VisitedUrl(&visit); 151 init_VisitedUrl(&visit);
138 set_String(&visit.url, url); 152 set_String(&visit.url, url);
139 size_t pos; 153 size_t pos;
154 lock_Mutex(d->mtx);
140 if (locate_SortedArray(&d->visited, &visit, &pos)) { 155 if (locate_SortedArray(&d->visited, &visit, &pos)) {
141 iVisitedUrl *old = at_SortedArray(&d->visited, pos); 156 iVisitedUrl *old = at_SortedArray(&d->visited, pos);
142 if (cmpNewer_VisitedUrl_(&visit, old)) { 157 if (cmpNewer_VisitedUrl_(&visit, old)) {
143 old->when = visit.when; 158 old->when = visit.when;
159 unlock_Mutex(d->mtx);
144 deinit_VisitedUrl(&visit); 160 deinit_VisitedUrl(&visit);
145 return; 161 return;
146 } 162 }
147 } 163 }
148 insert_SortedArray(&d->visited, &visit); 164 insert_SortedArray(&d->visited, &visit);
165 unlock_Mutex(d->mtx);
149} 166}
150 167
151void removeUrl_Visited(iVisited *d, const iString *url) { 168void removeUrl_Visited(iVisited *d, const iString *url) {
152 size_t pos = find_Visited_(d, url); 169 iGuardMutex(d->mtx, {
153 if (pos != iInvalidPos) { 170 size_t pos = find_Visited_(d, url);
154 deinit_VisitedUrl(at_SortedArray(&d->visited, pos)); 171 if (pos != iInvalidPos) {
155 remove_Array(&d->visited.values, pos); 172 deinit_VisitedUrl(at_SortedArray(&d->visited, pos));
156 } 173 remove_Array(&d->visited.values, pos);
174 }
175 });
157} 176}
158 177
159iTime urlVisitTime_Visited(const iVisited *d, const iString *url) { 178iTime urlVisitTime_Visited(const iVisited *d, const iString *url) {
@@ -161,9 +180,11 @@ iTime urlVisitTime_Visited(const iVisited *d, const iString *url) {
161 size_t pos; 180 size_t pos;
162 iZap(item); 181 iZap(item);
163 initCopy_String(&item.url, url); 182 initCopy_String(&item.url, url);
183 lock_Mutex(d->mtx);
164 if (locate_SortedArray(&d->visited, &item, &pos)) { 184 if (locate_SortedArray(&d->visited, &item, &pos)) {
165 item.when = ((const iVisitedUrl *) constAt_SortedArray(&d->visited, pos))->when; 185 item.when = ((const iVisitedUrl *) constAt_SortedArray(&d->visited, pos))->when;
166 } 186 }
187 unlock_Mutex(d->mtx);
167 deinit_String(&item.url); 188 deinit_String(&item.url);
168 return item.when; 189 return item.when;
169} 190}
@@ -175,9 +196,14 @@ static int cmpWhenDescending_VisitedUrlPtr_(const void *a, const void *b) {
175 196
176const iArray *list_Visited(const iVisited *d, size_t count) { 197const iArray *list_Visited(const iVisited *d, size_t count) {
177 iPtrArray *urls = collectNew_PtrArray(); 198 iPtrArray *urls = collectNew_PtrArray();
178 iConstForEach(Array, i, &d->visited.values) { 199 iGuardMutex(d->mtx, {
179 pushBack_PtrArray(urls, i.value); 200 iConstForEach(Array, i, &d->visited.values) {
180 } 201 pushBack_PtrArray(urls, i.value);
202 }
203 });
181 sort_Array(urls, cmpWhenDescending_VisitedUrlPtr_); 204 sort_Array(urls, cmpWhenDescending_VisitedUrlPtr_);
205 if (count > 0 && size_Array(urls) > count) {
206 resize_Array(urls, count);
207 }
182 return urls; 208 return urls;
183} 209}