diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/app.c | 150 | ||||
-rw-r--r-- | src/app.h | 6 | ||||
-rw-r--r-- | src/gmdocument.h | 1 | ||||
-rw-r--r-- | src/history.c | 158 | ||||
-rw-r--r-- | src/history.h | 41 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 8 | ||||
-rw-r--r-- | src/ui/text.c | 7 |
8 files changed, 226 insertions, 147 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 43260264..5048360e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -42,6 +42,8 @@ set (SOURCES | |||
42 | src/gmrequest.h | 42 | src/gmrequest.h |
43 | src/gmutil.c | 43 | src/gmutil.c |
44 | src/gmutil.h | 44 | src/gmutil.h |
45 | src/history.c | ||
46 | src/history.h | ||
45 | src/stb_image.h | 47 | src/stb_image.h |
46 | src/stb_truetype.h | 48 | src/stb_truetype.h |
47 | # User interface: | 49 | # User interface: |
@@ -1,5 +1,6 @@ | |||
1 | #include "app.h" | 1 | #include "app.h" |
2 | #include "embedded.h" | 2 | #include "embedded.h" |
3 | #include "history.h" | ||
3 | #include "ui/command.h" | 4 | #include "ui/command.h" |
4 | #include "ui/window.h" | 5 | #include "ui/window.h" |
5 | #include "ui/inputwidget.h" | 6 | #include "ui/inputwidget.h" |
@@ -29,21 +30,6 @@ | |||
29 | #endif | 30 | #endif |
30 | 31 | ||
31 | iDeclareType(App) | 32 | iDeclareType(App) |
32 | iDeclareType(HistoryItem) | ||
33 | |||
34 | struct Impl_HistoryItem { | ||
35 | iTime when; | ||
36 | iString url; | ||
37 | }; | ||
38 | |||
39 | void init_HistoryItem(iHistoryItem *d) { | ||
40 | initCurrent_Time(&d->when); | ||
41 | init_String(&d->url); | ||
42 | } | ||
43 | |||
44 | void deinit_HistoryItem(iHistoryItem *d) { | ||
45 | deinit_String(&d->url); | ||
46 | } | ||
47 | 33 | ||
48 | #if defined (iPlatformApple) | 34 | #if defined (iPlatformApple) |
49 | static const char *dataDir_App_ = "~/Library/Application Support/fi.skyjake.Lagrange"; | 35 | static const char *dataDir_App_ = "~/Library/Application Support/fi.skyjake.Lagrange"; |
@@ -57,16 +43,13 @@ static const char *dataDir_App_ = "~/.config/lagrange"; | |||
57 | static const char *prefsFileName_App_ = "prefs.cfg"; | 43 | static const char *prefsFileName_App_ = "prefs.cfg"; |
58 | static const char *historyFileName_App_ = "history.txt"; | 44 | static const char *historyFileName_App_ = "history.txt"; |
59 | 45 | ||
60 | static const size_t historyMax_App_ = 100; | ||
61 | |||
62 | struct Impl_App { | 46 | struct Impl_App { |
63 | iCommandLine args; | 47 | iCommandLine args; |
64 | iBool running; | 48 | iBool running; |
65 | iWindow * window; | 49 | iWindow * window; |
66 | iSortedArray tickers; | 50 | iSortedArray tickers; |
67 | iBool pendingRefresh; | 51 | iBool pendingRefresh; |
68 | iArray history; | 52 | iHistory history; |
69 | size_t historyPos; /* zero at the latest item */ | ||
70 | /* Preferences: */ | 53 | /* Preferences: */ |
71 | iBool retainWindowSize; | 54 | iBool retainWindowSize; |
72 | float uiScale; | 55 | float uiScale; |
@@ -156,71 +139,16 @@ static void savePrefs_App_(const iApp *d) { | |||
156 | delete_String(cfg); | 139 | delete_String(cfg); |
157 | } | 140 | } |
158 | 141 | ||
159 | static void saveHistory_App_(const iApp *d) { | ||
160 | iFile *f = new_File(historyFileName_()); | ||
161 | if (open_File(f, writeOnly_FileMode | text_FileMode)) { | ||
162 | iString *line = new_String(); | ||
163 | iConstForEach(Array, i, &d->history) { | ||
164 | const iHistoryItem *item = i.value; | ||
165 | iDate date; | ||
166 | init_Date(&date, &item->when); | ||
167 | format_String(line, | ||
168 | "%04d-%02d-%02dT%02d:%02d:%02d %s\n", | ||
169 | date.year, | ||
170 | date.month, | ||
171 | date.day, | ||
172 | date.hour, | ||
173 | date.minute, | ||
174 | date.second, | ||
175 | cstr_String(&item->url)); | ||
176 | writeData_File(f, cstr_String(line), size_String(line)); | ||
177 | } | ||
178 | delete_String(line); | ||
179 | } | ||
180 | iRelease(f); | ||
181 | } | ||
182 | |||
183 | static void loadHistory_App_(iApp *d) { | ||
184 | iFile *f = new_File(historyFileName_()); | ||
185 | if (open_File(f, readOnly_FileMode | text_FileMode)) { | ||
186 | iString *src = newBlock_String(collect_Block(readAll_File(f))); | ||
187 | const iRangecc range = range_String(src); | ||
188 | iRangecc line = iNullRange; | ||
189 | while (nextSplit_Rangecc(&range, "\n", &line)) { | ||
190 | int y, m, D, H, M, S; | ||
191 | sscanf(line.start, "%04d-%02d-%02dT%02d:%02d:%02d", &y, &m, &D, &H, &M, &S); | ||
192 | if (!y) break; | ||
193 | iHistoryItem item; | ||
194 | init_HistoryItem(&item); | ||
195 | init_Time( | ||
196 | &item.when, | ||
197 | &(iDate){ .year = y, .month = m, .day = D, .hour = H, .minute = M, .second = S }); | ||
198 | initCStrN_String(&item.url, line.start + 20, size_Range(&line) - 20); | ||
199 | pushBack_Array(&d->history, &item); | ||
200 | } | ||
201 | delete_String(src); | ||
202 | } | ||
203 | iRelease(f); | ||
204 | } | ||
205 | |||
206 | static void clearHistory_App_(iApp *d) { | ||
207 | iForEach(Array, i, &d->history) { | ||
208 | deinit_HistoryItem(i.value); | ||
209 | } | ||
210 | clear_Array(&d->history); | ||
211 | } | ||
212 | |||
213 | static void init_App_(iApp *d, int argc, char **argv) { | 142 | static void init_App_(iApp *d, int argc, char **argv) { |
214 | init_CommandLine(&d->args, argc, argv); | 143 | init_CommandLine(&d->args, argc, argv); |
215 | init_SortedArray(&d->tickers, sizeof(iTicker), cmp_Ticker_); | 144 | init_SortedArray(&d->tickers, sizeof(iTicker), cmp_Ticker_); |
216 | init_Array(&d->history, sizeof(iHistoryItem)); | ||
217 | d->historyPos = 0; | ||
218 | d->running = iFalse; | 145 | d->running = iFalse; |
219 | d->window = NULL; | 146 | d->window = NULL; |
220 | d->retainWindowSize = iTrue; | 147 | d->retainWindowSize = iTrue; |
221 | d->pendingRefresh = iFalse; | 148 | d->pendingRefresh = iFalse; |
149 | init_History(&d->history); | ||
222 | loadPrefs_App_(d); | 150 | loadPrefs_App_(d); |
223 | loadHistory_App_(d); | 151 | load_History(&d->history, historyFileName_()); |
224 | #if defined (iHaveLoadEmbed) | 152 | #if defined (iHaveLoadEmbed) |
225 | /* Load the resources from a file. */ { | 153 | /* Load the resources from a file. */ { |
226 | if (!load_Embed( | 154 | if (!load_Embed( |
@@ -238,9 +166,8 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
238 | 166 | ||
239 | static void deinit_App(iApp *d) { | 167 | static void deinit_App(iApp *d) { |
240 | savePrefs_App_(d); | 168 | savePrefs_App_(d); |
241 | saveHistory_App_(d); | 169 | save_History(&d->history, historyFileName_()); |
242 | clearHistory_App_(d); | 170 | deinit_History(&d->history); |
243 | deinit_Array(&d->history); | ||
244 | deinit_SortedArray(&d->tickers); | 171 | deinit_SortedArray(&d->tickers); |
245 | delete_Window(d->window); | 172 | delete_Window(d->window); |
246 | d->window = NULL; | 173 | d->window = NULL; |
@@ -393,32 +320,6 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { | |||
393 | return iFalse; | 320 | return iFalse; |
394 | } | 321 | } |
395 | 322 | ||
396 | static iHistoryItem *historyItem_App_(iApp *d, size_t pos) { | ||
397 | if (isEmpty_Array(&d->history)) return NULL; | ||
398 | return &value_Array(&d->history, size_Array(&d->history) - 1 - pos, iHistoryItem); | ||
399 | } | ||
400 | |||
401 | static const iString *historyUrl_App_(iApp *d, size_t pos) { | ||
402 | const iHistoryItem *item = historyItem_App_(d, pos); | ||
403 | if (item) { | ||
404 | return &item->url; | ||
405 | } | ||
406 | return collectNew_String(); | ||
407 | } | ||
408 | |||
409 | static void printHistory_App_(const iApp *d) { | ||
410 | #if 0 | ||
411 | iConstForEach(Array, i, &d->history) { | ||
412 | const size_t idx = index_ArrayConstIterator(&i); | ||
413 | printf("%s[%zu]: %s\n", | ||
414 | d->historyPos == size_Array(&d->history) - idx - 1 ? "->" : " ", | ||
415 | idx, | ||
416 | cstr_String(&((const iHistoryItem *) i.value)->url)); | ||
417 | } | ||
418 | fflush(stdout); | ||
419 | #endif | ||
420 | } | ||
421 | |||
422 | iBool handleCommand_App(const char *cmd) { | 323 | iBool handleCommand_App(const char *cmd) { |
423 | iApp *d = &app_; | 324 | iApp *d = &app_; |
424 | iWidget *root = d->window->root; | 325 | iWidget *root = d->window->root; |
@@ -427,37 +328,16 @@ iBool handleCommand_App(const char *cmd) { | |||
427 | if (!argLabel_Command(cmd, "history")) { | 328 | if (!argLabel_Command(cmd, "history")) { |
428 | if (argLabel_Command(cmd, "redirect")) { | 329 | if (argLabel_Command(cmd, "redirect")) { |
429 | /* Update in the history. */ | 330 | /* Update in the history. */ |
430 | iHistoryItem *item = historyItem_App_(d, d->historyPos); | 331 | iHistoryItem *item = item_History(&d->history); |
431 | if (item) { | 332 | if (item) { |
432 | set_String(&item->url, url); | 333 | set_String(&item->url, url); |
433 | } | 334 | } |
434 | } | 335 | } |
435 | else { | 336 | else { |
436 | /* Cut the trailing history items. */ | 337 | addUrl_History(&d->history, url); |
437 | if (d->historyPos > 0) { | ||
438 | for (size_t i = 0; i < d->historyPos - 1; i++) { | ||
439 | deinit_HistoryItem(historyItem_App_(d, i)); | ||
440 | } | ||
441 | removeN_Array( | ||
442 | &d->history, size_Array(&d->history) - d->historyPos, iInvalidSize); | ||
443 | d->historyPos = 0; | ||
444 | } | ||
445 | /* Insert new item. */ | ||
446 | const iHistoryItem *lastItem = historyItem_App_(d, 0); | ||
447 | if (!lastItem || cmpString_String(&lastItem->url, url) != 0) { | ||
448 | iHistoryItem item; | ||
449 | init_HistoryItem(&item); | ||
450 | set_String(&item.url, url); | ||
451 | pushBack_Array(&d->history, &item); | ||
452 | /* Don't make it too long. */ | ||
453 | if (size_Array(&d->history) > historyMax_App_) { | ||
454 | deinit_HistoryItem(front_Array(&d->history)); | ||
455 | remove_Array(&d->history, 0); | ||
456 | } | ||
457 | } | ||
458 | } | 338 | } |
459 | } | 339 | } |
460 | printHistory_App_(d); | 340 | print_History(&d->history); |
461 | setUrl_DocumentWidget(findChild_Widget(root, "document"), url); | 341 | setUrl_DocumentWidget(findChild_Widget(root, "document"), url); |
462 | } | 342 | } |
463 | else if (equal_Command(cmd, "document.request.cancelled")) { | 343 | else if (equal_Command(cmd, "document.request.cancelled")) { |
@@ -499,19 +379,11 @@ iBool handleCommand_App(const char *cmd) { | |||
499 | return iFalse; | 379 | return iFalse; |
500 | } | 380 | } |
501 | else if (equal_Command(cmd, "navigate.back")) { | 381 | else if (equal_Command(cmd, "navigate.back")) { |
502 | if (d->historyPos < size_Array(&d->history) - 1) { | 382 | goBack_History(&d->history); |
503 | d->historyPos++; | ||
504 | postCommandf_App("open history:1 url:%s", | ||
505 | cstr_String(historyUrl_App_(d, d->historyPos))); | ||
506 | } | ||
507 | return iTrue; | 383 | return iTrue; |
508 | } | 384 | } |
509 | else if (equal_Command(cmd, "navigate.forward")) { | 385 | else if (equal_Command(cmd, "navigate.forward")) { |
510 | if (d->historyPos > 0) { | 386 | goForward_History(&d->history); |
511 | d->historyPos--; | ||
512 | postCommandf_App("open history:1 url:%s", | ||
513 | cstr_String(historyUrl_App_(d, d->historyPos))); | ||
514 | } | ||
515 | return iTrue; | 387 | return iTrue; |
516 | } | 388 | } |
517 | else if (equal_Command(cmd, "navigate.home")) { | 389 | else if (equal_Command(cmd, "navigate.home")) { |
@@ -3,6 +3,7 @@ | |||
3 | /* Application core: event loop, base event processing, audio synth. */ | 3 | /* Application core: event loop, base event processing, audio synth. */ |
4 | 4 | ||
5 | #include <the_Foundation/string.h> | 5 | #include <the_Foundation/string.h> |
6 | #include <the_Foundation/time.h> | ||
6 | 7 | ||
7 | iDeclareType(Window) | 8 | iDeclareType(Window) |
8 | 9 | ||
@@ -20,11 +21,12 @@ const iString *execPath_App (void); | |||
20 | 21 | ||
21 | int run_App (int argc, char **argv); | 22 | int run_App (int argc, char **argv); |
22 | void processEvents_App (enum iAppEventMode mode); | 23 | void processEvents_App (enum iAppEventMode mode); |
24 | iBool handleCommand_App (const char *cmd); | ||
23 | void refresh_App (void); | 25 | void refresh_App (void); |
24 | 26 | ||
27 | iTime urlVisitTime_App (const iString *url); | ||
25 | iAny * findWidget_App (const char *id); | 28 | iAny * findWidget_App (const char *id); |
26 | void addTicker_App (void (*ticker)(iAny *), iAny *context); | 29 | void addTicker_App (void (*ticker)(iAny *), iAny *context); |
27 | |||
28 | void postRefresh_App (void); | 30 | void postRefresh_App (void); |
29 | void postCommand_App (const char *command); | 31 | void postCommand_App (const char *command); |
30 | void postCommandf_App (const char *command, ...); | 32 | void postCommandf_App (const char *command, ...); |
@@ -32,5 +34,3 @@ void postCommandf_App (const char *command, ...); | |||
32 | iLocalDef void postCommandString_App(const iString *command) { | 34 | iLocalDef void postCommandString_App(const iString *command) { |
33 | postCommand_App(cstr_String(command)); | 35 | postCommand_App(cstr_String(command)); |
34 | } | 36 | } |
35 | |||
36 | iBool handleCommand_App (const char *cmd); | ||
diff --git a/src/gmdocument.h b/src/gmdocument.h index c353c733..4997398b 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -20,6 +20,7 @@ enum iGmLinkFlags { | |||
20 | imageFileExtension_GmLinkFlag = iBit(6), | 20 | imageFileExtension_GmLinkFlag = iBit(6), |
21 | audioFileExtension_GmLinkFlag = iBit(7), | 21 | audioFileExtension_GmLinkFlag = iBit(7), |
22 | content_GmLinkFlag = iBit(8), /* content visible below */ | 22 | content_GmLinkFlag = iBit(8), /* content visible below */ |
23 | visited_GmLinkFlag = iBit(9), /* in the history */ | ||
23 | }; | 24 | }; |
24 | 25 | ||
25 | iDeclareType(GmImageInfo) | 26 | iDeclareType(GmImageInfo) |
diff --git a/src/history.c b/src/history.c new file mode 100644 index 00000000..94b88677 --- /dev/null +++ b/src/history.c | |||
@@ -0,0 +1,158 @@ | |||
1 | #include "history.h" | ||
2 | #include "app.h" | ||
3 | |||
4 | #include <the_Foundation/file.h> | ||
5 | |||
6 | static const size_t maxSize_History_ = 5000; | ||
7 | |||
8 | void init_HistoryItem(iHistoryItem *d) { | ||
9 | initCurrent_Time(&d->when); | ||
10 | init_String(&d->url); | ||
11 | } | ||
12 | |||
13 | void deinit_HistoryItem(iHistoryItem *d) { | ||
14 | deinit_String(&d->url); | ||
15 | } | ||
16 | |||
17 | void init_History(iHistory *d) { | ||
18 | init_Array(&d->history, sizeof(iHistoryItem)); | ||
19 | d->historyPos = 0; | ||
20 | } | ||
21 | |||
22 | void deinit_History(iHistory *d) { | ||
23 | clear_History(d); | ||
24 | deinit_Array(&d->history); | ||
25 | } | ||
26 | |||
27 | void save_History(const iHistory *d, const iString *path) { | ||
28 | iFile *f = new_File(path); | ||
29 | if (open_File(f, writeOnly_FileMode | text_FileMode)) { | ||
30 | iString *line = new_String(); | ||
31 | iConstForEach(Array, i, &d->history) { | ||
32 | const iHistoryItem *item = i.value; | ||
33 | iDate date; | ||
34 | init_Date(&date, &item->when); | ||
35 | format_String(line, | ||
36 | "%04d-%02d-%02dT%02d:%02d:%02d %s\n", | ||
37 | date.year, | ||
38 | date.month, | ||
39 | date.day, | ||
40 | date.hour, | ||
41 | date.minute, | ||
42 | date.second, | ||
43 | cstr_String(&item->url)); | ||
44 | writeData_File(f, cstr_String(line), size_String(line)); | ||
45 | } | ||
46 | delete_String(line); | ||
47 | } | ||
48 | iRelease(f); | ||
49 | } | ||
50 | |||
51 | void load_History(iHistory *d, const iString *path) { | ||
52 | iFile *f = new_File(path); | ||
53 | if (open_File(f, readOnly_FileMode | text_FileMode)) { | ||
54 | iString *src = newBlock_String(collect_Block(readAll_File(f))); | ||
55 | const iRangecc range = range_String(src); | ||
56 | iRangecc line = iNullRange; | ||
57 | while (nextSplit_Rangecc(&range, "\n", &line)) { | ||
58 | int y, m, D, H, M, S; | ||
59 | sscanf(line.start, "%04d-%02d-%02dT%02d:%02d:%02d", &y, &m, &D, &H, &M, &S); | ||
60 | if (!y) break; | ||
61 | iHistoryItem item; | ||
62 | init_HistoryItem(&item); | ||
63 | init_Time( | ||
64 | &item.when, | ||
65 | &(iDate){ .year = y, .month = m, .day = D, .hour = H, .minute = M, .second = S }); | ||
66 | initRange_String(&item.url, (iRangecc){ line.start + 20, line.end }); | ||
67 | pushBack_Array(&d->history, &item); | ||
68 | } | ||
69 | delete_String(src); | ||
70 | } | ||
71 | iRelease(f); | ||
72 | } | ||
73 | |||
74 | void clear_History(iHistory *d) { | ||
75 | iForEach(Array, i, &d->history) { | ||
76 | deinit_HistoryItem(i.value); | ||
77 | } | ||
78 | clear_Array(&d->history); | ||
79 | } | ||
80 | |||
81 | iHistoryItem *itemAtPos_History(iHistory *d, size_t pos) { | ||
82 | if (isEmpty_Array(&d->history)) return NULL; | ||
83 | return &value_Array(&d->history, size_Array(&d->history) - 1 - pos, iHistoryItem); | ||
84 | } | ||
85 | |||
86 | const iString *url_History(iHistory *d, size_t pos) { | ||
87 | const iHistoryItem *item = itemAtPos_History(d, pos); | ||
88 | if (item) { | ||
89 | return &item->url; | ||
90 | } | ||
91 | return collectNew_String(); | ||
92 | } | ||
93 | |||
94 | void addUrl_History(iHistory *d, const iString *url ){ | ||
95 | /* Cut the trailing history items. */ | ||
96 | if (d->historyPos > 0) { | ||
97 | for (size_t i = 0; i < d->historyPos - 1; i++) { | ||
98 | deinit_HistoryItem(itemAtPos_History(d, i)); | ||
99 | } | ||
100 | removeN_Array( | ||
101 | &d->history, size_Array(&d->history) - d->historyPos, iInvalidSize); | ||
102 | d->historyPos = 0; | ||
103 | } | ||
104 | /* Insert new item. */ | ||
105 | const iHistoryItem *lastItem = itemAtPos_History(d, 0); | ||
106 | if (!lastItem || cmpString_String(&lastItem->url, url) != 0) { | ||
107 | iHistoryItem item; | ||
108 | init_HistoryItem(&item); | ||
109 | set_String(&item.url, url); | ||
110 | pushBack_Array(&d->history, &item); | ||
111 | /* Don't make it too long. */ | ||
112 | if (size_Array(&d->history) > maxSize_History_) { | ||
113 | deinit_HistoryItem(front_Array(&d->history)); | ||
114 | remove_Array(&d->history, 0); | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | |||
119 | iBool goBack_History(iHistory *d) { | ||
120 | if (d->historyPos < size_Array(&d->history) - 1) { | ||
121 | d->historyPos++; | ||
122 | postCommandf_App("open history:1 url:%s", | ||
123 | cstr_String(url_History(d, d->historyPos))); | ||
124 | return iTrue; | ||
125 | } | ||
126 | return iFalse; | ||
127 | } | ||
128 | |||
129 | iBool goForward_History(iHistory *d) { | ||
130 | if (d->historyPos > 0) { | ||
131 | d->historyPos--; | ||
132 | postCommandf_App("open history:1 url:%s", | ||
133 | cstr_String(url_History(d, d->historyPos))); | ||
134 | return iTrue; | ||
135 | } | ||
136 | return iFalse; | ||
137 | } | ||
138 | |||
139 | iTime urlVisitTime_History(const iHistory *d, const iString *url) { | ||
140 | iTime when; | ||
141 | iZap(when); | ||
142 | |||
143 | return when; | ||
144 | } | ||
145 | |||
146 | void print_History(const iHistory *d) { | ||
147 | #if 0 | ||
148 | iConstForEach(Array, i, &d->history) { | ||
149 | const size_t idx = index_ArrayConstIterator(&i); | ||
150 | printf("%s[%zu]: %s\n", | ||
151 | d->historyPos == size_Array(&d->history) - idx - 1 ? "->" : " ", | ||
152 | idx, | ||
153 | cstr_String(&((const iHistoryItem *) i.value)->url)); | ||
154 | } | ||
155 | fflush(stdout); | ||
156 | #endif | ||
157 | } | ||
158 | |||
diff --git a/src/history.h b/src/history.h new file mode 100644 index 00000000..15a179d6 --- /dev/null +++ b/src/history.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <the_Foundation/array.h> | ||
4 | #include <the_Foundation/string.h> | ||
5 | #include <the_Foundation/time.h> | ||
6 | |||
7 | iDeclareType(History) | ||
8 | iDeclareType(HistoryItem) | ||
9 | |||
10 | struct Impl_HistoryItem { | ||
11 | iTime when; | ||
12 | iString url; | ||
13 | }; | ||
14 | |||
15 | iDeclareTypeConstruction(HistoryItem) | ||
16 | |||
17 | struct Impl_History { | ||
18 | iArray history; | ||
19 | size_t historyPos; /* zero at the latest item */ | ||
20 | }; | ||
21 | |||
22 | iDeclareTypeConstruction(History) | ||
23 | |||
24 | void clear_History (iHistory *); | ||
25 | |||
26 | void load_History (iHistory *, const iString *path); | ||
27 | void save_History (const iHistory *, const iString *path); | ||
28 | |||
29 | iHistoryItem * itemAtPos_History (iHistory *, size_t pos); | ||
30 | const iString * url_History (iHistory *, size_t pos); | ||
31 | iTime urlVisitTime_History(const iHistory *, const iString *url); | ||
32 | void print_History (const iHistory *); | ||
33 | |||
34 | iLocalDef iHistoryItem *item_History(iHistory *d) { | ||
35 | return itemAtPos_History(d, d->historyPos); | ||
36 | } | ||
37 | |||
38 | void addUrl_History (iHistory *, const iString *url); | ||
39 | |||
40 | iBool goBack_History (iHistory *); | ||
41 | iBool goForward_History (iHistory *); | ||
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index e908f090..52628143 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -903,10 +903,10 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
903 | /* Text markers. */ | 903 | /* Text markers. */ |
904 | fillRange_DrawContext_(d, run, teal_ColorId, d->widget->foundMark, &d->inFoundMark); | 904 | fillRange_DrawContext_(d, run, teal_ColorId, d->widget->foundMark, &d->inFoundMark); |
905 | fillRange_DrawContext_(d, run, brown_ColorId, d->widget->selectMark, &d->inSelectMark); | 905 | fillRange_DrawContext_(d, run, brown_ColorId, d->widget->selectMark, &d->inSelectMark); |
906 | if (run->linkId) { | 906 | if (run->linkId && !isEmpty_Rect(run->bounds)) { |
907 | fg = linkColor_GmDocument(doc, run->linkId); | 907 | fg = white_ColorId; |
908 | if (isHover && ~linkFlags_GmDocument(doc, run->linkId) & content_GmLinkFlag) { | 908 | if (isHover || linkFlags_GmDocument(doc, run->linkId) & content_GmLinkFlag) { |
909 | fg = white_ColorId; | 909 | fg = linkColor_GmDocument(doc, run->linkId); |
910 | } | 910 | } |
911 | } | 911 | } |
912 | drawString_Text(run->font, visPos, fg, &text); | 912 | drawString_Text(run->font, visPos, fg, &text); |
diff --git a/src/ui/text.c b/src/ui/text.c index 12255fdb..ad1c37c8 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -298,6 +298,11 @@ static iChar nextChar_(const char **chPos, const char *end) { | |||
298 | 298 | ||
299 | int enableHalfPixelGlyphs_Text = iTrue; | 299 | int enableHalfPixelGlyphs_Text = iTrue; |
300 | 300 | ||
301 | iLocalDef iBool isWrapBoundary_(iChar a, iChar b) { | ||
302 | if (b == '/' || b == '-') return iTrue; | ||
303 | return !isSpace_Char(a) && isSpace_Char(b); | ||
304 | } | ||
305 | |||
301 | static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos, | 306 | static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos, |
302 | int xposLimit, const char **continueFrom_out, int *runAdvance_out) { | 307 | int xposLimit, const char **continueFrom_out, int *runAdvance_out) { |
303 | iInt2 size = zero_I2(); | 308 | iInt2 size = zero_I2(); |
@@ -364,7 +369,7 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
364 | } | 369 | } |
365 | xpos += glyph->advance; | 370 | xpos += glyph->advance; |
366 | xposMax = iMax(xposMax, xpos); | 371 | xposMax = iMax(xposMax, xpos); |
367 | if (mode == measureNoWrap_RunMode || (!isSpace_Char(prevCh) && isSpace_Char(ch))) { | 372 | if (mode == measureNoWrap_RunMode || isWrapBoundary_(prevCh, ch)) { |
368 | lastWordEnd = chPos; | 373 | lastWordEnd = chPos; |
369 | } | 374 | } |
370 | /* Check the next character. */ | 375 | /* Check the next character. */ |