diff options
-rw-r--r-- | src/app.c | 15 | ||||
-rw-r--r-- | src/app.h | 2 | ||||
-rw-r--r-- | src/history.c | 42 | ||||
-rw-r--r-- | src/history.h | 6 | ||||
-rw-r--r-- | src/ui/lookupwidget.c | 64 |
5 files changed, 114 insertions, 15 deletions
@@ -254,13 +254,24 @@ static iBool loadState_App_(iApp *d) { | |||
254 | return iFalse; | 254 | return iFalse; |
255 | } | 255 | } |
256 | 256 | ||
257 | iObjectList *listDocuments_App(void) { | ||
258 | iObjectList *docs = new_ObjectList(); | ||
259 | const iWidget *tabs = findWidget_App("doctabs"); | ||
260 | iForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) { | ||
261 | if (isInstance_Object(i.object, &Class_DocumentWidget)) { | ||
262 | pushBack_ObjectList(docs, i.object); | ||
263 | } | ||
264 | } | ||
265 | return docs; | ||
266 | } | ||
267 | |||
257 | static void saveState_App_(const iApp *d) { | 268 | static void saveState_App_(const iApp *d) { |
269 | iUnused(d); | ||
258 | iFile *f = newCStr_File(concatPath_CStr(dataDir_App_, stateFileName_App_)); | 270 | iFile *f = newCStr_File(concatPath_CStr(dataDir_App_, stateFileName_App_)); |
259 | if (open_File(f, writeOnly_FileMode)) { | 271 | if (open_File(f, writeOnly_FileMode)) { |
260 | writeData_File(f, magicState_App_, 4); | 272 | writeData_File(f, magicState_App_, 4); |
261 | write32_File(f, 0); /* version */ | 273 | write32_File(f, 0); /* version */ |
262 | iWidget *tabs = findChild_Widget(d->window->root, "doctabs"); | 274 | iConstForEach(ObjectList, i, iClob(listDocuments_App())) { |
263 | iConstForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) { | ||
264 | if (isInstance_Object(i.object, &Class_DocumentWidget)) { | 275 | if (isInstance_Object(i.object, &Class_DocumentWidget)) { |
265 | writeData_File(f, magicTabDocument_App_, 4); | 276 | writeData_File(f, magicTabDocument_App_, 4); |
266 | write8_File(f, document_App() == i.object ? 1 : 0); | 277 | write8_File(f, document_App() == i.object ? 1 : 0); |
@@ -24,6 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
24 | 24 | ||
25 | /* Application core: event loop, base event processing, audio synth. */ | 25 | /* Application core: event loop, base event processing, audio synth. */ |
26 | 26 | ||
27 | #include <the_Foundation/objectlist.h> | ||
27 | #include <the_Foundation/string.h> | 28 | #include <the_Foundation/string.h> |
28 | #include <the_Foundation/time.h> | 29 | #include <the_Foundation/time.h> |
29 | 30 | ||
@@ -64,6 +65,7 @@ iGmCerts * certs_App (void); | |||
64 | iVisited * visited_App (void); | 65 | iVisited * visited_App (void); |
65 | iBookmarks * bookmarks_App (void); | 66 | iBookmarks * bookmarks_App (void); |
66 | iDocumentWidget * document_App (void); | 67 | iDocumentWidget * document_App (void); |
68 | iObjectList * listDocuments_App (void); | ||
67 | iDocumentWidget * document_Command (const char *cmd); | 69 | iDocumentWidget * document_Command (const char *cmd); |
68 | iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf); | 70 | iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf); |
69 | 71 | ||
diff --git a/src/history.c b/src/history.c index 63fa1104..57e556f7 100644 --- a/src/history.c +++ b/src/history.c | |||
@@ -67,8 +67,10 @@ void init_History(iHistory *d) { | |||
67 | } | 67 | } |
68 | 68 | ||
69 | void deinit_History(iHistory *d) { | 69 | void deinit_History(iHistory *d) { |
70 | clear_History(d); | 70 | iGuardMutex(d->mtx, { |
71 | deinit_Array(&d->recent); | 71 | clear_History(d); |
72 | deinit_Array(&d->recent); | ||
73 | }); | ||
72 | delete_Mutex(d->mtx); | 74 | delete_Mutex(d->mtx); |
73 | } | 75 | } |
74 | 76 | ||
@@ -249,3 +251,39 @@ void setCachedResponse_History(iHistory *d, const iGmResponse *response) { | |||
249 | } | 251 | } |
250 | unlock_Mutex(d->mtx); | 252 | unlock_Mutex(d->mtx); |
251 | } | 253 | } |
254 | |||
255 | const iStringArray *searchContents_History(const iHistory *d, const iRegExp *pattern) { | ||
256 | iStringArray *urls = iClob(new_StringArray()); | ||
257 | lock_Mutex(d->mtx); | ||
258 | iConstForEach(Array, i, &d->recent) { | ||
259 | const iRecentUrl *url = i.value; | ||
260 | const iGmResponse *resp = url->cachedResponse; | ||
261 | if (resp && category_GmStatusCode(resp->statusCode) == categorySuccess_GmStatusCode) { | ||
262 | if (indexOfCStrSc_String(&resp->meta, "text/", &iCaseInsensitive) == iInvalidPos) { | ||
263 | continue; | ||
264 | } | ||
265 | iRegExpMatch m; | ||
266 | init_RegExpMatch(&m); | ||
267 | if (matchRange_RegExp(pattern, range_Block(&resp->body), &m)) { | ||
268 | iString entry; | ||
269 | init_String(&entry); | ||
270 | iRangei cap = m.range; | ||
271 | cap.start = iMax(cap.start - 4, 0); | ||
272 | cap.end = iMin(cap.end + 10, (int) size_Block(&resp->body)); | ||
273 | iString content; | ||
274 | initRange_String(&content, (iRangecc){ m.subject + cap.start, m.subject + cap.end }); | ||
275 | /* This needs cleaning up; highlight the matched word. */ { | ||
276 | |||
277 | } | ||
278 | format_String(&entry, "match len:%zu str:%s", size_String(&content), cstr_String(&content)); | ||
279 | deinit_String(&content); | ||
280 | //appendRange_String(&entry, ); | ||
281 | appendFormat_String(&entry, " url:%s", cstr_String(&url->url)); | ||
282 | pushBack_StringArray(urls, &entry); | ||
283 | deinit_String(&entry); | ||
284 | } | ||
285 | } | ||
286 | } | ||
287 | unlock_Mutex(d->mtx); | ||
288 | return urls; | ||
289 | } | ||
diff --git a/src/history.h b/src/history.h index 22fe8bf8..41005d02 100644 --- a/src/history.h +++ b/src/history.h | |||
@@ -24,8 +24,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
24 | 24 | ||
25 | #include "gmrequest.h" | 25 | #include "gmrequest.h" |
26 | 26 | ||
27 | #include <the_Foundation/array.h> | 27 | #include <the_Foundation/ptrarray.h> |
28 | #include <the_Foundation/regexp.h> | ||
28 | #include <the_Foundation/string.h> | 29 | #include <the_Foundation/string.h> |
30 | #include <the_Foundation/stringarray.h> | ||
29 | #include <the_Foundation/time.h> | 31 | #include <the_Foundation/time.h> |
30 | 32 | ||
31 | iDeclareType(RecentUrl) | 33 | iDeclareType(RecentUrl) |
@@ -55,6 +57,8 @@ iRecentUrl *recentUrl_History (iHistory *, size_t pos); | |||
55 | iRecentUrl *mostRecentUrl_History (iHistory *); | 57 | iRecentUrl *mostRecentUrl_History (iHistory *); |
56 | iRecentUrl *findUrl_History (iHistory *, const iString *url); | 58 | iRecentUrl *findUrl_History (iHistory *, const iString *url); |
57 | 59 | ||
60 | const iStringArray * searchContents_History (const iHistory *, const iRegExp *pattern); /* chronologically ascending */ | ||
61 | |||
58 | const iString * | 62 | const iString * |
59 | url_History (const iHistory *, size_t pos); | 63 | url_History (const iHistory *, size_t pos); |
60 | const iRecentUrl * | 64 | const iRecentUrl * |
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c index 74f394e6..52f3d864 100644 --- a/src/ui/lookupwidget.c +++ b/src/ui/lookupwidget.c | |||
@@ -21,12 +21,14 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
22 | 22 | ||
23 | #include "lookupwidget.h" | 23 | #include "lookupwidget.h" |
24 | #include "documentwidget.h" | ||
24 | #include "lookup.h" | 25 | #include "lookup.h" |
25 | #include "listwidget.h" | 26 | #include "listwidget.h" |
26 | #include "inputwidget.h" | 27 | #include "inputwidget.h" |
27 | #include "util.h" | 28 | #include "util.h" |
28 | #include "command.h" | 29 | #include "command.h" |
29 | #include "bookmarks.h" | 30 | #include "bookmarks.h" |
31 | #include "history.h" | ||
30 | #include "visited.h" | 32 | #include "visited.h" |
31 | #include "gmutil.h" | 33 | #include "gmutil.h" |
32 | #include "app.h" | 34 | #include "app.h" |
@@ -40,12 +42,14 @@ iDeclareType(LookupJob) | |||
40 | struct Impl_LookupJob { | 42 | struct Impl_LookupJob { |
41 | iRegExp *term; | 43 | iRegExp *term; |
42 | iTime now; | 44 | iTime now; |
45 | iObjectList *docs; | ||
43 | iPtrArray results; | 46 | iPtrArray results; |
44 | }; | 47 | }; |
45 | 48 | ||
46 | static void init_LookupJob(iLookupJob *d) { | 49 | static void init_LookupJob(iLookupJob *d) { |
47 | d->term = NULL; | 50 | d->term = NULL; |
48 | initCurrent_Time(&d->now); | 51 | initCurrent_Time(&d->now); |
52 | d->docs = NULL; | ||
49 | init_PtrArray(&d->results); | 53 | init_PtrArray(&d->results); |
50 | } | 54 | } |
51 | 55 | ||
@@ -54,6 +58,7 @@ static void deinit_LookupJob(iLookupJob *d) { | |||
54 | delete_LookupResult(i.ptr); | 58 | delete_LookupResult(i.ptr); |
55 | } | 59 | } |
56 | deinit_PtrArray(&d->results); | 60 | deinit_PtrArray(&d->results); |
61 | iRelease(d->docs); | ||
57 | iRelease(d->term); | 62 | iRelease(d->term); |
58 | } | 63 | } |
59 | 64 | ||
@@ -125,7 +130,8 @@ struct Impl_LookupWidget { | |||
125 | iThread * work; | 130 | iThread * work; |
126 | iCondition jobAvailable; /* wakes up the work thread */ | 131 | iCondition jobAvailable; /* wakes up the work thread */ |
127 | iMutex * mtx; | 132 | iMutex * mtx; |
128 | iString nextJob; | 133 | iString pendingTerm; |
134 | iObjectList *pendingDocs; | ||
129 | iLookupJob * finishedJob; | 135 | iLookupJob * finishedJob; |
130 | }; | 136 | }; |
131 | 137 | ||
@@ -200,13 +206,37 @@ static void searchVisited_LookupJob_(iLookupJob *d) { | |||
200 | } | 206 | } |
201 | } | 207 | } |
202 | 208 | ||
209 | static void searchHistory_LookupJob_(iLookupJob *d) { | ||
210 | /* Note: Called in a background thread. */ | ||
211 | size_t index = 0; | ||
212 | iForEach(ObjectList, i, d->docs) { | ||
213 | iConstForEach(StringArray, j, | ||
214 | searchContents_History(history_DocumentWidget(i.object), d->term)) { | ||
215 | const char *match = cstr_String(j.value); | ||
216 | const size_t matchLen = argLabel_Command(match, "len"); | ||
217 | iRangecc text; | ||
218 | text.start = strstr(match, " str:") + 5; | ||
219 | text.end = text.start + matchLen; | ||
220 | const char *url = strstr(text.end, " url:") + 5; | ||
221 | iLookupResult *res = new_LookupResult(); | ||
222 | res->type = content_LookupResultType; | ||
223 | res->relevance = ++index; /* most recent comes last */ | ||
224 | setCStr_String(&res->label, "..."); | ||
225 | appendRange_String(&res->label, text); | ||
226 | appendCStr_String(&res->label, "..."); | ||
227 | setCStr_String(&res->url, url); | ||
228 | pushBack_PtrArray(&d->results, res); | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | |||
203 | static iThreadResult worker_LookupWidget_(iThread *thread) { | 233 | static iThreadResult worker_LookupWidget_(iThread *thread) { |
204 | iLookupWidget *d = userData_Thread(thread); | 234 | iLookupWidget *d = userData_Thread(thread); |
205 | printf("[LookupWidget] worker is running\n"); fflush(stdout); | 235 | printf("[LookupWidget] worker is running\n"); fflush(stdout); |
206 | lock_Mutex(d->mtx); | 236 | lock_Mutex(d->mtx); |
207 | for (;;) { | 237 | for (;;) { |
208 | wait_Condition(&d->jobAvailable, d->mtx); | 238 | wait_Condition(&d->jobAvailable, d->mtx); |
209 | if (isEmpty_String(&d->nextJob)) { | 239 | if (isEmpty_String(&d->pendingTerm)) { |
210 | break; /* Time to quit. */ | 240 | break; /* Time to quit. */ |
211 | } | 241 | } |
212 | iLookupJob *job = new_LookupJob(); | 242 | iLookupJob *job = new_LookupJob(); |
@@ -214,7 +244,7 @@ static iThreadResult worker_LookupWidget_(iThread *thread) { | |||
214 | iString *pattern = new_String(); | 244 | iString *pattern = new_String(); |
215 | iRangecc word = iNullRange; | 245 | iRangecc word = iNullRange; |
216 | iBool isFirst = iTrue; | 246 | iBool isFirst = iTrue; |
217 | while (nextSplit_Rangecc(range_String(&d->nextJob), " ", &word)) { | 247 | while (nextSplit_Rangecc(range_String(&d->pendingTerm), " ", &word)) { |
218 | if (isEmpty_Range(&word)) continue; | 248 | if (isEmpty_Range(&word)) continue; |
219 | if (!isFirst) appendChar_String(pattern, '|'); | 249 | if (!isFirst) appendChar_String(pattern, '|'); |
220 | for (const char *ch = word.start; ch != word.end; ch++) { | 250 | for (const char *ch = word.start; ch != word.end; ch++) { |
@@ -230,11 +260,14 @@ static iThreadResult worker_LookupWidget_(iThread *thread) { | |||
230 | job->term = new_RegExp(cstr_String(pattern), caseInsensitive_RegExpOption); | 260 | job->term = new_RegExp(cstr_String(pattern), caseInsensitive_RegExpOption); |
231 | delete_String(pattern); | 261 | delete_String(pattern); |
232 | } | 262 | } |
233 | clear_String(&d->nextJob); | 263 | clear_String(&d->pendingTerm); |
264 | job->docs = d->pendingDocs; | ||
265 | d->pendingDocs = NULL; | ||
234 | unlock_Mutex(d->mtx); | 266 | unlock_Mutex(d->mtx); |
235 | /* Do the lookup. */ { | 267 | /* Do the lookup. */ { |
236 | searchBookmarks_LookupJob_(job); | 268 | searchBookmarks_LookupJob_(job); |
237 | searchVisited_LookupJob_(job); | 269 | searchVisited_LookupJob_(job); |
270 | searchHistory_LookupJob_(job); | ||
238 | } | 271 | } |
239 | /* Submit the result. */ | 272 | /* Submit the result. */ |
240 | lock_Mutex(d->mtx); | 273 | lock_Mutex(d->mtx); |
@@ -266,7 +299,8 @@ void init_LookupWidget(iLookupWidget *d) { | |||
266 | setUserData_Thread(d->work, d); | 299 | setUserData_Thread(d->work, d); |
267 | init_Condition(&d->jobAvailable); | 300 | init_Condition(&d->jobAvailable); |
268 | d->mtx = new_Mutex(); | 301 | d->mtx = new_Mutex(); |
269 | init_String(&d->nextJob); | 302 | init_String(&d->pendingTerm); |
303 | d->pendingDocs = NULL; | ||
270 | d->finishedJob = NULL; | 304 | d->finishedJob = NULL; |
271 | start_Thread(d->work); | 305 | start_Thread(d->work); |
272 | } | 306 | } |
@@ -274,23 +308,26 @@ void init_LookupWidget(iLookupWidget *d) { | |||
274 | void deinit_LookupWidget(iLookupWidget *d) { | 308 | void deinit_LookupWidget(iLookupWidget *d) { |
275 | /* Stop the worker. */ { | 309 | /* Stop the worker. */ { |
276 | iGuardMutex(d->mtx, { | 310 | iGuardMutex(d->mtx, { |
277 | clear_String(&d->nextJob); | 311 | iReleasePtr(&d->pendingDocs); |
312 | clear_String(&d->pendingTerm); | ||
278 | signal_Condition(&d->jobAvailable); | 313 | signal_Condition(&d->jobAvailable); |
279 | }); | 314 | }); |
280 | join_Thread(d->work); | 315 | join_Thread(d->work); |
281 | iRelease(d->work); | 316 | iRelease(d->work); |
282 | } | 317 | } |
283 | delete_LookupJob(d->finishedJob); | 318 | delete_LookupJob(d->finishedJob); |
284 | deinit_String(&d->nextJob); | 319 | deinit_String(&d->pendingTerm); |
285 | delete_Mutex(d->mtx); | 320 | delete_Mutex(d->mtx); |
286 | deinit_Condition(&d->jobAvailable); | 321 | deinit_Condition(&d->jobAvailable); |
287 | } | 322 | } |
288 | 323 | ||
289 | void submit_LookupWidget(iLookupWidget *d, const iString *term) { | 324 | void submit_LookupWidget(iLookupWidget *d, const iString *term) { |
290 | iGuardMutex(d->mtx, { | 325 | iGuardMutex(d->mtx, { |
291 | set_String(&d->nextJob, term); | 326 | set_String(&d->pendingTerm, term); |
292 | trim_String(&d->nextJob); | 327 | trim_String(&d->pendingTerm); |
293 | if (!isEmpty_String(&d->nextJob)) { | 328 | iReleasePtr(&d->pendingDocs); |
329 | if (!isEmpty_String(&d->pendingTerm)) { | ||
330 | d->pendingDocs = listDocuments_App(); /* holds reference to all open tabs */ | ||
294 | signal_Condition(&d->jobAvailable); | 331 | signal_Condition(&d->jobAvailable); |
295 | } | 332 | } |
296 | else { | 333 | else { |
@@ -386,6 +423,13 @@ static void presentResults_LookupWidget_(iLookupWidget *d) { | |||
386 | format_String(&item->command, "open url:%s", cstr_String(&res->url)); | 423 | format_String(&item->command, "open url:%s", cstr_String(&res->url)); |
387 | break; | 424 | break; |
388 | } | 425 | } |
426 | case content_LookupResultType: { | ||
427 | item->fg = uiText_ColorId; | ||
428 | item->font = uiContent_FontId; | ||
429 | format_String(&item->text, "%s \u2014 %s", cstr_String(&res->label), url); | ||
430 | format_String(&item->command, "open url:%s", cstr_String(&res->url)); | ||
431 | break; | ||
432 | } | ||
389 | } | 433 | } |
390 | addItem_ListWidget(d->list, item); | 434 | addItem_ListWidget(d->list, item); |
391 | iRelease(item); | 435 | iRelease(item); |