finalcut/src/ffiledialog.cpp

818 lines
18 KiB
C++
Raw Normal View History

// File: ffiledialog.cpp
// Provides: class FFileDialog
2015-05-23 13:35:12 +02:00
#include "ffiledialog.h"
// non-member functions
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
bool sortByName ( const FFileDialog::dir_entry& lhs
, const FFileDialog::dir_entry& rhs )
2015-05-23 13:35:12 +02:00
{
// lhs < rhs
return bool(strcasecmp(lhs.name, rhs.name) < 0);
}
//----------------------------------------------------------------------
bool sortDirFirst ( const FFileDialog::dir_entry& lhs
, const FFileDialog::dir_entry& rhs )
2015-05-23 13:35:12 +02:00
{
// sort directories first
if ( lhs.type == DT_DIR && rhs.type != DT_DIR )
return true;
else
return false;
}
//----------------------------------------------------------------------
// class FFileDialog
//----------------------------------------------------------------------
// constructors and destructor
//----------------------------------------------------------------------
FFileDialog::FFileDialog (FWidget* parent)
2015-09-22 04:18:20 +02:00
: FDialog(parent)
, directory_stream(0)
, dir_entries()
, directory()
, filter_pattern()
, filebrowser()
, filename()
, hidden()
, cancel()
, open()
, dlg_type(FFileDialog::Open)
, show_hidden(false)
2015-05-23 13:35:12 +02:00
{
init();
}
//----------------------------------------------------------------------
2015-09-22 04:18:20 +02:00
FFileDialog::FFileDialog (const FFileDialog& fdlg)
: FDialog(fdlg.getParentWidget())
2015-09-22 04:18:20 +02:00
, directory_stream(0)
, dir_entries()
, directory(fdlg.directory)
, filter_pattern(fdlg.filter_pattern)
, filebrowser()
, filename()
, hidden()
, cancel()
, open()
, dlg_type(fdlg.dlg_type)
, show_hidden(fdlg.show_hidden)
{
if ( directory )
setPath(directory);
2015-09-22 04:18:20 +02:00
init();
}
//----------------------------------------------------------------------
FFileDialog::FFileDialog ( const FString& dirname
, const FString& filter
, DialogType type
, FWidget* parent )
: FDialog(parent)
, directory_stream(0)
, dir_entries()
, directory()
, filter_pattern(filter)
, filebrowser()
, filename()
, hidden()
, cancel()
, open()
, dlg_type(type)
, show_hidden(false)
2015-05-23 13:35:12 +02:00
{
if ( dirname )
setPath(dirname);
2015-05-23 13:35:12 +02:00
init();
}
//----------------------------------------------------------------------
FFileDialog::~FFileDialog() // destructor
{
delete open;
delete cancel;
delete hidden;
delete filebrowser;
delete filename;
clear();
}
// public methods of FFileDialog
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
FFileDialog& FFileDialog::operator = (const FFileDialog& fdlg)
2015-05-23 13:35:12 +02:00
{
if ( &fdlg == this )
{
return *this;
}
else
{
delete open;
delete cancel;
delete hidden;
delete filebrowser;
delete filename;
clear();
2015-05-23 13:35:12 +02:00
if ( fdlg.getParentWidget() )
fdlg.getParentWidget()->addChild (this);
2015-05-23 13:35:12 +02:00
directory = fdlg.directory;
filter_pattern = fdlg.filter_pattern;
dlg_type = fdlg.dlg_type;
show_hidden = fdlg.show_hidden;
2015-05-23 13:35:12 +02:00
if ( directory )
setPath(directory);
2015-05-23 13:35:12 +02:00
init();
return *this;
}
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
const FString FFileDialog::getSelectedFile() const
{
uLong n = uLong(filebrowser->currentItem() - 1);
2015-05-23 13:35:12 +02:00
if ( dir_entries[n].type == DT_DIR )
return FString("");
2015-05-23 13:35:12 +02:00
else
return FString(dir_entries[n].name);
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
void FFileDialog::setPath (const FString& dir)
2015-05-23 13:35:12 +02:00
{
2017-03-17 22:59:06 +01:00
const char* const dirname = dir.c_str();
char resolved_path[MAXPATHLEN];
FString r_dir;
struct stat sb;
2015-05-23 13:35:12 +02:00
if ( stat(dirname, &sb) != 0 )
2015-05-23 13:35:12 +02:00
{
directory = '/';
return;
2015-05-23 13:35:12 +02:00
}
if ( S_ISLNK(sb.st_mode) )
{
if ( lstat(dirname, &sb) != 0 )
{
directory = '/';
return;
}
}
if ( ! S_ISDIR(sb.st_mode) )
2015-05-23 13:35:12 +02:00
{
directory = '/';
return;
2015-05-23 13:35:12 +02:00
}
if ( realpath(dir.c_str(), resolved_path) != 0 )
r_dir = resolved_path;
2015-05-23 13:35:12 +02:00
else
r_dir = dir;
2015-05-23 13:35:12 +02:00
if ( r_dir[r_dir.getLength()-1] != '/' )
directory = r_dir + "/";
2015-05-23 13:35:12 +02:00
else
directory = r_dir;
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
void FFileDialog::setFilter (const FString& filter)
2015-05-23 13:35:12 +02:00
{
filter_pattern = filter;
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
bool FFileDialog::setShowHiddenFiles (bool on)
{
if ( on == show_hidden )
return show_hidden;
2015-05-23 13:35:12 +02:00
show_hidden = on;
readDir();
filebrowser->redraw();
return show_hidden;
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
void FFileDialog::onKeyPress (FKeyEvent* ev)
2015-05-23 13:35:12 +02:00
{
if ( ! isEnabled() )
return;
FDialog::onKeyPress (ev);
if ( ! filebrowser->hasFocus() )
return;
int key = ev->key();
switch ( key )
2015-05-23 13:35:12 +02:00
{
case fc::Fkey_erase:
case fc::Fkey_backspace:
changeDir("..");
ev->accept();
break;
2015-05-23 13:35:12 +02:00
default:
break;
2015-05-23 13:35:12 +02:00
}
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
int FFileDialog::readDir()
2015-05-23 13:35:12 +02:00
{
int start, dir_num;
2017-03-17 22:59:06 +01:00
const char* const dir = directory.c_str();
const char* const filter = filter_pattern.c_str();
errno = 0;
directory_stream = opendir(dir);
2015-05-23 13:35:12 +02:00
if ( ! directory_stream )
{
FMessageBox::error (this, "Can't open directory\n" + directory);
return -1;
}
2015-05-23 13:35:12 +02:00
clear();
2015-05-23 13:35:12 +02:00
while ( true )
2015-05-23 13:35:12 +02:00
{
errno = 0;
struct dirent* next = readdir (directory_stream);
2015-09-20 05:44:50 +02:00
if ( next )
{
if ( next->d_name[0] == '.' && next->d_name[1] == '\0' )
continue;
2015-09-20 05:44:50 +02:00
if ( ! show_hidden
&& next->d_name[0] == '.'
&& next->d_name[1] != '\0'
&& next->d_name[1] != '.' )
2015-05-23 13:35:12 +02:00
{
continue;
}
if ( dir[0] == '/' && dir[1] == '\0' && std::strcmp(next->d_name, "..") == 0 )
continue;
dir_entry entry;
entry.name = strdup(next->d_name);
entry.type = next->d_type;
if ( next->d_type == DT_LNK ) // symbolic link
{
char resolved_path[MAXPATHLEN] = {};
char symLink[MAXPATHLEN] = {};
std::strncpy (symLink, dir, sizeof(symLink) - 1);
std::strncat (symLink, next->d_name, sizeof(symLink) - std::strlen(symLink) - 1);
if ( realpath(symLink, resolved_path) != 0 ) // follow link
2015-05-23 13:35:12 +02:00
{
struct stat sb;
2015-05-23 13:35:12 +02:00
if ( lstat(resolved_path, &sb) == 0 )
2015-05-23 13:35:12 +02:00
{
if ( S_ISDIR(sb.st_mode) )
entry.type = DT_DIR;
2015-05-23 13:35:12 +02:00
}
}
}
if ( entry.type == DT_DIR )
dir_entries.push_back (entry);
else if ( pattern_match(filter, entry.name) )
dir_entries.push_back (entry);
2015-05-23 13:35:12 +02:00
else
std::free(entry.name);
}
else if (errno != 0)
{
FMessageBox::error (this, "Reading directory\n" + directory);
if ( errno != EOVERFLOW )
break;
}
else
break;
} // end while
2015-09-20 05:44:50 +02:00
if ( closedir (directory_stream) != 0 )
{
FMessageBox::error (this, "Closing directory\n" + directory);
return -2;
2015-05-23 13:35:12 +02:00
}
if ( std::strcmp((*dir_entries.begin()).name, "..") == 0 )
start=1;
2015-05-23 13:35:12 +02:00
else
start=0;
2015-05-23 13:35:12 +02:00
dir_num = numOfDirs();
// directories first
std::sort(dir_entries.begin()+start, dir_entries.end(), sortDirFirst);
// sort directories by name
std::sort(dir_entries.begin()+start, dir_entries.begin()+dir_num, sortByName);
// sort files by name
std::sort(dir_entries.begin()+dir_num, dir_entries.end(), sortByName);
// fill list with directory entries
filebrowser->clear();
if ( ! dir_entries.empty() )
2015-05-23 13:35:12 +02:00
{
std::vector<dir_entry>::const_iterator iter, end;
iter = dir_entries.begin();
end = dir_entries.end();
while ( iter != end )
2015-05-23 13:35:12 +02:00
{
if ( (*iter).type == DT_DIR )
filebrowser->insert(FString((*iter).name), fc::SquareBrackets);
else
filebrowser->insert(FString((*iter).name));
++iter;
2015-05-23 13:35:12 +02:00
}
}
return 0;
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
2017-03-17 22:59:06 +01:00
const FString FFileDialog::fileOpenChooser ( FWidget* parent
, const FString& dirname
, const FString& filter )
2015-05-23 13:35:12 +02:00
{
FFileDialog* fileopen;
FString ret;
FString path = dirname;
FString file_filter = filter;
if ( path.isNull() || path.isEmpty() )
{
path = getHomeDir();
2017-03-26 20:40:04 +02:00
if ( path.isNull() || path.isEmpty() )
path = FString("/");
}
if ( file_filter.isNull() || file_filter.isEmpty() )
file_filter = FString("*");
fileopen = new FFileDialog ( path
, file_filter
, FFileDialog::Open
, parent );
if ( fileopen->exec() == FDialog::Accept )
ret = fileopen->getPath() + fileopen->getSelectedFile();
2015-05-23 13:35:12 +02:00
else
ret = FString();
delete fileopen;
return ret;
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
2017-03-17 22:59:06 +01:00
const FString FFileDialog::fileSaveChooser ( FWidget* parent
, const FString& dirname
, const FString& filter )
2015-05-23 13:35:12 +02:00
{
FFileDialog* fileopen;
FString ret;
FString path = dirname;
FString file_filter = filter;
2015-05-23 13:35:12 +02:00
if ( path.isNull() || path.isEmpty() )
{
path = getHomeDir();
2015-05-23 13:35:12 +02:00
2017-03-26 20:40:04 +02:00
if ( path.isNull() || path.isEmpty() )
path = FString("/");
}
2015-05-23 13:35:12 +02:00
if ( file_filter.isNull() || file_filter.isEmpty() )
file_filter = FString("*");
2015-05-23 13:35:12 +02:00
fileopen = new FFileDialog ( path
, file_filter
, FFileDialog::Save
, parent );
if ( fileopen->exec() == FDialog::Accept )
ret = fileopen->getPath() + fileopen->getSelectedFile();
else
ret = FString();
delete fileopen;
return ret;
2015-05-23 13:35:12 +02:00
}
// protected methods of FFileDialog
//----------------------------------------------------------------------
void FFileDialog::adjustSize()
{
int h, X, Y, max_width, max_height;
FWidget* root_widget = getRootWidget();
if ( root_widget )
{
max_width = root_widget->getClientWidth();
max_height = root_widget->getClientHeight();
}
else
{
max_width = 80;
max_height = 24;
}
h = max_height - 6;
2015-05-23 13:35:12 +02:00
if ( h < 15 ) // minimum
h = 15;
2015-05-23 13:35:12 +02:00
if ( h > 30 ) // maximum
h = 30;
2015-05-23 13:35:12 +02:00
setHeight (h, false);
X = 1 + int((max_width - getWidth()) / 2);
Y = 1 + int((max_height - getHeight()) / 3);
2016-07-31 20:25:25 +02:00
setPos(X, Y, false);
filebrowser->setHeight (h-8, false);
2015-05-23 13:35:12 +02:00
hidden->setY(h-4, false);
cancel->setY(h-4, false);
open->setY(h-4, false);
FDialog::adjustSize();
printPath(directory);
}
// private methods of FFileDialog
2015-09-22 04:18:20 +02:00
//----------------------------------------------------------------------
void FFileDialog::init()
2015-09-22 04:18:20 +02:00
{
FWidget* parent_widget;
int x, y, w, h;
w = 42;
h = 15;
setGeometry(1, 1, w, h, false);
parent_widget = getParentWidget();
if ( parent_widget )
{
x = 1 + int((parent_widget->getWidth()-w)/2);
y = 1 + int((parent_widget->getHeight()-h)/3);
}
2015-09-22 04:18:20 +02:00
else
x = y = 1;
if ( dlg_type == FFileDialog::Save )
FDialog::setText("Save file");
else
FDialog::setText("Open file");
filename = new FLineEdit(this);
filename->setLabelText("File&name");
filename->setText(filter_pattern);
filename->setGeometry(11, 1, 28, 1);
filename->setFocus();
2015-09-22 04:18:20 +02:00
filebrowser = new FListBox(this);
filebrowser->setGeometry(2, 3, 38, 6);
printPath(directory);
2015-09-22 04:18:20 +02:00
hidden = new FCheckBox("&hidden files", this);
hidden->setGeometry(2, 10, 16, 1);
2015-09-22 04:18:20 +02:00
cancel = new FButton("&Cancel", this);
cancel->setGeometry(19, 10, 9, 1);
2015-05-23 13:35:12 +02:00
if ( dlg_type == FFileDialog::Save )
open = new FButton("&Save",this);
else
open = new FButton("&Open",this);
2015-05-23 13:35:12 +02:00
open->setGeometry(30, 10, 9, 1);
setGeometry (x, y, getWidth(), getHeight());
2015-05-23 13:35:12 +02:00
filename->addCallback
(
"activate",
_METHOD_CALLBACK (this, &FFileDialog::cb_processActivate)
);
filebrowser->addCallback
(
"row-changed",
_METHOD_CALLBACK (this, &FFileDialog::cb_processRowChanged)
);
2015-09-20 05:44:50 +02:00
filebrowser->addCallback
(
"clicked",
_METHOD_CALLBACK (this, &FFileDialog::cb_processClicked)
);
hidden->addCallback
(
"toggled",
_METHOD_CALLBACK (this, &FFileDialog::cb_processShowHidden)
);
cancel->addCallback
(
"clicked",
_METHOD_CALLBACK (this, &FFileDialog::cb_processCancel)
);
open->addCallback
(
"clicked",
_METHOD_CALLBACK (this, &FFileDialog::cb_processOpen)
);
2015-05-23 13:35:12 +02:00
setModal();
readDir();
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
char* FFileDialog::getHomeDir()
2015-05-23 13:35:12 +02:00
{
struct passwd* pwd;
pwd = getpwuid( geteuid() );
2015-05-23 13:35:12 +02:00
if ( ! pwd )
return const_cast<char*>("");
else
2015-10-01 03:48:58 +02:00
{
pwd = getpwnam(pwd->pw_name);
if ( ! pwd )
return const_cast<char*>("");
else
return pwd->pw_dir;
2015-05-23 13:35:12 +02:00
}
}
//----------------------------------------------------------------------
2017-03-17 22:59:06 +01:00
inline bool FFileDialog::pattern_match ( const char* const pattern
, char*& fname )
{
char search[128] = {};
if ( show_hidden && fname[0] == '.' && fname[1] != '\0' ) // hidden files
2015-05-23 13:35:12 +02:00
{
search[0] = '.';
search[1] = '\0';
std::strncat(search, pattern, sizeof(search) - std::strlen(search) - 1);
2015-05-23 13:35:12 +02:00
}
else
std::strncpy(search, pattern, sizeof(search) - 1);
2015-05-23 13:35:12 +02:00
if ( fnmatch (search, fname, FNM_PERIOD) == 0 )
return true;
2015-05-23 13:35:12 +02:00
else
return false;
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
void FFileDialog::clear()
2015-05-23 13:35:12 +02:00
{
std::vector<dir_entry>::const_iterator iter, end;
2015-05-23 13:35:12 +02:00
if ( dir_entries.empty() )
return;
2015-05-23 13:35:12 +02:00
// delete all directory entries;
iter = dir_entries.begin();
end = dir_entries.end();
while ( iter != end )
{
std::free ((*iter).name);
++iter;
}
dir_entries.clear();
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
int FFileDialog::numOfDirs()
2015-05-23 13:35:12 +02:00
{
if ( dir_entries.empty() )
return 0;
int n = 0;
std::vector<dir_entry>::const_iterator iter, end;
iter = dir_entries.begin();
end = dir_entries.end();
while ( iter != end )
2015-05-23 13:35:12 +02:00
{
if ( (*iter).type == DT_DIR && std::strcmp((*iter).name, ".") != 0 )
n++;
++iter;
2015-05-23 13:35:12 +02:00
}
return n;
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
int FFileDialog::changeDir (const FString& dirname)
{
FString lastdir = directory;
FString newdir = dirname;
if ( newdir.includes('~') )
newdir = newdir.replace('~', getHomeDir());
if ( newdir[0] == '/' )
setPath(newdir);
else
setPath(directory + newdir);
switch ( readDir() )
{
case -1:
setPath(lastdir);
return -1;
case -2:
setPath(lastdir);
readDir();
return -2;
2015-05-23 13:35:12 +02:00
case 0:
if ( newdir == FString("..") )
2015-05-23 13:35:12 +02:00
{
if ( lastdir == FString('/') )
filename->setText('/');
else if ( ! dir_entries.empty() )
2015-05-23 13:35:12 +02:00
{
int i = 1;
std::vector<dir_entry>::const_iterator iter, end;
2017-03-17 22:59:06 +01:00
const char* const baseName = basename(const_cast<char*>(lastdir.c_str()));
iter = dir_entries.begin();
end = dir_entries.end();
while ( iter != end )
2015-10-01 03:48:58 +02:00
{
if ( std::strcmp((*iter).name, baseName) == 0 )
{
filebrowser->setCurrentItem(i);
filename->setText(FString(baseName) + '/');
break;
}
i++;
++iter;
2015-10-01 03:48:58 +02:00
}
2015-05-23 13:35:12 +02:00
}
}
else
{
FString firstname = dir_entries[0].name;
if ( dir_entries[0].type == DT_DIR )
filename->setText(firstname + '/');
else
filename->setText(firstname);
}
printPath(directory);
filename->redraw();
filebrowser->redraw();
2015-05-23 13:35:12 +02:00
default:
return 0;
2015-05-23 13:35:12 +02:00
}
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
void FFileDialog::printPath (const FString& txt)
{
FString path = txt;
uInt max_width = uInt(filebrowser->getWidth()) - 4;
2015-05-23 13:35:12 +02:00
if ( path.getLength() > max_width )
filebrowser->setText(".." + path.right(max_width-2));
else
filebrowser->setText(path);
}
//----------------------------------------------------------------------
void FFileDialog::cb_processActivate (FWidget*, data_ptr)
{
if ( filename->getText().includes('*')
|| filename->getText().includes('?') )
2015-05-23 13:35:12 +02:00
{
setFilter(filename->getText());
readDir();
filebrowser->redraw();
}
else if ( filename->getText().getLength() == 0 )
{
setFilter("*");
readDir();
filebrowser->redraw();
}
else if ( filename->getText().trim() == FString("..")
|| filename->getText().includes('/')
|| filename->getText().includes('~') )
{
changeDir(filename->getText().trim());
}
else
{
bool found = false;
2015-05-23 13:35:12 +02:00
if ( ! dir_entries.empty() )
2015-05-23 13:35:12 +02:00
{
std::vector<dir_entry>::const_iterator iter, end;
FString input = filename->getText().trim();
iter = dir_entries.begin();
end = dir_entries.end();
while ( iter != end )
{
if ( std::strcmp((*iter).name, input) == 0 && (*iter).type == DT_DIR )
{
found = true;
changeDir(input);
break;
}
2015-05-23 13:35:12 +02:00
++iter;
}
}
2015-05-23 13:35:12 +02:00
if ( ! found )
done (FDialog::Accept);
}
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
void FFileDialog::cb_processRowChanged (FWidget*, data_ptr)
2015-05-23 13:35:12 +02:00
{
int n = filebrowser->currentItem();
if ( n == 0 )
return;
FString name = dir_entries[uLong(n-1)].name;
if ( dir_entries[uLong(n-1)].type == DT_DIR )
filename->setText( name + '/' );
2015-05-23 13:35:12 +02:00
else
filename->setText( name );
2015-09-22 04:18:20 +02:00
filename->redraw();
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
void FFileDialog::cb_processClicked (FWidget*, data_ptr)
2015-05-23 13:35:12 +02:00
{
uLong n = uLong(filebrowser->currentItem() - 1);
if ( dir_entries[n].type == DT_DIR )
changeDir(dir_entries[n].name);
else
done (FDialog::Accept);
}
//----------------------------------------------------------------------
void FFileDialog::cb_processCancel (FWidget*, data_ptr)
{
done (FDialog::Reject);
}
//----------------------------------------------------------------------
void FFileDialog::cb_processOpen (FWidget*, data_ptr)
{
done (FDialog::Accept);
}
//----------------------------------------------------------------------
void FFileDialog::cb_processShowHidden (FWidget*, data_ptr)
{
setShowHiddenFiles(not show_hidden);
2015-05-23 13:35:12 +02:00
}