summaryrefslogtreecommitdiff
path: root/src/history.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/history.c')
-rw-r--r--src/history.c272
1 files changed, 151 insertions, 121 deletions
diff --git a/src/history.c b/src/history.c
index de0c36ca..a2c4271f 100644
--- a/src/history.c
+++ b/src/history.c
@@ -8,136 +8,165 @@
8static const size_t maxStack_History_ = 50; /* back/forward navigable items */ 8static const size_t maxStack_History_ = 50; /* back/forward navigable items */
9static const size_t maxAgeVisited_History_ = 3600 * 24 * 30; /* one month */ 9static const size_t maxAgeVisited_History_ = 3600 * 24 * 30; /* one month */
10 10
11void init_HistoryItem(iHistoryItem *d) { 11void init_RecentUrl(iRecentUrl *d) {
12 initCurrent_Time(&d->when);
13 init_String(&d->url); 12 init_String(&d->url);
14 d->scrollY = 0; 13 d->scrollY = 0;
14 d->cachedResponse = NULL;
15} 15}
16 16
17void deinit_HistoryItem(iHistoryItem *d) { 17void deinit_RecentUrl(iRecentUrl *d) {
18 deinit_String(&d->url); 18 deinit_String(&d->url);
19 delete_GmResponse(d->cachedResponse);
19} 20}
20 21
21struct Impl_History { 22void init_VisitedUrl(iVisitedUrl *d) {
22 iArray stack; /* TODO: should be specific to a DocumentWidget */ 23 initCurrent_Time(&d->when);
23 size_t stackPos; /* zero at the latest item */ 24 init_String(&d->url);
24 iSortedArray visitedUrls; 25}
25};
26 26
27iDefineTypeConstruction(History) 27void deinit_VisitedUrl(iVisitedUrl *d) {
28 deinit_String(&d->url);
29}
28 30
29static int cmpUrl_HistoryItem_(const void *a, const void *b) { 31static int cmpUrl_VisitedUrl_(const void *a, const void *b) {
30 return cmpString_String(&((const iHistoryItem *) a)->url, &((const iHistoryItem *) b)->url); 32 return cmpString_String(&((const iVisitedUrl *) a)->url, &((const iVisitedUrl *) b)->url);
31} 33}
32 34
33static int cmpNewer_HistoryItem_(const void *insert, const void *existing) { 35static int cmpNewer_VisitedUrl_(const void *insert, const void *existing) {
34 return seconds_Time(&((const iHistoryItem *) insert )->when) > 36 return seconds_Time(&((const iVisitedUrl *) insert )->when) >
35 seconds_Time(&((const iHistoryItem *) existing)->when); 37 seconds_Time(&((const iVisitedUrl *) existing)->when);
36} 38}
37 39
40/*----------------------------------------------------------------------------------------------*/
41
42struct Impl_History {
43 iArray recent; /* TODO: should be specific to a DocumentWidget */
44 size_t recentPos; /* zero at the latest item */
45 iSortedArray visited;
46};
47
48iDefineTypeConstruction(History)
49
38void init_History(iHistory *d) { 50void init_History(iHistory *d) {
39 init_Array(&d->stack, sizeof(iHistoryItem)); 51 init_Array(&d->recent, sizeof(iRecentUrl));
40 d->stackPos = 0; 52 d->recentPos = 0;
41 init_SortedArray(&d->visitedUrls, sizeof(iHistoryItem), cmpUrl_HistoryItem_); 53 init_SortedArray(&d->visited, sizeof(iVisitedUrl), cmpUrl_VisitedUrl_);
42} 54}
43 55
44void deinit_History(iHistory *d) { 56void deinit_History(iHistory *d) {
45 clear_History(d); 57 clear_History(d);
46 deinit_Array(&d->stack); 58 deinit_Array(&d->recent);
47} 59 deinit_SortedArray(&d->visited);
48
49static void writeItems_(const iArray *items, iFile *f) {
50 iString *line = new_String();
51 iConstForEach(Array, i, items) {
52 const iHistoryItem *item = i.value;
53 iDate date;
54 init_Date(&date, &item->when);
55 format_String(line,
56 "%04d-%02d-%02dT%02d:%02d:%02d %04x %s\n",
57 date.year,
58 date.month,
59 date.day,
60 date.hour,
61 date.minute,
62 date.second,
63 item->scrollY,
64 cstr_String(&item->url));
65 writeData_File(f, cstr_String(line), size_String(line));
66 }
67 delete_String(line);
68} 60}
69 61
70void save_History(const iHistory *d, const char *dirPath) { 62void save_History(const iHistory *d, const char *dirPath) {
63 iString *line = new_String();
71 iFile *f = newCStr_File(concatPath_CStr(dirPath, "recent.txt")); 64 iFile *f = newCStr_File(concatPath_CStr(dirPath, "recent.txt"));
72 if (open_File(f, writeOnly_FileMode | text_FileMode)) { 65 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
73 writeItems_(&d->stack, f); 66 iConstForEach(Array, i, &d->recent) {
67 const iRecentUrl *item = i.value;
68 format_String(line, "%04x %s\n", item->scrollY, cstr_String(&item->url));
69 writeData_File(f, cstr_String(line), size_String(line));
70 }
74 } 71 }
75 iRelease(f); 72 iRelease(f);
76 f = newCStr_File(concatPath_CStr(dirPath, "visited.txt")); 73 f = newCStr_File(concatPath_CStr(dirPath, "visited.txt"));
77 if (open_File(f, writeOnly_FileMode | text_FileMode)) { 74 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
78 writeItems_(&d->visitedUrls.values, f); 75 iConstForEach(Array, i, &d->visited.values) {
79 } 76 const iVisitedUrl *item = i.value;
80 iRelease(f); 77 iDate date;
81} 78 init_Date(&date, &item->when);
82 79 format_String(line,
83static void loadItems_(iArray *items, iFile *f, double maxAge) { 80 "%04d-%02d-%02dT%02d:%02d:%02d %s\n",
84 const iRangecc src = range_Block(collect_Block(readAll_File(f))); 81 date.year,
85 iRangecc line = iNullRange; 82 date.month,
86 iTime now; 83 date.day,
87 initCurrent_Time(&now); 84 date.hour,
88 while (nextSplit_Rangecc(&src, "\n", &line)) { 85 date.minute,
89 int y, m, D, H, M, S, scroll = 0; 86 date.second,
90 sscanf(line.start, "%04d-%02d-%02dT%02d:%02d:%02d %04x", &y, &m, &D, &H, &M, &S, &scroll); 87 cstr_String(&item->url));
91 if (!y) break; 88 writeData_File(f, cstr_String(line), size_String(line));
92 iHistoryItem item;
93 init_HistoryItem(&item);
94 item.scrollY = scroll;
95 init_Time(
96 &item.when,
97 &(iDate){ .year = y, .month = m, .day = D, .hour = H, .minute = M, .second = S });
98 if (maxAge > 0.0 && secondsSince_Time(&now, &item.when) > maxAge) {
99 continue; /* Too old. */
100 } 89 }
101 initRange_String(&item.url, (iRangecc){ line.start + 25, line.end });
102 pushBack_Array(items, &item);
103 } 90 }
91 iRelease(f);
92 delete_String(line);
104} 93}
105 94
106void load_History(iHistory *d, const char *dirPath) { 95void load_History(iHistory *d, const char *dirPath) {
107 iFile *f = newCStr_File(concatPath_CStr(dirPath, "recent.txt")); 96 iFile *f = newCStr_File(concatPath_CStr(dirPath, "recent.txt"));
108 if (open_File(f, readOnly_FileMode | text_FileMode)) { 97 if (open_File(f, readOnly_FileMode | text_FileMode)) {
109 loadItems_(&d->stack, f, 0); 98 const iRangecc src = range_Block(collect_Block(readAll_File(f)));
99 iRangecc line = iNullRange;
100 while (nextSplit_Rangecc(&src, "\n", &line)) {
101 iRangecc nonwhite = line;
102 trim_Rangecc(&nonwhite);
103 if (isEmpty_Range(&nonwhite)) continue;
104 int scroll = 0;
105 sscanf(nonwhite.start, "%04x", &scroll);
106 iRecentUrl item;
107 init_RecentUrl(&item);
108 item.scrollY = scroll;
109 initRange_String(&item.url, (iRangecc){ nonwhite.start + 5, nonwhite.end });
110 pushBack_Array(&d->recent, &item);
111 }
110 } 112 }
111 iRelease(f); 113 iRelease(f);
112 f = newCStr_File(concatPath_CStr(dirPath, "visited.txt")); 114 f = newCStr_File(concatPath_CStr(dirPath, "visited.txt"));
113 if (open_File(f, readOnly_FileMode | text_FileMode)) { 115 if (open_File(f, readOnly_FileMode | text_FileMode)) {
114 loadItems_(&d->visitedUrls.values, f, maxAgeVisited_History_); 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 }
115 } 135 }
116 iRelease(f); 136 iRelease(f);
117} 137}
118 138
119void clear_History(iHistory *d) { 139void clear_History(iHistory *d) {
120 iForEach(Array, s, &d->stack) { 140 iForEach(Array, s, &d->recent) {
121 deinit_HistoryItem(s.value); 141 deinit_RecentUrl(s.value);
122 } 142 }
123 clear_Array(&d->stack); 143 clear_Array(&d->recent);
124 iForEach(Array, v, &d->visitedUrls.values) { 144 iForEach(Array, v, &d->visited.values) {
125 deinit_HistoryItem(v.value); 145 deinit_VisitedUrl(v.value);
126 } 146 }
127 clear_SortedArray(&d->visitedUrls); 147 clear_SortedArray(&d->visited);
128} 148}
129 149
130iHistoryItem *itemAtPos_History(iHistory *d, size_t pos) { 150iRecentUrl *recentUrl_History(iHistory *d, size_t pos) {
131 if (isEmpty_Array(&d->stack)) return NULL; 151 if (isEmpty_Array(&d->recent)) return NULL;
132 return &value_Array(&d->stack, size_Array(&d->stack) - 1 - pos, iHistoryItem); 152 return &value_Array(&d->recent, size_Array(&d->recent) - 1 - pos, iRecentUrl);
133} 153}
134 154
135iHistoryItem *item_History(iHistory *d) { 155const iRecentUrl *constRecentUrl_History(const iHistory *d, size_t pos) {
136 return itemAtPos_History(d, d->stackPos); 156 if (isEmpty_Array(&d->recent)) return NULL;
157 return &constValue_Array(&d->recent, size_Array(&d->recent) - 1 - pos, iRecentUrl);
137} 158}
138 159
139const iString *url_History(iHistory *d, size_t pos) { 160iRecentUrl *mostRecentUrl_History(iHistory *d) {
140 const iHistoryItem *item = itemAtPos_History(d, pos); 161 return recentUrl_History(d, d->recentPos);
162}
163
164const iRecentUrl *constMostRecentUrl_History(const iHistory *d) {
165 return constRecentUrl_History(d, d->recentPos);
166}
167
168const iString *url_History(const iHistory *d, size_t pos) {
169 const iRecentUrl *item = constRecentUrl_History(d, pos);
141 if (item) { 170 if (item) {
142 return &item->url; 171 return &item->url;
143 } 172 }
@@ -145,24 +174,24 @@ const iString *url_History(iHistory *d, size_t pos) {
145} 174}
146 175
147static void addVisited_History_(iHistory *d, const iString *url) { 176static void addVisited_History_(iHistory *d, const iString *url) {
148 iHistoryItem visit; 177 iVisitedUrl visit;
149 init_HistoryItem(&visit); 178 init_VisitedUrl(&visit);
150 set_String(&visit.url, url); 179 set_String(&visit.url, url);
151 size_t pos; 180 size_t pos;
152 if (locate_SortedArray(&d->visitedUrls, &visit, &pos)) { 181 if (locate_SortedArray(&d->visited, &visit, &pos)) {
153 iHistoryItem *old = at_SortedArray(&d->visitedUrls, pos); 182 iVisitedUrl *old = at_SortedArray(&d->visited, pos);
154 if (cmpNewer_HistoryItem_(&visit, old)) { 183 if (cmpNewer_VisitedUrl_(&visit, old)) {
155 old->when = visit.when; 184 old->when = visit.when;
156 deinit_HistoryItem(&visit); 185 deinit_VisitedUrl(&visit);
157 return; 186 return;
158 } 187 }
159 } 188 }
160 insert_SortedArray(&d->visitedUrls, &visit); 189 insert_SortedArray(&d->visited, &visit);
161} 190}
162 191
163void replace_History(iHistory *d, const iString *url) { 192void replace_History(iHistory *d, const iString *url) {
164 /* Update in the history. */ 193 /* Update in the history. */
165 iHistoryItem *item = item_History(d); 194 iRecentUrl *item = mostRecentUrl_History(d);
166 if (item) { 195 if (item) {
167 set_String(&item->url, url); 196 set_String(&item->url, url);
168 } 197 }
@@ -171,24 +200,24 @@ void replace_History(iHistory *d, const iString *url) {
171 200
172void addUrl_History(iHistory *d, const iString *url ){ 201void addUrl_History(iHistory *d, const iString *url ){
173 /* Cut the trailing history items. */ 202 /* Cut the trailing history items. */
174 if (d->stackPos > 0) { 203 if (d->recentPos > 0) {
175 for (size_t i = 0; i < d->stackPos - 1; i++) { 204 for (size_t i = 0; i < d->recentPos - 1; i++) {
176 deinit_HistoryItem(itemAtPos_History(d, i)); 205 deinit_RecentUrl(recentUrl_History(d, i));
177 } 206 }
178 removeN_Array(&d->stack, size_Array(&d->stack) - d->stackPos, iInvalidSize); 207 removeN_Array(&d->recent, size_Array(&d->recent) - d->recentPos, iInvalidSize);
179 d->stackPos = 0; 208 d->recentPos = 0;
180 } 209 }
181 /* Insert new item. */ 210 /* Insert new item. */
182 const iHistoryItem *lastItem = itemAtPos_History(d, 0); 211 const iRecentUrl *lastItem = recentUrl_History(d, 0);
183 if (!lastItem || cmpString_String(&lastItem->url, url) != 0) { 212 if (!lastItem || cmpString_String(&lastItem->url, url) != 0) {
184 iHistoryItem item; 213 iRecentUrl item;
185 init_HistoryItem(&item); 214 init_RecentUrl(&item);
186 set_String(&item.url, url); 215 set_String(&item.url, url);
187 pushBack_Array(&d->stack, &item); 216 pushBack_Array(&d->recent, &item);
188 /* Limit the number of items. */ 217 /* Limit the number of items. */
189 if (size_Array(&d->stack) > maxStack_History_) { 218 if (size_Array(&d->recent) > maxStack_History_) {
190 deinit_HistoryItem(front_Array(&d->stack)); 219 deinit_RecentUrl(front_Array(&d->recent));
191 remove_Array(&d->stack, 0); 220 remove_Array(&d->recent, 0);
192 } 221 }
193 } 222 }
194 addVisited_History_(d, url); 223 addVisited_History_(d, url);
@@ -199,48 +228,49 @@ void visitUrl_History(iHistory *d, const iString *url) {
199} 228}
200 229
201iBool goBack_History(iHistory *d) { 230iBool goBack_History(iHistory *d) {
202 if (d->stackPos < size_Array(&d->stack) - 1) { 231 if (d->recentPos < size_Array(&d->recent) - 1) {
203 d->stackPos++; 232 d->recentPos++;
204 postCommandf_App("open history:1 scroll:%d url:%s", 233 postCommandf_App("open history:1 scroll:%d url:%s",
205 item_History(d)->scrollY, 234 mostRecentUrl_History(d)->scrollY,
206 cstr_String(url_History(d, d->stackPos))); 235 cstr_String(url_History(d, d->recentPos)));
207 return iTrue; 236 return iTrue;
208 } 237 }
209 return iFalse; 238 return iFalse;
210} 239}
211 240
212iBool goForward_History(iHistory *d) { 241iBool goForward_History(iHistory *d) {
213 if (d->stackPos > 0) { 242 if (d->recentPos > 0) {
214 d->stackPos--; 243 d->recentPos--;
215 postCommandf_App("open history:1 url:%s", cstr_String(url_History(d, d->stackPos))); 244 postCommandf_App("open history:1 url:%s", cstr_String(url_History(d, d->recentPos)));
216 return iTrue; 245 return iTrue;
217 } 246 }
218 return iFalse; 247 return iFalse;
219} 248}
220 249
221iTime urlVisitTime_History(const iHistory *d, const iString *url) { 250iTime urlVisitTime_History(const iHistory *d, const iString *url) {
222 iHistoryItem item; 251 iVisitedUrl item;
223 size_t pos; 252 size_t pos;
224 iZap(item); 253 iZap(item);
225 initCopy_String(&item.url, url); 254 initCopy_String(&item.url, url);
226 if (locate_SortedArray(&d->visitedUrls, &item, &pos)) { 255 if (locate_SortedArray(&d->visited, &item, &pos)) {
227 item.when = ((const iHistoryItem *) constAt_SortedArray(&d->visitedUrls, pos))->when; 256 item.when = ((const iVisitedUrl *) constAt_SortedArray(&d->visited, pos))->when;
228 } 257 }
229 deinit_String(&item.url); 258 deinit_String(&item.url);
230 return item.when; 259 return item.when;
231} 260}
232 261
233void print_History(const iHistory *d) { 262const iGmResponse *cachedResponse_History(const iHistory *d) {
234 iUnused(d); 263 const iRecentUrl *item = constMostRecentUrl_History(d);
235#if 0 264 return item ? item->cachedResponse : NULL;
236 iConstForEach(Array, i, &d->history) {
237 const size_t idx = index_ArrayConstIterator(&i);
238 printf("%s[%zu]: %s\n",
239 d->historyPos == size_Array(&d->history) - idx - 1 ? "->" : " ",
240 idx,
241 cstr_String(&((const iHistoryItem *) i.value)->url));
242 }
243 fflush(stdout);
244#endif
245} 265}
246 266
267void setCachedResponse_History(iHistory *d, const iGmResponse *response) {
268 iRecentUrl *item = mostRecentUrl_History(d);
269 if (item) {
270 delete_GmResponse(item->cachedResponse);
271 item->cachedResponse = NULL;
272 if (category_GmStatusCode(response->statusCode) == categorySuccess_GmStatusCode) {
273 item->cachedResponse = copy_GmResponse(response);
274 }
275 }
276}