[1mdiff --git a/CMakeLists.txt b/CMakeLists.txt[m
[1mindex a3687e9c..095a279f 100644[m
[1m--- a/CMakeLists.txt[m
[1m+++ b/CMakeLists.txt[m
[36m@@ -31,6 +31,7 @@[m [moption (ENABLE_KERNING "Enable kerning in font renderer (slower)" ON)[m
option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF)[m
option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF)[m
option (ENABLE_IDLE_SLEEP "While idle, sleep in the main thread instead of waiting for events" ON)[m
[32m+[m[32moption (ENABLE_DOWNLOAD_EDIT "Allow changing the Downloads directory" ON)[m
[m
include (BuildType.cmake)[m
include (res/Embed.cmake)[m
[36m@@ -226,6 +227,9 @@[m [mendif ()[m
if (ENABLE_IDLE_SLEEP)[m
target_compile_definitions (app PUBLIC LAGRANGE_IDLE_SLEEP=1)[m
endif ()[m
[32m+[m[32mif (ENABLE_DOWNLOAD_EDIT)[m
[32m+[m[32m target_compile_definitions (app PUBLIC LAGRANGE_DOWNLOAD_EDIT=1)[m
[32m+[m[32mendif ()[m
target_link_libraries (app PUBLIC the_Foundation::the_Foundation)[m
target_link_libraries (app PUBLIC ${SDL2_LDFLAGS})[m
if (APPLE)[m
[1mdiff --git a/src/app.c b/src/app.c[m
[1mindex 10e6d4ee..195b3b43 100644[m
[1m--- a/src/app.c[m
[1m+++ b/src/app.c[m
[36m@@ -83,17 +83,17 @@[m [mstatic const char *dataDir_App_ = "~/AppData/Roaming/fi.skyjake.Lagrange";[m
#endif[m
#if defined (iPlatformLinux) || defined (iPlatformOther)[m
#define EMB_BIN "../../share/lagrange/resources.lgr"[m
[31m-static const char *dataDir_App_ = "~/.config/lagrange";[m
[32m+[m[32mstatic const char *defaultDataDir_App_ = "~/.config/lagrange";[m
#endif[m
#if defined (LAGRANGE_EMB_BIN) /* specified in build config */[m
#endif[m
#define EMB_BIN2 "../resources.lgr" /* fallback from build/executable dir */[m
[31m-static const char *prefsFileName_App_ = "prefs.cfg";[m
[31m-static const char *oldStateFileName_App_ = "state.binary";[m
[31m-static const char *stateFileName_App_ = "state.lgr";[m
[31m-static const char *downloadDir_App_ = "~/Downloads";[m
[32m+[m[32mstatic const char *prefsFileName_App_ = "prefs.cfg";[m
[32m+[m[32mstatic const char *oldStateFileName_App_ = "state.binary";[m
[32m+[m[32mstatic const char *stateFileName_App_ = "state.lgr";[m
[32m+[m[32mstatic const char *defaultDownloadDir_App_ = "~/Downloads";[m
[m
static const int idleThreshold_App_ = 1000; /* ms */[m
[m
[36m@@ -213,14 +213,42 @@[m [mstatic iString *serializePrefs_App_(const iApp *d) {[m
return str;[m
}[m
[m
[32m+[m[32mstatic const char *dataDir_App_(void) {[m
[32m+[m[32m#if defined (iPlatformLinux) || defined (iPlatformOther)[m
[32m+[m[32m const char *configHome = getenv("XDG_CONFIG_HOME");[m
[32m+[m[32m if (configHome) {[m
[32m+[m[32m return concatPath_CStr(configHome, "lagrange");[m
[32m+[m[32m }[m
[32m+[m[32m#endif[m
[32m+[m[32m return defaultDataDir_App_;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic const char *downloadDir_App_(void) {[m
[32m+[m[32m#if defined (iPlatformLinux) || defined (iPlatformOther)[m
[32m+[m[32m /* Parse user-dirs.dirs using the xdg-user-dir
tool. */[m
[32m+[m[32m iProcess *proc = iClob(new_Process());[m
[32m+[m[32m setArguments_Process([m
[32m+[m[32m proc, iClob(newStringsCStr_StringList("/usr/bin/env", "xdg-user-dir", "DOWNLOAD", NULL)));[m
[32m+[m[32m if (start_Process(proc)) {[m
[32m+[m[32m iString *path = collect_String(newLocal_String(collect_Block([m
[32m+[m[32m readOutputUntilClosed_Process(proc))));[m
[32m+[m[32m trim_String(path);[m
[32m+[m[32m if (!isEmpty_String(path)) {[m
[32m+[m[32m return cstr_String(path);[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m#endif[m
[32m+[m[32m return defaultDownloadDir_App_;[m
[32m+[m[32m}[m
[32m+[m
static const iString *prefsFileName_(void) {[m
[31m- return collect_String(concatCStr_Path(&iStringLiteral(dataDir_App_), prefsFileName_App_));[m
[32m+[m[32m return collectNewCStr_String(concatPath_CStr(dataDir_App_(), prefsFileName_App_));[m
}[m
[m
static void loadPrefs_App_(iApp *d) {[m
iUnused(d);[m
/* Create the data dir if it doesn't exist yet. */[m
[31m- makeDirs_Path(collectNewCStr_String(dataDir_App_));[m
[32m+[m[32m makeDirs_Path(collectNewCStr_String(dataDir_App_()));[m
iFile *f = new_File(prefsFileName_());[m
if (open_File(f, readOnly_FileMode | text_FileMode)) {[m
iString *str = readString_File(f);[m
[36m@@ -267,8 +295,8 @@[m [mstatic const char *magicTabDocument_App_ = "tabd";[m
[m
static iBool loadState_App_(iApp *d) {[m
iUnused(d);[m
[31m- const char *oldPath = concatPath_CStr(dataDir_App_, oldStateFileName_App_);[m
[31m- const char *path = concatPath_CStr(dataDir_App_, stateFileName_App_);[m
[32m+[m[32m const char *oldPath = concatPath_CStr(dataDir_App_(), oldStateFileName_App_);[m
[32m+[m[32m const char *path = concatPath_CStr(dataDir_App_(), stateFileName_App_);[m
iFile *f = iClob(newCStr_File(fileExistsCStr_FileInfo(path) ? path : oldPath));[m
if (open_File(f, readOnly_FileMode)) {[m
char magic[4];[m
[36m@@ -323,7 +351,7 @@[m [miObjectList *listDocuments_App(void) {[m
static void saveState_App_(const iApp *d) {[m
iUnused(d);[m
trimCache_App();[m
[31m- iFile *f = newCStr_File(concatPath_CStr(dataDir_App_, stateFileName_App_));[m
[32m+[m[32m 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
writeU32_File(f, latest_FileVersion); /* version */[m
[36m@@ -348,7 +376,7 @@[m [mstatic uint32_t checkAsleep_App_(uint32_t interval, void *param) {[m
#endif[m
[m
static void init_App_(iApp *d, int argc, char **argv) {[m
[31m- const iBool isFirstRun = !fileExistsCStr_FileInfo(cleanedPath_CStr(dataDir_App_));[m
[32m+[m[32m const iBool isFirstRun = !fileExistsCStr_FileInfo(cleanedPath_CStr(dataDir_App_()));[m
d->isFinishedLaunching = iFalse;[m
d->launchCommands = new_StringList();[m
iZap(d->lastDropTime);[m
[36m@@ -385,12 +413,12 @@[m [mstatic void init_App_(iApp *d, int argc, char **argv) {[m
}[m
#endif[m
init_Prefs(&d->prefs);[m
[31m- setCStr_String(&d->prefs.downloadDir, downloadDir_App_);[m
[32m+[m[32m setCStr_String(&d->prefs.downloadDir, downloadDir_App_());[m
d->isRunning = iFalse;[m
d->window = NULL;[m
set_Atomic(&d->pendingRefresh, iFalse);[m
d->mimehooks = new_MimeHooks();[m
[31m- d->certs = new_GmCerts(dataDir_App_);[m
[32m+[m[32m d->certs = new_GmCerts(dataDir_App_());[m
d->visited = new_Visited();[m
d->bookmarks = new_Bookmarks();[m
d->tabEnum = 0; /* generates unique IDs for tab pages */[m
[36m@@ -405,10 +433,10 @@[m [mstatic void init_App_(iApp *d, int argc, char **argv) {[m
#endif[m
init_Keys();[m
loadPrefs_App_(d);[m
[31m- load_Keys(dataDir_App_);[m
[31m- load_Visited(d->visited, dataDir_App_);[m
[31m- load_Bookmarks(d->bookmarks, dataDir_App_);[m
[31m- load_MimeHooks(d->mimehooks, dataDir_App_);[m
[32m+[m[32m load_Keys(dataDir_App_());[m
[32m+[m[32m load_Visited(d->visited, dataDir_App_());[m
[32m+[m[32m load_Bookmarks(d->bookmarks, dataDir_App_());[m
[32m+[m[32m load_MimeHooks(d->mimehooks, dataDir_App_());[m
if (isFirstRun) {[m
/* Create the default bookmarks for a quick start. */[m
add_Bookmarks(d->bookmarks,[m
[36m@@ -434,7 +462,7 @@[m [mstatic void init_App_(iApp *d, int argc, char **argv) {[m
}[m
#endif[m
d->window = new_Window(d->initialWindowRect);[m
[31m- init_Feeds(dataDir_App_);[m
[32m+[m[32m init_Feeds(dataDir_App_());[m
/* Widget state init. */[m
processEvents_App(postedEventsOnly_AppEventMode);[m
if (!loadState_App_(d)) {[m
[36m@@ -469,13 +497,13 @@[m [mstatic void init_App_(iApp *d, int argc, char **argv) {[m
static void deinit_App(iApp *d) {[m
saveState_App_(d);[m
deinit_Feeds();[m
[31m- save_Keys(dataDir_App_);[m
[32m+[m[32m save_Keys(dataDir_App_());[m
deinit_Keys();[m
savePrefs_App_(d);[m
deinit_Prefs(&d->prefs);[m
[31m- save_Bookmarks(d->bookmarks, dataDir_App_);[m
[32m+[m[32m save_Bookmarks(d->bookmarks, dataDir_App_());[m
delete_Bookmarks(d->bookmarks);[m
[31m- save_Visited(d->visited, dataDir_App_);[m
[32m+[m[32m save_Visited(d->visited, dataDir_App_());[m
delete_Visited(d->visited);[m
delete_GmCerts(d->certs);[m
save_MimeHooks(d->mimehooks);[m
[36m@@ -494,7 +522,7 @@[m [mconst iString *execPath_App(void) {[m
}[m
[m
const iString *dataDir_App(void) {[m
[31m- return collect_String(cleanedCStr_Path(dataDir_App_));[m
[32m+[m[32m return collect_String(cleanedCStr_Path(dataDir_App_()));[m
}[m
[m
const iString *downloadDir_App(void) {[m
[36m@@ -854,8 +882,10 @@[m [mstatic iBool handlePrefsCommands_(iWidget *d, const char *cmd) {[m
if (equal_Command(cmd, "prefs.dismiss") || equal_Command(cmd, "preferences")) {[m
setUiScale_Window(get_Window(),[m
toFloat_String(text_InputWidget(findChild_Widget(d, "prefs.uiscale"))));[m
[32m+[m[32m#if defined (LAGRANGE_DOWNLOAD_EDIT)[m
postCommandf_App("downloads path:%s",[m
cstr_String(text_InputWidget(findChild_Widget(d, "prefs.downloads"))));[m
[32m+[m[32m#endif[m
postCommandf_App("window.retain arg:%d",[m
isSelected_Widget(findChild_Widget(d, "prefs.retainwindow")));[m
postCommandf_App("smoothscroll arg:%d",[m
[36m@@ -1449,7 +1479,7 @@[m [miBool handleCommand_App(const char *cmd) {[m
return iTrue;[m
}[m
else if (equal_Command(cmd, "bookmarks.changed")) {[m
[31m- save_Bookmarks(d->bookmarks, dataDir_App_);[m
[32m+[m[32m save_Bookmarks(d->bookmarks, dataDir_App_());[m
return iFalse;[m
}[m
else if (equal_Command(cmd, "feeds.refresh")) {[m
[36m@@ -1468,7 +1498,7 @@[m [miBool handleCommand_App(const char *cmd) {[m
return iFalse;[m
}[m
else if (equal_Command(cmd, "visited.changed")) {[m
[31m- save_Visited(d->visited, dataDir_App_);[m
[32m+[m[32m save_Visited(d->visited, dataDir_App_());[m
return iFalse;[m
}[m
else if (equal_Command(cmd, "ident.new")) {[m
[1mdiff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c[m
[1mindex 3f799e3c..1674040b 100644[m
[1m--- a/src/ui/inputwidget.c[m
[1m+++ b/src/ui/inputwidget.c[m
[36m@@ -223,6 +223,7 @@[m [mstatic void updateBuffered_InputWidget_(iInputWidget *d) {[m
}[m
[m
void setText_InputWidget(iInputWidget *d, const iString *text) {[m
[32m+[m[32m if (!d) return;[m
if (d->inFlags & isUrl_InputWidgetFlag) {[m
/* If user wants URLs encoded, also Punycode the domain. */[m
if (!prefs_App()->decodeUserVisibleURLs) {[m
[1mdiff --git a/src/ui/util.c b/src/ui/util.c[m
[1mindex 4d5ed916..d64a93b6 100644[m
[1m--- a/src/ui/util.c[m
[1m+++ b/src/ui/util.c[m
[36m@@ -1023,6 +1023,7 @@[m [mstatic void makeTwoColumnHeading_(const char *title, iWidget *headings, iWidget[m
}[m
[m
static void expandInputFieldWidth_(iInputWidget *input) {[m
[32m+[m[32m if (!input) return;[m
iWidget *page = as_Widget(input)->parent->parent->parent->parent; /* tabs > page > values > input */[m
as_Widget(input)->rect.size.x =[m
right_Rect(bounds_Widget(page)) - left_Rect(bounds_Widget(constAs_Widget(input)));[m
[36m@@ -1056,8 +1057,10 @@[m [miWidget *makePreferences_Widget(void) {[m
iWidget *headings, *values;[m
/* General preferences. */ {[m
appendTwoColumnPage_(tabs, "General", '1', &headings, &values);[m
[32m+[m[32m#if defined (LAGRANGE_DOWNLOAD_EDIT)[m
addChild_Widget(headings, iClob(makeHeading_Widget("Downloads folder:")));[m
setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.downloads");[m
[32m+[m[32m#endif[m
addChild_Widget(headings, iClob(makeHeading_Widget("Show URL on hover:")));[m
addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoverlink")));[m
addChild_Widget(headings, iClob(makeHeading_Widget("Smooth scrolling:")));[m
text/plain
This content has been proxied by September (ba2dc).