diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-12 10:28:02 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-12 10:28:02 +0300 |
commit | 1d54f7b990ea7f676403681577fc4df984cab0be (patch) | |
tree | 94a8ac83159f021dbbb354a6d76ecbaf83a63aad /src | |
parent | 38e09f15ff3e9c4781236016bfbb0b0f9062590b (diff) |
Save and load app state (tabs, history)
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 66 | ||||
-rw-r--r-- | src/app.h | 2 | ||||
-rw-r--r-- | src/gmrequest.c | 18 | ||||
-rw-r--r-- | src/gmrequest.h | 1 | ||||
-rw-r--r-- | src/history.c | 66 | ||||
-rw-r--r-- | src/history.h | 4 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 222 | ||||
-rw-r--r-- | src/ui/documentwidget.h | 6 | ||||
-rw-r--r-- | src/ui/widget.h | 5 | ||||
-rw-r--r-- | src/ui/window.c | 2 |
10 files changed, 270 insertions, 122 deletions
@@ -48,6 +48,7 @@ static const char *dataDir_App_ = "~/AppData/Roaming/fi.skyjake.Lagrange"; | |||
48 | static const char *dataDir_App_ = "~/.config/lagrange"; | 48 | static const char *dataDir_App_ = "~/.config/lagrange"; |
49 | #endif | 49 | #endif |
50 | static const char *prefsFileName_App_ = "prefs.cfg"; | 50 | static const char *prefsFileName_App_ = "prefs.cfg"; |
51 | static const char *stateFileName_App_ = "state.bin"; | ||
51 | 52 | ||
52 | struct Impl_App { | 53 | struct Impl_App { |
53 | iCommandLine args; | 54 | iCommandLine args; |
@@ -143,6 +144,67 @@ static void savePrefs_App_(const iApp *d) { | |||
143 | delete_String(cfg); | 144 | delete_String(cfg); |
144 | } | 145 | } |
145 | 146 | ||
147 | static const char *magicState_App_ = "lgL1"; | ||
148 | static const char *magicTabDocument_App_ = "tabd"; | ||
149 | |||
150 | static iBool loadState_App_(iApp *d) { | ||
151 | iFile *f = iClob(newCStr_File(concatPath_CStr(dataDir_App_, stateFileName_App_))); | ||
152 | if (open_File(f, readOnly_FileMode)) { | ||
153 | char magic[4]; | ||
154 | readData_File(f, 4, magic); | ||
155 | if (memcmp(magic, magicState_App_, 4)) { | ||
156 | printf("%s: format not recognized\n", cstr_String(path_File(f))); | ||
157 | return iFalse; | ||
158 | } | ||
159 | const int version = read32_File(f); | ||
160 | /* Check supported versions. */ | ||
161 | if (version != 0) { | ||
162 | printf("%s: unsupported version\n", cstr_String(path_File(f))); | ||
163 | return iFalse; | ||
164 | } | ||
165 | setVersion_Stream(stream_File(f), version); | ||
166 | iDocumentWidget *doc = document_App(); | ||
167 | iDocumentWidget *current = NULL; | ||
168 | while (!atEnd_File(f)) { | ||
169 | readData_File(f, 4, magic); | ||
170 | if (!memcmp(magic, magicTabDocument_App_, 4)) { | ||
171 | if (!doc) { | ||
172 | doc = newTab_App(NULL); | ||
173 | } | ||
174 | if (read8_File(f)) { | ||
175 | current = doc; | ||
176 | } | ||
177 | deserializeState_DocumentWidget(doc, stream_File(f)); | ||
178 | doc = NULL; | ||
179 | } | ||
180 | else { | ||
181 | printf("%s: unrecognized data\n", cstr_String(path_File(f))); | ||
182 | return iFalse; | ||
183 | } | ||
184 | } | ||
185 | postCommandf_App("tabs.switch page:%p", current); | ||
186 | return iTrue; | ||
187 | } | ||
188 | return iFalse; | ||
189 | } | ||
190 | |||
191 | static void saveState_App_(const iApp *d) { | ||
192 | iFile *f = newCStr_File(concatPath_CStr(dataDir_App_, stateFileName_App_)); | ||
193 | if (open_File(f, writeOnly_FileMode)) { | ||
194 | writeData_File(f, magicState_App_, 4); | ||
195 | write32_File(f, 0); /* version */ | ||
196 | iWidget *tabs = findChild_Widget(d->window->root, "doctabs"); | ||
197 | iConstForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) { | ||
198 | if (isInstance_Object(i.object, &Class_DocumentWidget)) { | ||
199 | writeData_File(f, magicTabDocument_App_, 4); | ||
200 | write8_File(f, document_App() == i.object ? 1 : 0); | ||
201 | serializeState_DocumentWidget(i.object, stream_File(f)); | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | iRelease(f); | ||
206 | } | ||
207 | |||
146 | static void init_App_(iApp *d, int argc, char **argv) { | 208 | static void init_App_(iApp *d, int argc, char **argv) { |
147 | init_CommandLine(&d->args, argc, argv); | 209 | init_CommandLine(&d->args, argc, argv); |
148 | init_SortedArray(&d->tickers, sizeof(iTicker), cmp_Ticker_); | 210 | init_SortedArray(&d->tickers, sizeof(iTicker), cmp_Ticker_); |
@@ -166,12 +228,14 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
166 | } | 228 | } |
167 | #endif | 229 | #endif |
168 | d->window = new_Window(); | 230 | d->window = new_Window(); |
169 | /* Widget state init. */ { | 231 | /* Widget state init. */ |
232 | if (!loadState_App_(d)) { | ||
170 | postCommand_App("navigate.home"); | 233 | postCommand_App("navigate.home"); |
171 | } | 234 | } |
172 | } | 235 | } |
173 | 236 | ||
174 | static void deinit_App(iApp *d) { | 237 | static void deinit_App(iApp *d) { |
238 | saveState_App_(d); | ||
175 | savePrefs_App_(d); | 239 | savePrefs_App_(d); |
176 | save_Visited(d->visited, dataDir_App_); | 240 | save_Visited(d->visited, dataDir_App_); |
177 | delete_Visited(d->visited); | 241 | delete_Visited(d->visited); |
@@ -32,7 +32,7 @@ iGmCerts * certs_App (void); | |||
32 | iVisited * visited_App (void); | 32 | iVisited * visited_App (void); |
33 | iDocumentWidget * document_App (void); | 33 | iDocumentWidget * document_App (void); |
34 | iDocumentWidget * document_Command (const char *cmd); | 34 | iDocumentWidget * document_Command (const char *cmd); |
35 | 35 | iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf); | |
36 | 36 | ||
37 | iAny * findWidget_App (const char *id); | 37 | iAny * findWidget_App (const char *id); |
38 | void addTicker_App (void (*ticker)(iAny *), iAny *context); | 38 | void addTicker_App (void (*ticker)(iAny *), iAny *context); |
diff --git a/src/gmrequest.c b/src/gmrequest.c index e1cc76d8..ce7a3522 100644 --- a/src/gmrequest.c +++ b/src/gmrequest.c | |||
@@ -51,6 +51,24 @@ iGmResponse *copy_GmResponse(const iGmResponse *d) { | |||
51 | return copied; | 51 | return copied; |
52 | } | 52 | } |
53 | 53 | ||
54 | void serialize_GmResponse(const iGmResponse *d, iStream *outs) { | ||
55 | write32_Stream(outs, d->statusCode); | ||
56 | serialize_String(&d->meta, outs); | ||
57 | serialize_Block(&d->body, outs); | ||
58 | write32_Stream(outs, d->certFlags); | ||
59 | serialize_Date(&d->certValidUntil, outs); | ||
60 | serialize_String(&d->certSubject, outs); | ||
61 | } | ||
62 | |||
63 | void deserialize_GmResponse(iGmResponse *d, iStream *ins) { | ||
64 | d->statusCode = read32_Stream(ins); | ||
65 | deserialize_String(&d->meta, ins); | ||
66 | deserialize_Block(&d->body, ins); | ||
67 | d->certFlags = read32_Stream(ins); | ||
68 | deserialize_Date(&d->certValidUntil, ins); | ||
69 | deserialize_String(&d->certSubject, ins); | ||
70 | } | ||
71 | |||
54 | /*----------------------------------------------------------------------------------------------*/ | 72 | /*----------------------------------------------------------------------------------------------*/ |
55 | 73 | ||
56 | static const int bodyTimeout_GmRequest_ = 3000; /* ms */ | 74 | static const int bodyTimeout_GmRequest_ = 3000; /* ms */ |
diff --git a/src/gmrequest.h b/src/gmrequest.h index 05290128..cb7c151a 100644 --- a/src/gmrequest.h +++ b/src/gmrequest.h | |||
@@ -25,6 +25,7 @@ struct Impl_GmResponse { | |||
25 | }; | 25 | }; |
26 | 26 | ||
27 | iDeclareTypeConstruction(GmResponse) | 27 | iDeclareTypeConstruction(GmResponse) |
28 | iDeclareTypeSerialization(GmResponse) | ||
28 | 29 | ||
29 | iGmResponse * copy_GmResponse (const iGmResponse *); | 30 | iGmResponse * copy_GmResponse (const iGmResponse *); |
30 | 31 | ||
diff --git a/src/history.c b/src/history.c index 5be24608..9779795a 100644 --- a/src/history.c +++ b/src/history.c | |||
@@ -55,39 +55,38 @@ iHistory *copy_History(const iHistory *d) { | |||
55 | return copy; | 55 | return copy; |
56 | } | 56 | } |
57 | 57 | ||
58 | void save_History(const iHistory *d, const char *dirPath) { | 58 | void serialize_History(const iHistory *d, iStream *outs) { |
59 | iString *line = new_String(); | 59 | writeU16_Stream(outs, d->recentPos); |
60 | iFile *f = newCStr_File(concatPath_CStr(dirPath, "recent.txt")); | 60 | writeU16_Stream(outs, size_Array(&d->recent)); |
61 | if (open_File(f, writeOnly_FileMode | text_FileMode)) { | 61 | iConstForEach(Array, i, &d->recent) { |
62 | iConstForEach(Array, i, &d->recent) { | 62 | const iRecentUrl *item = i.value; |
63 | const iRecentUrl *item = i.value; | 63 | serialize_String(&item->url, outs); |
64 | format_String(line, "%04x %s\n", item->scrollY, cstr_String(&item->url)); | 64 | write32_Stream(outs, item->scrollY); |
65 | writeData_File(f, cstr_String(line), size_String(line)); | 65 | if (item->cachedResponse) { |
66 | write8_Stream(outs, 1); | ||
67 | serialize_GmResponse(item->cachedResponse, outs); | ||
68 | } | ||
69 | else { | ||
70 | write8_Stream(outs, 0); | ||
66 | } | 71 | } |
67 | } | 72 | } |
68 | iRelease(f); | 73 | } |
69 | delete_String(line); | 74 | |
70 | } | 75 | void deserialize_History(iHistory *d, iStream *ins) { |
71 | 76 | clear_History(d); | |
72 | void load_History(iHistory *d, const char *dirPath) { | 77 | d->recentPos = readU16_Stream(ins); |
73 | iFile *f = newCStr_File(concatPath_CStr(dirPath, "recent.txt")); | 78 | size_t count = readU16_Stream(ins); |
74 | if (open_File(f, readOnly_FileMode | text_FileMode)) { | 79 | while (count--) { |
75 | const iRangecc src = range_Block(collect_Block(readAll_File(f))); | 80 | iRecentUrl item; |
76 | iRangecc line = iNullRange; | 81 | init_RecentUrl(&item); |
77 | while (nextSplit_Rangecc(&src, "\n", &line)) { | 82 | deserialize_String(&item.url, ins); |
78 | iRangecc nonwhite = line; | 83 | item.scrollY = read32_Stream(ins); |
79 | trim_Rangecc(&nonwhite); | 84 | if (read8_Stream(ins)) { |
80 | if (isEmpty_Range(&nonwhite)) continue; | 85 | item.cachedResponse = new_GmResponse(); |
81 | int scroll = 0; | 86 | deserialize_GmResponse(item.cachedResponse, ins); |
82 | sscanf(nonwhite.start, "%04x", &scroll); | ||
83 | iRecentUrl item; | ||
84 | init_RecentUrl(&item); | ||
85 | item.scrollY = scroll; | ||
86 | initRange_String(&item.url, (iRangecc){ nonwhite.start + 5, nonwhite.end }); | ||
87 | pushBack_Array(&d->recent, &item); | ||
88 | } | 87 | } |
88 | pushBack_Array(&d->recent, &item); | ||
89 | } | 89 | } |
90 | iRelease(f); | ||
91 | } | 90 | } |
92 | 91 | ||
93 | void clear_History(iHistory *d) { | 92 | void clear_History(iHistory *d) { |
@@ -123,6 +122,15 @@ const iString *url_History(const iHistory *d, size_t pos) { | |||
123 | return collectNew_String(); | 122 | return collectNew_String(); |
124 | } | 123 | } |
125 | 124 | ||
125 | iRecentUrl *findUrl_History(iHistory *d, const iString *url) { | ||
126 | iReverseForEach(Array, i, &d->recent) { | ||
127 | if (cmpStringCase_String(url, &((iRecentUrl *) i.value)->url) == 0) { | ||
128 | return i.value; | ||
129 | } | ||
130 | } | ||
131 | return NULL; | ||
132 | } | ||
133 | |||
126 | void replace_History(iHistory *d, const iString *url) { | 134 | void replace_History(iHistory *d, const iString *url) { |
127 | /* Update in the history. */ | 135 | /* Update in the history. */ |
128 | iRecentUrl *item = mostRecentUrl_History(d); | 136 | iRecentUrl *item = mostRecentUrl_History(d); |
diff --git a/src/history.h b/src/history.h index 9deae8ea..6ccfd199 100644 --- a/src/history.h +++ b/src/history.h | |||
@@ -19,11 +19,11 @@ struct Impl_RecentUrl { | |||
19 | 19 | ||
20 | iDeclareType(History) | 20 | iDeclareType(History) |
21 | iDeclareTypeConstruction(History) | 21 | iDeclareTypeConstruction(History) |
22 | iDeclareTypeSerialization(History) | ||
22 | 23 | ||
23 | iHistory * copy_History (const iHistory *); | 24 | iHistory * copy_History (const iHistory *); |
24 | 25 | ||
25 | void clear_History (iHistory *); | 26 | void clear_History (iHistory *); |
26 | void load_History (iHistory *, const char *dirPath); | ||
27 | void add_History (iHistory *, const iString *url); | 27 | void add_History (iHistory *, const iString *url); |
28 | void replace_History (iHistory *, const iString *url); | 28 | void replace_History (iHistory *, const iString *url); |
29 | void setCachedResponse_History (iHistory *, const iGmResponse *response); | 29 | void setCachedResponse_History (iHistory *, const iGmResponse *response); |
@@ -31,8 +31,8 @@ iBool goBack_History (iHistory *); | |||
31 | iBool goForward_History (iHistory *); | 31 | iBool goForward_History (iHistory *); |
32 | iRecentUrl *recentUrl_History (iHistory *, size_t pos); | 32 | iRecentUrl *recentUrl_History (iHistory *, size_t pos); |
33 | iRecentUrl *mostRecentUrl_History (iHistory *); | 33 | iRecentUrl *mostRecentUrl_History (iHistory *); |
34 | iRecentUrl *findUrl_History (iHistory *, const iString *url); | ||
34 | 35 | ||
35 | void save_History (const iHistory *, const char *dirPath); | ||
36 | const iString * | 36 | const iString * |
37 | url_History (const iHistory *, size_t pos); | 37 | url_History (const iHistory *, size_t pos); |
38 | const iRecentUrl * | 38 | const iRecentUrl * |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 0ba1e6b0..57709384 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -21,22 +21,14 @@ | |||
21 | #include <SDL_clipboard.h> | 21 | #include <SDL_clipboard.h> |
22 | #include <SDL_timer.h> | 22 | #include <SDL_timer.h> |
23 | 23 | ||
24 | enum iDocumentState { | ||
25 | blank_DocumentState, | ||
26 | fetching_DocumentState, | ||
27 | receivedPartialResponse_DocumentState, | ||
28 | layout_DocumentState, | ||
29 | ready_DocumentState, | ||
30 | }; | ||
31 | |||
32 | iDeclareClass(MediaRequest) | 24 | iDeclareClass(MediaRequest) |
33 | 25 | ||
34 | struct Impl_MediaRequest { | 26 | struct Impl_MediaRequest { |
35 | iObject object; | 27 | iObject object; |
36 | iDocumentWidget *doc; | 28 | iDocumentWidget *doc; |
37 | iGmLinkId linkId; | 29 | iGmLinkId linkId; |
38 | iGmRequest *req; | 30 | iGmRequest * req; |
39 | iAtomicInt isUpdated; | 31 | iAtomicInt isUpdated; |
40 | }; | 32 | }; |
41 | 33 | ||
42 | static void updated_MediaRequest_(iAnyObject *obj) { | 34 | static void updated_MediaRequest_(iAnyObject *obj) { |
@@ -74,30 +66,75 @@ iDefineObjectConstructionArgs(MediaRequest, | |||
74 | doc, linkId, url) | 66 | doc, linkId, url) |
75 | iDefineClass(MediaRequest) | 67 | iDefineClass(MediaRequest) |
76 | 68 | ||
69 | /*----------------------------------------------------------------------------------------------*/ | ||
70 | |||
71 | iDeclareType(Model) | ||
72 | iDeclareTypeConstruction(Model) | ||
73 | iDeclareTypeSerialization(Model) | ||
74 | |||
75 | struct Impl_Model { | ||
76 | /* state that persists across sessions */ | ||
77 | iHistory *history; | ||
78 | iString * url; | ||
79 | int textSizePercent; | ||
80 | }; | ||
81 | |||
82 | void init_Model(iModel *d) { | ||
83 | d->history = new_History(); | ||
84 | d->url = new_String(); | ||
85 | d->textSizePercent = 100; | ||
86 | } | ||
87 | |||
88 | void deinit_Model(iModel *d) { | ||
89 | delete_String(d->url); | ||
90 | delete_History(d->history); | ||
91 | } | ||
92 | |||
93 | void serialize_Model(const iModel *d, iStream *outs) { | ||
94 | serialize_String(d->url, outs); | ||
95 | write16_Stream(outs, d->textSizePercent); | ||
96 | serialize_History(d->history, outs); | ||
97 | } | ||
98 | |||
99 | void deserialize_Model(iModel *d, iStream *ins) { | ||
100 | deserialize_String(d->url, ins); | ||
101 | d->textSizePercent = read16_Stream(ins); | ||
102 | deserialize_History(d->history, ins); | ||
103 | } | ||
104 | |||
105 | iDefineTypeConstruction(Model) | ||
106 | |||
107 | /*----------------------------------------------------------------------------------------------*/ | ||
108 | |||
109 | enum iRequestState { | ||
110 | blank_RequestState, | ||
111 | fetching_RequestState, | ||
112 | receivedPartialResponse_RequestState, | ||
113 | ready_RequestState, | ||
114 | }; | ||
115 | |||
77 | struct Impl_DocumentWidget { | 116 | struct Impl_DocumentWidget { |
78 | iWidget widget; | 117 | iWidget widget; |
79 | iHistory *history; | 118 | enum iRequestState state; |
80 | enum iDocumentState state; | 119 | iModel mod; |
81 | iString *url; | ||
82 | iString *titleUser; | 120 | iString *titleUser; |
83 | iGmRequest *request; | 121 | iGmRequest *request; |
84 | iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ | 122 | iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ |
85 | iObjectList *media; | 123 | iObjectList *media; |
86 | int textSizePercent; | ||
87 | iGmDocument *doc; | 124 | iGmDocument *doc; |
88 | int certFlags; | 125 | int certFlags; |
89 | iDate certExpiry; | 126 | iDate certExpiry; |
90 | iString *certSubject; | 127 | iString * certSubject; |
91 | iBool selecting; | 128 | iBool selecting; |
92 | iRangecc selectMark; | 129 | iRangecc selectMark; |
93 | iRangecc foundMark; | 130 | iRangecc foundMark; |
94 | int pageMargin; | 131 | int pageMargin; |
95 | int scrollY; | ||
96 | iPtrArray visibleLinks; | 132 | iPtrArray visibleLinks; |
97 | const iGmRun *hoverLink; | 133 | const iGmRun *hoverLink; |
98 | iBool noHoverWhileScrolling; | 134 | iBool noHoverWhileScrolling; |
99 | iClick click; | 135 | iClick click; |
100 | int initialScrollY; | 136 | int initialScrollY; |
137 | int scrollY; | ||
101 | iScrollWidget *scroll; | 138 | iScrollWidget *scroll; |
102 | iWidget *menu; | 139 | iWidget *menu; |
103 | SDL_Cursor *arrowCursor; /* TODO: cursors belong in Window */ | 140 | SDL_Cursor *arrowCursor; /* TODO: cursors belong in Window */ |
@@ -112,23 +149,21 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
112 | init_Widget(w); | 149 | init_Widget(w); |
113 | setId_Widget(w, "document000"); | 150 | setId_Widget(w, "document000"); |
114 | setFlags_Widget(w, hover_WidgetFlag, iTrue); | 151 | setFlags_Widget(w, hover_WidgetFlag, iTrue); |
152 | init_Model(&d->mod); | ||
115 | iZap(d->certExpiry); | 153 | iZap(d->certExpiry); |
116 | d->history = new_History(); | 154 | d->certFlags = 0; |
117 | d->state = blank_DocumentState; | 155 | d->certSubject = new_String(); |
118 | d->url = new_String(); | 156 | d->state = blank_RequestState; |
119 | d->titleUser = new_String(); | 157 | d->titleUser = new_String(); |
120 | d->request = NULL; | 158 | d->request = NULL; |
121 | d->isRequestUpdated = iFalse; | 159 | d->isRequestUpdated = iFalse; |
122 | d->media = new_ObjectList(); | 160 | d->media = new_ObjectList(); |
123 | d->textSizePercent = 100; | ||
124 | d->doc = new_GmDocument(); | 161 | d->doc = new_GmDocument(); |
125 | d->certFlags = 0; | 162 | d->scrollY = 0; |
126 | d->certSubject = new_String(); | ||
127 | d->selecting = iFalse; | 163 | d->selecting = iFalse; |
128 | d->selectMark = iNullRange; | 164 | d->selectMark = iNullRange; |
129 | d->foundMark = iNullRange; | 165 | d->foundMark = iNullRange; |
130 | d->pageMargin = 5; | 166 | d->pageMargin = 5; |
131 | d->scrollY = 0; | ||
132 | d->hoverLink = NULL; | 167 | d->hoverLink = NULL; |
133 | d->noHoverWhileScrolling = iFalse; | 168 | d->noHoverWhileScrolling = iFalse; |
134 | d->arrowCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); | 169 | d->arrowCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); |
@@ -156,20 +191,19 @@ void deinit_DocumentWidget(iDocumentWidget *d) { | |||
156 | iRelease(d->request); | 191 | iRelease(d->request); |
157 | iRelease(d->doc); | 192 | iRelease(d->doc); |
158 | deinit_PtrArray(&d->visibleLinks); | 193 | deinit_PtrArray(&d->visibleLinks); |
159 | delete_String(d->url); | ||
160 | delete_String(d->certSubject); | 194 | delete_String(d->certSubject); |
161 | delete_String(d->titleUser); | 195 | delete_String(d->titleUser); |
162 | SDL_FreeCursor(d->arrowCursor); | 196 | SDL_FreeCursor(d->arrowCursor); |
163 | SDL_FreeCursor(d->beamCursor); | 197 | SDL_FreeCursor(d->beamCursor); |
164 | SDL_FreeCursor(d->handCursor); | 198 | SDL_FreeCursor(d->handCursor); |
165 | delete_History(d->history); | 199 | deinit_Model(&d->mod); |
166 | } | 200 | } |
167 | 201 | ||
168 | static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { | 202 | static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { |
169 | const iWidget *w = constAs_Widget(d); | 203 | const iWidget *w = constAs_Widget(d); |
170 | const iRect bounds = bounds_Widget(w); | 204 | const iRect bounds = bounds_Widget(w); |
171 | return iMini(bounds.size.x - gap_UI * d->pageMargin * 2, | 205 | return iMini(bounds.size.x - gap_UI * d->pageMargin * 2, |
172 | fontSize_UI * 38 * d->textSizePercent / 100); /* TODO: Add user preference .*/ | 206 | fontSize_UI * 38 * d->mod.textSizePercent / 100); /* TODO: Add user preference .*/ |
173 | } | 207 | } |
174 | 208 | ||
175 | static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | 209 | static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { |
@@ -240,7 +274,7 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { | |||
240 | d->hoverLink = NULL; | 274 | d->hoverLink = NULL; |
241 | const iInt2 hoverPos = addY_I2(sub_I2(mouse, topLeft_Rect(docBounds)), d->scrollY); | 275 | const iInt2 hoverPos = addY_I2(sub_I2(mouse, topLeft_Rect(docBounds)), d->scrollY); |
242 | if (!d->noHoverWhileScrolling && | 276 | if (!d->noHoverWhileScrolling && |
243 | (d->state == ready_DocumentState || d->state == receivedPartialResponse_DocumentState)) { | 277 | (d->state == ready_RequestState || d->state == receivedPartialResponse_RequestState)) { |
244 | iConstForEach(PtrArray, i, &d->visibleLinks) { | 278 | iConstForEach(PtrArray, i, &d->visibleLinks) { |
245 | const iGmRun *run = i.ptr; | 279 | const iGmRun *run = i.ptr; |
246 | if (contains_Rect(run->bounds, hoverPos)) { | 280 | if (contains_Rect(run->bounds, hoverPos)) { |
@@ -273,7 +307,7 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | |||
273 | render_GmDocument(d->doc, visRange, addVisibleLink_DocumentWidget_, d); | 307 | render_GmDocument(d->doc, visRange, addVisibleLink_DocumentWidget_, d); |
274 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window())); | 308 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window())); |
275 | /* Remember scroll positions of recently visited pages. */ { | 309 | /* Remember scroll positions of recently visited pages. */ { |
276 | iRecentUrl *recent = mostRecentUrl_History(d->history); | 310 | iRecentUrl *recent = mostRecentUrl_History(d->mod.history); |
277 | if (recent) { | 311 | if (recent) { |
278 | recent->scrollY = d->scrollY / gap_UI; | 312 | recent->scrollY = d->scrollY / gap_UI; |
279 | } | 313 | } |
@@ -295,7 +329,7 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
295 | } | 329 | } |
296 | else { | 330 | else { |
297 | iUrl parts; | 331 | iUrl parts; |
298 | init_Url(&parts, d->url); | 332 | init_Url(&parts, d->mod.url); |
299 | if (!isEmpty_Range(&parts.host)) { | 333 | if (!isEmpty_Range(&parts.host)) { |
300 | pushBackRange_StringArray(title, parts.host); | 334 | pushBackRange_StringArray(title, parts.host); |
301 | } | 335 | } |
@@ -344,7 +378,7 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
344 | } | 378 | } |
345 | 379 | ||
346 | static void setSource_DocumentWidget_(iDocumentWidget *d, const iString *source) { | 380 | static void setSource_DocumentWidget_(iDocumentWidget *d, const iString *source) { |
347 | setUrl_GmDocument(d->doc, d->url); | 381 | setUrl_GmDocument(d->doc, d->mod.url); |
348 | setSource_GmDocument(d->doc, source, documentWidth_DocumentWidget_(d)); | 382 | setSource_GmDocument(d->doc, source, documentWidth_DocumentWidget_(d)); |
349 | d->foundMark = iNullRange; | 383 | d->foundMark = iNullRange; |
350 | d->selectMark = iNullRange; | 384 | d->selectMark = iNullRange; |
@@ -376,13 +410,13 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
376 | } | 410 | } |
377 | setSource_DocumentWidget_(d, src); | 411 | setSource_DocumentWidget_(d, src); |
378 | d->scrollY = 0; | 412 | d->scrollY = 0; |
379 | d->state = ready_DocumentState; | 413 | d->state = ready_RequestState; |
380 | } | 414 | } |
381 | 415 | ||
382 | static void updateTheme_DocumentWidget_(iDocumentWidget *d) { | 416 | static void updateTheme_DocumentWidget_(iDocumentWidget *d) { |
383 | if (isEmpty_String(d->titleUser)) { | 417 | if (isEmpty_String(d->titleUser)) { |
384 | setThemeSeed_GmDocument(d->doc, | 418 | setThemeSeed_GmDocument(d->doc, |
385 | collect_Block(newRange_Block(urlHost_String(d->url)))); | 419 | collect_Block(newRange_Block(urlHost_String(d->mod.url)))); |
386 | } | 420 | } |
387 | else { | 421 | else { |
388 | setThemeSeed_GmDocument(d->doc, &d->titleUser->chars); | 422 | setThemeSeed_GmDocument(d->doc, &d->titleUser->chars); |
@@ -390,7 +424,7 @@ static void updateTheme_DocumentWidget_(iDocumentWidget *d) { | |||
390 | } | 424 | } |
391 | 425 | ||
392 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse *response) { | 426 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse *response) { |
393 | if (d->state == ready_DocumentState) { | 427 | if (d->state == ready_RequestState) { |
394 | return; | 428 | return; |
395 | } | 429 | } |
396 | /* TODO: Do this in the background. However, that requires a text metrics calculator | 430 | /* TODO: Do this in the background. However, that requires a text metrics calculator |
@@ -422,13 +456,13 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
422 | /* Make a simple document with an image. */ | 456 | /* Make a simple document with an image. */ |
423 | const char *imageTitle = "Image"; | 457 | const char *imageTitle = "Image"; |
424 | iUrl parts; | 458 | iUrl parts; |
425 | init_Url(&parts, d->url); | 459 | init_Url(&parts, d->mod.url); |
426 | if (!isEmpty_Range(&parts.path)) { | 460 | if (!isEmpty_Range(&parts.path)) { |
427 | imageTitle = | 461 | imageTitle = |
428 | baseName_Path(collect_String(newRange_String(parts.path))).start; | 462 | baseName_Path(collect_String(newRange_String(parts.path))).start; |
429 | } | 463 | } |
430 | format_String( | 464 | format_String( |
431 | &str, "=> %s %s\n", cstr_String(d->url), imageTitle); | 465 | &str, "=> %s %s\n", cstr_String(d->mod.url), imageTitle); |
432 | setImage_GmDocument(d->doc, 1, mimeStr, &response->body); | 466 | setImage_GmDocument(d->doc, 1, mimeStr, &response->body); |
433 | } | 467 | } |
434 | else { | 468 | else { |
@@ -467,13 +501,13 @@ static void fetch_DocumentWidget_(iDocumentWidget *d) { | |||
467 | iRelease(d->request); | 501 | iRelease(d->request); |
468 | d->request = NULL; | 502 | d->request = NULL; |
469 | } | 503 | } |
470 | postCommandf_App("document.request.started doc:%p url:%s", d, cstr_String(d->url)); | 504 | postCommandf_App("document.request.started doc:%p url:%s", d, cstr_String(d->mod.url)); |
471 | clear_ObjectList(d->media); | 505 | clear_ObjectList(d->media); |
472 | d->certFlags = 0; | 506 | d->certFlags = 0; |
473 | d->state = fetching_DocumentState; | 507 | d->state = fetching_RequestState; |
474 | set_Atomic(&d->isRequestUpdated, iFalse); | 508 | set_Atomic(&d->isRequestUpdated, iFalse); |
475 | d->request = new_GmRequest(certs_App()); | 509 | d->request = new_GmRequest(certs_App()); |
476 | setUrl_GmRequest(d->request, d->url); | 510 | setUrl_GmRequest(d->request, d->mod.url); |
477 | iConnect(GmRequest, d->request, updated, d, requestUpdated_DocumentWidget_); | 511 | iConnect(GmRequest, d->request, updated, d, requestUpdated_DocumentWidget_); |
478 | iConnect(GmRequest, d->request, timeout, d, requestTimedOut_DocumentWidget_); | 512 | iConnect(GmRequest, d->request, timeout, d, requestTimedOut_DocumentWidget_); |
479 | iConnect(GmRequest, d->request, finished, d, requestFinished_DocumentWidget_); | 513 | iConnect(GmRequest, d->request, finished, d, requestFinished_DocumentWidget_); |
@@ -507,44 +541,58 @@ static void updateTrust_DocumentWidget_(iDocumentWidget *d, const iGmResponse *r | |||
507 | } | 541 | } |
508 | 542 | ||
509 | iHistory *history_DocumentWidget(iDocumentWidget *d) { | 543 | iHistory *history_DocumentWidget(iDocumentWidget *d) { |
510 | return d->history; | 544 | return d->mod.history; |
511 | } | 545 | } |
512 | 546 | ||
513 | const iString *url_DocumentWidget(const iDocumentWidget *d) { | 547 | const iString *url_DocumentWidget(const iDocumentWidget *d) { |
514 | return d->url; | 548 | return d->mod.url; |
515 | } | 549 | } |
516 | 550 | ||
517 | const iGmDocument *document_DocumentWidget(const iDocumentWidget *d) { | 551 | const iGmDocument *document_DocumentWidget(const iDocumentWidget *d) { |
518 | return d->doc; | 552 | return d->doc; |
519 | } | 553 | } |
520 | 554 | ||
555 | static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { | ||
556 | const iRecentUrl *recent = findUrl_History(d->mod.history, d->mod.url); | ||
557 | if (recent && recent->cachedResponse) { | ||
558 | const iGmResponse *resp = recent->cachedResponse; | ||
559 | d->state = fetching_RequestState; | ||
560 | /* Use the cached response data. */ | ||
561 | d->scrollY = recent->scrollY; | ||
562 | updateTrust_DocumentWidget_(d, resp); | ||
563 | updateDocument_DocumentWidget_(d, resp); | ||
564 | d->state = ready_RequestState; | ||
565 | postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | ||
566 | return iTrue; | ||
567 | } | ||
568 | return iFalse; | ||
569 | } | ||
570 | |||
571 | void serializeState_DocumentWidget(const iDocumentWidget *d, iStream *outs) { | ||
572 | serialize_Model(&d->mod, outs); | ||
573 | } | ||
574 | |||
575 | void deserializeState_DocumentWidget(iDocumentWidget *d, iStream *ins) { | ||
576 | deserialize_Model(&d->mod, ins); | ||
577 | updateFromHistory_DocumentWidget_(d); | ||
578 | } | ||
579 | |||
521 | void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { | 580 | void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { |
522 | if (cmpStringSc_String(d->url, url, &iCaseInsensitive)) { | 581 | if (cmpStringSc_String(d->mod.url, url, &iCaseInsensitive)) { |
523 | set_String(d->url, url); | 582 | set_String(d->mod.url, url); |
524 | /* See if there a username in the URL. */ { | 583 | /* See if there a username in the URL. */ { |
525 | clear_String(d->titleUser); | 584 | clear_String(d->titleUser); |
526 | iRegExp *userPats[2] = { new_RegExp("~([^/?]+)", 0), | 585 | iRegExp *userPats[2] = { new_RegExp("~([^/?]+)", 0), |
527 | new_RegExp("/users/([^/?]+)", caseInsensitive_RegExpOption) }; | 586 | new_RegExp("/users/([^/?]+)", caseInsensitive_RegExpOption) }; |
528 | iRegExpMatch m; | 587 | iRegExpMatch m; |
529 | iForIndices(i, userPats) { | 588 | iForIndices(i, userPats) { |
530 | if (matchString_RegExp(userPats[i], d->url, &m)) { | 589 | if (matchString_RegExp(userPats[i], d->mod.url, &m)) { |
531 | setRange_String(d->titleUser, capturedRange_RegExpMatch(&m, 1)); | 590 | setRange_String(d->titleUser, capturedRange_RegExpMatch(&m, 1)); |
532 | } | 591 | } |
533 | iRelease(userPats[i]); | 592 | iRelease(userPats[i]); |
534 | } | 593 | } |
535 | } | 594 | } |
536 | const iRecentUrl *recent = mostRecentUrl_History(d->history); | 595 | if (!isFromCache || !updateFromHistory_DocumentWidget_(d)) { |
537 | if (isFromCache && recent && recent->cachedResponse) { | ||
538 | const iGmResponse *resp = recent->cachedResponse; | ||
539 | d->state = fetching_DocumentState; | ||
540 | /* Use the cached response data. */ | ||
541 | d->scrollY = d->initialScrollY; | ||
542 | updateTrust_DocumentWidget_(d, resp); | ||
543 | updateDocument_DocumentWidget_(d, resp); | ||
544 | d->state = ready_DocumentState; | ||
545 | postCommandf_App("document.changed url:%s", cstr_String(d->url)); | ||
546 | } | ||
547 | else { | ||
548 | fetch_DocumentWidget_(d); | 596 | fetch_DocumentWidget_(d); |
549 | } | 597 | } |
550 | } | 598 | } |
@@ -552,11 +600,11 @@ void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBoo | |||
552 | 600 | ||
553 | iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { | 601 | iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { |
554 | iDocumentWidget *d = new_DocumentWidget(); | 602 | iDocumentWidget *d = new_DocumentWidget(); |
555 | delete_History(d->history); | 603 | delete_History(d->mod.history); |
556 | d->textSizePercent = orig->textSizePercent; | 604 | d->mod.textSizePercent = orig->mod.textSizePercent; |
557 | d->initialScrollY = orig->scrollY; | 605 | d->initialScrollY = orig->scrollY; |
558 | d->history = copy_History(orig->history); | 606 | d->mod.history = copy_History(orig->mod.history); |
559 | setUrlFromCache_DocumentWidget(d, orig->url, iTrue); | 607 | setUrlFromCache_DocumentWidget(d, orig->mod.url, iTrue); |
560 | return d; | 608 | return d; |
561 | } | 609 | } |
562 | 610 | ||
@@ -569,7 +617,7 @@ void setInitialScroll_DocumentWidget (iDocumentWidget *d, int scrollY) { | |||
569 | } | 617 | } |
570 | 618 | ||
571 | iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { | 619 | iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { |
572 | return d->state == fetching_DocumentState || d->state == receivedPartialResponse_DocumentState; | 620 | return d->state == fetching_RequestState || d->state == receivedPartialResponse_RequestState; |
573 | } | 621 | } |
574 | 622 | ||
575 | static void scroll_DocumentWidget_(iDocumentWidget *d, int offset) { | 623 | static void scroll_DocumentWidget_(iDocumentWidget *d, int offset) { |
@@ -602,13 +650,13 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
602 | if (statusCode == none_GmStatusCode) { | 650 | if (statusCode == none_GmStatusCode) { |
603 | return; | 651 | return; |
604 | } | 652 | } |
605 | if (d->state == fetching_DocumentState) { | 653 | if (d->state == fetching_RequestState) { |
606 | d->state = receivedPartialResponse_DocumentState; | 654 | d->state = receivedPartialResponse_RequestState; |
607 | updateTrust_DocumentWidget_(d, response_GmRequest(d->request)); | 655 | updateTrust_DocumentWidget_(d, response_GmRequest(d->request)); |
608 | switch (category_GmStatusCode(statusCode)) { | 656 | switch (category_GmStatusCode(statusCode)) { |
609 | case categoryInput_GmStatusCode: { | 657 | case categoryInput_GmStatusCode: { |
610 | iUrl parts; | 658 | iUrl parts; |
611 | init_Url(&parts, d->url); | 659 | init_Url(&parts, d->mod.url); |
612 | printf("%s\n", cstr_String(meta_GmRequest(d->request))); | 660 | printf("%s\n", cstr_String(meta_GmRequest(d->request))); |
613 | iWidget *dlg = makeValueInput_Widget( | 661 | iWidget *dlg = makeValueInput_Widget( |
614 | as_Widget(d), | 662 | as_Widget(d), |
@@ -636,7 +684,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
636 | /* TODO: only accept redirects that use gemini protocol */ | 684 | /* TODO: only accept redirects that use gemini protocol */ |
637 | postCommandf_App( | 685 | postCommandf_App( |
638 | "open redirect:1 url:%s", | 686 | "open redirect:1 url:%s", |
639 | cstr_String(absoluteUrl_String(d->url, meta_GmRequest(d->request)))); | 687 | cstr_String(absoluteUrl_String(d->mod.url, meta_GmRequest(d->request)))); |
640 | iReleasePtr(&d->request); | 688 | iReleasePtr(&d->request); |
641 | } | 689 | } |
642 | break; | 690 | break; |
@@ -655,7 +703,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
655 | break; | 703 | break; |
656 | } | 704 | } |
657 | } | 705 | } |
658 | else if (d->state == receivedPartialResponse_DocumentState) { | 706 | else if (d->state == receivedPartialResponse_RequestState) { |
659 | switch (category_GmStatusCode(statusCode)) { | 707 | switch (category_GmStatusCode(statusCode)) { |
660 | case categorySuccess_GmStatusCode: | 708 | case categorySuccess_GmStatusCode: |
661 | /* More content available. */ | 709 | /* More content available. */ |
@@ -722,7 +770,7 @@ static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId) | |||
722 | pushBack_ObjectList( | 770 | pushBack_ObjectList( |
723 | d->media, | 771 | d->media, |
724 | iClob(new_MediaRequest( | 772 | iClob(new_MediaRequest( |
725 | d, linkId, absoluteUrl_String(d->url, linkUrl_GmDocument(d->doc, linkId))))); | 773 | d, linkId, absoluteUrl_String(d->mod.url, linkUrl_GmDocument(d->doc, linkId))))); |
726 | return iTrue; | 774 | return iTrue; |
727 | } | 775 | } |
728 | return iFalse; | 776 | return iFalse; |
@@ -764,16 +812,16 @@ static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char * | |||
764 | 812 | ||
765 | static void changeTextSize_DocumentWidget_(iDocumentWidget *d, int delta) { | 813 | static void changeTextSize_DocumentWidget_(iDocumentWidget *d, int delta) { |
766 | if (delta == 0) { | 814 | if (delta == 0) { |
767 | d->textSizePercent = 100; | 815 | d->mod.textSizePercent = 100; |
768 | } | 816 | } |
769 | else { | 817 | else { |
770 | if (d->textSizePercent < 100 || (delta < 0 && d->textSizePercent == 100)) { | 818 | if (d->mod.textSizePercent < 100 || (delta < 0 && d->mod.textSizePercent == 100)) { |
771 | delta /= 2; | 819 | delta /= 2; |
772 | } | 820 | } |
773 | d->textSizePercent += delta; | 821 | d->mod.textSizePercent += delta; |
774 | d->textSizePercent = iClamp(d->textSizePercent, 50, 200); | 822 | d->mod.textSizePercent = iClamp(d->mod.textSizePercent, 50, 200); |
775 | } | 823 | } |
776 | postCommandf_App("font.setfactor arg:%d", d->textSizePercent); | 824 | postCommandf_App("font.setfactor arg:%d", d->mod.textSizePercent); |
777 | } | 825 | } |
778 | 826 | ||
779 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | 827 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { |
@@ -848,17 +896,17 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
848 | else if (equalWidget_Command(cmd, w, "document.copylink")) { | 896 | else if (equalWidget_Command(cmd, w, "document.copylink")) { |
849 | if (d->hoverLink) { | 897 | if (d->hoverLink) { |
850 | SDL_SetClipboardText(cstr_String( | 898 | SDL_SetClipboardText(cstr_String( |
851 | absoluteUrl_String(d->url, linkUrl_GmDocument(d->doc, d->hoverLink->linkId)))); | 899 | absoluteUrl_String(d->mod.url, linkUrl_GmDocument(d->doc, d->hoverLink->linkId)))); |
852 | } | 900 | } |
853 | else { | 901 | else { |
854 | SDL_SetClipboardText(cstr_String(d->url)); | 902 | SDL_SetClipboardText(cstr_String(d->mod.url)); |
855 | } | 903 | } |
856 | return iTrue; | 904 | return iTrue; |
857 | } | 905 | } |
858 | else if (equal_Command(cmd, "document.input.submit")) { | 906 | else if (equal_Command(cmd, "document.input.submit")) { |
859 | iString *value = collect_String(suffix_Command(cmd, "value")); | 907 | iString *value = collect_String(suffix_Command(cmd, "value")); |
860 | urlEncode_String(value); | 908 | urlEncode_String(value); |
861 | iString *url = collect_String(copy_String(d->url)); | 909 | iString *url = collect_String(copy_String(d->mod.url)); |
862 | const size_t qPos = indexOfCStr_String(url, "?"); | 910 | const size_t qPos = indexOfCStr_String(url, "?"); |
863 | if (qPos != iInvalidPos) { | 911 | if (qPos != iInvalidPos) { |
864 | remove_Block(&url->chars, qPos, iInvalidSize); | 912 | remove_Block(&url->chars, qPos, iInvalidSize); |
@@ -881,10 +929,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
881 | else if (equalWidget_Command(cmd, w, "document.request.finished") && | 929 | else if (equalWidget_Command(cmd, w, "document.request.finished") && |
882 | pointerLabel_Command(cmd, "request") == d->request) { | 930 | pointerLabel_Command(cmd, "request") == d->request) { |
883 | checkResponse_DocumentWidget_(d); | 931 | checkResponse_DocumentWidget_(d); |
884 | d->state = ready_DocumentState; | 932 | d->state = ready_RequestState; |
885 | setCachedResponse_History(d->history, response_GmRequest(d->request)); | 933 | setCachedResponse_History(d->mod.history, response_GmRequest(d->request)); |
886 | iReleasePtr(&d->request); | 934 | iReleasePtr(&d->request); |
887 | postCommandf_App("document.changed url:%s", cstr_String(d->url)); | 935 | postCommandf_App("document.changed url:%s", cstr_String(d->mod.url)); |
888 | return iFalse; | 936 | return iFalse; |
889 | } | 937 | } |
890 | else if (equal_Command(cmd, "document.request.timeout") && | 938 | else if (equal_Command(cmd, "document.request.timeout") && |
@@ -898,9 +946,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
898 | } | 946 | } |
899 | else if (equal_Command(cmd, "document.stop")) { | 947 | else if (equal_Command(cmd, "document.stop")) { |
900 | if (d->request) { | 948 | if (d->request) { |
901 | postCommandf_App("document.request.cancelled doc:%p url:%s", d, cstr_String(d->url)); | 949 | postCommandf_App("document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); |
902 | iReleasePtr(&d->request); | 950 | iReleasePtr(&d->request); |
903 | d->state = ready_DocumentState; | 951 | d->state = ready_RequestState; |
904 | return iTrue; | 952 | return iTrue; |
905 | } | 953 | } |
906 | } | 954 | } |
@@ -912,11 +960,11 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
912 | return iTrue; | 960 | return iTrue; |
913 | } | 961 | } |
914 | else if (equal_Command(cmd, "navigate.back") && document_App() == d) { | 962 | else if (equal_Command(cmd, "navigate.back") && document_App() == d) { |
915 | goBack_History(d->history); | 963 | goBack_History(d->mod.history); |
916 | return iTrue; | 964 | return iTrue; |
917 | } | 965 | } |
918 | else if (equal_Command(cmd, "navigate.forward") && document_App() == d) { | 966 | else if (equal_Command(cmd, "navigate.forward") && document_App() == d) { |
919 | goForward_History(d->history); | 967 | goForward_History(d->mod.history); |
920 | return iTrue; | 968 | return iTrue; |
921 | } | 969 | } |
922 | else if (equalWidget_Command(cmd, w, "scroll.moved")) { | 970 | else if (equalWidget_Command(cmd, w, "scroll.moved")) { |
@@ -1135,7 +1183,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1135 | postCommandf_App("open newtab:%d url:%s", | 1183 | postCommandf_App("open newtab:%d url:%s", |
1136 | (SDL_GetModState() & KMOD_PRIMARY) != 0, | 1184 | (SDL_GetModState() & KMOD_PRIMARY) != 0, |
1137 | cstr_String(absoluteUrl_String( | 1185 | cstr_String(absoluteUrl_String( |
1138 | d->url, linkUrl_GmDocument(d->doc, linkId)))); | 1186 | d->mod.url, linkUrl_GmDocument(d->doc, linkId)))); |
1139 | } | 1187 | } |
1140 | } | 1188 | } |
1141 | if (d->selectMark.start) { | 1189 | if (d->selectMark.start) { |
diff --git a/src/ui/documentwidget.h b/src/ui/documentwidget.h index 1e63034b..810c1392 100644 --- a/src/ui/documentwidget.h +++ b/src/ui/documentwidget.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include "widget.h" | 3 | #include "widget.h" |
4 | #include <the_Foundation/stream.h> | ||
4 | 5 | ||
5 | iDeclareType(GmDocument) | 6 | iDeclareType(GmDocument) |
6 | iDeclareType(History) | 7 | iDeclareType(History) |
@@ -8,6 +9,9 @@ iDeclareType(History) | |||
8 | iDeclareWidgetClass(DocumentWidget) | 9 | iDeclareWidgetClass(DocumentWidget) |
9 | iDeclareObjectConstruction(DocumentWidget) | 10 | iDeclareObjectConstruction(DocumentWidget) |
10 | 11 | ||
12 | void serializeState_DocumentWidget (const iDocumentWidget *, iStream *outs); | ||
13 | void deserializeState_DocumentWidget (iDocumentWidget *, iStream *ins); | ||
14 | |||
11 | iDocumentWidget * duplicate_DocumentWidget (const iDocumentWidget *); | 15 | iDocumentWidget * duplicate_DocumentWidget (const iDocumentWidget *); |
12 | iHistory * history_DocumentWidget (iDocumentWidget *); | 16 | iHistory * history_DocumentWidget (iDocumentWidget *); |
13 | 17 | ||
@@ -16,5 +20,5 @@ iBool isRequestOngoing_DocumentWidget (const iDocumentWidget *); | |||
16 | const iGmDocument * document_DocumentWidget (const iDocumentWidget *); | 20 | const iGmDocument * document_DocumentWidget (const iDocumentWidget *); |
17 | 21 | ||
18 | void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); | 22 | void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); |
19 | void setUrlFromCache_DocumentWidget (iDocumentWidget *d, const iString *url, iBool isFromCache); | 23 | void setUrlFromCache_DocumentWidget (iDocumentWidget *, const iString *url, iBool isFromCache); |
20 | void setInitialScroll_DocumentWidget (iDocumentWidget *, int scrollY); /* set after content received */ | 24 | void setInitialScroll_DocumentWidget (iDocumentWidget *, int scrollY); /* set after content received */ |
diff --git a/src/ui/widget.h b/src/ui/widget.h index bf6489a0..c82e37f8 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h | |||
@@ -114,6 +114,11 @@ iAny * findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focu | |||
114 | size_t childCount_Widget (const iWidget *); | 114 | size_t childCount_Widget (const iWidget *); |
115 | void draw_Widget (const iWidget *); | 115 | void draw_Widget (const iWidget *); |
116 | 116 | ||
117 | iLocalDef iObjectList *children_Widget(iAnyObject *d) { | ||
118 | iAssert(isInstance_Object(d, &Class_Widget)); | ||
119 | return ((iWidget *) d)->children; | ||
120 | } | ||
121 | |||
117 | iBool isVisible_Widget (const iWidget *); | 122 | iBool isVisible_Widget (const iWidget *); |
118 | iBool isDisabled_Widget (const iWidget *); | 123 | iBool isDisabled_Widget (const iWidget *); |
119 | iBool isFocused_Widget (const iWidget *); | 124 | iBool isFocused_Widget (const iWidget *); |
diff --git a/src/ui/window.c b/src/ui/window.c index c68331a0..3a6b6e30 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -104,7 +104,7 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
104 | setFlags_Widget( | 104 | setFlags_Widget( |
105 | child, tight_WidgetFlag, isNarrow || !cmp_String(id_Widget(child), "lock")); | 105 | child, tight_WidgetFlag, isNarrow || !cmp_String(id_Widget(child), "lock")); |
106 | if (isInstance_Object(i.object, &Class_LabelWidget)) { | 106 | if (isInstance_Object(i.object, &Class_LabelWidget)) { |
107 | iLabelWidget *label = (iLabelWidget *) i.object; | 107 | iLabelWidget *label = i.object; |
108 | updateSize_LabelWidget(label); | 108 | updateSize_LabelWidget(label); |
109 | } | 109 | } |
110 | } | 110 | } |