[1mdiff --git a/src/app.c b/src/app.c[m
[1mindex 5d27618b..82628c9c 100644[m
[1m--- a/src/app.c[m
[1m+++ b/src/app.c[m
[36m@@ -194,6 +194,7 @@[m [mstatic iString *serializePrefs_App_(const iApp *d) {[m
appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent);[m
appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling);[m
appendFormat_String(str, "imageloadscroll arg:%d\n", d->prefs.loadImageInsteadOfScrolling);[m
[32m+[m[32m appendFormat_String(str, "cachesize.set arg:%d\n", d->prefs.maxCacheSize);[m
appendFormat_String(str, "decodeurls arg:%d\n", d->prefs.decodeUserVisibleURLs);[m
appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth);[m
appendFormat_String(str, "prefs.biglede.changed arg:%d\n", d->prefs.bigFirstParagraph);[m
[36m@@ -321,6 +322,7 @@[m [miObjectList *listDocuments_App(void) {[m
[m
static void saveState_App_(const iApp *d) {[m
iUnused(d);[m
[32m+[m[32m trimCache_App();[m
iFile *f = newCStr_File(concatPath_CStr(dataDir_App_, stateFileName_App_));[m
if (open_File(f, writeOnly_FileMode)) {[m
writeData_File(f, magicState_App_, 4);[m
[36m@@ -503,7 +505,7 @@[m [mconst iString *debugInfo_App(void) {[m
iString *msg = collectNew_String();[m
format_String(msg, "# Debug information\n");[m
appendFormat_String(msg, "## Documents\n");[m
[31m- iForEach(ObjectList, k, listDocuments_App()) {[m
[32m+[m[32m iForEach(ObjectList, k, iClob(listDocuments_App())) {[m
iDocumentWidget *doc = k.object;[m
appendFormat_String(msg, "### Tab %zu: %s\n",[m
childIndex_Widget(constAs_Widget(doc)->parent, k.object),[m
[36m@@ -857,6 +859,8 @@[m [mstatic iBool handlePrefsCommands_(iWidget *d, const char *cmd) {[m
isSelected_Widget(findChild_Widget(d, "prefs.ostheme")));[m
postCommandf_App("decodeurls arg:%d",[m
isSelected_Widget(findChild_Widget(d, "prefs.decodeurls")));[m
[32m+[m[32m postCommandf_App("cachesize.set arg:%d",[m
[32m+[m[32m toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize"))));[m
postCommandf_App("proxy.gemini address:%s",[m
cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.gemini"))));[m
postCommandf_App("proxy.gopher address:%s",[m
[36m@@ -940,6 +944,33 @@[m [miDocumentWidget *newTab_App(const iDocumentWidget *duplicateOf, iBool switchToNe[m
return doc;[m
}[m
[m
[32m+[m[32mvoid trimCache_App(void) {[m
[32m+[m[32m iApp *d = &app_;[m
[32m+[m[32m size_t cacheSize = 0;[m
[32m+[m[32m const size_t limit = d->prefs.maxCacheSize * 1000000;[m
[32m+[m[32m iObjectList *docs = listDocuments_App();[m
[32m+[m[32m iForEach(ObjectList, i, docs) {[m
[32m+[m[32m cacheSize += cacheSize_History(history_DocumentWidget(i.object));[m
[32m+[m[32m }[m
[32m+[m[32m init_ObjectListIterator(&i, docs);[m
[32m+[m[32m iBool wasPruned = iFalse;[m
[32m+[m[32m while (cacheSize > limit) {[m
[32m+[m[32m iDocumentWidget *doc = i.object;[m
[32m+[m[32m const size_t pruned = pruneLeastImportant_History(history_DocumentWidget(doc));[m
[32m+[m[32m if (pruned) {[m
[32m+[m[32m cacheSize -= pruned;[m
[32m+[m[32m wasPruned = iTrue;[m
[32m+[m[32m }[m
[32m+[m[32m next_ObjectListIterator(&i);[m
[32m+[m[32m if (!i.value) {[m
[32m+[m[32m if (!wasPruned) break;[m
[32m+[m[32m wasPruned = iFalse;[m
[32m+[m[32m init_ObjectListIterator(&i, docs);[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m iRelease(docs);[m
[32m+[m[32m}[m
[32m+[m
static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) {[m
iApp *d = &app_;[m
if (equal_Command(cmd, "ident.temp.changed")) {[m
[36m@@ -1154,6 +1185,13 @@[m [miBool handleCommand_App(const char *cmd) {[m
postCommandf_App("theme.changed auto:1");[m
return iTrue;[m
}[m
[32m+[m[32m else if (equal_Command(cmd, "cachesize.set")) {[m
[32m+[m[32m d->prefs.maxCacheSize = arg_Command(cmd);[m
[32m+[m[32m if (d->prefs.maxCacheSize <= 0) {[m
[32m+[m[32m d->prefs.maxCacheSize = 0;[m
[32m+[m[32m }[m
[32m+[m[32m return iTrue;[m
[32m+[m[32m }[m
else if (equal_Command(cmd, "proxy.gemini")) {[m
setCStr_String(&d->prefs.geminiProxy, suffixPtr_Command(cmd, "address"));[m
return iTrue;[m
[36m@@ -1331,6 +1369,8 @@[m [miBool handleCommand_App(const char *cmd) {[m
dlg, format_CStr("prefs.saturation.%d", (int) (d->prefs.saturation * 3.99f))),[m
selected_WidgetFlag,[m
iTrue);[m
[32m+[m[32m setText_InputWidget(findChild_Widget(dlg, "prefs.cachesize"),[m
[32m+[m[32m collectNewFormat_String("%d", d->prefs.maxCacheSize));[m
setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs);[m
setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gemini"), &d->prefs.geminiProxy);[m
setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gopher"), &d->prefs.gopherProxy);[m
[1mdiff --git a/src/app.h b/src/app.h[m
[1mindex 0e8351aa..efaf0a3e 100644[m
[1m--- a/src/app.h[m
[1m+++ b/src/app.h[m
[36m@@ -61,20 +61,20 @@[m [mvoid refresh_App (void);[m
iBool isRefreshPending_App (void);[m
uint32_t elapsedSinceLastTicker_App (void); /* milliseconds */[m
[m
[31m-const iPrefs * prefs_App (void);[m
[31m-iBool forceSoftwareRender_App(void);[m
[31m-enum iColorTheme colorTheme_App (void);[m
[31m-const iString * schemeProxy_App (iRangecc scheme);[m
[31m-iBool willUseProxy_App (const iRangecc scheme);[m
[31m-[m
[31m-iMimeHooks * mimeHooks_App (void);[m
iGmCerts * certs_App (void);[m
iVisited * visited_App (void);[m
iBookmarks * bookmarks_App (void);[m
[32m+[m[32miMimeHooks * mimeHooks_App (void);[m
iDocumentWidget * document_App (void);[m
iObjectList * listDocuments_App (void);[m
[31m-iDocumentWidget * document_Command (const char *cmd);[m
iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf, iBool switchToNew);[m
[32m+[m[32mvoid trimCache_App (void);[m
[32m+[m
[32m+[m[32mconst iPrefs * prefs_App (void);[m
[32m+[m[32miBool forceSoftwareRender_App(void);[m
[32m+[m[32menum iColorTheme colorTheme_App (void);[m
[32m+[m[32mconst iString * schemeProxy_App (iRangecc scheme);[m
[32m+[m[32miBool willUseProxy_App (const iRangecc scheme);[m
[m
typedef void (*iTickerFunc)(iAny *);[m
[m
[36m@@ -91,5 +91,7 @@[m [miLocalDef void postCommandString_App(const iString *command) {[m
}[m
}[m
[m
[32m+[m[32miDocumentWidget * document_Command (const char *cmd);[m
[32m+[m
void openInDefaultBrowser_App (const iString *url);[m
void revealPath_App (const iString *path);[m
[1mdiff --git a/src/history.c b/src/history.c[m
[1mindex 202549a9..59d515dc 100644[m
[1m--- a/src/history.c[m
[1m+++ b/src/history.c[m
[36m@@ -27,6 +27,7 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m
#include <the_Foundation/mutex.h>[m
#include <the_Foundation/path.h>[m
#include <the_Foundation/stringset.h>[m
[32m+[m[32m#include <math.h>[m
[m
static const size_t maxStack_History_ = 50; /* back/forward navigable items */[m
[m
[36m@@ -285,6 +286,48 @@[m [mvoid setCachedResponse_History(iHistory *d, const iGmResponse *response) {[m
unlock_Mutex(d->mtx);[m
}[m
[m
[32m+[m[32msize_t cacheSize_History(const iHistory *d) {[m
[32m+[m[32m size_t cached = 0;[m
[32m+[m[32m lock_Mutex(d->mtx);[m
[32m+[m[32m iConstForEach(Array, i, &d->recent) {[m
[32m+[m[32m const iRecentUrl *url = i.value;[m
[32m+[m[32m if (url->cachedResponse) {[m
[32m+[m[32m cached += size_Block(&url->cachedResponse->body);[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m unlock_Mutex(d->mtx);[m
[32m+[m[32m return cached;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32msize_t pruneLeastImportant_History(iHistory *d) {[m
[32m+[m[32m size_t delta = 0;[m
[32m+[m[32m size_t chosen = iInvalidPos;[m
[32m+[m[32m double score = 0.0f;[m
[32m+[m[32m iTime now;[m
[32m+[m[32m initCurrent_Time(&now);[m
[32m+[m[32m lock_Mutex(d->mtx);[m
[32m+[m[32m iConstForEach(Array, i, &d->recent) {[m
[32m+[m[32m const iRecentUrl *url = i.value;[m
[32m+[m[32m if (url->cachedResponse) {[m
[32m+[m[32m const double urlScore =[m
[32m+[m[32m size_Block(&url->cachedResponse->body) *[m
[32m+[m[32m pow(secondsSince_Time(&now, &url->cachedResponse->when) / 60.0, 1.25);[m
[32m+[m[32m if (urlScore > score) {[m
[32m+[m[32m chosen = index_ArrayConstIterator(&i);[m
[32m+[m[32m score = urlScore;[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m if (chosen != iInvalidPos) {[m
[32m+[m[32m iRecentUrl *url = at_Array(&d->recent, chosen);[m
[32m+[m[32m delta = size_Block(&url->cachedResponse->body);[m
[32m+[m[32m delete_GmResponse(url->cachedResponse);[m
[32m+[m[32m url->cachedResponse = NULL;[m
[32m+[m[32m }[m
[32m+[m[32m unlock_Mutex(d->mtx);[m
[32m+[m[32m return delta;[m
[32m+[m[32m}[m
[32m+[m
const iStringArray *searchContents_History(const iHistory *d, const iRegExp *pattern) {[m
iStringArray *urls = iClob(new_StringArray());[m
lock_Mutex(d->mtx);[m
[1mdiff --git a/src/history.h b/src/history.h[m
[1mindex 4c6507e3..7c2684f1 100644[m
[1m--- a/src/history.h[m
[1m+++ b/src/history.h[m
[36m@@ -56,6 +56,7 @@[m [miBool goForward_History (iHistory *);[m
iRecentUrl *recentUrl_History (iHistory *, size_t pos);[m
iRecentUrl *mostRecentUrl_History (iHistory *);[m
iRecentUrl *findUrl_History (iHistory *, const iString *url);[m
[32m+[m[32msize_t pruneLeastImportant_History (iHistory *);[m
[m
const iStringArray * searchContents_History (const iHistory *, const iRegExp pattern); / chronologically ascending */[m
[m
[36m@@ -67,6 +68,7 @@[m [mconst iRecentUrl *[m
constMostRecentUrl_History (const iHistory *);[m
const iGmResponse *[m
cachedResponse_History (const iHistory *);[m
[32m+[m[32msize_t cacheSize_History (const iHistory *);[m
[m
iString * debugInfo_History (const iHistory *);[m
[m
[1mdiff --git a/src/prefs.c b/src/prefs.c[m
[1mindex 188938a2..ce32962b 100644[m
[1m--- a/src/prefs.c[m
[1m+++ b/src/prefs.c[m
[36m@@ -34,6 +34,7 @@[m [mvoid init_Prefs(iPrefs *d) {[m
d->smoothScrolling = iTrue;[m
d->loadImageInsteadOfScrolling = iFalse;[m
d->decodeUserVisibleURLs = iTrue;[m
[32m+[m[32m d->maxCacheSize = 10;[m
d->font = nunito_TextFont;[m
d->headingFont = nunito_TextFont;[m
d->monospaceGemini = iFalse;[m
[1mdiff --git a/src/prefs.h b/src/prefs.h[m
[1mindex 07298eac..1c3274d9 100644[m
[1m--- a/src/prefs.h[m
[1m+++ b/src/prefs.h[m
[36m@@ -49,6 +49,7 @@[m [mstruct Impl_Prefs {[m
iBool loadImageInsteadOfScrolling;[m
/* Network */[m
iBool decodeUserVisibleURLs;[m
[32m+[m[32m int maxCacheSize; /* MB */[m
iString geminiProxy;[m
iString gopherProxy;[m
iString httpProxy;[m
[1mdiff --git a/src/ui/util.c b/src/ui/util.c[m
[1mindex 7fc27130..91945db8 100644[m
[1m--- a/src/ui/util.c[m
[1m+++ b/src/ui/util.c[m
[36m@@ -1090,7 +1090,7 @@[m [miWidget *makePreferences_Widget(void) {[m
}[m
/* Colors. */ {[m
appendTwoColumnPage_(tabs, "Colors", '3', &headings, &values);[m
[31m- makeTwoColumnHeading_("PAGE CONTENTS", headings, values);[m
[32m+[m[32m makeTwoColumnHeading_("PAGE CONTENT", headings, values);[m
for (int i = 0; i < 2; ++i) {[m
const iBool isDark = (i == 0);[m
const char *mode = isDark ? "dark" : "light";[m
[36m@@ -1121,6 +1121,7 @@[m [miWidget *makePreferences_Widget(void) {[m
}[m
/* Layout. */ {[m
appendTwoColumnPage_(tabs, "Style", '4', &headings, &values);[m
[32m+[m[32m makeTwoColumnHeading_("FONTS", headings, values);[m
/* Fonts. */ {[m
iWidget *fonts;[m
addChild_Widget(headings, iClob(makeHeading_Widget("Heading font:")));[m
[36m@@ -1140,8 +1141,7 @@[m [miWidget *makePreferences_Widget(void) {[m
addChild_Widget(mono, iClob(makeToggle_Widget("prefs.mono.gopher"))), "Gopher");[m
addChildFlags_Widget(values, iClob(mono), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);[m
}[m
[31m- addChild_Widget(headings, iClob(makePadding_Widget(2 * gap_UI)));[m
[31m- addChild_Widget(values, iClob(makePadding_Widget(2 * gap_UI)));[m
[32m+[m[32m makeTwoColumnHeading_("PARAGRAPH", headings, values);[m
addChild_Widget(headings, iClob(makeHeading_Widget("Line width:")));[m
iWidget *widths = new_Widget();[m
/* Line widths. */ {[m
[36m@@ -1162,10 +1162,19 @@[m [miWidget *makePreferences_Widget(void) {[m
addChild_Widget(headings, iClob(makeHeading_Widget("Big 1st paragaph:")));[m
addChild_Widget(values, iClob(makeToggle_Widget("prefs.biglede")));[m
}[m
[31m- /* Proxies. */ {[m
[32m+[m[32m /* Network. */ {[m
appendTwoColumnPage_(tabs, "Network", '5', &headings, &values);[m
[31m- addChild_Widget(headings, iClob(makeHeading_Widget("Decode paths:")));[m
[32m+[m[32m addChild_Widget(headings, iClob(makeHeading_Widget("Cache size:")));[m
[32m+[m[32m iWidget *cacheGroup = new_Widget(); {[m
[32m+[m[32m iInputWidget *cache = new_InputWidget(4);[m
[32m+[m[32m setSelectAllOnFocus_InputWidget(cache, iTrue);[m
[32m+[m[32m setId_Widget(addChild_Widget(cacheGroup, iClob(cache)), "prefs.cachesize");[m
[32m+[m[32m addChildFlags_Widget(cacheGroup, iClob(new_LabelWidget("MB", NULL)), frameless_WidgetFlag);[m
[32m+[m[32m }[m
[32m+[m[32m addChildFlags_Widget(values, iClob(cacheGroup), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);[m
[32m+[m[32m addChild_Widget(headings, iClob(makeHeading_Widget("Decode URLs:")));[m
addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls")));[m
[32m+[m[32m makeTwoColumnHeading_("PROXIES", headings, values);[m
addChild_Widget(headings, iClob(makeHeading_Widget("Gemini proxy:")));[m
setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.proxy.gemini");[m
addChild_Widget(headings, iClob(makeHeading_Widget("Gopher proxy:")));[m
[1mdiff --git a/src/ui/window.c b/src/ui/window.c[m
[1mindex 2e38512b..66994e79 100644[m
[1m--- a/src/ui/window.c[m
[1m+++ b/src/ui/window.c[m
[36m@@ -397,6 +397,7 @@[m [mstatic iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {[m
if (equal_Command(cmd, "document.changed")) {[m
iInputWidget *url = findWidget_App("url");[m
const iString *urlStr = collect_String(suffix_Command(cmd, "url"));[m
[32m+[m[32m trimCache_App();[m
visitUrl_Visited(visited_App(), urlStr, 0);[m
postCommand_App("visited.changed"); /* sidebar will update */[m
setText_InputWidget(url, urlStr);[m
text/plain
This content has been proxied by September (ba2dc).