diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-08 08:06:22 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-08 08:06:22 +0300 |
commit | d6875fba63cc674d2d9cac64a66c3b4c9f3eaba1 (patch) | |
tree | a12111a19b594fc17792a7a851d1526597470121 /src | |
parent | 9abe27c63e088bf1e139d108cbc29ac39222d74c (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.c | 41 | ||||
-rw-r--r-- | src/app.h | 4 | ||||
-rw-r--r-- | src/gmdocument.c | 4 | ||||
-rw-r--r-- | src/history.c | 111 | ||||
-rw-r--r-- | src/history.h | 46 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 23 | ||||
-rw-r--r-- | src/ui/documentwidget.h | 5 | ||||
-rw-r--r-- | src/ui/window.c | 5 | ||||
-rw-r--r-- | src/visited.c | 128 | ||||
-rw-r--r-- | src/visited.h | 25 |
10 files changed, 222 insertions, 170 deletions
@@ -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 | ||
171 | static void deinit_App(iApp *d) { | 173 | static 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 | ||
316 | iHistory *history_App(void) { | 318 | iVisited *visited_App(void) { |
317 | return app_.history; | 319 | return app_.visited; |
318 | } | 320 | } |
319 | 321 | ||
320 | static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { | 322 | static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { |
@@ -332,40 +334,43 @@ iDocumentWidget *document_App(void) { | |||
332 | } | 334 | } |
333 | 335 | ||
334 | iDocumentWidget *document_Command(const char *cmd) { | 336 | iDocumentWidget *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 | ||
346 | iBool handleCommand_App(const char *cmd) { | 351 | iBool 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); |
@@ -6,7 +6,7 @@ | |||
6 | #include <the_Foundation/time.h> | 6 | #include <the_Foundation/time.h> |
7 | 7 | ||
8 | iDeclareType(GmCerts) | 8 | iDeclareType(GmCerts) |
9 | iDeclareType(History) | 9 | iDeclareType(Visited) |
10 | iDeclareType(Window) | 10 | iDeclareType(Window) |
11 | 11 | ||
12 | enum iAppEventMode { | 12 | enum iAppEventMode { |
@@ -27,7 +27,7 @@ iBool handleCommand_App (const char *cmd); | |||
27 | void refresh_App (void); | 27 | void refresh_App (void); |
28 | 28 | ||
29 | iGmCerts * certs_App (void); | 29 | iGmCerts * certs_App (void); |
30 | iHistory * history_App (void); | 30 | iVisited * visited_App (void); |
31 | 31 | ||
32 | iAny * findWidget_App (const char *id); | 32 | iAny * findWidget_App (const char *id); |
33 | void addTicker_App (void (*ticker)(iAny *), iAny *context); | 33 | void 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 | ||
8 | static const size_t maxStack_History_ = 50; /* back/forward navigable items */ | 7 | static const size_t maxStack_History_ = 50; /* back/forward navigable items */ |
9 | static const size_t maxAgeVisited_History_ = 3600 * 24 * 30; /* one month */ | ||
10 | 8 | ||
11 | void init_RecentUrl(iRecentUrl *d) { | 9 | void 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 | ||
22 | void init_VisitedUrl(iVisitedUrl *d) { | ||
23 | initCurrent_Time(&d->when); | ||
24 | init_String(&d->url); | ||
25 | } | ||
26 | |||
27 | void deinit_VisitedUrl(iVisitedUrl *d) { | ||
28 | deinit_String(&d->url); | ||
29 | } | ||
30 | |||
31 | static int cmpUrl_VisitedUrl_(const void *a, const void *b) { | ||
32 | return cmpString_String(&((const iVisitedUrl *) a)->url, &((const iVisitedUrl *) b)->url); | ||
33 | } | ||
34 | |||
35 | static 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 | ||
42 | struct Impl_History { | 22 | struct 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 | ||
48 | iDefineTypeConstruction(History) | 27 | iDefineTypeConstruction(History) |
@@ -50,13 +29,11 @@ iDefineTypeConstruction(History) | |||
50 | void init_History(iHistory *d) { | 29 | void 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 | ||
56 | void deinit_History(iHistory *d) { | 34 | void 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 | ||
62 | void save_History(const iHistory *d, const char *dirPath) { | 39 | void 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 | ||
139 | void clear_History(iHistory *d) { | 74 | void 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 | ||
150 | iRecentUrl *recentUrl_History(iHistory *d, size_t pos) { | 81 | iRecentUrl *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 | ||
176 | static 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 | |||
192 | void replace_History(iHistory *d, const iString *url) { | 107 | void 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 | ||
201 | void addUrl_History(iHistory *d, const iString *url ){ | 115 | void 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 | |||
226 | void visitUrl_History(iHistory *d, const iString *url) { | ||
227 | addVisited_History_(d, url); | ||
228 | } | 137 | } |
229 | 138 | ||
230 | iBool goBack_History(iHistory *d) { | 139 | iBool goBack_History(iHistory *d) { |
@@ -247,18 +156,6 @@ iBool goForward_History(iHistory *d) { | |||
247 | return iFalse; | 156 | return iFalse; |
248 | } | 157 | } |
249 | 158 | ||
250 | iTime 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 | |||
262 | const iGmResponse *cachedResponse_History(const iHistory *d) { | 159 | const 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 | ||
18 | iDeclareType(VisitedUrl) | ||
19 | iDeclareTypeConstruction(VisitedUrl) | ||
20 | |||
21 | struct Impl_VisitedUrl { | ||
22 | iString url; | ||
23 | iTime when; | ||
24 | }; | ||
25 | |||
26 | /*----------------------------------------------------------------------------------------------*/ | 18 | /*----------------------------------------------------------------------------------------------*/ |
27 | 19 | ||
28 | iDeclareType(History) | 20 | iDeclareType(History) |
29 | iDeclareTypeConstruction(History) | 21 | iDeclareTypeConstruction(History) |
30 | 22 | ||
31 | void clear_History (iHistory *); | 23 | void clear_History (iHistory *); |
32 | 24 | void load_History (iHistory *, const char *dirPath); | |
33 | void load_History (iHistory *, const char *dirPath); | 25 | void add_History (iHistory *, const iString *url); |
34 | void save_History (const iHistory *, const char *dirPath); | 26 | void replace_History (iHistory *, const iString *url); |
35 | 27 | void setCachedResponse_History (iHistory *, const iGmResponse *response); | |
36 | iRecentUrl * | 28 | iBool goBack_History (iHistory *); |
37 | recentUrl_History (iHistory *, size_t pos); | 29 | iBool goForward_History (iHistory *); |
38 | iRecentUrl * | 30 | iRecentUrl *recentUrl_History (iHistory *, size_t pos); |
39 | mostRecentUrl_History (iHistory *); | 31 | iRecentUrl *mostRecentUrl_History (iHistory *); |
40 | 32 | ||
33 | void save_History (const iHistory *, const char *dirPath); | ||
41 | const iString * | 34 | const iString * |
42 | url_History (const iHistory *, size_t pos); | 35 | url_History (const iHistory *, size_t pos); |
43 | const iRecentUrl * | 36 | const iRecentUrl * |
44 | constRecentUrl_History (const iHistory *d, size_t pos); | 37 | constRecentUrl_History (const iHistory *d, size_t pos); |
45 | const iRecentUrl * | 38 | const iRecentUrl * |
46 | constMostRecentUrl_History (const iHistory *); | 39 | constMostRecentUrl_History (const iHistory *); |
47 | iTime urlVisitTime_History (const iHistory *, const iString *url); | ||
48 | const iGmResponse * | 40 | const iGmResponse * |
49 | cachedResponse_History (const iHistory *); | 41 | cachedResponse_History (const iHistory *); |
50 | 42 | ||
51 | void addUrl_History (iHistory *, const iString *url); /* adds to the stack of recents */ | ||
52 | void setCachedResponse_History (iHistory *, const iGmResponse *response); | ||
53 | void visitUrl_History (iHistory *, const iString *url); /* adds URL to the visited URLs set */ | ||
54 | void replace_History (iHistory *, const iString *url); | ||
55 | iBool goBack_History (iHistory *); | ||
56 | iBool 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 | ||
77 | struct Impl_DocumentWidget { | 77 | struct 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 | ||
158 | static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { | 161 | static 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 | ||
430 | iHistory *history_DocumentWidget(iDocumentWidget *d) { | ||
431 | return d->history; | ||
432 | } | ||
433 | |||
427 | void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { | 434 | void 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 | ||
5 | iDeclareType(History) | ||
6 | |||
5 | iDeclareWidgetClass(DocumentWidget) | 7 | iDeclareWidgetClass(DocumentWidget) |
6 | iDeclareObjectConstruction(DocumentWidget) | 8 | iDeclareObjectConstruction(DocumentWidget) |
7 | 9 | ||
10 | iHistory * history_DocumentWidget (iDocumentWidget *); | ||
11 | |||
8 | void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); | 12 | void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); |
9 | void setUrlFromCache_DocumentWidget (iDocumentWidget *d, const iString *url, iBool isFromCache); | 13 | void setUrlFromCache_DocumentWidget (iDocumentWidget *d, const iString *url, iBool isFromCache); |
10 | void setInitialScroll_DocumentWidget (iDocumentWidget *, int scrollY); /* set after content received */ | 14 | void setInitialScroll_DocumentWidget (iDocumentWidget *, int scrollY); /* set after content received */ |
15 | |||
11 | iBool isRequestOngoing_DocumentWidget (const iDocumentWidget *); | 16 | iBool 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 | |||
8 | static const size_t maxAgeVisited_Visited_ = 3600 * 24 * 30; /* one month */ | ||
9 | |||
10 | void init_VisitedUrl(iVisitedUrl *d) { | ||
11 | initCurrent_Time(&d->when); | ||
12 | init_String(&d->url); | ||
13 | } | ||
14 | |||
15 | void deinit_VisitedUrl(iVisitedUrl *d) { | ||
16 | deinit_String(&d->url); | ||
17 | } | ||
18 | |||
19 | static int cmpUrl_VisitedUrl_(const void *a, const void *b) { | ||
20 | return cmpString_String(&((const iVisitedUrl *) a)->url, &((const iVisitedUrl *) b)->url); | ||
21 | } | ||
22 | |||
23 | static 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 | |||
30 | struct Impl_Visited { | ||
31 | iSortedArray visited; | ||
32 | }; | ||
33 | |||
34 | iDefineTypeConstruction(Visited) | ||
35 | |||
36 | void init_Visited(iVisited *d) { | ||
37 | init_SortedArray(&d->visited, sizeof(iVisitedUrl), cmpUrl_VisitedUrl_); | ||
38 | } | ||
39 | |||
40 | void deinit_Visited(iVisited *d) { | ||
41 | clear_Visited(d); | ||
42 | deinit_SortedArray(&d->visited); | ||
43 | } | ||
44 | |||
45 | void 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 | |||
69 | void 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 | |||
95 | void clear_Visited(iVisited *d) { | ||
96 | iForEach(Array, v, &d->visited.values) { | ||
97 | deinit_VisitedUrl(v.value); | ||
98 | } | ||
99 | clear_SortedArray(&d->visited); | ||
100 | } | ||
101 | |||
102 | void 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 | |||
118 | iTime 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 | |||
9 | iDeclareType(VisitedUrl) | ||
10 | iDeclareTypeConstruction(VisitedUrl) | ||
11 | |||
12 | struct Impl_VisitedUrl { | ||
13 | iString url; | ||
14 | iTime when; | ||
15 | }; | ||
16 | |||
17 | iDeclareType(Visited) | ||
18 | iDeclareTypeConstruction(Visited) | ||
19 | |||
20 | void clear_Visited (iVisited *); | ||
21 | void load_Visited (iVisited *, const char *dirPath); | ||
22 | void save_Visited (const iVisited *, const char *dirPath); | ||
23 | |||
24 | iTime urlVisitTime_Visited (const iVisited *, const iString *url); | ||
25 | void visitUrl_Visited (iVisited *, const iString *url); /* adds URL to the visited URLs set */ | ||