From 18c592661fdfce7222dd9172874fc1efaaf47e7a Mon Sep 17 00:00:00 2001
From: Albrecht Schlosser <albrechts.fltk@online.de>
Date: Sun, 19 May 2019 14:01:32 +0200
Subject: [PATCH] Patch as given by OP in STR 3520

See: FileChooser.1.4.0.diff (inside .tar file)

Modified slightly to be closer to the FLTK coding standard:
 - remove trailing spaces
 - fix indenting
 - other minor formatting issues
---
 FL/Fl_File_Chooser.H     |  3 +-
 FL/filename.H            |  1 +
 src/Fl_File_Chooser.fl   |  4 +-
 src/Fl_File_Chooser2.cxx | 86 +++++++++++++++++++++++-----------------
 src/Fl_File_Input.cxx    | 28 ++++++++++---
 src/filename_isdir.cxx   |  6 +++
 6 files changed, 84 insertions(+), 44 deletions(-)

diff --git a/FL/Fl_File_Chooser.H b/FL/Fl_File_Chooser.H
index 4ce459ebf..6f3beac1f 100644
--- a/FL/Fl_File_Chooser.H
+++ b/FL/Fl_File_Chooser.H
@@ -65,6 +65,7 @@ private:
   void showChoiceCB(); 
   void update_favorites(); 
   void update_preview(); 
+  void activate_okButton_if_file();
 public:
   Fl_File_Chooser(const char *d, const char *p, int t, const char *title);
 private:
@@ -132,7 +133,7 @@ public:
   void color(Fl_Color c);
   Fl_Color color();
   int count(); 
-  void directory(const char *d); 
+  void directory(const char *d, bool f = true);
   char * directory();
   void filter(const char *p); 
   const char * filter();
diff --git a/FL/filename.H b/FL/filename.H
index 9d41008a9..cfda5d1fa 100644
--- a/FL/filename.H
+++ b/FL/filename.H
@@ -54,6 +54,7 @@ FL_EXPORT int fl_filename_absolute(char *to, int tolen, const char *from);
 FL_EXPORT int fl_filename_relative(char *to, int tolen, const char *from);
 FL_EXPORT int fl_filename_match(const char *name, const char *pattern);
 FL_EXPORT int fl_filename_isdir(const char *name);
