summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-08-08 08:06:22 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-08-08 08:06:22 +0300
commitd6875fba63cc674d2d9cac64a66c3b4c9f3eaba1 (patch)
treea12111a19b594fc17792a7a851d1526597470121 /src
parent9abe27c63e088bf1e139d108cbc29ac39222d74c (diff)
Moved recent URLs history to DocumentWidget
App maintains the visited URLs database, but each DocumentWidget has its own stack of recent URLs for timeline navigation.
Diffstat (limited to 'src')
-rw-r--r--src/app.c41
-rw-r--r--src/app.h4
-rw-r--r--src/gmdocument.c4
-rw-r--r--src/history.c111
-rw-r--r--src/history.h46
-rw-r--r--src/ui/documentwidget.c23
-rw-r--r--src/ui/documentwidget.h5
-rw-r--r--src/ui/window.c5
-rw-r--r--src/visited.c128
-rw-r--r--src/visited.h25
10 files changed, 222 insertions, 170 deletions
diff --git a/src/app.c b/src/app.c
index 0d15bce1..5b31faa7 100644
--- a/src/app.c
+++ b/src/app.c
@@ -3,6 +3,7 @@
3#include "gmcerts.h" 3#include "gmcerts.h"
4#include "gmutil.h" 4#include "gmutil.h"
5#include "history.h" 5#include "history.h"
6#include "visited.h"
6#include "ui/command.h" 7#include "ui/command.h"
7#include "ui/window.h" 8#include "ui/window.h"
8#include "ui/inputwidget.h" 9#include "ui/inputwidget.h"
@@ -55,7 +56,8 @@ struct Impl_App {
55 iSortedArray tickers; 56 iSortedArray tickers;
56 iBool pendingRefresh; 57 iBool pendingRefresh;
57 iGmCerts * certs; 58 iGmCerts * certs;
58 iHistory * history; 59// iHistory * history;
60 iVisited * visited;
59 /* Preferences: */ 61 /* Preferences: */
60 iBool retainWindowSize; 62 iBool retainWindowSize;
61 float uiScale; 63 float uiScale;
@@ -149,9 +151,9 @@ static void init_App_(iApp *d, int argc, char **argv) {
149 d->retainWindowSize = iTrue; 151 d->retainWindowSize = iTrue;
150 d->pendingRefresh = iFalse; 152 d->pendingRefresh = iFalse;
151 d->certs = new_GmCerts(dataDir_App_); 153 d->certs = new_GmCerts(dataDir_App_);
152 d->history = new_History(); 154 d->visited = new_Visited();
153 loadPrefs_App_(d); 155 loadPrefs_App_(d);
154 load_History(d->history, dataDir_App_); 156 load_Visited(d->visited, dataDir_App_);
155#if defined (iHaveLoadEmbed) 157#if defined (iHaveLoadEmbed)
156 /* Load the resources from a file. */ { 158 /* Load the resources from a file. */ {
157 if (!load_Embed(concatPath_CStr(cstr_String(execPath_App()), "../resources.bin"))) { 159 if (!load_Embed(concatPath_CStr(cstr_String(execPath_App()), "../resources.bin"))) {
@@ -170,8 +172,8 @@ static void init_App_(iApp *d, int argc, char **argv) {
170 172
171static void deinit_App(iApp *d) { 173static void deinit_App(iApp *d) {
172 savePrefs_App_(d); 174 savePrefs_App_(d);
173 save_History(d->history, dataDir_App_); 175 save_Visited(d->visited, dataDir_App_);
174 delete_History(d->history); 176 delete_Visited(d->visited);
175 delete_GmCerts(d->certs); 177 delete_GmCerts(d->certs);
176 deinit_SortedArray(&d->tickers); 178 deinit_SortedArray(&d->tickers);
177 delete_Window(d->window); 179 delete_Window(d->window);
@@ -313,8 +315,8 @@ iGmCerts *certs_App(void) {
313 return app_.certs; 315 return app_.certs;
314} 316}
315 317
316iHistory *history_App(void) { 318iVisited *visited_App(void) {
317 return app_.history; 319 return app_.visited;
318} 320}
319 321
320static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { 322static iBool handlePrefsCommands_(iWidget *d, const char *cmd) {
@@ -332,40 +334,43 @@ iDocumentWidget *document_App(void) {
332} 334}
333 335
334iDocumentWidget *document_Command(const char *cmd) { 336iDocumentWidget *document_Command(const char *cmd) {
337 /* Explicitly referenced. */
335 iAnyObject *obj = pointerLabel_Command(cmd, "doc"); 338 iAnyObject *obj = pointerLabel_Command(cmd, "doc");
336 if (obj) { 339 if (obj) {
337 return obj; 340 return obj;
338 } 341 }
342 /* Implicit via source widget. */
339 obj = pointer_Command(cmd); 343 obj = pointer_Command(cmd);
340 if (obj && isInstance_Object(obj, &Class_DocumentWidget)) { 344 if (obj && isInstance_Object(obj, &Class_DocumentWidget)) {
341 return obj; 345 return obj;
342 } 346 }
347 /* Currently visible document. */
343 return document_App(); 348 return document_App();
344} 349}
345 350
346iBool handleCommand_App(const char *cmd) { 351iBool handleCommand_App(const char *cmd) {
347 iApp *d = &app_; 352 iApp *d = &app_;
348 iWidget *root = d->window->root;
349 if (equal_Command(cmd, "open")) { 353 if (equal_Command(cmd, "open")) {
350 const iString *url = collect_String(newCStr_String(suffixPtr_Command(cmd, "url"))); 354 const iString *url = collect_String(newCStr_String(suffixPtr_Command(cmd, "url")));
351 iUrl parts; 355 iUrl parts;
352 init_Url(&parts, url); 356 init_Url(&parts, url);
353 if (equalCase_Rangecc(&parts.protocol, "http") || 357 if (equalCase_Rangecc(&parts.protocol, "http") ||
354 equalCase_Rangecc(&parts.protocol, "https")) { 358 equalCase_Rangecc(&parts.protocol, "https")) {
355 visitUrl_History(d->history, url);
356 openInDefaultBrowser_App(url); 359 openInDefaultBrowser_App(url);
357 return iTrue; 360 return iTrue;
358 } 361 }
362 iDocumentWidget *doc = document_Command(cmd);
363 iHistory *history = history_DocumentWidget(doc);
359 const iBool isHistory = argLabel_Command(cmd, "history") != 0; 364 const iBool isHistory = argLabel_Command(cmd, "history") != 0;
360 if (!isHistory) { 365 if (!isHistory) {
361 if (argLabel_Command(cmd, "redirect")) { 366 if (argLabel_Command(cmd, "redirect")) {
362 replace_History(d->history, url); 367 replace_History(history, url);
363 } 368 }
364 else { 369 else {
365 addUrl_History(d->history, url); 370 add_History(history, url);
366 } 371 }
367 } 372 }
368 iDocumentWidget *doc = findChild_Widget(root, "document"); 373 visitUrl_Visited(d->visited, url);
369 setInitialScroll_DocumentWidget(doc, argLabel_Command(cmd, "scroll") * gap_UI); 374 setInitialScroll_DocumentWidget(doc, argLabel_Command(cmd, "scroll") * gap_UI);
370 setUrlFromCache_DocumentWidget(doc, url, isHistory); 375 setUrlFromCache_DocumentWidget(doc, url, isHistory);
371 } 376 }
@@ -403,18 +408,6 @@ iBool handleCommand_App(const char *cmd) {
403 const iInt2 pos = coord_Command(cmd); 408 const iInt2 pos = coord_Command(cmd);
404 SDL_SetWindowPosition(d->window->win, pos.x, pos.y); 409 SDL_SetWindowPosition(d->window->win, pos.x, pos.y);
405 } 410 }
406 else if (equal_Command(cmd, "document.changed")) {
407 /* TODO: Update current history item with this actual/redirected URL. */
408 return iFalse;
409 }
410 else if (equal_Command(cmd, "navigate.back")) {
411 goBack_History(d->history);
412 return iTrue;
413 }
414 else if (equal_Command(cmd, "navigate.forward")) {
415 goForward_History(d->history);
416 return iTrue;
417 }
418 else if (equal_Command(cmd, "navigate.home")) { 411 else if (equal_Command(cmd, "navigate.home")) {
419 iString *homePath = newCStr_String(dataDir_App_); 412 iString *homePath = newCStr_String(dataDir_App_);
420 clean_Path(homePath); 413 clean_Path(homePath);
diff --git a/src/app.h b/src/app.h
index cdfc387d..64c1f64d 100644
--- a/src/app.h
+++ b/src/app.h
@@ -6,7 +6,7 @@
6#include <the_Foundation/time.h> 6#include <the_Foundation/time.h>
7 7
8iDeclareType(GmCerts) 8iDeclareType(GmCerts)
9iDeclareType(History) 9iDeclareType(Visited)
10iDeclareType(Window) 10iDeclareType(Window)
11 11
12enum iAppEventMode { 12enum iAppEventMode {
@@ -27,7 +27,7 @@ iBool handleCommand_App (const char *cmd);
27void refresh_App (void); 27void refresh_App (void);
28 28
29iGmCerts * certs_App (void); 29iGmCerts * certs_App (void);
30iHistory * history_App (void); 30iVisited * visited_App (void);
31 31
32iAny * findWidget_App (const char *id); 32iAny * findWidget_App (const char *id);
33void addTicker_App (void (*ticker)(iAny *), iAny *context); 33void addTicker_App (void (*ticker)(iAny *), iAny *context);
diff --git a/src/gmdocument.c b/src/gmdocument.c
index 4c0ca86e..bdf30807 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -4,7 +4,7 @@
4#include "ui/text.h" 4#include "ui/text.h"
5#include "ui/metrics.h" 5#include "ui/metrics.h"
6#include "ui/window.h" 6#include "ui/window.h"
7#include "history.h" 7#include "visited.h"
8#include "app.h" 8#include "app.h"
9 9
10#include <the_Foundation/ptrarray.h> 10#include <the_Foundation/ptrarray.h>
@@ -212,7 +212,7 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li
212 } 212 }
213 /* Check if visited. */ 213 /* Check if visited. */
214 if (cmpString_String(&link->url, &d->url)) { 214 if (cmpString_String(&link->url, &d->url)) {
215 link->when = urlVisitTime_History(history_App(), &link->url); 215 link->when = urlVisitTime_Visited(visited_App(), &link->url);
216 if (isValid_Time(&link->when)) { 216 if (isValid_Time(&link->when)) {
217 link->flags |= visited_GmLinkFlag; 217 link->flags |= visited_GmLinkFlag;
218 } 218 }
diff --git a/src/history.c b/src/history.c
index a2c4271f..c009f484 100644
--- a/src/history.c
+++ b/src/history.c
@@ -3,10 +3,8 @@
3 3
4#include <the_Foundation/file.h> 4#include <the_Foundation/file.h>
5#include <the_Foundation/path.h> 5#include <the_Foundation/path.h>
6#include <the_Foundation/sortedarray.h>
7 6
8static const size_t maxStack_History_ = 50; /* back/forward navigable items */ 7static const size_t maxStack_History_ = 50; /* back/forward navigable items */
9static const size_t maxAgeVisited_History_ = 3600 * 24 * 30; /* one month */
10 8
11void init_RecentUrl(iRecentUrl *d) { 9void init_RecentUrl(iRecentUrl *d) {
12 init_String(&d->url); 10 init_String(&d->url);
@@ -19,30 +17,11 @@ void deinit_RecentUrl(iRecentUrl *d) {
19 delete_GmResponse(d->cachedResponse); 17 delete_GmResponse(d->cachedResponse);
20} 18}
21 19
22void init_VisitedUrl(iVisitedUrl *d) {
23 initCurrent_Time(&d->when);
24 init_String(&d->url);
25}
26
27void deinit_VisitedUrl(iVisitedUrl *d) {
28 deinit_String(&d->url);
29}
30
31static int cmpUrl_VisitedUrl_(const void *a, const void *b) {
32 return cmpString_String(&((const iVisitedUrl *) a)->url, &((const iVisitedUrl *) b)->url);
33}
34
35static int cmpNewer_VisitedUrl_(const void *insert, const void *existing) {
36 return seconds_Time(&((const iVisitedUrl *) insert )->when) >
37 seconds_Time(&((const iVisitedUrl *) existing)->when);
38}
39
40/*----------------------------------------------------------------------------------------------*/ 20/*----------------------------------------------------------------------------------------------*/
41 21
42struct Impl_History { 22struct Impl_History {
43 iArray recent; /* TODO: should be specific to a DocumentWidget */ 23 iArray recent; /* TODO: should be specific to a DocumentWidget */
44 size_t recentPos; /* zero at the latest item */ 24 size_t recentPos; /* zero at the latest item */
45 iSortedArray visited;
46}; 25};
47 26
48iDefineTypeConstruction(History) 27iDefineTypeConstruction(History)
@@ -50,13 +29,11 @@ iDefineTypeConstruction(History)
50void init_History(iHistory *d) { 29void init_History(iHistory *d) {
51 init_Array(&d->recent, sizeof(iRecentUrl)); 30 init_Array(&d->recent, sizeof(iRecentUrl));
52 d->recentPos = 0; 31 d->recentPos = 0;
53 init_SortedArray(&d->visited, sizeof(iVisitedUrl), cmpUrl_VisitedUrl_);
54} 32}
55 33
56void deinit_History(iHistory *d) { 34void deinit_History(iHistory *d) {
57 clear_History(d); 35 clear_History(d);
58 deinit_Array(&d->recent); 36 deinit_Array(&d->recent);
59 deinit_SortedArray(&d->visited);
60} 37}
61 38
62void save_History(const iHistory *d, const char *dirPath) { 39void save_History(const iHistory *d, const char *dirPath) {
@@ -70,25 +47,6 @@ void save_History(const iHistory *d, const char *dirPath) {
70 } 47 }
71 } 48 }
72 iRelease(f); 49 iRelease(f);
73 f = newCStr_File(concatPath_CStr(dirPath, "visited.txt"));
74 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
75 iConstForEach(Array, i, &d->visited.values) {
76 const iVisitedUrl *item = i.value;
77 iDate date;
78 init_Date(&date, &item->when);
79 format_String(line,
80 "%04d-%02d-%02dT%02d:%02d:%02d %s\n",
81 date.year,
82 date.month,
83 date.day,
84 date.hour,
85 date.minute,
86 date.second,
87 cstr_String(&item->url));
88 writeData_File(f, cstr_String(line), size_String(line));
89 }
90 }
91 iRelease(f);
92 delete_String(line); 50 delete_String(line);
93} 51}
94 52
@@ -111,29 +69,6 @@ void load_History(iHistory *d, const char *dirPath) {
111 } 69 }
112 } 70 }
113 iRelease(f); 71 iRelease(f);
114 f = newCStr_File(concatPath_CStr(dirPath, "visited.txt"));
115 if (open_File(f, readOnly_FileMode | text_FileMode)) {
116 const iRangecc src = range_Block(collect_Block(readAll_File(f)));
117 iRangecc line = iNullRange;
118 iTime now;
119 initCurrent_Time(&now);
120 while (nextSplit_Rangecc(&src, "\n", &line)) {
121 int y, m, D, H, M, S;
122 sscanf(line.start, "%04d-%02d-%02dT%02d:%02d:%02d ", &y, &m, &D, &H, &M, &S);
123 if (!y) break;
124 iVisitedUrl item;
125 init_VisitedUrl(&item);
126 init_Time(
127 &item.when,
128 &(iDate){ .year = y, .month = m, .day = D, .hour = H, .minute = M, .second = S });
129 if (secondsSince_Time(&now, &item.when) > maxAgeVisited_History_) {
130 continue; /* Too old. */
131 }
132 initRange_String(&item.url, (iRangecc){ line.start + 20, line.end });
133 insert_SortedArray(&d->visited, &item);
134 }
135 }
136 iRelease(f);
137} 72}
138 73
139void clear_History(iHistory *d) { 74void clear_History(iHistory *d) {
@@ -141,10 +76,6 @@ void clear_History(iHistory *d) {
141 deinit_RecentUrl(s.value); 76 deinit_RecentUrl(s.value);
142 } 77 }
143 clear_Array(&d->recent); 78 clear_Array(&d->recent);
144 iForEach(Array, v, &d->visited.values) {
145 deinit_VisitedUrl(v.value);
146 }
147 clear_SortedArray(&d->visited);
148} 79}
149 80
150iRecentUrl *recentUrl_History(iHistory *d, size_t pos) { 81iRecentUrl *recentUrl_History(iHistory *d, size_t pos) {
@@ -173,32 +104,15 @@ const iString *url_History(const iHistory *d, size_t pos) {
173 return collectNew_String(); 104 return collectNew_String();
174} 105}
175 106
176static void addVisited_History_(iHistory *d, const iString *url) {
177 iVisitedUrl visit;
178 init_VisitedUrl(&visit);
179 set_String(&visit.url, url);
180 size_t pos;
181 if (locate_SortedArray(&d->visited, &visit, &pos)) {
182 iVisitedUrl *old = at_SortedArray(&d->visited, pos);
183 if (cmpNewer_VisitedUrl_(&visit, old)) {
184 old->when = visit.when;
185 deinit_VisitedUrl(&visit);
186 return;
187 }
188 }
189 insert_SortedArray(&d->visited, &visit);
190}
191
192void replace_History(iHistory *d, const iString *url) { 107void replace_History(iHistory *d, const iString *url) {
193 /* Update in the history. */ 108 /* Update in the history. */
194 iRecentUrl *item = mostRecentUrl_History(d); 109 iRecentUrl *item = mostRecentUrl_History(d);
195 if (item) { 110 if (item) {
196 set_String(&item->url, url); 111 set_String(&item->url, url);
197 } 112 }
198 addVisited_History_(d, url);
199} 113}
200 114
201void addUrl_History(iHistory *d, const iString *url ){ 115void add_History(iHistory *d, const iString *url ){
202 /* Cut the trailing history items. */ 116 /* Cut the trailing history items. */
203 if (d->recentPos > 0) { 117 if (d->recentPos > 0) {
204 for (size_t i = 0; i < d->recentPos - 1; i++) { 118 for (size_t i = 0; i < d->recentPos - 1; i++) {
@@ -220,11 +134,6 @@ void addUrl_History(iHistory *d, const iString *url ){
220 remove_Array(&d->recent, 0); 134 remove_Array(&d->recent, 0);
221 } 135 }
222 } 136 }
223 addVisited_History_(d, url);
224}
225
226void visitUrl_History(iHistory *d, const iString *url) {
227 addVisited_History_(d, url);
228} 137}
229 138
230iBool goBack_History(iHistory *d) { 139iBool goBack_History(iHistory *d) {
@@ -247,18 +156,6 @@ iBool goForward_History(iHistory *d) {
247 return iFalse; 156 return iFalse;
248} 157}
249 158
250iTime urlVisitTime_History(const iHistory *d, const iString *url) {
251 iVisitedUrl item;
252 size_t pos;
253 iZap(item);
254 initCopy_String(&item.url, url);
255 if (locate_SortedArray(&d->visited, &item, &pos)) {
256 item.when = ((const iVisitedUrl *) constAt_SortedArray(&d->visited, pos))->when;
257 }
258 deinit_String(&item.url);
259 return item.when;
260}
261
262const iGmResponse *cachedResponse_History(const iHistory *d) { 159const iGmResponse *cachedResponse_History(const iHistory *d) {
263 const iRecentUrl *item = constMostRecentUrl_History(d); 160 const iRecentUrl *item = constMostRecentUrl_History(d);
264 return item ? item->cachedResponse : NULL; 161 return item ? item->cachedResponse : NULL;
diff --git a/src/history.h b/src/history.h
index 8593a7ad..bb7f5c88 100644
--- a/src/history.h
+++ b/src/history.h
@@ -15,42 +15,28 @@ struct Impl_RecentUrl {
15 iGmResponse *cachedResponse; /* kept in memory for quicker back navigation */ 15 iGmResponse *cachedResponse; /* kept in memory for quicker back navigation */
16}; 16};
17 17
18iDeclareType(VisitedUrl)
19iDeclareTypeConstruction(VisitedUrl)
20
21struct Impl_VisitedUrl {
22 iString url;
23 iTime when;
24};
25
26/*----------------------------------------------------------------------------------------------*/ 18/*----------------------------------------------------------------------------------------------*/
27 19
28iDeclareType(History) 20iDeclareType(History)
29iDeclareTypeConstruction(History) 21iDeclareTypeConstruction(History)
30 22
31void clear_History (iHistory *); 23void clear_History (iHistory *);
32 24void load_History (iHistory *, const char *dirPath);
33void load_History (iHistory *, const char *dirPath); 25void add_History (iHistory *, const iString *url);
34void save_History (const iHistory *, const char *dirPath); 26void replace_History (iHistory *, const iString *url);
35 27void setCachedResponse_History (iHistory *, const iGmResponse *response);
36iRecentUrl * 28iBool goBack_History (iHistory *);
37 recentUrl_History (iHistory *, size_t pos); 29iBool goForward_History (iHistory *);
38iRecentUrl * 30iRecentUrl *recentUrl_History (iHistory *, size_t pos);
39 mostRecentUrl_History (iHistory *); 31iRecentUrl *mostRecentUrl_History (iHistory *);
40 32
33void save_History (const iHistory *, const char *dirPath);
41const iString * 34const iString *
42 url_History (const iHistory *, size_t pos); 35 url_History (const iHistory *, size_t pos);
43const iRecentUrl * 36const iRecentUrl *
44 constRecentUrl_History (const iHistory *d, size_t pos); 37 constRecentUrl_History (const iHistory *d, size_t pos);
45const iRecentUrl * 38const iRecentUrl *
46 constMostRecentUrl_History (const iHistory *); 39 constMostRecentUrl_History (const iHistory *);
47iTime urlVisitTime_History (const iHistory *, const iString *url);
48const iGmResponse * 40const iGmResponse *
49 cachedResponse_History (const iHistory *); 41 cachedResponse_History (const iHistory *);
50 42
51void addUrl_History (iHistory *, const iString *url); /* adds to the stack of recents */
52void setCachedResponse_History (iHistory *, const iGmResponse *response);
53void visitUrl_History (iHistory *, const iString *url); /* adds URL to the visited URLs set */
54void replace_History (iHistory *, const iString *url);
55iBool goBack_History (iHistory *);
56iBool goForward_History (iHistory *);
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 661a9d46..a1299727 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -76,6 +76,7 @@ iDefineClass(MediaRequest)
76 76
77struct Impl_DocumentWidget { 77struct Impl_DocumentWidget {
78 iWidget widget; 78 iWidget widget;
79 iHistory *history;
79 enum iDocumentState state; 80 enum iDocumentState state;
80 iString *url; 81 iString *url;
81 iString *titleUser; 82 iString *titleUser;
@@ -98,7 +99,7 @@ struct Impl_DocumentWidget {
98 int initialScrollY; 99 int initialScrollY;
99 iScrollWidget *scroll; 100 iScrollWidget *scroll;
100 iWidget *menu; 101 iWidget *menu;
101 SDL_Cursor *arrowCursor; 102 SDL_Cursor *arrowCursor; /* TODO: cursors belong in Window */
102 SDL_Cursor *beamCursor; 103 SDL_Cursor *beamCursor;
103 SDL_Cursor *handCursor; 104 SDL_Cursor *handCursor;
104}; 105};
@@ -110,6 +111,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
110 init_Widget(w); 111 init_Widget(w);
111 setId_Widget(w, "document"); 112 setId_Widget(w, "document");
112 iZap(d->certExpiry); 113 iZap(d->certExpiry);
114 d->history = new_History();
113 d->state = blank_DocumentState; 115 d->state = blank_DocumentState;
114 d->url = new_String(); 116 d->url = new_String();
115 d->titleUser = new_String(); 117 d->titleUser = new_String();
@@ -153,6 +155,7 @@ void deinit_DocumentWidget(iDocumentWidget *d) {
153 SDL_FreeCursor(d->arrowCursor); 155 SDL_FreeCursor(d->arrowCursor);
154 SDL_FreeCursor(d->beamCursor); 156 SDL_FreeCursor(d->beamCursor);
155 SDL_FreeCursor(d->handCursor); 157 SDL_FreeCursor(d->handCursor);
158 delete_History(d->history);
156} 159}
157 160
158static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { 161static int documentWidth_DocumentWidget_(const iDocumentWidget *d) {
@@ -258,7 +261,7 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
258 render_GmDocument(d->doc, visRange, addVisibleLink_DocumentWidget_, d); 261 render_GmDocument(d->doc, visRange, addVisibleLink_DocumentWidget_, d);
259 updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window())); 262 updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window()));
260 /* Remember scroll positions of recently visited pages. */ { 263 /* Remember scroll positions of recently visited pages. */ {
261 iRecentUrl *recent = mostRecentUrl_History(history_App()); 264 iRecentUrl *recent = mostRecentUrl_History(d->history);
262 if (recent) { 265 if (recent) {
263 recent->scrollY = d->scrollY / gap_UI; 266 recent->scrollY = d->scrollY / gap_UI;
264 } 267 }
@@ -424,6 +427,10 @@ static void updateTrust_DocumentWidget_(iDocumentWidget *d, const iGmResponse *r
424 } 427 }
425} 428}
426 429
430iHistory *history_DocumentWidget(iDocumentWidget *d) {
431 return d->history;
432}
433
427void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { 434void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) {
428 if (cmpStringSc_String(d->url, url, &iCaseInsensitive)) { 435 if (cmpStringSc_String(d->url, url, &iCaseInsensitive)) {
429 set_String(d->url, url); 436 set_String(d->url, url);
@@ -439,7 +446,7 @@ void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBoo
439 iRelease(userPats[i]); 446 iRelease(userPats[i]);
440 } 447 }
441 } 448 }
442 const iRecentUrl *recent = mostRecentUrl_History(history_App()); 449 const iRecentUrl *recent = mostRecentUrl_History(d->history);
443 if (isFromCache && recent && recent->cachedResponse) { 450 if (isFromCache && recent && recent->cachedResponse) {
444 const iGmResponse *resp = recent->cachedResponse; 451 const iGmResponse *resp = recent->cachedResponse;
445 d->state = fetching_DocumentState; 452 d->state = fetching_DocumentState;
@@ -764,7 +771,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
764 pointerLabel_Command(command_UserEvent(ev), "request") == d->request) { 771 pointerLabel_Command(command_UserEvent(ev), "request") == d->request) {
765 checkResponse_DocumentWidget_(d); 772 checkResponse_DocumentWidget_(d);
766 d->state = ready_DocumentState; 773 d->state = ready_DocumentState;
767 setCachedResponse_History(history_App(), response_GmRequest(d->request)); 774 setCachedResponse_History(d->history, response_GmRequest(d->request));
768 iReleasePtr(&d->request); 775 iReleasePtr(&d->request);
769 postCommandf_App("document.changed url:%s", cstr_String(d->url)); 776 postCommandf_App("document.changed url:%s", cstr_String(d->url));
770 return iFalse; 777 return iFalse;
@@ -788,6 +795,14 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
788 fetch_DocumentWidget_(d); 795 fetch_DocumentWidget_(d);
789 return iTrue; 796 return iTrue;
790 } 797 }
798 else if (isCommand_UserEvent(ev, "navigate.back")) {
799 goBack_History(d->history);
800 return iTrue;
801 }
802 else if (isCommand_UserEvent(ev, "navigate.forward")) {
803 goForward_History(d->history);
804 return iTrue;
805 }
791 else if (isCommand_Widget(w, ev, "scroll.moved")) { 806 else if (isCommand_Widget(w, ev, "scroll.moved")) {
792 d->scrollY = arg_Command(command_UserEvent(ev)); 807 d->scrollY = arg_Command(command_UserEvent(ev));
793 updateVisible_DocumentWidget_(d); 808 updateVisible_DocumentWidget_(d);
diff --git a/src/ui/documentwidget.h b/src/ui/documentwidget.h
index 7064d0d7..5ce28f4e 100644
--- a/src/ui/documentwidget.h
+++ b/src/ui/documentwidget.h
@@ -2,10 +2,15 @@
2 2
3#include "widget.h" 3#include "widget.h"
4 4
5iDeclareType(History)
6
5iDeclareWidgetClass(DocumentWidget) 7iDeclareWidgetClass(DocumentWidget)
6iDeclareObjectConstruction(DocumentWidget) 8iDeclareObjectConstruction(DocumentWidget)
7 9
10iHistory * history_DocumentWidget (iDocumentWidget *);
11
8void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); 12void setUrl_DocumentWidget (iDocumentWidget *, const iString *url);
9void setUrlFromCache_DocumentWidget (iDocumentWidget *d, const iString *url, iBool isFromCache); 13void setUrlFromCache_DocumentWidget (iDocumentWidget *d, const iString *url, iBool isFromCache);
10void setInitialScroll_DocumentWidget (iDocumentWidget *, int scrollY); /* set after content received */ 14void setInitialScroll_DocumentWidget (iDocumentWidget *, int scrollY); /* set after content received */
15
11iBool isRequestOngoing_DocumentWidget (const iDocumentWidget *); 16iBool isRequestOngoing_DocumentWidget (const iDocumentWidget *);
diff --git a/src/ui/window.c b/src/ui/window.c
index 2661fd3f..defe998f 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -6,6 +6,7 @@
6#include "paint.h" 6#include "paint.h"
7#include "text.h" 7#include "text.h"
8#include "util.h" 8#include "util.h"
9#include "../visited.h"
9#include "labelwidget.h" 10#include "labelwidget.h"
10#include "inputwidget.h" 11#include "inputwidget.h"
11#include "documentwidget.h" 12#include "documentwidget.h"
@@ -108,7 +109,9 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {
108 } 109 }
109 else if (equal_Command(cmd, "document.changed")) { 110 else if (equal_Command(cmd, "document.changed")) {
110 iInputWidget *url = findWidget_App("url"); 111 iInputWidget *url = findWidget_App("url");
111 setTextCStr_InputWidget(url, suffixPtr_Command(cmd, "url")); 112 const iString *urlStr = collect_String(suffix_Command(cmd, "url"));
113 visitUrl_Visited(visited_App(), urlStr);
114 setText_InputWidget(url, urlStr);
112 updateTextCStr_LabelWidget(findChild_Widget(navBar, "reload"), reloadCStr_); 115 updateTextCStr_LabelWidget(findChild_Widget(navBar, "reload"), reloadCStr_);
113 return iFalse; 116 return iFalse;
114 } 117 }
diff --git a/src/visited.c b/src/visited.c
new file mode 100644
index 00000000..af976be5
--- /dev/null
+++ b/src/visited.c
@@ -0,0 +1,128 @@
1#include "visited.h"
2#include "app.h"
3
4#include <the_Foundation/file.h>
5#include <the_Foundation/path.h>
6#include <the_Foundation/sortedarray.h>
7
8static const size_t maxAgeVisited_Visited_ = 3600 * 24 * 30; /* one month */
9
10void init_VisitedUrl(iVisitedUrl *d) {
11 initCurrent_Time(&d->when);
12 init_String(&d->url);
13}
14
15void deinit_VisitedUrl(iVisitedUrl *d) {
16 deinit_String(&d->url);
17}
18
19static int cmpUrl_VisitedUrl_(const void *a, const void *b) {
20 return cmpString_String(&((const iVisitedUrl *) a)->url, &((const iVisitedUrl *) b)->url);
21}
22
23static int cmpNewer_VisitedUrl_(const void *insert, const void *existing) {
24 return seconds_Time(&((const iVisitedUrl *) insert )->when) >
25 seconds_Time(&((const iVisitedUrl *) existing)->when);
26}
27
28/*----------------------------------------------------------------------------------------------*/
29
30struct Impl_Visited {
31 iSortedArray visited;
32};
33
34iDefineTypeConstruction(Visited)
35
36void init_Visited(iVisited *d) {
37 init_SortedArray(&d->visited, sizeof(iVisitedUrl), cmpUrl_VisitedUrl_);
38}
39
40void deinit_Visited(iVisited *d) {
41 clear_Visited(d);
42 deinit_SortedArray(&d->visited);
43}
44
45void save_Visited(const iVisited *d, const char *dirPath) {
46 iString *line = new_String();
47 iFile *f = newCStr_File(concatPath_CStr(dirPath, "visited.txt"));
48 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
49 iConstForEach(Array, i, &d->visited.values) {
50 const iVisitedUrl *item = i.value;
51 iDate date;
52 init_Date(&date, &item->when);
53 format_String(line,
54 "%04d-%02d-%02dT%02d:%02d:%02d %s\n",
55 date.year,
56 date.month,
57 date.day,
58 date.hour,
59 date.minute,
60 date.second,
61 cstr_String(&item->url));
62 writeData_File(f, cstr_String(line), size_String(line));
63 }
64 }
65 iRelease(f);
66 delete_String(line);
67}
68
69void load_Visited(iVisited *d, const char *dirPath) {
70 iFile *f = newCStr_File(concatPath_CStr(dirPath, "visited.txt"));
71 if (open_File(f, readOnly_FileMode | text_FileMode)) {
72 const iRangecc src = range_Block(collect_Block(readAll_File(f)));
73 iRangecc line = iNullRange;
74 iTime now;
75 initCurrent_Time(&now);
76 while (nextSplit_Rangecc(&src, "\n", &line)) {
77 int y, m, D, H, M, S;
78 sscanf(line.start, "%04d-%02d-%02dT%02d:%02d:%02d ", &y, &m, &D, &H, &M, &S);
79 if (!y) break;
80 iVisitedUrl item;
81 init_VisitedUrl(&item);
82 init_Time(
83 &item.when,
84 &(iDate){ .year = y, .month = m, .day = D, .hour = H, .minute = M, .second = S });
85 if (secondsSince_Time(&now, &item.when) > maxAgeVisited_Visited_) {
86 continue; /* Too old. */
87 }
88 initRange_String(&item.url, (iRangecc){ line.start + 20, line.end });
89 insert_SortedArray(&d->visited, &item);
90 }
91 }
92 iRelease(f);
93}
94
95void clear_Visited(iVisited *d) {
96 iForEach(Array, v, &d->visited.values) {
97 deinit_VisitedUrl(v.value);
98 }
99 clear_SortedArray(&d->visited);
100}
101
102void visitUrl_Visited(iVisited *d, const iString *url) {
103 iVisitedUrl visit;
104 init_VisitedUrl(&visit);
105 set_String(&visit.url, url);
106 size_t pos;
107 if (locate_SortedArray(&d->visited, &visit, &pos)) {
108 iVisitedUrl *old = at_SortedArray(&d->visited, pos);
109 if (cmpNewer_VisitedUrl_(&visit, old)) {
110 old->when = visit.when;
111 deinit_VisitedUrl(&visit);
112 return;
113 }
114 }
115 insert_SortedArray(&d->visited, &visit);
116}
117
118iTime urlVisitTime_Visited(const iVisited *d, const iString *url) {
119 iVisitedUrl item;
120 size_t pos;
121 iZap(item);
122 initCopy_String(&item.url, url);
123 if (locate_SortedArray(&d->visited, &item, &pos)) {
124 item.when = ((const iVisitedUrl *) constAt_SortedArray(&d->visited, pos))->when;
125 }
126 deinit_String(&item.url);
127 return item.when;
128}
diff --git a/src/visited.h b/src/visited.h
new file mode 100644
index 00000000..2f8382c7
--- /dev/null
+++ b/src/visited.h
@@ -0,0 +1,25 @@
1#pragma once
2
3#include "gmrequest.h"
4
5#include <the_Foundation/array.h>
6#include <the_Foundation/string.h>
7#include <the_Foundation/time.h>
8
9iDeclareType(VisitedUrl)
10iDeclareTypeConstruction(VisitedUrl)
11
12struct Impl_VisitedUrl {
13 iString url;
14 iTime when;
15};
16
17iDeclareType(Visited)
18iDeclareTypeConstruction(Visited)
19
20void clear_Visited (iVisited *);
21void load_Visited (iVisited *, const char *dirPath);
22void save_Visited (const iVisited *, const char *dirPath);
23
24iTime urlVisitTime_Visited (const iVisited *, const iString *url);
25void visitUrl_Visited (iVisited *, const iString *url); /* adds URL to the visited URLs set */