summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-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
11 files changed, 224 insertions, 170 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 573bf09b..1f0014bd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,6 +54,8 @@ set (SOURCES
54 src/history.h 54 src/history.h
55 src/stb_image.h 55 src/stb_image.h
56 src/stb_truetype.h 56 src/stb_truetype.h
57 src/visited.c
58 src/visited.h
57 # User interface: 59 # User interface:
58 src/ui/color.c 60 src/ui/color.c
59 src/ui/color.h 61 src/ui/color.h
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 */