+FL_EXPORT int fl_filename_isfile(const char *name);
 
 #  if defined(__cplusplus) && !defined(FL_DOXYGEN)
 /*
diff --git a/src/Fl_File_Chooser.fl b/src/Fl_File_Chooser.fl
index fca0a32f3..dcb80dc6f 100644
--- a/src/Fl_File_Chooser.fl
+++ b/src/Fl_File_Chooser.fl
@@ -68,6 +68,8 @@ class FL_EXPORT Fl_File_Chooser {open
   }
   decl {void update_preview();} {private local
   }
+  decl {void activate_okButton_if_file();} {
+  }
   Function {Fl_File_Chooser(const char *d, const char *p, int t, const char *title)} {} {
     code {if (!prefs_) {
   prefs_ = new Fl_Preferences(Fl_Preferences::USER, "fltk.org", "filechooser");
@@ -267,7 +269,7 @@ data_     = d;} {}
   }
   decl {int count();} {public local
   }
-  decl {void directory(const char *d);} {public local
+  decl {void directory(const char *d, bool f = true);} {public local
   }
   Function {directory()} {return_type {char *}
   } {
diff --git a/src/Fl_File_Chooser2.cxx b/src/Fl_File_Chooser2.cxx
index 80d842bcf..06b99e107 100644
--- a/src/Fl_File_Chooser2.cxx
+++ b/src/Fl_File_Chooser2.cxx
@@ -430,7 +430,8 @@ Fl_File_Chooser::count() {
 //
 
 void
-Fl_File_Chooser::directory(const char *d)// I - Directory to change to
+Fl_File_Chooser::directory(const char *d,// I - Directory to change to
+                       bool       f)// I - update file name field?
 {
   char	*dirptr;			    // Pointer into directory
   char  fixpath[FL_PATH_MAX];   // Path with slashes converted
@@ -489,6 +490,24 @@ Fl_File_Chooser::directory(const char *d)// I - Directory to change to
   else
     directory_[0] = '\0';
 
+  if (f) {
+    // Update the current filename accordingly...
+    char pathname[sizeof(directory_)];	// New pathname for filename field
+
+    strlcpy(pathname, directory_, sizeof(pathname));
+    if (pathname[0] && pathname[strlen(pathname) - 1] != '/')
+      strlcat(pathname, "/", sizeof(pathname));
+
+    // Prevent users from cursing us: keep basename, if not a directory
+    if (!fl_filename_isdir(fileName->value())) {
+      dirptr = strchr(pathname, 0);
+      strlcat(pathname, fl_filename_name(fileName->value()), sizeof(pathname));
+      if (!(type_ & CREATE) && !fl_filename_isfile(pathname))
+        *dirptr = 0;
+    }
+    fileName->value(pathname);
+  }
+
   if (shown()) {
     // Rescan the directory...
     rescan();
@@ -743,7 +762,10 @@ Fl_File_Chooser::fileListCB()
     if (*filename == '/') *filename = '\0';
 
 //    puts("Setting fileName from fileListCB...");
-    fileName->value(pathname);
+    if (!*pathname || fl_filename_isdir(pathname))
+      directory(pathname);
+    else
+      fileName->value(pathname);
 
     // Update the preview box...
     Fl::remove_timeout((Fl_Timeout_Handler)previewCB, this);
@@ -753,7 +775,7 @@ Fl_File_Chooser::fileListCB()
     if (callback_) (*callback_)(this, data_);
 
     // Activate the OK button as needed...
-    if (!Fl::system_driver()->filename_isdir_quick(pathname) || (type_ & DIRECTORY))
+    if (!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))
       okButton->activate();
     else
       okButton->deactivate();
@@ -818,7 +840,7 @@ Fl_File_Chooser::fileNameCB()
     if (Fl::system_driver()->colon_is_drive()) condition = isalpha(pathname[0] & 255) && pathname[1] == ':' && !pathname[2];
     if (!condition) condition = ( Fl::system_driver()->filename_isdir_quick(pathname) && compare_dirnames(pathname, directory_) );
     if (condition) {
-      directory(pathname);
+      directory(pathname, false);
     } else if ((type_ & CREATE) || fl_access(pathname, 0) == 0) {
       if (!Fl::system_driver()->filename_isdir_quick(pathname) || (type_ & DIRECTORY)) {
 	// Update the preview box...
@@ -853,7 +875,7 @@ Fl_File_Chooser::fileNameCB()
       int p = fileName->position();
       int m = fileName->mark();
 
-      directory(pathname);
+      directory(pathname, false);
 
       if (filename[0]) {
 	char tempname[FL_PATH_MAX];
@@ -933,22 +955,13 @@ Fl_File_Chooser::fileNameCB()
     }
 
     // See if we need to enable the OK button...
-    if (((type_ & CREATE) || !fl_access(fileName->value(), 0)) &&
-        (!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))) {
-      okButton->activate();
-    } else {
-      okButton->deactivate();
-    }
+    activate_okButton_if_file();
+
   } else {
     // FL_Delete or FL_BackSpace
     fileList->deselect(0);
     fileList->redraw();
-    if (((type_ & CREATE) || !fl_access(fileName->value(), 0)) &&
-        (!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))) {
-      okButton->activate();
-    } else {
-      okButton->deactivate();
-    }
+    activate_okButton_if_file();
   }
 }
 
@@ -1082,21 +1095,7 @@ Fl_File_Chooser::previewCB(Fl_File_Chooser *fc) {	// I - File chooser
 void
 Fl_File_Chooser::rescan()
 {
-  char	pathname[FL_PATH_MAX];		// New pathname for filename field
-
-
-  // Clear the current filename
-  strlcpy(pathname, directory_, sizeof(pathname));
-  if (pathname[0] && pathname[strlen(pathname) - 1] != '/') {
-    strlcat(pathname, "/", sizeof(pathname));
-  }
-//  puts("Setting fileName in rescan()");
-  fileName->value(pathname);
-
-  if (type_ & DIRECTORY)
-    okButton->activate();
-  else
-    okButton->deactivate();
+  activate_okButton_if_file();
 
   // Build the file list...
   fileList->load(directory_, sort);
@@ -1470,7 +1469,7 @@ Fl_File_Chooser::value(const char *filename)
   // See if the filename is the "My System" directory...
   if (filename == NULL || !filename[0]) {
     // Yes, just change the current directory...
-    directory(filename);
+    directory(filename, false);
     fileName->value("");
     okButton->deactivate();
     return;
@@ -1495,12 +1494,16 @@ Fl_File_Chooser::value(const char *filename)
 
   if ((slash = strrchr(pathname, '/')) != NULL) {
     // Yes, change the display to the directory... 
-    if (!fl_filename_isdir(pathname)) *slash++ = '\0';
+    if (fl_filename_isdir(pathname))
+      slash = pathname;
+    else
+      *slash++ = '\0';
 
-    directory(pathname);
-    if (*slash == '/') slash = pathname;
+    directory(pathname, false);
+    if (!shown()) fileList->load(pathname);
+    if (slash > pathname) slash[-1] = '/';
   } else {
-    directory(".");
+    directory(".", false);
     slash = pathname;
   }
 
@@ -1525,6 +1528,15 @@ Fl_File_Chooser::value(const char *filename)
       break;
     }
 }
+
+void Fl_File_Chooser::activate_okButton_if_file() {
+
+  if (((type_ & CREATE) || !fl_access(fileName->value(), 0)) &&
+      (!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY)))
+    okButton->activate();
+  else
+    okButton->deactivate();
+}
   
 void Fl_File_Chooser::show()
 {
diff --git a/src/Fl_File_Input.cxx b/src/Fl_File_Input.cxx
index 201101809..05e52aef0 100644
--- a/src/Fl_File_Input.cxx
+++ b/src/Fl_File_Input.cxx
@@ -266,12 +266,30 @@ Fl_File_Input::handle_button(int event)		// I - Event
 
   if (i < 0) {
     // Found the end; truncate the value and update the buttons...
-    *start = '\0';
-    value(newvalue, (int) (start - newvalue) );
+    const char *basename = 0;
+    if (!fl_filename_isdir(newvalue))
+      if (!*(basename = fl_filename_name(newvalue)))
+	basename = 0;
+
+    if (!basename)
+      *start = '\0';
+    else {
+      // Prevent users from cursing us: keep basename, if not a directory
+      memmove(start, basename, strlen(basename)+1);
+      // Should have some 'ftype_' field to reflect the caller's
+      // intentions, but for now we get along without...
+      // if (!(ftype_ & CREATE) && !fl_filename_isfile(newvalue))
+      //   *start = 0;
+      // else
+      if (start == basename) i = 0; // unchanged!
+    }
+    if (i < 0) {
+      value(newvalue);
 
-    // Then do the callbacks, if necessary...
-    set_changed();
-    if (when() & (FL_WHEN_CHANGED|FL_WHEN_RELEASE) ) do_callback();
+      // Then do the callbacks, if necessary...
+      set_changed();
+      if (when() & (FL_WHEN_CHANGED|FL_WHEN_RELEASE) ) do_callback();
+    }
   }
 
   return 1;
diff --git a/src/filename_isdir.cxx b/src/filename_isdir.cxx
index 5488f9131..e39ee11aa 100644
--- a/src/filename_isdir.cxx
+++ b/src/filename_isdir.cxx
@@ -81,6 +81,12 @@ int Fl_System_Driver::filename_isdir(const char* n) {
  \endcond
  */
 
+/** Returns true if the file exists and is a regular file. */
+int fl_filename_isfile(const char* name) {
+  struct stat s;
+  return !fl_stat(name, &s) && (s.st_mode & 0170000) == 0100000;
+}
+
 //
 // End of "$Id$".
 //
-- 
2.20.1

