summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-08-12 10:28:02 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-08-12 10:28:02 +0300
commit1d54f7b990ea7f676403681577fc4df984cab0be (patch)
tree94a8ac83159f021dbbb354a6d76ecbaf83a63aad /src/ui
parent38e09f15ff3e9c4781236016bfbb0b0f9062590b (diff)
Save and load app state (tabs, history)
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/documentwidget.c222
-rw-r--r--src/ui/documentwidget.h6
-rw-r--r--src/ui/widget.h5
-rw-r--r--src/ui/window.c2
4 files changed, 146 insertions, 89 deletions
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
24enum iDocumentState {
25 blank_DocumentState,
26 fetching_DocumentState,
27 receivedPartialResponse_DocumentState,
28 layout_DocumentState,
29 ready_DocumentState,
30};
31
32iDeclareClass(MediaRequest) 24iDeclareClass(MediaRequest)
33 25
34struct Impl_MediaRequest { 26struct 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
42static void updated_MediaRequest_(iAnyObject *obj) { 34static void updated_MediaRequest_(iAnyObject *obj) {
@@ -74,30 +66,75 @@ iDefineObjectConstructionArgs(MediaRequest,
74 doc, linkId, url) 66 doc, linkId, url)
75iDefineClass(MediaRequest) 67iDefineClass(MediaRequest)
76 68
69/*----------------------------------------------------------------------------------------------*/
70
71iDeclareType(Model)
72iDeclareTypeConstruction(Model)
73iDeclareTypeSerialization(Model)
74
75struct Impl_Model {
76 /* state that persists across sessions */
77 iHistory *history;
78 iString * url;
79 int textSizePercent;
80};
81
82void init_Model(iModel *d) {
83 d->history = new_History();
84 d->url = new_String();
85 d->textSizePercent = 100;
86}
87
88void deinit_Model(iModel *d) {
89 delete_String(d->url);
90 delete_History(d->history);
91}
92
93void 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
99void 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
105iDefineTypeConstruction(Model)
106
107/*----------------------------------------------------------------------------------------------*/
108
109enum iRequestState {
110 blank_RequestState,
111 fetching_RequestState,
112 receivedPartialResponse_RequestState,
113 ready_RequestState,
114};
115
77struct Impl_DocumentWidget { 116struct 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
168static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { 202static 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
175static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { 209static 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
346static void setSource_DocumentWidget_(iDocumentWidget *d, const iString *source) { 380static 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
382static void updateTheme_DocumentWidget_(iDocumentWidget *d) { 416static 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
392static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse *response) { 426static 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
509iHistory *history_DocumentWidget(iDocumentWidget *d) { 543iHistory *history_DocumentWidget(iDocumentWidget *d) {
510 return d->history; 544 return d->mod.history;
511} 545}
512 546
513const iString *url_DocumentWidget(const iDocumentWidget *d) { 547const iString *url_DocumentWidget(const iDocumentWidget *d) {
514 return d->url; 548 return d->mod.url;
515} 549}
516 550
517const iGmDocument *document_DocumentWidget(const iDocumentWidget *d) { 551const iGmDocument *document_DocumentWidget(const iDocumentWidget *d) {
518 return d->doc; 552 return d->doc;
519} 553}
520 554
555static 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
571void serializeState_DocumentWidget(const iDocumentWidget *d, iStream *outs) {
572 serialize_Model(&d->mod, outs);
573}
574
575void deserializeState_DocumentWidget(iDocumentWidget *d, iStream *ins) {
576 deserialize_Model(&d->mod, ins);
577 updateFromHistory_DocumentWidget_(d);
578}
579
521void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { 580void 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
553iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { 601iDocumentWidget *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
571iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { 619iBool 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
575static void scroll_DocumentWidget_(iDocumentWidget *d, int offset) { 623static 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
765static void changeTextSize_DocumentWidget_(iDocumentWidget *d, int delta) { 813static 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
779static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { 827static 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
5iDeclareType(GmDocument) 6iDeclareType(GmDocument)
6iDeclareType(History) 7iDeclareType(History)
@@ -8,6 +9,9 @@ iDeclareType(History)
8iDeclareWidgetClass(DocumentWidget) 9iDeclareWidgetClass(DocumentWidget)
9iDeclareObjectConstruction(DocumentWidget) 10iDeclareObjectConstruction(DocumentWidget)
10 11
12void serializeState_DocumentWidget (const iDocumentWidget *, iStream *outs);
13void deserializeState_DocumentWidget (iDocumentWidget *, iStream *ins);
14
11iDocumentWidget * duplicate_DocumentWidget (const iDocumentWidget *); 15iDocumentWidget * duplicate_DocumentWidget (const iDocumentWidget *);
12iHistory * history_DocumentWidget (iDocumentWidget *); 16iHistory * history_DocumentWidget (iDocumentWidget *);
13 17
@@ -16,5 +20,5 @@ iBool isRequestOngoing_DocumentWidget (const iDocumentWidget *);
16const iGmDocument * document_DocumentWidget (const iDocumentWidget *); 20const iGmDocument * document_DocumentWidget (const iDocumentWidget *);
17 21
18void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); 22void setUrl_DocumentWidget (iDocumentWidget *, const iString *url);
19void setUrlFromCache_DocumentWidget (iDocumentWidget *d, const iString *url, iBool isFromCache); 23void setUrlFromCache_DocumentWidget (iDocumentWidget *, const iString *url, iBool isFromCache);
20void setInitialScroll_DocumentWidget (iDocumentWidget *, int scrollY); /* set after content received */ 24void 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
114size_t childCount_Widget (const iWidget *); 114size_t childCount_Widget (const iWidget *);
115void draw_Widget (const iWidget *); 115void draw_Widget (const iWidget *);
116 116
117iLocalDef iObjectList *children_Widget(iAnyObject *d) {
118 iAssert(isInstance_Object(d, &Class_Widget));
119 return ((iWidget *) d)->children;
120}
121
117iBool isVisible_Widget (const iWidget *); 122iBool isVisible_Widget (const iWidget *);
118iBool isDisabled_Widget (const iWidget *); 123iBool isDisabled_Widget (const iWidget *);
119iBool isFocused_Widget (const iWidget *); 124iBool 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 }