00ed897277
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9704 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
1701 lines
46 KiB
C++
1701 lines
46 KiB
C++
//
|
|
// "$Id$"
|
|
//
|
|
// More Fl_File_Chooser routines.
|
|
//
|
|
// Copyright 1999-2011 by Michael Sweet.
|
|
//
|
|
// This library is free software. Distribution and use rights are outlined in
|
|
// the file "COPYING" which should have been included with this file. If this
|
|
// file is missing or damaged, see the license at:
|
|
//
|
|
// http://www.fltk.org/COPYING.php
|
|
//
|
|
// Please report all bugs and problems on the following page:
|
|
//
|
|
// http://www.fltk.org/str.php
|
|
//
|
|
|
|
// fabien: ATTENTION: Only Out Of Source Gen. because cxx/H files are autogenerated by fluid.
|
|
/** \defgroup group_comdlg Common Dialogs classes and functions
|
|
@{
|
|
*/
|
|
/** \class Fl_File_Chooser
|
|
The Fl_File_Chooser widget displays a standard file selection
|
|
dialog that supports various selection modes.
|
|
|
|
\image html Fl_File_Chooser.jpg
|
|
\image latex Fl_File_Chooser.jpg "Fl_File_Chooser" width=12cm
|
|
|
|
The Fl_File_Chooser widget transmits UTF-8 encoded filenames to its user. It is
|
|
recommended to open files that may have non-ASCII names with the fl_fopen() or
|
|
fl_open() utility functions that handle these names in a cross-platform way
|
|
(whereas the standard fopen()/open() functions fail on the MSWindows platform
|
|
to open files with a non-ASCII name).
|
|
|
|
The Fl_File_Chooser class also exports several static values
|
|
that may be used to localize or customize the appearance of all file chooser
|
|
dialogs:
|
|
|
|
<CENTER><TABLE BORDER="1">
|
|
<TR>
|
|
<TH>Member</TH>
|
|
<TH>Default value</TH>
|
|
</TR>
|
|
<TR>
|
|
<TD>add_favorites_label</TD>
|
|
<TD>"Add to Favorites"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>all_files_label</TD>
|
|
<TD>"All Files (*)"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>custom_filter_label</TD>
|
|
<TD>"Custom Filter"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>existing_file_label</TD>
|
|
<TD>"Please choose an existing file!"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>favorites_label</TD>
|
|
<TD>"Favorites"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>filename_label</TD>
|
|
<TD>"Filename:"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>filesystems_label</TD>
|
|
<TD>"My Computer" (WIN32)<BR>
|
|
"File Systems" (all others)</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>hidden_label</TD>
|
|
<TD>"Show hidden files:"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>manage_favorites_label</TD>
|
|
<TD>"Manage Favorites"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>new_directory_label</TD>
|
|
<TD>"New Directory?"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>new_directory_tooltip</TD>
|
|
<TD>"Create a new directory."</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>preview_label</TD>
|
|
<TD>"Preview"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>save_label</TD>
|
|
<TD>"Save"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>show_label</TD>
|
|
<TD>"Show:"</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>sort</TD>
|
|
<TD>fl_numericsort</TD>
|
|
</TR>
|
|
</TABLE></CENTER>
|
|
|
|
The Fl_File_Chooser::sort member specifies the sort function that is
|
|
used when loading the contents of a directory and can be customized
|
|
at run-time.
|
|
|
|
The Fl_File_Chooser class also exports the Fl_File_Chooser::newButton
|
|
and Fl_File_Chooser::previewButton widgets so that application developers
|
|
can control their appearance and use. For more complex customization,
|
|
consider copying the FLTK file chooser code and changing it accordingly.
|
|
*/
|
|
/** @} */
|
|
|
|
/** \fn Fl_File_Chooser::Fl_File_Chooser(const char *pathname, const char *pattern, int type, const char *title)
|
|
The constructor creates the Fl_File_Chooser dialog shown.
|
|
The pathname argument can be a directory name or a
|
|
complete file name (in which case the corresponding file is highlighted
|
|
in the list and in the filename input field.)
|
|
|
|
The pattern argument can be a NULL
|
|
string or "*" to list all files, or it can be a
|
|
series of descriptions and filter strings separated by tab
|
|
characters (\\t). The format of filters is either
|
|
"Description text (patterns)" or just "patterns". A file chooser
|
|
that provides filters for HTML and image files might look like:
|
|
|
|
\code
|
|
"HTML Files (*.html)\tImage Files (*.{bmp,gif,jpg,png})"
|
|
\endcode
|
|
|
|
The file chooser will automatically add the "All Files (*)"
|
|
pattern to the end of the string you pass if you do not provide
|
|
one. The first filter in the string is the default filter.
|
|
|
|
See the FLTK documentation on fl_filename_match()
|
|
for the kinds of pattern strings that are supported.
|
|
|
|
The type argument can be one of the following:
|
|
|
|
\li \c SINGLE - allows the user to select a single, existing file.
|
|
\li \c MULTI - allows the user to select one or more existing files.
|
|
\li \c CREATE - allows the user to select a single, existing file or
|
|
specify a new filename.
|
|
\li \c DIRECTORY - allows the user to select a single, existing directory.
|
|
|
|
The title argument is used to set the title bar text for the
|
|
Fl_File_Chooser window.
|
|
*/
|
|
|
|
/** \var Fl_File_Chooser::newButton
|
|
The "new directory" button is exported so that application developers
|
|
can control the appearance and use.
|
|
*/
|
|
|
|
/** \var Fl_File_Chooser::previewButton
|
|
The "preview" button is exported so that application developers can
|
|
control the appearance and use.
|
|
*/
|
|
|
|
/** \var Fl_File_Chooser::showHiddenButton
|
|
When checked, hidden files (i.e., filename begins with dot) are displayed.
|
|
|
|
The "showHiddenButton" button is exported so that application developers can
|
|
control its appearance.
|
|
*/
|
|
|
|
/** \fn Fl_File_Chooser::~Fl_File_Chooser()
|
|
Destroys the widget and frees all memory used by it.*/
|
|
|
|
/** \fn void Fl_File_Chooser::color(Fl_Color c)
|
|
Sets the background color of the Fl_File_Browser list.*/
|
|
|
|
/** \fn Fl_Color Fl_File_Chooser::color()
|
|
Gets the background color of the Fl_File_Browser list.*/
|
|
|
|
/** \fn int Fl_File_Chooser::count()
|
|
Returns the number of selected files.*/
|
|
|
|
/** \fn void Fl_File_Chooser::directory(const char *pathname)
|
|
Sets the current directory.*/
|
|
|
|
/** \fn const char *Fl_File_Chooser::directory()
|
|
Gets the current directory.*/
|
|
|
|
/** \fn void Fl_File_Chooser::filter(const char *pattern)
|
|
Sets or gets the current filename filter patterns. The filter
|
|
patterns use fl_filename_match().
|
|
Multiple patterns can be used by separating them with tabs, like
|
|
<tt>"*.jpg\t*.png\t*.gif\t*"</tt>. In addition, you can provide
|
|
human-readable labels with the patterns inside parenthesis, like
|
|
<tt>"JPEG Files (*.jpg)\tPNG Files (*.png)\tGIF Files (*.gif)\tAll Files (*)"
|
|
</tt>.
|
|
|
|
Use filter(NULL) to show all files.
|
|
*/
|
|
|
|
/** \fn const char *Fl_File_Chooser::filter()
|
|
See void filter(const char *pattern)*/
|
|
|
|
/** \fn void Fl_File_Chooser::filter_value(int f)
|
|
Sets the current filename filter selection.*/
|
|
|
|
/** \fn int Fl_File_Chooser::filter_value()
|
|
Gets the current filename filter selection.*/
|
|
|
|
/** \fn void Fl_File_Chooser::hide()
|
|
Hides the Fl_File_Chooser window.*/
|
|
|
|
/** \fn void Fl_File_Chooser::iconsize(uchar s)
|
|
Sets the size of the icons in the Fl_File_Browser. By
|
|
default the icon size is set to 1.5 times the textsize().
|
|
*/
|
|
|
|
/** \fn uchar Fl_File_Chooser::iconsize()
|
|
Gets the size of the icons in the Fl_File_Browser. By
|
|
default the icon size is set to 1.5 times the textsize().
|
|
*/
|
|
|
|
/** \fn void Fl_File_Chooser::label(const char *l)
|
|
Sets the title bar text for the Fl_File_Chooser.*/
|
|
|
|
/** \fn const char *Fl_File_Chooser::label()
|
|
Gets the title bar text for the Fl_File_Chooser.*/
|
|
|
|
/** \fn void Fl_File_Chooser::ok_label(const char *l)
|
|
Sets the label for the "ok" button in the Fl_File_Chooser.
|
|
*/
|
|
|
|
/** \fn const char *Fl_File_Chooser::ok_label()
|
|
Gets the label for the "ok" button in the Fl_File_Chooser.
|
|
*/
|
|
|
|
/** \fn int Fl_File_Chooser::preview() const
|
|
Returns the current state of the preview box. */
|
|
|
|
/** \fn void Fl_File_Chooser::rescan()
|
|
Reloads the current directory in the Fl_File_Browser.*/
|
|
|
|
/** \fn void Fl_File_Chooser::show()
|
|
Shows the Fl_File_Chooser window.*/
|
|
|
|
/** \fn void Fl_File_Chooser::textcolor(Fl_Color c)
|
|
Sets the current Fl_File_Browser text color.*/
|
|
|
|
/** \fn Fl_Color Fl_File_Chooser::textcolor()
|
|
Gets the current Fl_File_Browser text color.*/
|
|
|
|
/** \fn void Fl_File_Chooser::textfont(Fl_Font f)
|
|
Sets the current Fl_File_Browser text font.*/
|
|
|
|
/** \fn Fl_Font Fl_File_Chooser::textfont()
|
|
Gets the current Fl_File_Browser text font.*/
|
|
|
|
/** \fn void Fl_File_Chooser::textsize(Fl_Fontsize s)
|
|
Sets the current Fl_File_Browser text size.*/
|
|
|
|
/** \fn Fl_Fontsize Fl_File_Chooser::textsize()
|
|
Gets the current Fl_File_Browser text size.*/
|
|
|
|
/** \fn void Fl_File_Chooser::type(int t)
|
|
Sets the current type of Fl_File_Chooser.*/
|
|
|
|
/** \fn int Fl_File_Chooser::type()
|
|
Gets the current type of Fl_File_Chooser.*/
|
|
|
|
/** \fn void Fl_File_Chooser::value(const char *pathname)
|
|
Sets the current value of the selected file.
|
|
*/
|
|
|
|
/** \fn const char *Fl_File_Chooser::value(int f)
|
|
Gets the current value of the selected file(s).
|
|
\p f is a \c 1-based index into a list of
|
|
file names. The number of selected files is returned by
|
|
Fl_File_Chooser::count().
|
|
|
|
This sample code loops through all selected files:
|
|
\code
|
|
// Get list of filenames user selected from a MULTI chooser
|
|
for ( int t=1; t<=chooser->count(); t++ ) {
|
|
const char *filename = chooser->value(t);
|
|
...
|
|
}
|
|
\endcode
|
|
*/
|
|
|
|
/** \fn int Fl_File_Chooser::visible()
|
|
Returns 1 if the Fl_File_Chooser window is visible.*/
|
|
|
|
/** \fn Fl_Widget* Fl_File_Chooser::add_extra(Fl_Widget*)
|
|
Adds extra widget at the bottom of Fl_File_Chooser window.
|
|
Returns pointer for previous extra widget or NULL if not set previously.
|
|
If argument is NULL only remove previous extra widget.
|
|
|
|
\note Fl_File_Chooser does \b not delete extra widget in destructor!
|
|
To prevent memory leakage, don't forget to delete unused extra widgets
|
|
*/
|
|
/** \fn int Fl_File_Chooser::shown()
|
|
Returns non-zero if the file chooser main window show() has been called (but not hide()
|
|
see Fl_Window::shown()
|
|
*/
|
|
|
|
/** \fn void Fl_File_Chooser::callback(void (*cb)(Fl_File_Chooser *, void *), void *d = 0)
|
|
Sets the file chooser callback cb and associated data d */
|
|
|
|
/** \fn void Fl_File_Chooser::user_data(void *d)
|
|
Sets the file chooser user data d */
|
|
|
|
/** \fn void * Fl_File_Chooser::user_data() const
|
|
Gets the file chooser user data */
|
|
// *** END OF OUT OF SOURCE DOC ***
|
|
|
|
// Contents:
|
|
//
|
|
// Fl_File_Chooser::count() - Return the number of selected files.
|
|
// Fl_File_Chooser::directory() - Set the directory in the file chooser.
|
|
// Fl_File_Chooser::filter() - Set the filter(s) for the chooser.
|
|
// Fl_File_Chooser::newdir() - Make a new directory.
|
|
// Fl_File_Chooser::value() - Return a selected filename.
|
|
// Fl_File_Chooser::rescan() - Rescan the current directory.
|
|
// Fl_File_Chooser::favoritesButtonCB() - Handle favorites selections.
|
|
// Fl_File_Chooser::fileListCB() - Handle clicks (and double-clicks)
|
|
// in the Fl_File_Browser.
|
|
// Fl_File_Chooser::fileNameCB() - Handle text entry in the FileBrowser.
|
|
// Fl_File_Chooser::showChoiceCB() - Handle show selections.
|
|
// compare_dirnames() - Compare two directory names.
|
|
// quote_pathname() - Quote a pathname for a menu.
|
|
// unquote_pathname() - Unquote a pathname from a menu.
|
|
//
|
|
// Fl_File_Chooser::add_extra() - add extra widget at the bottom, return pointer
|
|
// to previous extra widget or NULL if none,
|
|
// If argument is NULL extra widget removed.
|
|
// NOTE! file chooser does't delete extra widget in
|
|
// destructor! To prevent memory leakage don't forget
|
|
// delete unused extra widgets by yourself.
|
|
//
|
|
|
|
//
|
|
// Include necessary headers.
|
|
//
|
|
|
|
#include <FL/Fl_File_Chooser.H>
|
|
#include <FL/filename.H>
|
|
#include <FL/fl_ask.H>
|
|
#include <FL/x.H>
|
|
#include <FL/Fl_Shared_Image.H>
|
|
#include <FL/fl_draw.H>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "flstring.h"
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#if defined(WIN32) && ! defined (__CYGWIN__)
|
|
# include <direct.h>
|
|
# include <io.h>
|
|
// Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs
|
|
// on Windows, which is supposed to be POSIX compliant...
|
|
# define access _access
|
|
# define mkdir _mkdir
|
|
// Apparently Borland C++ defines DIRECTORY in <direct.h>, which
|
|
// interfers with the Fl_File_Icon enumeration of the same name.
|
|
# ifdef DIRECTORY
|
|
# undef DIRECTORY
|
|
# endif // DIRECTORY
|
|
#else
|
|
# include <unistd.h>
|
|
# include <pwd.h>
|
|
#endif /* WIN32 */
|
|
|
|
|
|
//
|
|
// File chooser label strings and sort function...
|
|
//
|
|
|
|
Fl_Preferences Fl_File_Chooser::prefs_(Fl_Preferences::USER, "fltk.org", "filechooser");
|
|
|
|
const char *Fl_File_Chooser::add_favorites_label = "Add to Favorites";
|
|
const char *Fl_File_Chooser::all_files_label = "All Files (*)";
|
|
const char *Fl_File_Chooser::custom_filter_label = "Custom Filter";
|
|
const char *Fl_File_Chooser::existing_file_label = "Please choose an existing file!";
|
|
const char *Fl_File_Chooser::favorites_label = "Favorites";
|
|
const char *Fl_File_Chooser::filename_label = "Filename:";
|
|
#ifdef WIN32
|
|
const char *Fl_File_Chooser::filesystems_label = "My Computer";
|
|
#else
|
|
const char *Fl_File_Chooser::filesystems_label = "File Systems";
|
|
#endif // WIN32
|
|
const char *Fl_File_Chooser::manage_favorites_label = "Manage Favorites";
|
|
const char *Fl_File_Chooser::new_directory_label = "New Directory?";
|
|
const char *Fl_File_Chooser::new_directory_tooltip = "Create a new directory.";
|
|
const char *Fl_File_Chooser::preview_label = "Preview";
|
|
const char *Fl_File_Chooser::save_label = "Save";
|
|
const char *Fl_File_Chooser::show_label = "Show:";
|
|
const char *Fl_File_Chooser::hidden_label = "Show hidden files";
|
|
Fl_File_Sort_F *Fl_File_Chooser::sort = fl_numericsort;
|
|
|
|
|
|
//
|
|
// Local functions...
|
|
//
|
|
|
|
static int compare_dirnames(const char *a, const char *b);
|
|
static void quote_pathname(char *, const char *, int);
|
|
static void unquote_pathname(char *, const char *, int);
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::count()' - Return the number of selected files.
|
|
//
|
|
|
|
int // O - Number of selected files
|
|
Fl_File_Chooser::count() {
|
|
int i; // Looping var
|
|
int fcount; // Number of selected files
|
|
const char *filename; // Filename in input field or list
|
|
|
|
|
|
filename = fileName->value();
|
|
|
|
if (!(type_ & MULTI)) {
|
|
// Check to see if the file name input field is blank...
|
|
if (!filename || !filename[0]) return 0;
|
|
else return 1;
|
|
}
|
|
|
|
for (i = 1, fcount = 0; i <= fileList->size(); i ++)
|
|
if (fileList->selected(i)) {
|
|
// See if this file is a directory...
|
|
// matt: why would we do that? It is perfectly legal to select multiple
|
|
// directories in a DIR chooser. They are visually selected and value(i)
|
|
// returns all of them as expected
|
|
//filename = (char *)fileList->text(i);
|
|
|
|
//if (filename[strlen(filename) - 1] != '/')
|
|
fcount ++;
|
|
}
|
|
|
|
if (fcount) return fcount;
|
|
else if (!filename || !filename[0]) return 0;
|
|
else return 1;
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::directory()' - Set the directory in the file chooser.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::directory(const char *d)// I - Directory to change to
|
|
{
|
|
char *dirptr; // Pointer into directory
|
|
|
|
|
|
// printf("Fl_File_Chooser::directory(\"%s\")\n", d == NULL ? "(null)" : d);
|
|
|
|
// NULL == current directory
|
|
if (d == NULL)
|
|
d = ".";
|
|
|
|
#ifdef WIN32
|
|
// See if the filename contains backslashes...
|
|
char *slash; // Pointer to slashes
|
|
char fixpath[FL_PATH_MAX]; // Path with slashes converted
|
|
if (strchr(d, '\\')) {
|
|
// Convert backslashes to slashes...
|
|
strlcpy(fixpath, d, sizeof(fixpath));
|
|
|
|
for (slash = strchr(fixpath, '\\'); slash; slash = strchr(slash + 1, '\\'))
|
|
*slash = '/';
|
|
|
|
d = fixpath;
|
|
}
|
|
#endif // WIN32
|
|
|
|
if (d[0] != '\0')
|
|
{
|
|
// Make the directory absolute...
|
|
#if (defined(WIN32) && ! defined(__CYGWIN__))|| defined(__EMX__)
|
|
if (d[0] != '/' && d[0] != '\\' && d[1] != ':')
|
|
#else
|
|
if (d[0] != '/' && d[0] != '\\')
|
|
#endif /* WIN32 || __EMX__ */
|
|
fl_filename_absolute(directory_, d);
|
|
else
|
|
strlcpy(directory_, d, sizeof(directory_));
|
|
|
|
// Strip any trailing slash...
|
|
dirptr = directory_ + strlen(directory_) - 1;
|
|
if ((*dirptr == '/' || *dirptr == '\\') && dirptr > directory_)
|
|
*dirptr = '\0';
|
|
|
|
// See if we have a trailing .. or . in the filename...
|
|
dirptr = directory_ + strlen(directory_) - 3;
|
|
if (dirptr >= directory_ && strcmp(dirptr, "/..") == 0) {
|
|
// Yes, we have "..", so strip the trailing path...
|
|
*dirptr = '\0';
|
|
while (dirptr > directory_) {
|
|
if (*dirptr == '/') break;
|
|
dirptr --;
|
|
}
|
|
|
|
if (dirptr >= directory_ && *dirptr == '/')
|
|
*dirptr = '\0';
|
|
} else if ((dirptr + 1) >= directory_ && strcmp(dirptr + 1, "/.") == 0) {
|
|
// Strip trailing "."...
|
|
dirptr[1] = '\0';
|
|
}
|
|
}
|
|
else
|
|
directory_[0] = '\0';
|
|
|
|
if (shown()) {
|
|
// Rescan the directory...
|
|
rescan();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::favoritesButtonCB()' - Handle favorites selections.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::favoritesButtonCB()
|
|
{
|
|
int v; // Current selection
|
|
char pathname[FL_PATH_MAX], // Pathname
|
|
menuname[FL_PATH_MAX]; // Menu name
|
|
|
|
|
|
v = favoritesButton->value();
|
|
|
|
if (!v) {
|
|
// Add current directory to favorites...
|
|
if (getenv("HOME")) v = favoritesButton->size() - 5;
|
|
else v = favoritesButton->size() - 4;
|
|
|
|
sprintf(menuname, "favorite%02d", v);
|
|
|
|
prefs_.set(menuname, directory_);
|
|
prefs_.flush();
|
|
|
|
quote_pathname(menuname, directory_, sizeof(menuname));
|
|
favoritesButton->add(menuname);
|
|
|
|
if (favoritesButton->size() > 104) {
|
|
((Fl_Menu_Item *)favoritesButton->menu())[0].deactivate();
|
|
}
|
|
} else if (v == 1) {
|
|
// Manage favorites...
|
|
favoritesCB(0);
|
|
} else if (v == 2) {
|
|
// Filesystems/My Computer
|
|
directory("");
|
|
} else {
|
|
unquote_pathname(pathname, favoritesButton->text(v), sizeof(pathname));
|
|
directory(pathname);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::favoritesCB()' - Handle favorites dialog.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::favoritesCB(Fl_Widget *w)
|
|
// I - Widget
|
|
{
|
|
int i; // Looping var
|
|
char name[32], // Preference name
|
|
pathname[1024]; // Directory in list
|
|
|
|
|
|
if (!w) {
|
|
// Load the favorites list...
|
|
favList->clear();
|
|
favList->deselect();
|
|
|
|
for (i = 0; i < 100; i ++) {
|
|
// Get favorite directory 0 to 99...
|
|
sprintf(name, "favorite%02d", i);
|
|
|
|
prefs_.get(name, pathname, "", sizeof(pathname));
|
|
|
|
// Stop on the first empty favorite...
|
|
if (!pathname[0]) break;
|
|
|
|
// Add the favorite to the list...
|
|
favList->add(pathname,
|
|
Fl_File_Icon::find(pathname, Fl_File_Icon::DIRECTORY));
|
|
}
|
|
|
|
favUpButton->deactivate();
|
|
favDeleteButton->deactivate();
|
|
favDownButton->deactivate();
|
|
favOkButton->deactivate();
|
|
|
|
favWindow->hotspot(favList);
|
|
favWindow->show();
|
|
} else if (w == favList) {
|
|
i = favList->value();
|
|
if (i) {
|
|
if (i > 1) favUpButton->activate();
|
|
else favUpButton->deactivate();
|
|
|
|
favDeleteButton->activate();
|
|
|
|
if (i < favList->size()) favDownButton->activate();
|
|
else favDownButton->deactivate();
|
|
} else {
|
|
favUpButton->deactivate();
|
|
favDeleteButton->deactivate();
|
|
favDownButton->deactivate();
|
|
}
|
|
} else if (w == favUpButton) {
|
|
i = favList->value();
|
|
|
|
favList->insert(i - 1, favList->text(i), favList->data(i));
|
|
favList->remove(i + 1);
|
|
favList->select(i - 1);
|
|
|
|
if (i == 2) favUpButton->deactivate();
|
|
|
|
favDownButton->activate();
|
|
|
|
favOkButton->activate();
|
|
} else if (w == favDeleteButton) {
|
|
i = favList->value();
|
|
|
|
favList->remove(i);
|
|
|
|
if (i > favList->size()) i --;
|
|
favList->select(i);
|
|
|
|
if (i < favList->size()) favDownButton->activate();
|
|
else favDownButton->deactivate();
|
|
|
|
if (i > 1) favUpButton->activate();
|
|
else favUpButton->deactivate();
|
|
|
|
if (!i) favDeleteButton->deactivate();
|
|
|
|
favOkButton->activate();
|
|
} else if (w == favDownButton) {
|
|
i = favList->value();
|
|
|
|
favList->insert(i + 2, favList->text(i), favList->data(i));
|
|
favList->remove(i);
|
|
favList->select(i + 1);
|
|
|
|
if ((i + 1) == favList->size()) favDownButton->deactivate();
|
|
|
|
favUpButton->activate();
|
|
|
|
favOkButton->activate();
|
|
} else if (w == favOkButton) {
|
|
// Copy the new list over...
|
|
for (i = 0; i < favList->size(); i ++) {
|
|
// Set favorite directory 0 to 99...
|
|
sprintf(name, "favorite%02d", i);
|
|
|
|
prefs_.set(name, favList->text(i + 1));
|
|
}
|
|
|
|
// Clear old entries as necessary...
|
|
for (; i < 100; i ++) {
|
|
// Clear favorite directory 0 to 99...
|
|
sprintf(name, "favorite%02d", i);
|
|
|
|
prefs_.get(name, pathname, "", sizeof(pathname));
|
|
|
|
if (pathname[0]) prefs_.set(name, "");
|
|
else break;
|
|
}
|
|
|
|
update_favorites();
|
|
prefs_.flush();
|
|
|
|
favWindow->hide();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::fileListCB()' - Handle clicks (and double-clicks) in the
|
|
// Fl_File_Browser.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::fileListCB()
|
|
{
|
|
char *filename, // New filename
|
|
pathname[FL_PATH_MAX]; // Full pathname to file
|
|
|
|
|
|
filename = (char *)fileList->text(fileList->value());
|
|
if (!filename)
|
|
return;
|
|
|
|
if (!directory_[0]) {
|
|
strlcpy(pathname, filename, sizeof(pathname));
|
|
} else if (strcmp(directory_, "/") == 0) {
|
|
snprintf(pathname, sizeof(pathname), "/%s", filename);
|
|
} else {
|
|
snprintf(pathname, sizeof(pathname), "%s/%s", directory_, filename);
|
|
}
|
|
|
|
if (Fl::event_clicks()) {
|
|
#if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
|
|
if ((strlen(pathname) == 2 && pathname[1] == ':') ||
|
|
_fl_filename_isdir_quick(pathname))
|
|
#else
|
|
if (_fl_filename_isdir_quick(pathname))
|
|
#endif /* WIN32 || __EMX__ */
|
|
{
|
|
// Change directories...
|
|
directory(pathname);
|
|
|
|
// Reset the click count so that a click in the same spot won't
|
|
// be treated as a triple-click. We use a value of -1 because
|
|
// the next click will increment click count to 0, which is what
|
|
// we really want...
|
|
Fl::event_clicks(-1);
|
|
}
|
|
else
|
|
{
|
|
// Hide the window - picked the file...
|
|
window->hide();
|
|
if (callback_) (*callback_)(this, data_);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check if the user clicks on a directory when picking files;
|
|
// if so, make sure only that item is selected...
|
|
filename = pathname + strlen(pathname) - 1;
|
|
|
|
if ((type_ & MULTI) && !(type_ & DIRECTORY)) {
|
|
if (*filename == '/') {
|
|
// Clicked on a directory, deselect everything else...
|
|
int i = fileList->value();
|
|
fileList->deselect();
|
|
fileList->select(i);
|
|
} else {
|
|
// Clicked on a file - see if there are other directories selected...
|
|
int i;
|
|
const char *temp;
|
|
for (i = 1; i <= fileList->size(); i ++) {
|
|
if (i != fileList->value() && fileList->selected(i)) {
|
|
temp = fileList->text(i);
|
|
temp += strlen(temp) - 1;
|
|
if (*temp == '/') break; // Yes, selected directory
|
|
}
|
|
}
|
|
|
|
if (i <= fileList->size()) {
|
|
i = fileList->value();
|
|
fileList->deselect();
|
|
fileList->select(i);
|
|
}
|
|
}
|
|
}
|
|
// Strip any trailing slash from the directory name...
|
|
if (*filename == '/') *filename = '\0';
|
|
|
|
// puts("Setting fileName from fileListCB...");
|
|
fileName->value(pathname);
|
|
|
|
// Update the preview box...
|
|
Fl::remove_timeout((Fl_Timeout_Handler)previewCB, this);
|
|
Fl::add_timeout(1.0, (Fl_Timeout_Handler)previewCB, this);
|
|
|
|
// Do any callback that is registered...
|
|
if (callback_) (*callback_)(this, data_);
|
|
|
|
// Activate the OK button as needed...
|
|
if (!_fl_filename_isdir_quick(pathname) || (type_ & DIRECTORY))
|
|
okButton->activate();
|
|
else
|
|
okButton->deactivate();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::fileNameCB()' - Handle text entry in the FileBrowser.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::fileNameCB()
|
|
{
|
|
char *filename, // New filename
|
|
*slash, // Pointer to trailing slash
|
|
pathname[FL_PATH_MAX], // Full pathname to file
|
|
matchname[FL_PATH_MAX]; // Matching filename
|
|
int i, // Looping var
|
|
min_match, // Minimum number of matching chars
|
|
max_match, // Maximum number of matching chars
|
|
num_files, // Number of files in directory
|
|
first_line; // First matching line
|
|
const char *file; // File from directory
|
|
|
|
// puts("fileNameCB()");
|
|
// printf("Event: %s\n", fl_eventnames[Fl::event()]);
|
|
|
|
// Get the filename from the text field...
|
|
filename = (char *)fileName->value();
|
|
|
|
if (!filename || !filename[0]) {
|
|
okButton->deactivate();
|
|
return;
|
|
}
|
|
|
|
// Expand ~ and $ variables as needed...
|
|
if (strchr(filename, '~') || strchr(filename, '$')) {
|
|
fl_filename_expand(pathname, sizeof(pathname), filename);
|
|
filename = pathname;
|
|
value(pathname);
|
|
}
|
|
|
|
// Make sure we have an absolute path...
|
|
#if (defined(WIN32) && !defined(__CYGWIN__)) || defined(__EMX__)
|
|
if (directory_[0] != '\0' && filename[0] != '/' &&
|
|
filename[0] != '\\' &&
|
|
!(isalpha(filename[0] & 255) && (!filename[1] || filename[1] == ':'))) {
|
|
#else
|
|
if (directory_[0] != '\0' && filename[0] != '/') {
|
|
#endif /* WIN32 || __EMX__ */
|
|
fl_filename_absolute(pathname, sizeof(pathname), filename);
|
|
value(pathname);
|
|
fileName->mark(fileName->position()); // no selection after expansion
|
|
} else if (filename != pathname) {
|
|
// Finally, make sure that we have a writable copy...
|
|
strlcpy(pathname, filename, sizeof(pathname));
|
|
}
|
|
|
|
filename = pathname;
|
|
|
|
// Now process things according to the key pressed...
|
|
if (Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter) {
|
|
// Enter pressed - select or change directory...
|
|
#if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
|
|
if ((isalpha(pathname[0] & 255) && pathname[1] == ':' && !pathname[2]) ||
|
|
(_fl_filename_isdir_quick(pathname) &&
|
|
compare_dirnames(pathname, directory_))) {
|
|
#else
|
|
if (_fl_filename_isdir_quick(pathname) &&
|
|
compare_dirnames(pathname, directory_)) {
|
|
#endif /* WIN32 || __EMX__ */
|
|
directory(pathname);
|
|
} else if ((type_ & CREATE) || access(pathname, 0) == 0) {
|
|
if (!_fl_filename_isdir_quick(pathname) || (type_ & DIRECTORY)) {
|
|
// Update the preview box...
|
|
update_preview();
|
|
|
|
// Do any callback that is registered...
|
|
if (callback_) (*callback_)(this, data_);
|
|
|
|
// Hide the window to signal things are done...
|
|
window->hide();
|
|
}
|
|
} else {
|
|
// File doesn't exist, so beep at and alert the user...
|
|
fl_alert("%s",existing_file_label);
|
|
}
|
|
}
|
|
else if (Fl::event_key() != FL_Delete &&
|
|
Fl::event_key() != FL_BackSpace) {
|
|
// Check to see if the user has entered a directory...
|
|
if ((slash = strrchr(pathname, '/')) == NULL)
|
|
slash = strrchr(pathname, '\\');
|
|
|
|
if (!slash) return;
|
|
|
|
// Yes, change directories if necessary...
|
|
*slash++ = '\0';
|
|
filename = slash;
|
|
|
|
#if defined(WIN32) || defined(__EMX__)
|
|
if (strcasecmp(pathname, directory_) &&
|
|
(pathname[0] || strcasecmp("/", directory_))) {
|
|
#else
|
|
if (strcmp(pathname, directory_) &&
|
|
(pathname[0] || strcasecmp("/", directory_))) {
|
|
#endif // WIN32 || __EMX__
|
|
int p = fileName->position();
|
|
int m = fileName->mark();
|
|
|
|
directory(pathname);
|
|
|
|
if (filename[0]) {
|
|
char tempname[FL_PATH_MAX];
|
|
|
|
snprintf(tempname, sizeof(tempname), "%s/%s", directory_, filename);
|
|
fileName->value(tempname);
|
|
strlcpy(pathname, tempname, sizeof(pathname));
|
|
}
|
|
|
|
fileName->position(p, m);
|
|
}
|
|
|
|
// Other key pressed - do filename completion as possible...
|
|
num_files = fileList->size();
|
|
min_match = (int) strlen(filename);
|
|
max_match = min_match + 1;
|
|
first_line = 0;
|
|
|
|
for (i = 1; i <= num_files && max_match > min_match; i ++) {
|
|
file = fileList->text(i);
|
|
|
|
#if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
|
|
if (strncasecmp(filename, file, min_match) == 0) {
|
|
#else
|
|
if (strncmp(filename, file, min_match) == 0) {
|
|
#endif // WIN32 || __EMX__
|
|
// OK, this one matches; check against the previous match
|
|
if (!first_line) {
|
|
// First match; copy stuff over...
|
|
strlcpy(matchname, file, sizeof(matchname));
|
|
max_match = (int) strlen(matchname);
|
|
|
|
// Strip trailing /, if any...
|
|
if (matchname[max_match - 1] == '/') {
|
|
max_match --;
|
|
matchname[max_match] = '\0';
|
|
}
|
|
|
|
// And then make sure that the item is visible
|
|
fileList->topline(i);
|
|
first_line = i;
|
|
} else {
|
|
// Succeeding match; compare to find maximum string match...
|
|
while (max_match > min_match)
|
|
#if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
|
|
if (strncasecmp(file, matchname, max_match) == 0)
|
|
#else
|
|
if (strncmp(file, matchname, max_match) == 0)
|
|
#endif // WIN32 || __EMX__
|
|
break;
|
|
else
|
|
max_match --;
|
|
|
|
// Truncate the string as needed...
|
|
matchname[max_match] = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we have any matches, add them to the input field...
|
|
if (first_line > 0 && min_match == max_match &&
|
|
max_match == (int)strlen(fileList->text(first_line))) {
|
|
// This is the only possible match...
|
|
fileList->deselect(0);
|
|
fileList->select(first_line);
|
|
fileList->redraw();
|
|
} else if (max_match > min_match && first_line) {
|
|
// Add the matching portion...
|
|
fileName->replace(
|
|
(int) (filename - pathname),
|
|
(int) (filename - pathname + min_match),
|
|
matchname);
|
|
|
|
// Highlight it with the cursor at the end of the selection so
|
|
// s/he can press the right arrow to accept the selection
|
|
// (Tab and End also do this for both cases.)
|
|
fileName->position(
|
|
(int) (filename - pathname + max_match),
|
|
(int) (filename - pathname + min_match));
|
|
} else if (max_match == 0) {
|
|
fileList->deselect(0);
|
|
fileList->redraw();
|
|
}
|
|
|
|
// See if we need to enable the OK button...
|
|
if (((type_ & CREATE) || !access(fileName->value(), 0)) &&
|
|
(!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))) {
|
|
okButton->activate();
|
|
} else {
|
|
okButton->deactivate();
|
|
}
|
|
} else {
|
|
// FL_Delete or FL_BackSpace
|
|
fileList->deselect(0);
|
|
fileList->redraw();
|
|
if (((type_ & CREATE) || !access(fileName->value(), 0)) &&
|
|
(!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))) {
|
|
okButton->activate();
|
|
} else {
|
|
okButton->deactivate();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::filter()' - Set the filter(s) for the chooser.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::filter(const char *p) // I - Pattern(s)
|
|
{
|
|
char *copyp, // Copy of pattern
|
|
*start, // Start of pattern
|
|
*end; // End of pattern
|
|
int allfiles; // Do we have a "*" pattern?
|
|
char temp[FL_PATH_MAX]; // Temporary pattern string
|
|
|
|
|
|
// Make sure we have a pattern...
|
|
if (!p || !*p) p = "*";
|
|
|
|
// Copy the pattern string...
|
|
copyp = strdup(p);
|
|
|
|
// Separate the pattern string as necessary...
|
|
showChoice->clear();
|
|
|
|
for (start = copyp, allfiles = 0; start && *start; start = end) {
|
|
end = strchr(start, '\t');
|
|
if (end) *end++ = '\0';
|
|
|
|
if (strcmp(start, "*") == 0) {
|
|
showChoice->add(all_files_label);
|
|
allfiles = 1;
|
|
} else {
|
|
quote_pathname(temp, start, sizeof(temp));
|
|
showChoice->add(temp);
|
|
if (strstr(start, "(*)") != NULL) allfiles = 1;
|
|
}
|
|
}
|
|
|
|
free(copyp);
|
|
|
|
if (!allfiles) showChoice->add(all_files_label);
|
|
|
|
showChoice->add(custom_filter_label);
|
|
|
|
showChoice->value(0);
|
|
showChoiceCB();
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::newdir()' - Make a new directory.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::newdir()
|
|
{
|
|
const char *dir; // New directory name
|
|
char pathname[FL_PATH_MAX]; // Full path of directory
|
|
|
|
|
|
// Get a directory name from the user
|
|
if ((dir = fl_input("%s", NULL, new_directory_label)) == NULL)
|
|
return;
|
|
|
|
// Make it relative to the current directory as needed...
|
|
#if (defined(WIN32) && ! defined (__CYGWIN__)) || defined(__EMX__)
|
|
if (dir[0] != '/' && dir[0] != '\\' && dir[1] != ':')
|
|
#else
|
|
if (dir[0] != '/' && dir[0] != '\\')
|
|
#endif /* WIN32 || __EMX__ */
|
|
snprintf(pathname, sizeof(pathname), "%s/%s", directory_, dir);
|
|
else
|
|
strlcpy(pathname, dir, sizeof(pathname));
|
|
|
|
// Create the directory; ignore EEXIST errors...
|
|
#if defined(WIN32) && ! defined (__CYGWIN__)
|
|
if (mkdir(pathname))
|
|
#else
|
|
if (mkdir(pathname, 0777))
|
|
#endif /* WIN32 */
|
|
if (errno != EEXIST)
|
|
{
|
|
fl_alert("%s", strerror(errno));
|
|
return;
|
|
}
|
|
|
|
// Show the new directory...
|
|
directory(pathname);
|
|
}
|
|
|
|
|
|
|
|
/** Enable or disable the preview tile. 1 = enable preview, 0 = disable preview. */
|
|
void Fl_File_Chooser::preview(int e)
|
|
{
|
|
previewButton->value(e);
|
|
prefs_.set("preview", e);
|
|
prefs_.flush();
|
|
|
|
Fl_Group *p = previewBox->parent();
|
|
if (e) {
|
|
int w = p->w() * 2 / 3;
|
|
fileList->resize(fileList->x(), fileList->y(),
|
|
w, fileList->h());
|
|
previewBox->resize(fileList->x()+w, previewBox->y(),
|
|
p->w()-w, previewBox->h());
|
|
previewBox->show();
|
|
update_preview();
|
|
} else {
|
|
fileList->resize(fileList->x(), fileList->y(),
|
|
p->w(), fileList->h());
|
|
previewBox->resize(p->x()+p->w(), previewBox->y(),
|
|
0, previewBox->h());
|
|
previewBox->hide();
|
|
}
|
|
p->init_sizes();
|
|
|
|
fileList->parent()->redraw();
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::previewCB()' - Timeout handler for the preview box.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::previewCB(Fl_File_Chooser *fc) { // I - File chooser
|
|
fc->update_preview();
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::rescan()' - Rescan the current directory.
|
|
//
|
|
|
|
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();
|
|
|
|
// Build the file list...
|
|
fileList->load(directory_, sort);
|
|
#ifndef WIN32
|
|
if (!showHiddenButton->value()) remove_hidden_files();
|
|
#endif
|
|
// Update the preview box...
|
|
update_preview();
|
|
}
|
|
|
|
//
|
|
/**
|
|
Rescan the current directory without clearing the filename,
|
|
then select the file if it is in the list
|
|
*/
|
|
void Fl_File_Chooser::rescan_keep_filename()
|
|
{
|
|
// if no filename was set, this is likely a diretory browser
|
|
const char *fn = fileName->value();
|
|
if (!fn || !*fn || fn[strlen(fn) - 1]=='/') {
|
|
rescan();
|
|
return;
|
|
}
|
|
|
|
int i;
|
|
char pathname[FL_PATH_MAX]; // New pathname for filename field
|
|
strlcpy(pathname, fn, sizeof(pathname));
|
|
|
|
// Build the file list...
|
|
fileList->load(directory_, sort);
|
|
#ifndef WIN32
|
|
if (!showHiddenButton->value()) remove_hidden_files();
|
|
#endif
|
|
// Update the preview box...
|
|
update_preview();
|
|
|
|
// and select the chosen file
|
|
char found = 0;
|
|
char *slash = strrchr(pathname, '/');
|
|
if (slash)
|
|
slash++;
|
|
else
|
|
slash = pathname;
|
|
for (i = 1; i <= fileList->size(); i ++)
|
|
#if defined(WIN32) || defined(__EMX__)
|
|
if (strcasecmp(fileList->text(i), slash) == 0) {
|
|
#else
|
|
if (strcmp(fileList->text(i), slash) == 0) {
|
|
#endif // WIN32 || __EMX__
|
|
fileList->topline(i);
|
|
fileList->select(i);
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
// update OK button activity
|
|
if (found || type_ & CREATE)
|
|
okButton->activate();
|
|
else
|
|
okButton->deactivate();
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::showChoiceCB()' - Handle show selections.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::showChoiceCB()
|
|
{
|
|
const char *item, // Selected item
|
|
*patstart; // Start of pattern
|
|
char *patend; // End of pattern
|
|
char temp[FL_PATH_MAX]; // Temporary string for pattern
|
|
|
|
|
|
item = showChoice->text(showChoice->value());
|
|
|
|
if (strcmp(item, custom_filter_label) == 0) {
|
|
if ((item = fl_input("%s", pattern_, custom_filter_label)) != NULL) {
|
|
strlcpy(pattern_, item, sizeof(pattern_));
|
|
|
|
quote_pathname(temp, item, sizeof(temp));
|
|
showChoice->add(temp);
|
|
showChoice->value(showChoice->size() - 2);
|
|
}
|
|
} else if ((patstart = strchr(item, '(')) == NULL) {
|
|
strlcpy(pattern_, item, sizeof(pattern_));
|
|
} else {
|
|
strlcpy(pattern_, patstart + 1, sizeof(pattern_));
|
|
if ((patend = strrchr(pattern_, ')')) != NULL) *patend = '\0';
|
|
}
|
|
|
|
fileList->filter(pattern_);
|
|
|
|
if (shown()) {
|
|
// Rescan the directory...
|
|
rescan_keep_filename();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::update_favorites()' - Update the favorites menu.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::update_favorites()
|
|
{
|
|
int i; // Looping var
|
|
char pathname[FL_PATH_MAX], // Pathname
|
|
menuname[2048]; // Menu name
|
|
const char *home; // Home directory
|
|
|
|
|
|
favoritesButton->clear();
|
|
favoritesButton->add("bla");
|
|
favoritesButton->clear();
|
|
favoritesButton->add(add_favorites_label, FL_ALT + 'a', 0);
|
|
favoritesButton->add(manage_favorites_label, FL_ALT + 'm', 0, 0, FL_MENU_DIVIDER);
|
|
favoritesButton->add(filesystems_label, FL_ALT + 'f', 0);
|
|
|
|
if ((home = getenv("HOME")) != NULL) {
|
|
quote_pathname(menuname, home, sizeof(menuname));
|
|
favoritesButton->add(menuname, FL_ALT + 'h', 0);
|
|
}
|
|
|
|
for (i = 0; i < 100; i ++) {
|
|
sprintf(menuname, "favorite%02d", i);
|
|
prefs_.get(menuname, pathname, "", sizeof(pathname));
|
|
if (!pathname[0]) break;
|
|
|
|
quote_pathname(menuname, pathname, sizeof(menuname));
|
|
|
|
if (i < 10) favoritesButton->add(menuname, FL_ALT + '0' + i, 0);
|
|
else favoritesButton->add(menuname);
|
|
}
|
|
|
|
if (i == 100) ((Fl_Menu_Item *)favoritesButton->menu())[0].deactivate();
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::update_preview()' - Update the preview box...
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::update_preview()
|
|
{
|
|
const char *filename; // Current filename
|
|
const char *newlabel = 0; // New label text
|
|
Fl_Shared_Image *image = 0, // New image
|
|
*oldimage; // Old image
|
|
int pbw, pbh; // Width and height of preview box
|
|
int w, h; // Width and height of preview image
|
|
int set = 0; // Set this flag as soon as a decent preview is found
|
|
|
|
if (!previewButton->value()) return;
|
|
|
|
filename = value();
|
|
if (filename == NULL) {
|
|
// no file name at all, so we have an empty preview
|
|
set = 1;
|
|
} else if (fl_filename_isdir(filename)) {
|
|
// filename is a directory, show a folder icon
|
|
newlabel = "@fileopen";
|
|
set = 1;
|
|
} else {
|
|
struct stat s;
|
|
if (fl_stat(filename, &s)==0) {
|
|
if ((s.st_mode&S_IFMT)!=S_IFREG) {
|
|
// this is no regular file, probably some kind of device
|
|
newlabel = "@-3refresh"; // a cross
|
|
set = 1;
|
|
} else if (s.st_size==0) {
|
|
// this file is emty
|
|
newlabel = "<empty file>";
|
|
set = 1;
|
|
} else {
|
|
// if this file is an image, try to load it
|
|
window->cursor(FL_CURSOR_WAIT);
|
|
Fl::check();
|
|
|
|
image = Fl_Shared_Image::get(filename);
|
|
|
|
if (image) {
|
|
window->cursor(FL_CURSOR_DEFAULT);
|
|
Fl::check();
|
|
set = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
oldimage = (Fl_Shared_Image *)previewBox->image();
|
|
|
|
if (oldimage) oldimage->release();
|
|
|
|
previewBox->image(0);
|
|
|
|
if (!set) {
|
|
FILE *fp;
|
|
int bytes;
|
|
char *ptr;
|
|
|
|
if (filename) fp = fl_fopen(filename, "rb");
|
|
else fp = NULL;
|
|
|
|
if (fp != NULL) {
|
|
// Try reading the first 1k of data for a label...
|
|
bytes = (int) fread(preview_text_, 1, sizeof(preview_text_) - 1, fp);
|
|
preview_text_[bytes] = '\0';
|
|
fclose(fp);
|
|
} else {
|
|
// Assume we can't read any data...
|
|
preview_text_[0] = '\0';
|
|
}
|
|
|
|
window->cursor(FL_CURSOR_DEFAULT);
|
|
Fl::check();
|
|
|
|
// Scan the buffer for printable UTF8 chars...
|
|
for (ptr = preview_text_; *ptr; ptr++) {
|
|
uchar c = uchar(*ptr);
|
|
if ( (c&0x80)==0 ) {
|
|
if (!isprint(c&255) && !isspace(c&255)) break;
|
|
} else if ( (c&0xe0)==0xc0 ) {
|
|
if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
|
|
ptr++;
|
|
} else if ( (c&0xf0)==0xe0 ) {
|
|
if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
|
|
ptr++;
|
|
if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
|
|
ptr++;
|
|
} else if ( (c&0xf8)==0xf0 ) {
|
|
if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
|
|
ptr++;
|
|
if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
|
|
ptr++;
|
|
if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
|
|
ptr++;
|
|
}
|
|
}
|
|
// *ptr && (isprint(*ptr & 255) || isspace(*ptr & 255));
|
|
// ptr ++);
|
|
|
|
// Scan the buffer for printable characters in 8 bit
|
|
if (*ptr || ptr == preview_text_) {
|
|
for (ptr = preview_text_;
|
|
*ptr && (isprint(*ptr & 255) || isspace(*ptr & 255));
|
|
ptr ++);
|
|
}
|
|
|
|
if (*ptr || ptr == preview_text_) {
|
|
// Non-printable file, just show a big ?...
|
|
previewBox->label(filename ? "?" : 0);
|
|
previewBox->align(FL_ALIGN_CLIP);
|
|
previewBox->labelsize(75);
|
|
previewBox->labelfont(FL_HELVETICA);
|
|
} else {
|
|
// Show the first 1k of text...
|
|
int size = previewBox->h() / 20;
|
|
if (size < 6) size = 6;
|
|
else if (size > FL_NORMAL_SIZE) size = FL_NORMAL_SIZE;
|
|
|
|
previewBox->label(preview_text_);
|
|
previewBox->align((Fl_Align)(FL_ALIGN_CLIP | FL_ALIGN_INSIDE |
|
|
FL_ALIGN_LEFT | FL_ALIGN_TOP));
|
|
previewBox->labelsize(size);
|
|
previewBox->labelfont(FL_COURIER);
|
|
}
|
|
} else if (image) {
|
|
pbw = previewBox->w() - 20;
|
|
pbh = previewBox->h() - 20;
|
|
|
|
if (image->w() > pbw || image->h() > pbh) {
|
|
w = pbw;
|
|
h = w * image->h() / image->w();
|
|
|
|
if (h > pbh) {
|
|
h = pbh;
|
|
w = h * image->w() / image->h();
|
|
}
|
|
|
|
oldimage = (Fl_Shared_Image *)image->copy(w, h);
|
|
previewBox->image((Fl_Image *)oldimage);
|
|
|
|
image->release();
|
|
} else {
|
|
previewBox->image((Fl_Image *)image);
|
|
}
|
|
|
|
previewBox->align(FL_ALIGN_CLIP);
|
|
previewBox->label(0);
|
|
} else if (newlabel) {
|
|
previewBox->label(newlabel);
|
|
previewBox->align(FL_ALIGN_CLIP);
|
|
previewBox->labelsize(newlabel[0]=='@'?75:12);
|
|
previewBox->labelfont(FL_HELVETICA);
|
|
}
|
|
|
|
previewBox->redraw();
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::value()' - Return a selected filename.
|
|
//
|
|
|
|
const char * // O - Filename or NULL
|
|
Fl_File_Chooser::value(int f) // I - File number
|
|
{
|
|
int i; // Looping var
|
|
int fcount; // Number of selected files
|
|
const char *name; // Current filename
|
|
static char pathname[FL_PATH_MAX]; // Filename + directory
|
|
|
|
|
|
name = fileName->value();
|
|
|
|
if (!(type_ & MULTI)) {
|
|
// Return the filename in the filename field...
|
|
if (!name || !name[0]) return NULL;
|
|
else return name;
|
|
}
|
|
|
|
// Return a filename from the list...
|
|
for (i = 1, fcount = 0; i <= fileList->size(); i ++)
|
|
if (fileList->selected(i)) {
|
|
// See if this file is a selected file/directory...
|
|
name = fileList->text(i);
|
|
|
|
fcount ++;
|
|
|
|
if (fcount == f) {
|
|
if (directory_[0]) {
|
|
snprintf(pathname, sizeof(pathname), "%s/%s", directory_, name);
|
|
} else {
|
|
strlcpy(pathname, name, sizeof(pathname));
|
|
}
|
|
|
|
return pathname;
|
|
}
|
|
}
|
|
|
|
// If nothing is selected, use the filename field...
|
|
if (!name || !name[0]) return NULL;
|
|
else return name;
|
|
}
|
|
|
|
|
|
//
|
|
// 'Fl_File_Chooser::value()' - Set the current filename.
|
|
//
|
|
|
|
void
|
|
Fl_File_Chooser::value(const char *filename)
|
|
// I - Filename + directory
|
|
{
|
|
int i, // Looping var
|
|
fcount; // Number of items in list
|
|
char *slash; // Directory separator
|
|
char pathname[FL_PATH_MAX]; // Local copy of filename
|
|
|
|
|
|
// printf("Fl_File_Chooser::value(\"%s\")\n", filename == NULL ? "(null)" : filename);
|
|
|
|
// See if the filename is the "My System" directory...
|
|
if (filename == NULL || !filename[0]) {
|
|
// Yes, just change the current directory...
|
|
directory(filename);
|
|
fileName->value("");
|
|
okButton->deactivate();
|
|
return;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
// See if the filename contains backslashes...
|
|
char fixpath[FL_PATH_MAX]; // Path with slashes converted
|
|
if (strchr(filename, '\\')) {
|
|
// Convert backslashes to slashes...
|
|
strlcpy(fixpath, filename, sizeof(fixpath));
|
|
|
|
for (slash = strchr(fixpath, '\\'); slash; slash = strchr(slash + 1, '\\'))
|
|
*slash = '/';
|
|
|
|
filename = fixpath;
|
|
}
|
|
#endif // WIN32
|
|
|
|
// See if there is a directory in there...
|
|
fl_filename_absolute(pathname, sizeof(pathname), filename);
|
|
|
|
if ((slash = strrchr(pathname, '/')) != NULL) {
|
|
// Yes, change the display to the directory...
|
|
if (!fl_filename_isdir(pathname)) *slash++ = '\0';
|
|
|
|
directory(pathname);
|
|
if (*slash == '/') slash = pathname;
|
|
} else {
|
|
directory(".");
|
|
slash = pathname;
|
|
}
|
|
|
|
// Set the input field to the absolute path...
|
|
if (slash > pathname) slash[-1] = '/';
|
|
|
|
fileName->value(pathname);
|
|
fileName->position(0, (int) strlen(pathname));
|
|
okButton->activate();
|
|
|
|
// Then find the file in the file list and select it...
|
|
fcount = fileList->size();
|
|
|
|
fileList->deselect(0);
|
|
fileList->redraw();
|
|
|
|
for (i = 1; i <= fcount; i ++)
|
|
#if defined(WIN32) || defined(__EMX__)
|
|
if (strcasecmp(fileList->text(i), slash) == 0) {
|
|
#else
|
|
if (strcmp(fileList->text(i), slash) == 0) {
|
|
#endif // WIN32 || __EMX__
|
|
// printf("Selecting line %d...\n", i);
|
|
fileList->topline(i);
|
|
fileList->select(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Fl_File_Chooser::show()
|
|
{
|
|
window->hotspot(fileList);
|
|
window->show();
|
|
Fl::flush();
|
|
fl_cursor(FL_CURSOR_WAIT);
|
|
rescan_keep_filename();
|
|
fl_cursor(FL_CURSOR_DEFAULT);
|
|
fileName->take_focus();
|
|
#ifdef WIN32
|
|
showHiddenButton->hide();
|
|
#endif
|
|
}
|
|
|
|
void Fl_File_Chooser::showHidden(int value)
|
|
{
|
|
if (value) {
|
|
fileList->load(directory());
|
|
} else {
|
|
remove_hidden_files();
|
|
fileList->redraw();
|
|
}
|
|
}
|
|
|
|
void Fl_File_Chooser::remove_hidden_files()
|
|
{
|
|
int count = fileList->size();
|
|
for(int num = count; num >= 1; num--) {
|
|
const char *p = fileList->text(num);
|
|
if (*p == '.' && strcmp(p, "../") != 0) fileList->remove(num);
|
|
}
|
|
fileList->topline(1);
|
|
}
|
|
|
|
|
|
//
|
|
// 'compare_dirnames()' - Compare two directory names.
|
|
//
|
|
|
|
static int
|
|
compare_dirnames(const char *a, const char *b) {
|
|
int alen, blen;
|
|
|
|
// Get length of each string...
|
|
alen = (int) (strlen(a) - 1);
|
|
blen = (int) (strlen(b) - 1);
|
|
|
|
if (alen < 0 || blen < 0) return alen - blen;
|
|
|
|
// Check for trailing slashes...
|
|
if (a[alen] != '/') alen ++;
|
|
if (b[blen] != '/') blen ++;
|
|
|
|
// If the lengths aren't the same, then return the difference...
|
|
if (alen != blen) return alen - blen;
|
|
|
|
// Do a comparison of the first N chars (alen == blen at this point)...
|
|
#ifdef WIN32
|
|
return strncasecmp(a, b, alen);
|
|
#else
|
|
return strncmp(a, b, alen);
|
|
#endif // WIN32
|
|
}
|
|
|
|
|
|
//
|
|
// 'quote_pathname()' - Quote a pathname for a menu.
|
|
//
|
|
|
|
static void
|
|
quote_pathname(char *dst, // O - Destination string
|
|
const char *src, // I - Source string
|
|
int dstsize) // I - Size of destination string
|
|
{
|
|
dstsize --;
|
|
|
|
while (*src && dstsize > 1) {
|
|
if (*src == '\\') {
|
|
// Convert backslash to forward slash...
|
|
*dst++ = '\\';
|
|
*dst++ = '/';
|
|
src ++;
|
|
} else {
|
|
if (*src == '/') *dst++ = '\\';
|
|
|
|
*dst++ = *src++;
|
|
}
|
|
}
|
|
|
|
*dst = '\0';
|
|
}
|
|
|
|
|
|
//
|
|
// 'unquote_pathname()' - Unquote a pathname from a menu.
|
|
//
|
|
|
|
static void
|
|
unquote_pathname(char *dst, // O - Destination string
|
|
const char *src, // I - Source string
|
|
int dstsize) // I - Size of destination string
|
|
{
|
|
dstsize --;
|
|
|
|
while (*src && dstsize > 1) {
|
|
if (*src == '\\') src ++;
|
|
*dst++ = *src++;
|
|
}
|
|
|
|
*dst = '\0';
|
|
}
|
|
|
|
|
|
//
|
|
// End of "$Id$".
|
|
//
|