summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/app.c15
-rw-r--r--src/app.h2
-rw-r--r--src/history.c42
-rw-r--r--src/history.h6
-rw-r--r--src/ui/lookupwidget.c64
5 files changed, 114 insertions, 15 deletions
diff --git a/src/app.c b/src/app.c
index a7aceb19..6b78f2e4 100644
--- a/src/app.c
+++ b/src/app.c
@@ -254,13 +254,24 @@ static iBool loadState_App_(iApp *d) {
254 return iFalse; 254 return iFalse;
255} 255}
256 256
257iObjectList *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
257static void saveState_App_(const iApp *d) { 268static 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);
diff --git a/src/app.h b/src/app.h
index 09d84f19..da86c37e 100644
--- a/src/app.h
+++ b/src/app.h
@@ -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);
64iVisited * visited_App (void); 65iVisited * visited_App (void);
65iBookmarks * bookmarks_App (void); 66iBookmarks * bookmarks_App (void);
66iDocumentWidget * document_App (void); 67iDocumentWidget * document_App (void);
68iObjectList * listDocuments_App (void);
67iDocumentWidget * document_Command (const char *cmd); 69iDocumentWidget * document_Command (const char *cmd);
68iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf); 70iDocumentWidget * 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
69void deinit_History(iHistory *d) { 69void 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
255const 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
31iDeclareType(RecentUrl) 33iDeclareType(RecentUrl)
@@ -55,6 +57,8 @@ iRecentUrl *recentUrl_History (iHistory *, size_t pos);
55iRecentUrl *mostRecentUrl_History (iHistory *); 57iRecentUrl *mostRecentUrl_History (iHistory *);
56iRecentUrl *findUrl_History (iHistory *, const iString *url); 58iRecentUrl *findUrl_History (iHistory *, const iString *url);
57 59
60const iStringArray * searchContents_History (const iHistory *, const iRegExp *pattern); /* chronologically ascending */
61
58const iString * 62const iString *
59 url_History (const iHistory *, size_t pos); 63 url_History (const iHistory *, size_t pos);
60const iRecentUrl * 64const 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
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 21SOFTWARE, 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)
40struct Impl_LookupJob { 42struct 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
46static void init_LookupJob(iLookupJob *d) { 49static 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
209static 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
203static iThreadResult worker_LookupWidget_(iThread *thread) { 233static 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) {
274void deinit_LookupWidget(iLookupWidget *d) { 308void 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
289void submit_LookupWidget(iLookupWidget *d, const iString *term) { 324void 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);