From 1d54f7b990ea7f676403681577fc4df984cab0be Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Wed, 12 Aug 2020 10:28:02 +0300 Subject: Save and load app state (tabs, history) --- src/app.c | 66 +++++++++++++- src/app.h | 2 +- src/gmrequest.c | 18 ++++ src/gmrequest.h | 1 + src/history.c | 66 +++++++------- src/history.h | 4 +- src/ui/documentwidget.c | 222 +++++++++++++++++++++++++++++------------------- src/ui/documentwidget.h | 6 +- src/ui/widget.h | 5 ++ src/ui/window.c | 2 +- 10 files changed, 270 insertions(+), 122 deletions(-) diff --git a/src/app.c b/src/app.c index 6f076c97..7885d154 100644 --- a/src/app.c +++ b/src/app.c @@ -48,6 +48,7 @@ static const char *dataDir_App_ = "~/AppData/Roaming/fi.skyjake.Lagrange"; static const char *dataDir_App_ = "~/.config/lagrange"; #endif static const char *prefsFileName_App_ = "prefs.cfg"; +static const char *stateFileName_App_ = "state.bin"; struct Impl_App { iCommandLine args; @@ -143,6 +144,67 @@ static void savePrefs_App_(const iApp *d) { delete_String(cfg); } +static const char *magicState_App_ = "lgL1"; +static const char *magicTabDocument_App_ = "tabd"; + +static iBool loadState_App_(iApp *d) { + iFile *f = iClob(newCStr_File(concatPath_CStr(dataDir_App_, stateFileName_App_))); + if (open_File(f, readOnly_FileMode)) { + char magic[4]; + readData_File(f, 4, magic); + if (memcmp(magic, magicState_App_, 4)) { + printf("%s: format not recognized\n", cstr_String(path_File(f))); + return iFalse; + } + const int version = read32_File(f); + /* Check supported versions. */ + if (version != 0) { + printf("%s: unsupported version\n", cstr_String(path_File(f))); + return iFalse; + } + setVersion_Stream(stream_File(f), version); + iDocumentWidget *doc = document_App(); + iDocumentWidget *current = NULL; + while (!atEnd_File(f)) { + readData_File(f, 4, magic); + if (!memcmp(magic, magicTabDocument_App_, 4)) { + if (!doc) { + doc = newTab_App(NULL); + } + if (read8_File(f)) { + current = doc; + } + deserializeState_DocumentWidget(doc, stream_File(f)); + doc = NULL; + } + else { + printf("%s: unrecognized data\n", cstr_String(path_File(f))); + return iFalse; + } + } + postCommandf_App("tabs.switch page:%p", current); + return iTrue; + } + return iFalse; +} + +static void saveState_App_(const iApp *d) { + iFile *f = newCStr_File(concatPath_CStr(dataDir_App_, stateFileName_App_)); + if (open_File(f, writeOnly_FileMode)) { + writeData_File(f, magicState_App_, 4); + write32_File(f, 0); /* version */ + iWidget *tabs = findChild_Widget(d->window->root, "doctabs"); + iConstForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) { + if (isInstance_Object(i.object, &Class_DocumentWidget)) { + writeData_File(f, magicTabDocument_App_, 4); + write8_File(f, document_App() == i.object ? 1 : 0); + serializeState_DocumentWidget(i.object, stream_File(f)); + } + } + } + iRelease(f); +} + static void init_App_(iApp *d, int argc, char **argv) { init_CommandLine(&d->args, argc, argv); init_SortedArray(&d->tickers, sizeof(iTicker), cmp_Ticker_); @@ -166,12 +228,14 @@ static void init_App_(iApp *d, int argc, char **argv) { } #endif d->window = new_Window(); - /* Widget state init. */ { + /* Widget state init. */ + if (!loadState_App_(d)) { postCommand_App("navigate.home"); } } static void deinit_App(iApp *d) { + saveState_App_(d); savePrefs_App_(d); save_Visited(d->visited, dataDir_App_); delete_Visited(d->visited); diff --git a/src/app.h b/src/app.h index e40c5b2e..a3e815a2 100644 --- a/src/app.h +++ b/src/app.h @@ -32,7 +32,7 @@ iGmCerts * certs_App (void); iVisited * visited_App (void); iDocumentWidget * document_App (void); iDocumentWidget * document_Command (const char *cmd); - +iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf); iAny * findWidget_App (const char *id); 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) { return copied; } +void serialize_GmResponse(const iGmResponse *d, iStream *outs) { + write32_Stream(outs, d->statusCode); + serialize_String(&d->meta, outs); + serialize_Block(&d->body, outs); + write32_Stream(outs, d->certFlags); + serialize_Date(&d->certValidUntil, outs); + serialize_String(&d->certSubject, outs); +} + +void deserialize_GmResponse(iGmResponse *d, iStream *ins) { + d->statusCode = read32_Stream(ins); + deserialize_String(&d->meta, ins); + deserialize_Block(&d->body, ins); + d->certFlags = read32_Stream(ins); + deserialize_Date(&d->certValidUntil, ins); + deserialize_String(&d->certSubject, ins); +} + /*----------------------------------------------------------------------------------------------*/ 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 { }; iDeclareTypeConstruction(GmResponse) +iDeclareTypeSerialization(GmResponse) iGmResponse * copy_GmResponse (const iGmResponse *); 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) { return copy; } -void save_History(const iHistory *d, const char *dirPath) { - iString *line = new_String(); - iFile *f = newCStr_File(concatPath_CStr(dirPath, "recent.txt")); - if (open_File(f, writeOnly_FileMode | text_FileMode)) { - iConstForEach(Array, i, &d->recent) { - const iRecentUrl *item = i.value; - format_String(line, "%04x %s\n", item->scrollY, cstr_String(&item->url)); - writeData_File(f, cstr_String(line), size_String(line)); +void serialize_History(const iHistory *d, iStream *outs) { + writeU16_Stream(outs, d->recentPos); + writeU16_Stream(outs, size_Array(&d->recent)); + iConstForEach(Array, i, &d->recent) { + const iRecentUrl *item = i.value; + serialize_String(&item->url, outs); + write32_Stream(outs, item->scrollY); + if (item->cachedResponse) { + write8_Stream(outs, 1); + serialize_GmResponse(item->cachedResponse, outs); + } + else { + write8_Stream(outs, 0); } } - iRelease(f); - delete_String(line); -} - -void load_History(iHistory *d, const char *dirPath) { - iFile *f = newCStr_File(concatPath_CStr(dirPath, "recent.txt")); - if (open_File(f, readOnly_FileMode | text_FileMode)) { - const iRangecc src = range_Block(collect_Block(readAll_File(f))); - iRangecc line = iNullRange; - while (nextSplit_Rangecc(&src, "\n", &line)) { - iRangecc nonwhite = line; - trim_Rangecc(&nonwhite); - if (isEmpty_Range(&nonwhite)) continue; - int scroll = 0; - sscanf(nonwhite.start, "%04x", &scroll); - iRecentUrl item; - init_RecentUrl(&item); - item.scrollY = scroll; - initRange_String(&item.url, (iRangecc){ nonwhite.start + 5, nonwhite.end }); - pushBack_Array(&d->recent, &item); +} + +void deserialize_History(iHistory *d, iStream *ins) { + clear_History(d); + d->recentPos = readU16_Stream(ins); + size_t count = readU16_Stream(ins); + while (count--) { + iRecentUrl item; + init_RecentUrl(&item); + deserialize_String(&item.url, ins); + item.scrollY = read32_Stream(ins); + if (read8_Stream(ins)) { + item.cachedResponse = new_GmResponse(); + deserialize_GmResponse(item.cachedResponse, ins); } + pushBack_Array(&d->recent, &item); } - iRelease(f); } void clear_History(iHistory *d) { @@ -123,6 +122,15 @@ const iString *url_History(const iHistory *d, size_t pos) { return collectNew_String(); } +iRecentUrl *findUrl_History(iHistory *d, const iString *url) { + iReverseForEach(Array, i, &d->recent) { + if (cmpStringCase_String(url, &((iRecentUrl *) i.value)->url) == 0) { + return i.value; + } + } + return NULL; +} + void replace_History(iHistory *d, const iString *url) { /* Update in the history. */ 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 { iDeclareType(History) iDeclareTypeConstruction(History) +iDeclareTypeSerialization(History) iHistory * copy_History (const iHistory *); void clear_History (iHistory *); -void load_History (iHistory *, const char *dirPath); void add_History (iHistory *, const iString *url); void replace_History (iHistory *, const iString *url); void setCachedResponse_History (iHistory *, const iGmResponse *response); @@ -31,8 +31,8 @@ iBool goBack_History (iHistory *); iBool goForward_History (iHistory *); iRecentUrl *recentUrl_History (iHistory *, size_t pos); iRecentUrl *mostRecentUrl_History (iHistory *); +iRecentUrl *findUrl_History (iHistory *, const iString *url); -void save_History (const iHistory *, const char *dirPath); const iString * url_History (const iHistory *, size_t pos); 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 @@ #include #include -enum iDocumentState { - blank_DocumentState, - fetching_DocumentState, - receivedPartialResponse_DocumentState, - layout_DocumentState, - ready_DocumentState, -}; - iDeclareClass(MediaRequest) struct Impl_MediaRequest { - iObject object; + iObject object; iDocumentWidget *doc; - iGmLinkId linkId; - iGmRequest *req; - iAtomicInt isUpdated; + iGmLinkId linkId; + iGmRequest * req; + iAtomicInt isUpdated; }; static void updated_MediaRequest_(iAnyObject *obj) { @@ -74,30 +66,75 @@ iDefineObjectConstructionArgs(MediaRequest, doc, linkId, url) iDefineClass(MediaRequest) +/*----------------------------------------------------------------------------------------------*/ + +iDeclareType(Model) +iDeclareTypeConstruction(Model) +iDeclareTypeSerialization(Model) + +struct Impl_Model { + /* state that persists across sessions */ + iHistory *history; + iString * url; + int textSizePercent; +}; + +void init_Model(iModel *d) { + d->history = new_History(); + d->url = new_String(); + d->textSizePercent = 100; +} + +void deinit_Model(iModel *d) { + delete_String(d->url); + delete_History(d->history); +} + +void serialize_Model(const iModel *d, iStream *outs) { + serialize_String(d->url, outs); + write16_Stream(outs, d->textSizePercent); + serialize_History(d->history, outs); +} + +void deserialize_Model(iModel *d, iStream *ins) { + deserialize_String(d->url, ins); + d->textSizePercent = read16_Stream(ins); + deserialize_History(d->history, ins); +} + +iDefineTypeConstruction(Model) + +/*----------------------------------------------------------------------------------------------*/ + +enum iRequestState { + blank_RequestState, + fetching_RequestState, + receivedPartialResponse_RequestState, + ready_RequestState, +}; + struct Impl_DocumentWidget { iWidget widget; - iHistory *history; - enum iDocumentState state; - iString *url; + enum iRequestState state; + iModel mod; iString *titleUser; iGmRequest *request; iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ iObjectList *media; - int textSizePercent; iGmDocument *doc; - int certFlags; - iDate certExpiry; - iString *certSubject; + int certFlags; + iDate certExpiry; + iString * certSubject; iBool selecting; iRangecc selectMark; iRangecc foundMark; int pageMargin; - int scrollY; iPtrArray visibleLinks; const iGmRun *hoverLink; iBool noHoverWhileScrolling; iClick click; int initialScrollY; + int scrollY; iScrollWidget *scroll; iWidget *menu; SDL_Cursor *arrowCursor; /* TODO: cursors belong in Window */ @@ -112,23 +149,21 @@ void init_DocumentWidget(iDocumentWidget *d) { init_Widget(w); setId_Widget(w, "document000"); setFlags_Widget(w, hover_WidgetFlag, iTrue); + init_Model(&d->mod); iZap(d->certExpiry); - d->history = new_History(); - d->state = blank_DocumentState; - d->url = new_String(); + d->certFlags = 0; + d->certSubject = new_String(); + d->state = blank_RequestState; d->titleUser = new_String(); d->request = NULL; d->isRequestUpdated = iFalse; d->media = new_ObjectList(); - d->textSizePercent = 100; d->doc = new_GmDocument(); - d->certFlags = 0; - d->certSubject = new_String(); + d->scrollY = 0; d->selecting = iFalse; d->selectMark = iNullRange; d->foundMark = iNullRange; d->pageMargin = 5; - d->scrollY = 0; d->hoverLink = NULL; d->noHoverWhileScrolling = iFalse; d->arrowCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); @@ -156,20 +191,19 @@ void deinit_DocumentWidget(iDocumentWidget *d) { iRelease(d->request); iRelease(d->doc); deinit_PtrArray(&d->visibleLinks); - delete_String(d->url); delete_String(d->certSubject); delete_String(d->titleUser); SDL_FreeCursor(d->arrowCursor); SDL_FreeCursor(d->beamCursor); SDL_FreeCursor(d->handCursor); - delete_History(d->history); + deinit_Model(&d->mod); } static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { const iWidget *w = constAs_Widget(d); const iRect bounds = bounds_Widget(w); return iMini(bounds.size.x - gap_UI * d->pageMargin * 2, - fontSize_UI * 38 * d->textSizePercent / 100); /* TODO: Add user preference .*/ + fontSize_UI * 38 * d->mod.textSizePercent / 100); /* TODO: Add user preference .*/ } static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { @@ -240,7 +274,7 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { d->hoverLink = NULL; const iInt2 hoverPos = addY_I2(sub_I2(mouse, topLeft_Rect(docBounds)), d->scrollY); if (!d->noHoverWhileScrolling && - (d->state == ready_DocumentState || d->state == receivedPartialResponse_DocumentState)) { + (d->state == ready_RequestState || d->state == receivedPartialResponse_RequestState)) { iConstForEach(PtrArray, i, &d->visibleLinks) { const iGmRun *run = i.ptr; if (contains_Rect(run->bounds, hoverPos)) { @@ -273,7 +307,7 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { render_GmDocument(d->doc, visRange, addVisibleLink_DocumentWidget_, d); updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window())); /* Remember scroll positions of recently visited pages. */ { - iRecentUrl *recent = mostRecentUrl_History(d->history); + iRecentUrl *recent = mostRecentUrl_History(d->mod.history); if (recent) { recent->scrollY = d->scrollY / gap_UI; } @@ -295,7 +329,7 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { } else { iUrl parts; - init_Url(&parts, d->url); + init_Url(&parts, d->mod.url); if (!isEmpty_Range(&parts.host)) { pushBackRange_StringArray(title, parts.host); } @@ -344,7 +378,7 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { } static void setSource_DocumentWidget_(iDocumentWidget *d, const iString *source) { - setUrl_GmDocument(d->doc, d->url); + setUrl_GmDocument(d->doc, d->mod.url); setSource_GmDocument(d->doc, source, documentWidth_DocumentWidget_(d)); d->foundMark = iNullRange; d->selectMark = iNullRange; @@ -376,13 +410,13 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode } setSource_DocumentWidget_(d, src); d->scrollY = 0; - d->state = ready_DocumentState; + d->state = ready_RequestState; } static void updateTheme_DocumentWidget_(iDocumentWidget *d) { if (isEmpty_String(d->titleUser)) { setThemeSeed_GmDocument(d->doc, - collect_Block(newRange_Block(urlHost_String(d->url)))); + collect_Block(newRange_Block(urlHost_String(d->mod.url)))); } else { setThemeSeed_GmDocument(d->doc, &d->titleUser->chars); @@ -390,7 +424,7 @@ static void updateTheme_DocumentWidget_(iDocumentWidget *d) { } static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse *response) { - if (d->state == ready_DocumentState) { + if (d->state == ready_RequestState) { return; } /* 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 /* Make a simple document with an image. */ const char *imageTitle = "Image"; iUrl parts; - init_Url(&parts, d->url); + init_Url(&parts, d->mod.url); if (!isEmpty_Range(&parts.path)) { imageTitle = baseName_Path(collect_String(newRange_String(parts.path))).start; } format_String( - &str, "=> %s %s\n", cstr_String(d->url), imageTitle); + &str, "=> %s %s\n", cstr_String(d->mod.url), imageTitle); setImage_GmDocument(d->doc, 1, mimeStr, &response->body); } else { @@ -467,13 +501,13 @@ static void fetch_DocumentWidget_(iDocumentWidget *d) { iRelease(d->request); d->request = NULL; } - postCommandf_App("document.request.started doc:%p url:%s", d, cstr_String(d->url)); + postCommandf_App("document.request.started doc:%p url:%s", d, cstr_String(d->mod.url)); clear_ObjectList(d->media); d->certFlags = 0; - d->state = fetching_DocumentState; + d->state = fetching_RequestState; set_Atomic(&d->isRequestUpdated, iFalse); d->request = new_GmRequest(certs_App()); - setUrl_GmRequest(d->request, d->url); + setUrl_GmRequest(d->request, d->mod.url); iConnect(GmRequest, d->request, updated, d, requestUpdated_DocumentWidget_); iConnect(GmRequest, d->request, timeout, d, requestTimedOut_DocumentWidget_); iConnect(GmRequest, d->request, finished, d, requestFinished_DocumentWidget_); @@ -507,44 +541,58 @@ static void updateTrust_DocumentWidget_(iDocumentWidget *d, const iGmResponse *r } iHistory *history_DocumentWidget(iDocumentWidget *d) { - return d->history; + return d->mod.history; } const iString *url_DocumentWidget(const iDocumentWidget *d) { - return d->url; + return d->mod.url; } const iGmDocument *document_DocumentWidget(const iDocumentWidget *d) { return d->doc; } +static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { + const iRecentUrl *recent = findUrl_History(d->mod.history, d->mod.url); + if (recent && recent->cachedResponse) { + const iGmResponse *resp = recent->cachedResponse; + d->state = fetching_RequestState; + /* Use the cached response data. */ + d->scrollY = recent->scrollY; + updateTrust_DocumentWidget_(d, resp); + updateDocument_DocumentWidget_(d, resp); + d->state = ready_RequestState; + postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); + return iTrue; + } + return iFalse; +} + +void serializeState_DocumentWidget(const iDocumentWidget *d, iStream *outs) { + serialize_Model(&d->mod, outs); +} + +void deserializeState_DocumentWidget(iDocumentWidget *d, iStream *ins) { + deserialize_Model(&d->mod, ins); + updateFromHistory_DocumentWidget_(d); +} + void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { - if (cmpStringSc_String(d->url, url, &iCaseInsensitive)) { - set_String(d->url, url); + if (cmpStringSc_String(d->mod.url, url, &iCaseInsensitive)) { + set_String(d->mod.url, url); /* See if there a username in the URL. */ { clear_String(d->titleUser); iRegExp *userPats[2] = { new_RegExp("~([^/?]+)", 0), new_RegExp("/users/([^/?]+)", caseInsensitive_RegExpOption) }; iRegExpMatch m; iForIndices(i, userPats) { - if (matchString_RegExp(userPats[i], d->url, &m)) { + if (matchString_RegExp(userPats[i], d->mod.url, &m)) { setRange_String(d->titleUser, capturedRange_RegExpMatch(&m, 1)); } iRelease(userPats[i]); } } - const iRecentUrl *recent = mostRecentUrl_History(d->history); - if (isFromCache && recent && recent->cachedResponse) { - const iGmResponse *resp = recent->cachedResponse; - d->state = fetching_DocumentState; - /* Use the cached response data. */ - d->scrollY = d->initialScrollY; - updateTrust_DocumentWidget_(d, resp); - updateDocument_DocumentWidget_(d, resp); - d->state = ready_DocumentState; - postCommandf_App("document.changed url:%s", cstr_String(d->url)); - } - else { + if (!isFromCache || !updateFromHistory_DocumentWidget_(d)) { fetch_DocumentWidget_(d); } } @@ -552,11 +600,11 @@ void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBoo iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { iDocumentWidget *d = new_DocumentWidget(); - delete_History(d->history); - d->textSizePercent = orig->textSizePercent; - d->initialScrollY = orig->scrollY; - d->history = copy_History(orig->history); - setUrlFromCache_DocumentWidget(d, orig->url, iTrue); + delete_History(d->mod.history); + d->mod.textSizePercent = orig->mod.textSizePercent; + d->initialScrollY = orig->scrollY; + d->mod.history = copy_History(orig->mod.history); + setUrlFromCache_DocumentWidget(d, orig->mod.url, iTrue); return d; } @@ -569,7 +617,7 @@ void setInitialScroll_DocumentWidget (iDocumentWidget *d, int scrollY) { } iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { - return d->state == fetching_DocumentState || d->state == receivedPartialResponse_DocumentState; + return d->state == fetching_RequestState || d->state == receivedPartialResponse_RequestState; } static void scroll_DocumentWidget_(iDocumentWidget *d, int offset) { @@ -602,13 +650,13 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { if (statusCode == none_GmStatusCode) { return; } - if (d->state == fetching_DocumentState) { - d->state = receivedPartialResponse_DocumentState; + if (d->state == fetching_RequestState) { + d->state = receivedPartialResponse_RequestState; updateTrust_DocumentWidget_(d, response_GmRequest(d->request)); switch (category_GmStatusCode(statusCode)) { case categoryInput_GmStatusCode: { iUrl parts; - init_Url(&parts, d->url); + init_Url(&parts, d->mod.url); printf("%s\n", cstr_String(meta_GmRequest(d->request))); iWidget *dlg = makeValueInput_Widget( as_Widget(d), @@ -636,7 +684,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { /* TODO: only accept redirects that use gemini protocol */ postCommandf_App( "open redirect:1 url:%s", - cstr_String(absoluteUrl_String(d->url, meta_GmRequest(d->request)))); + cstr_String(absoluteUrl_String(d->mod.url, meta_GmRequest(d->request)))); iReleasePtr(&d->request); } break; @@ -655,7 +703,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { break; } } - else if (d->state == receivedPartialResponse_DocumentState) { + else if (d->state == receivedPartialResponse_RequestState) { switch (category_GmStatusCode(statusCode)) { case categorySuccess_GmStatusCode: /* More content available. */ @@ -722,7 +770,7 @@ static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId) pushBack_ObjectList( d->media, iClob(new_MediaRequest( - d, linkId, absoluteUrl_String(d->url, linkUrl_GmDocument(d->doc, linkId))))); + d, linkId, absoluteUrl_String(d->mod.url, linkUrl_GmDocument(d->doc, linkId))))); return iTrue; } return iFalse; @@ -764,16 +812,16 @@ static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char * static void changeTextSize_DocumentWidget_(iDocumentWidget *d, int delta) { if (delta == 0) { - d->textSizePercent = 100; + d->mod.textSizePercent = 100; } else { - if (d->textSizePercent < 100 || (delta < 0 && d->textSizePercent == 100)) { + if (d->mod.textSizePercent < 100 || (delta < 0 && d->mod.textSizePercent == 100)) { delta /= 2; } - d->textSizePercent += delta; - d->textSizePercent = iClamp(d->textSizePercent, 50, 200); + d->mod.textSizePercent += delta; + d->mod.textSizePercent = iClamp(d->mod.textSizePercent, 50, 200); } - postCommandf_App("font.setfactor arg:%d", d->textSizePercent); + postCommandf_App("font.setfactor arg:%d", d->mod.textSizePercent); } static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { @@ -848,17 +896,17 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) else if (equalWidget_Command(cmd, w, "document.copylink")) { if (d->hoverLink) { SDL_SetClipboardText(cstr_String( - absoluteUrl_String(d->url, linkUrl_GmDocument(d->doc, d->hoverLink->linkId)))); + absoluteUrl_String(d->mod.url, linkUrl_GmDocument(d->doc, d->hoverLink->linkId)))); } else { - SDL_SetClipboardText(cstr_String(d->url)); + SDL_SetClipboardText(cstr_String(d->mod.url)); } return iTrue; } else if (equal_Command(cmd, "document.input.submit")) { iString *value = collect_String(suffix_Command(cmd, "value")); urlEncode_String(value); - iString *url = collect_String(copy_String(d->url)); + iString *url = collect_String(copy_String(d->mod.url)); const size_t qPos = indexOfCStr_String(url, "?"); if (qPos != iInvalidPos) { remove_Block(&url->chars, qPos, iInvalidSize); @@ -881,10 +929,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) else if (equalWidget_Command(cmd, w, "document.request.finished") && pointerLabel_Command(cmd, "request") == d->request) { checkResponse_DocumentWidget_(d); - d->state = ready_DocumentState; - setCachedResponse_History(d->history, response_GmRequest(d->request)); + d->state = ready_RequestState; + setCachedResponse_History(d->mod.history, response_GmRequest(d->request)); iReleasePtr(&d->request); - postCommandf_App("document.changed url:%s", cstr_String(d->url)); + postCommandf_App("document.changed url:%s", cstr_String(d->mod.url)); return iFalse; } else if (equal_Command(cmd, "document.request.timeout") && @@ -898,9 +946,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) } else if (equal_Command(cmd, "document.stop")) { if (d->request) { - postCommandf_App("document.request.cancelled doc:%p url:%s", d, cstr_String(d->url)); + postCommandf_App("document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); iReleasePtr(&d->request); - d->state = ready_DocumentState; + d->state = ready_RequestState; return iTrue; } } @@ -912,11 +960,11 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) return iTrue; } else if (equal_Command(cmd, "navigate.back") && document_App() == d) { - goBack_History(d->history); + goBack_History(d->mod.history); return iTrue; } else if (equal_Command(cmd, "navigate.forward") && document_App() == d) { - goForward_History(d->history); + goForward_History(d->mod.history); return iTrue; } else if (equalWidget_Command(cmd, w, "scroll.moved")) { @@ -1135,7 +1183,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e postCommandf_App("open newtab:%d url:%s", (SDL_GetModState() & KMOD_PRIMARY) != 0, cstr_String(absoluteUrl_String( - d->url, linkUrl_GmDocument(d->doc, linkId)))); + d->mod.url, linkUrl_GmDocument(d->doc, linkId)))); } } 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 @@ #pragma once #include "widget.h" +#include iDeclareType(GmDocument) iDeclareType(History) @@ -8,6 +9,9 @@ iDeclareType(History) iDeclareWidgetClass(DocumentWidget) iDeclareObjectConstruction(DocumentWidget) +void serializeState_DocumentWidget (const iDocumentWidget *, iStream *outs); +void deserializeState_DocumentWidget (iDocumentWidget *, iStream *ins); + iDocumentWidget * duplicate_DocumentWidget (const iDocumentWidget *); iHistory * history_DocumentWidget (iDocumentWidget *); @@ -16,5 +20,5 @@ iBool isRequestOngoing_DocumentWidget (const iDocumentWidget *); const iGmDocument * document_DocumentWidget (const iDocumentWidget *); void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); -void setUrlFromCache_DocumentWidget (iDocumentWidget *d, const iString *url, iBool isFromCache); +void setUrlFromCache_DocumentWidget (iDocumentWidget *, const iString *url, iBool isFromCache); 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 size_t childCount_Widget (const iWidget *); void draw_Widget (const iWidget *); +iLocalDef iObjectList *children_Widget(iAnyObject *d) { + iAssert(isInstance_Object(d, &Class_Widget)); + return ((iWidget *) d)->children; +} + iBool isVisible_Widget (const iWidget *); iBool isDisabled_Widget (const iWidget *); 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) { setFlags_Widget( child, tight_WidgetFlag, isNarrow || !cmp_String(id_Widget(child), "lock")); if (isInstance_Object(i.object, &Class_LabelWidget)) { - iLabelWidget *label = (iLabelWidget *) i.object; + iLabelWidget *label = i.object; updateSize_LabelWidget(label); } } -- cgit v1.2.3