Files
wxWidgets/src/generic/filectrlg.cpp
Vadim Zeitlin 3f66f6a5b3 Remove all lines containing cvs/svn "$Id$" keyword.
This keyword is not expanded by Git which means it's not replaced with the
correct revision value in the releases made using git-based scripts and it's
confusing to have lines with unexpanded "$Id$" in the released files. As
expanding them with Git is not that simple (it could be done with git archive
and export-subst attribute) and there are not many benefits in having them in
the first place, just remove all these lines.

If nothing else, this will make an eventual transition to Git simpler.

Closes #14487.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2013-07-26 16:02:46 +00:00

1480 lines
41 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/generic/filectrlg.cpp
// Purpose: wxGenericFileCtrl Implementation
// Author: Diaa M. Sami
// Created: 2007-07-07
// Copyright: (c) Diaa M. Sami
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_FILECTRL
#include "wx/generic/filectrlg.h"
#ifndef WX_PRECOMP
#include "wx/settings.h"
#include "wx/sizer.h"
#include "wx/stattext.h"
#include "wx/checkbox.h"
#include "wx/msgdlg.h"
#include "wx/log.h"
#include "wx/filedlg.h"
#endif
#include "wx/clntdata.h"
#include "wx/file.h" // for wxS_IXXX constants only
#include "wx/generic/dirctrlg.h" // for wxFileIconsTable
#include "wx/dir.h"
#include "wx/tokenzr.h"
#include "wx/imaglist.h"
#ifdef __WINDOWS__
#include "wx/msw/wrapwin.h"
#endif
#if defined(__WXWINCE__)
#define IsTopMostDir(dir) (dir == wxT("\\") || dir == wxT("/"))
#elif (defined(__DOS__) || defined(__WINDOWS__) || defined (__OS2__))
#define IsTopMostDir(dir) (dir.empty())
#else
#define IsTopMostDir(dir) (dir == wxT("/"))
#endif
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
static
int wxCALLBACK wxFileDataNameCompare( wxIntPtr data1, wxIntPtr data2, wxIntPtr sortOrder)
{
wxFileData *fd1 = (wxFileData *)wxUIntToPtr(data1);
wxFileData *fd2 = (wxFileData *)wxUIntToPtr(data2);
if (fd1->GetFileName() == wxT(".."))
return -sortOrder;
if (fd2->GetFileName() == wxT(".."))
return sortOrder;
if (fd1->IsDir() && !fd2->IsDir())
return -sortOrder;
if (fd2->IsDir() && !fd1->IsDir())
return sortOrder;
return sortOrder*wxStrcmp( fd1->GetFileName(), fd2->GetFileName() );
}
static
int wxCALLBACK wxFileDataSizeCompare(wxIntPtr data1, wxIntPtr data2, wxIntPtr sortOrder)
{
wxFileData *fd1 = (wxFileData *)wxUIntToPtr(data1);
wxFileData *fd2 = (wxFileData *)wxUIntToPtr(data2);
if (fd1->GetFileName() == wxT(".."))
return -sortOrder;
if (fd2->GetFileName() == wxT(".."))
return sortOrder;
if (fd1->IsDir() && !fd2->IsDir())
return -sortOrder;
if (fd2->IsDir() && !fd1->IsDir())
return sortOrder;
if (fd1->IsLink() && !fd2->IsLink())
return -sortOrder;
if (fd2->IsLink() && !fd1->IsLink())
return sortOrder;
return fd1->GetSize() > fd2->GetSize() ? sortOrder : -sortOrder;
}
static
int wxCALLBACK wxFileDataTypeCompare(wxIntPtr data1, wxIntPtr data2, wxIntPtr sortOrder)
{
wxFileData *fd1 = (wxFileData *)wxUIntToPtr(data1);
wxFileData *fd2 = (wxFileData *)wxUIntToPtr(data2);
if (fd1->GetFileName() == wxT(".."))
return -sortOrder;
if (fd2->GetFileName() == wxT(".."))
return sortOrder;
if (fd1->IsDir() && !fd2->IsDir())
return -sortOrder;
if (fd2->IsDir() && !fd1->IsDir())
return sortOrder;
if (fd1->IsLink() && !fd2->IsLink())
return -sortOrder;
if (fd2->IsLink() && !fd1->IsLink())
return sortOrder;
return sortOrder*wxStrcmp( fd1->GetFileType(), fd2->GetFileType() );
}
static
int wxCALLBACK wxFileDataTimeCompare(wxIntPtr data1, wxIntPtr data2, wxIntPtr sortOrder)
{
wxFileData *fd1 = (wxFileData *)wxUIntToPtr(data1);
wxFileData *fd2 = (wxFileData *)wxUIntToPtr(data2);
if (fd1->GetFileName() == wxT(".."))
return -sortOrder;
if (fd2->GetFileName() == wxT(".."))
return sortOrder;
if (fd1->IsDir() && !fd2->IsDir())
return -sortOrder;
if (fd2->IsDir() && !fd1->IsDir())
return sortOrder;
return fd1->GetDateTime().IsLaterThan(fd2->GetDateTime()) ? sortOrder : -sortOrder;
}
// defined in src/generic/dirctrlg.cpp
extern size_t wxGetAvailableDrives(wxArrayString &paths, wxArrayString &names, wxArrayInt &icon_ids);
//-----------------------------------------------------------------------------
// wxFileData
//-----------------------------------------------------------------------------
wxFileData::wxFileData( const wxString &filePath, const wxString &fileName, fileType type, int image_id )
{
Init();
m_fileName = fileName;
m_filePath = filePath;
m_type = type;
m_image = image_id;
ReadData();
}
void wxFileData::Init()
{
m_size = 0;
m_type = wxFileData::is_file;
m_image = wxFileIconsTable::file;
}
void wxFileData::Copy( const wxFileData& fileData )
{
m_fileName = fileData.GetFileName();
m_filePath = fileData.GetFilePath();
m_size = fileData.GetSize();
m_dateTime = fileData.GetDateTime();
m_permissions = fileData.GetPermissions();
m_type = fileData.GetType();
m_image = fileData.GetImageId();
}
void wxFileData::ReadData()
{
if (IsDrive())
{
m_size = 0;
return;
}
#if defined(__DOS__) || (defined(__WINDOWS__) && !defined(__WXWINCE__)) || defined(__OS2__)
// c:\.. is a drive don't stat it
if ((m_fileName == wxT("..")) && (m_filePath.length() <= 5))
{
m_type = is_drive;
m_size = 0;
return;
}
#endif // __DOS__ || __WINDOWS__
#ifdef __WXWINCE__
// WinCE
DWORD fileAttribs = GetFileAttributes(m_filePath.fn_str());
m_type |= (fileAttribs & FILE_ATTRIBUTE_DIRECTORY) != 0 ? is_dir : 0;
wxString p, f, ext;
wxFileName::SplitPath(m_filePath, & p, & f, & ext);
if (wxStricmp(ext, wxT("exe")) == 0)
m_type |= is_exe;
// Find out size
m_size = 0;
HANDLE fileHandle = CreateFile(m_filePath.fn_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (fileHandle != INVALID_HANDLE_VALUE)
{
m_size = GetFileSize(fileHandle, 0);
CloseHandle(fileHandle);
}
m_dateTime = wxFileModificationTime(m_filePath);
#else
// OTHER PLATFORMS
wxStructStat buff;
#if defined(__UNIX__) && (!defined( __OS2__ ) && !defined(__VMS))
const bool hasStat = lstat( m_filePath.fn_str(), &buff ) == 0;
if ( hasStat )
m_type |= S_ISLNK(buff.st_mode) ? is_link : 0;
#else // no lstat()
const bool hasStat = wxStat( m_filePath, &buff ) == 0;
#endif
if ( hasStat )
{
m_type |= (buff.st_mode & S_IFDIR) != 0 ? is_dir : 0;
m_type |= (buff.st_mode & wxS_IXUSR) != 0 ? is_exe : 0;
m_size = buff.st_size;
m_dateTime = buff.st_mtime;
}
#endif
// __WXWINCE__
#if defined(__UNIX__)
if ( hasStat )
{
m_permissions.Printf(wxT("%c%c%c%c%c%c%c%c%c"),
buff.st_mode & wxS_IRUSR ? wxT('r') : wxT('-'),
buff.st_mode & wxS_IWUSR ? wxT('w') : wxT('-'),
buff.st_mode & wxS_IXUSR ? wxT('x') : wxT('-'),
buff.st_mode & wxS_IRGRP ? wxT('r') : wxT('-'),
buff.st_mode & wxS_IWGRP ? wxT('w') : wxT('-'),
buff.st_mode & wxS_IXGRP ? wxT('x') : wxT('-'),
buff.st_mode & wxS_IROTH ? wxT('r') : wxT('-'),
buff.st_mode & wxS_IWOTH ? wxT('w') : wxT('-'),
buff.st_mode & wxS_IXOTH ? wxT('x') : wxT('-'));
}
#elif defined(__WIN32__)
DWORD attribs = ::GetFileAttributes(m_filePath.c_str());
if (attribs != (DWORD)-1)
{
m_permissions.Printf(wxT("%c%c%c%c"),
attribs & FILE_ATTRIBUTE_ARCHIVE ? wxT('A') : wxT(' '),
attribs & FILE_ATTRIBUTE_READONLY ? wxT('R') : wxT(' '),
attribs & FILE_ATTRIBUTE_HIDDEN ? wxT('H') : wxT(' '),
attribs & FILE_ATTRIBUTE_SYSTEM ? wxT('S') : wxT(' '));
}
#endif
// try to get a better icon
if (m_image == wxFileIconsTable::file)
{
if (m_fileName.Find(wxT('.'), true) != wxNOT_FOUND)
{
m_image = wxTheFileIconsTable->GetIconID( m_fileName.AfterLast(wxT('.')));
} else if (IsExe())
{
m_image = wxFileIconsTable::executable;
}
}
}
wxString wxFileData::GetFileType() const
{
if (IsDir())
return _("<DIR>");
else if (IsLink())
return _("<LINK>");
else if (IsDrive())
return _("<DRIVE>");
else if (m_fileName.Find(wxT('.'), true) != wxNOT_FOUND)
return m_fileName.AfterLast(wxT('.'));
return wxEmptyString;
}
wxString wxFileData::GetModificationTime() const
{
// want time as 01:02 so they line up nicely, no %r in WIN32
return m_dateTime.FormatDate() + wxT(" ") + m_dateTime.Format(wxT("%I:%M:%S %p"));
}
wxString wxFileData::GetHint() const
{
wxString s = m_filePath;
s += wxT(" ");
if (IsDir())
s += _("<DIR>");
else if (IsLink())
s += _("<LINK>");
else if (IsDrive())
s += _("<DRIVE>");
else // plain file
s += wxString::Format(wxPLURAL("%ld byte", "%ld bytes", m_size),
wxLongLong(m_size).ToString().c_str());
s += wxT(' ');
if ( !IsDrive() )
{
s << GetModificationTime()
<< wxT(" ")
<< m_permissions;
}
return s;
}
wxString wxFileData::GetEntry( fileListFieldType num ) const
{
wxString s;
switch ( num )
{
case FileList_Name:
s = m_fileName;
break;
case FileList_Size:
if (!IsDir() && !IsLink() && !IsDrive())
s = wxLongLong(m_size).ToString();
break;
case FileList_Type:
s = GetFileType();
break;
case FileList_Time:
if (!IsDrive())
s = GetModificationTime();
break;
#if defined(__UNIX__) || defined(__WIN32__)
case FileList_Perm:
s = m_permissions;
break;
#endif // defined(__UNIX__) || defined(__WIN32__)
default:
wxFAIL_MSG( wxT("unexpected field in wxFileData::GetEntry()") );
}
return s;
}
void wxFileData::SetNewName( const wxString &filePath, const wxString &fileName )
{
m_fileName = fileName;
m_filePath = filePath;
}
void wxFileData::MakeItem( wxListItem &item )
{
item.m_text = m_fileName;
item.ClearAttributes();
if (IsExe())
item.SetTextColour(*wxRED);
if (IsDir())
item.SetTextColour(*wxBLUE);
item.m_image = m_image;
if (IsLink())
{
wxColour dg = wxTheColourDatabase->Find( wxT("MEDIUM GREY") );
if ( dg.IsOk() )
item.SetTextColour(dg);
}
item.m_data = wxPtrToUInt(this);
}
//-----------------------------------------------------------------------------
// wxFileListCtrl
//-----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxFileListCtrl,wxListCtrl)
BEGIN_EVENT_TABLE(wxFileListCtrl,wxListCtrl)
EVT_LIST_DELETE_ITEM(wxID_ANY, wxFileListCtrl::OnListDeleteItem)
EVT_LIST_DELETE_ALL_ITEMS(wxID_ANY, wxFileListCtrl::OnListDeleteAllItems)
EVT_LIST_END_LABEL_EDIT(wxID_ANY, wxFileListCtrl::OnListEndLabelEdit)
EVT_LIST_COL_CLICK(wxID_ANY, wxFileListCtrl::OnListColClick)
END_EVENT_TABLE()
wxFileListCtrl::wxFileListCtrl()
{
m_showHidden = false;
m_sort_forward = true;
m_sort_field = wxFileData::FileList_Name;
}
wxFileListCtrl::wxFileListCtrl(wxWindow *win,
wxWindowID id,
const wxString& wild,
bool showHidden,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator &validator,
const wxString &name)
: wxListCtrl(win, id, pos, size, style, validator, name),
m_wild(wild)
{
wxImageList *imageList = wxTheFileIconsTable->GetSmallImageList();
SetImageList( imageList, wxIMAGE_LIST_SMALL );
m_showHidden = showHidden;
m_sort_forward = true;
m_sort_field = wxFileData::FileList_Name;
m_dirName = wxT("*");
if (style & wxLC_REPORT)
ChangeToReportMode();
}
void wxFileListCtrl::ChangeToListMode()
{
ClearAll();
SetSingleStyle( wxLC_LIST );
UpdateFiles();
}
void wxFileListCtrl::ChangeToReportMode()
{
ClearAll();
SetSingleStyle( wxLC_REPORT );
// do this since WIN32 does mm/dd/yy UNIX does mm/dd/yyyy
// don't hardcode since mm/dd is dd/mm elsewhere
int w, h;
wxDateTime dt(22, wxDateTime::Dec, 2002, 22, 22, 22);
wxString txt = dt.FormatDate() + wxT("22") + dt.Format(wxT("%I:%M:%S %p"));
GetTextExtent(txt, &w, &h);
InsertColumn( 0, _("Name"), wxLIST_FORMAT_LEFT, w );
InsertColumn( 1, _("Size"), wxLIST_FORMAT_LEFT, w/2 );
InsertColumn( 2, _("Type"), wxLIST_FORMAT_LEFT, w/2 );
InsertColumn( 3, _("Modified"), wxLIST_FORMAT_LEFT, w );
#if defined(__UNIX__)
GetTextExtent(wxT("Permissions 2"), &w, &h);
InsertColumn( 4, _("Permissions"), wxLIST_FORMAT_LEFT, w );
#elif defined(__WIN32__)
GetTextExtent(wxT("Attributes 2"), &w, &h);
InsertColumn( 4, _("Attributes"), wxLIST_FORMAT_LEFT, w );
#endif
UpdateFiles();
}
void wxFileListCtrl::ChangeToSmallIconMode()
{
ClearAll();
SetSingleStyle( wxLC_SMALL_ICON );
UpdateFiles();
}
void wxFileListCtrl::ShowHidden( bool show )
{
m_showHidden = show;
UpdateFiles();
}
long wxFileListCtrl::Add( wxFileData *fd, wxListItem &item )
{
long ret = -1;
item.m_mask = wxLIST_MASK_TEXT + wxLIST_MASK_DATA + wxLIST_MASK_IMAGE;
fd->MakeItem( item );
long my_style = GetWindowStyleFlag();
if (my_style & wxLC_REPORT)
{
ret = InsertItem( item );
for (int i = 1; i < wxFileData::FileList_Max; i++)
SetItem( item.m_itemId, i, fd->GetEntry((wxFileData::fileListFieldType)i) );
}
else if ((my_style & wxLC_LIST) || (my_style & wxLC_SMALL_ICON))
{
ret = InsertItem( item );
}
return ret;
}
void wxFileListCtrl::UpdateItem(const wxListItem &item)
{
wxFileData *fd = (wxFileData*)GetItemData(item);
wxCHECK_RET(fd, wxT("invalid filedata"));
fd->ReadData();
SetItemText(item, fd->GetFileName());
SetItemImage(item, fd->GetImageId());
if (GetWindowStyleFlag() & wxLC_REPORT)
{
for (int i = 1; i < wxFileData::FileList_Max; i++)
SetItem( item.m_itemId, i, fd->GetEntry((wxFileData::fileListFieldType)i) );
}
}
void wxFileListCtrl::UpdateFiles()
{
// don't do anything before ShowModal() call which sets m_dirName
if ( m_dirName == wxT("*") )
return;
wxBusyCursor bcur; // this may take a while...
DeleteAllItems();
wxListItem item;
item.m_itemId = 0;
item.m_col = 0;
#if (defined(__WINDOWS__) || defined(__DOS__) || defined(__WXMAC__) || defined(__OS2__)) && !defined(__WXWINCE__)
if ( IsTopMostDir(m_dirName) )
{
wxArrayString names, paths;
wxArrayInt icons;
const size_t count = wxGetAvailableDrives(paths, names, icons);
for ( size_t n = 0; n < count; n++ )
{
// use paths[n] as the drive name too as our HandleAction() can't
// deal with the drive names (of the form "System (C:)") currently
// as it mistakenly treats them as file names
//
// it would be preferable to show names, and not paths, in the
// dialog just as the native dialog does but for this we must:
// a) store the item type as item data and modify HandleAction()
// to use it instead of wxDirExists() to check whether the item
// is a directory
// b) store the drives by their drive letters and not their
// descriptions as otherwise it's pretty confusing to the user
wxFileData *fd = new wxFileData(paths[n], paths[n],
wxFileData::is_drive, icons[n]);
if (Add(fd, item) != -1)
item.m_itemId++;
else
delete fd;
}
}
else
#endif // defined(__DOS__) || defined(__WINDOWS__)
{
// Real directory...
if ( !IsTopMostDir(m_dirName) && !m_dirName.empty() )
{
wxString p(wxPathOnly(m_dirName));
#if (defined(__UNIX__) || defined(__WXWINCE__)) && !defined(__OS2__)
if (p.empty()) p = wxT("/");
#endif // __UNIX__
wxFileData *fd = new wxFileData(p, wxT(".."), wxFileData::is_dir, wxFileIconsTable::folder);
if (Add(fd, item) != -1)
item.m_itemId++;
else
delete fd;
}
wxString dirname(m_dirName);
#if defined(__DOS__) || defined(__WINDOWS__) || defined(__OS2__)
if (dirname.length() == 2 && dirname[1u] == wxT(':'))
dirname << wxT('\\');
#endif // defined(__DOS__) || defined(__WINDOWS__) || defined(__OS2__)
if (dirname.empty())
dirname = wxFILE_SEP_PATH;
wxLogNull logNull;
wxDir dir(dirname);
if ( dir.IsOpened() )
{
wxString dirPrefix(dirname);
if (dirPrefix.Last() != wxFILE_SEP_PATH)
dirPrefix += wxFILE_SEP_PATH;
int hiddenFlag = m_showHidden ? wxDIR_HIDDEN : 0;
bool cont;
wxString f;
// Get the directories first (not matched against wildcards):
cont = dir.GetFirst(&f, wxEmptyString, wxDIR_DIRS | hiddenFlag);
while (cont)
{
wxFileData *fd = new wxFileData(dirPrefix + f, f, wxFileData::is_dir, wxFileIconsTable::folder);
if (Add(fd, item) != -1)
item.m_itemId++;
else
delete fd;
cont = dir.GetNext(&f);
}
// Tokenize the wildcard string, so we can handle more than 1
// search pattern in a wildcard.
wxStringTokenizer tokenWild(m_wild, wxT(";"));
while ( tokenWild.HasMoreTokens() )
{
cont = dir.GetFirst(&f, tokenWild.GetNextToken(),
wxDIR_FILES | hiddenFlag);
while (cont)
{
wxFileData *fd = new wxFileData(dirPrefix + f, f, wxFileData::is_file, wxFileIconsTable::file);
if (Add(fd, item) != -1)
item.m_itemId++;
else
delete fd;
cont = dir.GetNext(&f);
}
}
}
}
SortItems(m_sort_field, m_sort_forward);
}
void wxFileListCtrl::SetWild( const wxString &wild )
{
if (wild.Find(wxT('|')) != wxNOT_FOUND)
return;
m_wild = wild;
UpdateFiles();
}
void wxFileListCtrl::MakeDir()
{
wxString new_name( _("NewName") );
wxString path( m_dirName );
path += wxFILE_SEP_PATH;
path += new_name;
if (wxFileExists(path))
{
// try NewName0, NewName1 etc.
int i = 0;
do {
new_name = _("NewName");
wxString num;
num.Printf( wxT("%d"), i );
new_name += num;
path = m_dirName;
path += wxFILE_SEP_PATH;
path += new_name;
i++;
} while (wxFileExists(path));
}
wxLogNull log;
if (!wxMkdir(path))
{
wxMessageDialog dialog(this, _("Operation not permitted."), _("Error"), wxOK | wxICON_ERROR );
dialog.ShowModal();
return;
}
wxFileData *fd = new wxFileData( path, new_name, wxFileData::is_dir, wxFileIconsTable::folder );
wxListItem item;
item.m_itemId = 0;
item.m_col = 0;
long itemid = Add( fd, item );
if (itemid != -1)
{
SortItems(m_sort_field, m_sort_forward);
itemid = FindItem( 0, wxPtrToUInt(fd) );
EnsureVisible( itemid );
EditLabel( itemid );
}
else
delete fd;
}
void wxFileListCtrl::GoToParentDir()
{
if (!IsTopMostDir(m_dirName))
{
size_t len = m_dirName.length();
if (wxEndsWithPathSeparator(m_dirName))
m_dirName.Remove( len-1, 1 );
wxString fname( wxFileNameFromPath(m_dirName) );
m_dirName = wxPathOnly( m_dirName );
#if defined(__DOS__) || defined(__WINDOWS__) || defined(__OS2__)
if (!m_dirName.empty())
{
if (m_dirName.Last() == wxT('.'))
m_dirName = wxEmptyString;
}
#elif defined(__UNIX__)
if (m_dirName.empty())
m_dirName = wxT("/");
#endif
UpdateFiles();
long id = FindItem( 0, fname );
if (id != wxNOT_FOUND)
{
SetItemState( id, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
EnsureVisible( id );
}
}
}
void wxFileListCtrl::GoToHomeDir()
{
wxString s = wxGetUserHome( wxString() );
GoToDir(s);
}
void wxFileListCtrl::GoToDir( const wxString &dir )
{
if (!wxDirExists(dir)) return;
m_dirName = dir;
UpdateFiles();
SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
EnsureVisible( 0 );
}
void wxFileListCtrl::FreeItemData(wxListItem& item)
{
if ( item.m_data )
{
wxFileData *fd = (wxFileData*)item.m_data;
delete fd;
item.m_data = 0;
}
}
void wxFileListCtrl::OnListDeleteItem( wxListEvent &event )
{
FreeItemData(event.m_item);
}
void wxFileListCtrl::OnListDeleteAllItems( wxListEvent & WXUNUSED(event) )
{
FreeAllItemsData();
}
void wxFileListCtrl::FreeAllItemsData()
{
wxListItem item;
item.m_mask = wxLIST_MASK_DATA;
item.m_itemId = GetNextItem( -1, wxLIST_NEXT_ALL );
while ( item.m_itemId != -1 )
{
GetItem( item );
FreeItemData(item);
item.m_itemId = GetNextItem( item.m_itemId, wxLIST_NEXT_ALL );
}
}
void wxFileListCtrl::OnListEndLabelEdit( wxListEvent &event )
{
wxFileData *fd = (wxFileData*)event.m_item.m_data;
wxASSERT( fd );
if ((event.GetLabel().empty()) ||
(event.GetLabel() == wxT(".")) ||
(event.GetLabel() == wxT("..")) ||
(event.GetLabel().First( wxFILE_SEP_PATH ) != wxNOT_FOUND))
{
wxMessageDialog dialog(this, _("Illegal directory name."), _("Error"), wxOK | wxICON_ERROR );
dialog.ShowModal();
event.Veto();
return;
}
wxString new_name( wxPathOnly( fd->GetFilePath() ) );
new_name += wxFILE_SEP_PATH;
new_name += event.GetLabel();
wxLogNull log;
if (wxFileExists(new_name))
{
wxMessageDialog dialog(this, _("File name exists already."), _("Error"), wxOK | wxICON_ERROR );
dialog.ShowModal();
event.Veto();
}
if (wxRenameFile(fd->GetFilePath(),new_name))
{
fd->SetNewName( new_name, event.GetLabel() );
SetItemState( event.GetItem(), wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
UpdateItem( event.GetItem() );
EnsureVisible( event.GetItem() );
}
else
{
wxMessageDialog dialog(this, _("Operation not permitted."), _("Error"), wxOK | wxICON_ERROR );
dialog.ShowModal();
event.Veto();
}
}
void wxFileListCtrl::OnListColClick( wxListEvent &event )
{
int col = event.GetColumn();
switch (col)
{
case wxFileData::FileList_Name :
case wxFileData::FileList_Size :
case wxFileData::FileList_Type :
case wxFileData::FileList_Time : break;
default : return;
}
if ((wxFileData::fileListFieldType)col == m_sort_field)
m_sort_forward = !m_sort_forward;
else
m_sort_field = (wxFileData::fileListFieldType)col;
SortItems(m_sort_field, m_sort_forward);
}
void wxFileListCtrl::SortItems(wxFileData::fileListFieldType field, bool forward)
{
m_sort_field = field;
m_sort_forward = forward;
const long sort_dir = forward ? 1 : -1;
switch (m_sort_field)
{
case wxFileData::FileList_Size :
wxListCtrl::SortItems(wxFileDataSizeCompare, sort_dir);
break;
case wxFileData::FileList_Type :
wxListCtrl::SortItems(wxFileDataTypeCompare, sort_dir);
break;
case wxFileData::FileList_Time :
wxListCtrl::SortItems(wxFileDataTimeCompare, sort_dir);
break;
case wxFileData::FileList_Name :
default :
wxListCtrl::SortItems(wxFileDataNameCompare, sort_dir);
break;
}
}
wxFileListCtrl::~wxFileListCtrl()
{
// Normally the data are freed via an EVT_LIST_DELETE_ALL_ITEMS event and
// wxFileListCtrl::OnListDeleteAllItems. But if the event is generated after
// the destruction of the wxFileListCtrl we need to free any data here:
FreeAllItemsData();
}
#define ID_CHOICE (wxID_FILECTRL + 1)
#define ID_TEXT (wxID_FILECTRL + 2)
#define ID_FILELIST_CTRL (wxID_FILECTRL + 3)
#define ID_CHECK (wxID_FILECTRL + 4)
///////////////////////////////////////////////////////////////////////////////
// wxGenericFileCtrl implementation
///////////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNAMIC_CLASS( wxGenericFileCtrl, wxNavigationEnabled<wxControl> )
BEGIN_EVENT_TABLE( wxGenericFileCtrl, wxNavigationEnabled<wxControl> )
EVT_LIST_ITEM_SELECTED( ID_FILELIST_CTRL, wxGenericFileCtrl::OnSelected )
EVT_LIST_ITEM_ACTIVATED( ID_FILELIST_CTRL, wxGenericFileCtrl::OnActivated )
EVT_CHOICE( ID_CHOICE, wxGenericFileCtrl::OnChoiceFilter )
EVT_TEXT_ENTER( ID_TEXT, wxGenericFileCtrl::OnTextEnter )
EVT_TEXT( ID_TEXT, wxGenericFileCtrl::OnTextChange )
EVT_CHECKBOX( ID_CHECK, wxGenericFileCtrl::OnCheck )
END_EVENT_TABLE()
bool wxGenericFileCtrl::Create( wxWindow *parent,
wxWindowID id,
const wxString& defaultDirectory,
const wxString& defaultFileName,
const wxString& wildCard,
long style,
const wxPoint& pos,
const wxSize& size,
const wxString& name )
{
this->m_style = style;
m_inSelected = false;
m_noSelChgEvent = false;
m_check = NULL;
// check that the styles are not contradictory
wxASSERT_MSG( !( ( m_style & wxFC_SAVE ) && ( m_style & wxFC_OPEN ) ),
wxT( "can't specify both wxFC_SAVE and wxFC_OPEN at once" ) );
wxASSERT_MSG( !( ( m_style & wxFC_SAVE ) && ( m_style & wxFC_MULTIPLE ) ),
wxT( "wxFC_MULTIPLE can't be used with wxFC_SAVE" ) );
wxNavigationEnabled<wxControl>::Create( parent, id,
pos, size,
wxTAB_TRAVERSAL,
wxDefaultValidator,
name );
m_dir = defaultDirectory;
m_ignoreChanges = true;
if ( ( m_dir.empty() ) || ( m_dir == wxT( "." ) ) )
{
m_dir = wxGetCwd();
if ( m_dir.empty() )
m_dir = wxFILE_SEP_PATH;
}
const size_t len = m_dir.length();
if ( ( len > 1 ) && ( wxEndsWithPathSeparator( m_dir ) ) )
m_dir.Remove( len - 1, 1 );
m_filterExtension = wxEmptyString;
// layout
const bool is_pda = ( wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA );
wxBoxSizer *mainsizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer *staticsizer = new wxBoxSizer( wxHORIZONTAL );
if ( is_pda )
staticsizer->Add( new wxStaticText( this, wxID_ANY, _( "Current directory:" ) ),
wxSizerFlags().DoubleBorder(wxRIGHT) );
m_static = new wxStaticText( this, wxID_ANY, m_dir );
staticsizer->Add( m_static, 1 );
mainsizer->Add( staticsizer, wxSizerFlags().Expand().Border());
long style2 = wxLC_LIST;
if ( !( m_style & wxFC_MULTIPLE ) )
style2 |= wxLC_SINGLE_SEL;
#ifdef __WXWINCE__
style2 |= wxSIMPLE_BORDER;
#else
style2 |= wxSUNKEN_BORDER;
#endif
m_list = new wxFileListCtrl( this, ID_FILELIST_CTRL,
wxEmptyString, false,
wxDefaultPosition, wxSize( 400, 140 ),
style2 );
m_text = new wxTextCtrl( this, ID_TEXT, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_PROCESS_ENTER );
m_choice = new wxChoice( this, ID_CHOICE );
if ( is_pda )
{
// PDAs have a different screen layout
mainsizer->Add( m_list, wxSizerFlags( 1 ).Expand().HorzBorder() );
wxBoxSizer *textsizer = new wxBoxSizer( wxHORIZONTAL );
textsizer->Add( m_text, wxSizerFlags( 1 ).Centre().Border() );
textsizer->Add( m_choice, wxSizerFlags( 1 ).Centre().Border() );
mainsizer->Add( textsizer, wxSizerFlags().Expand() );
}
else // !is_pda
{
mainsizer->Add( m_list, wxSizerFlags( 1 ).Expand().Border() );
mainsizer->Add( m_text, wxSizerFlags().Expand().Border() );
wxBoxSizer *choicesizer = new wxBoxSizer( wxHORIZONTAL );
choicesizer->Add( m_choice, wxSizerFlags( 1 ).Centre() );
if ( !( m_style & wxFC_NOSHOWHIDDEN ) )
{
m_check = new wxCheckBox( this, ID_CHECK, _( "Show &hidden files" ) );
choicesizer->Add( m_check, wxSizerFlags().Centre().DoubleBorder(wxLEFT) );
}
mainsizer->Add( choicesizer, wxSizerFlags().Expand().Border() );
}
SetWildcard( wildCard );
SetAutoLayout( true );
SetSizer( mainsizer );
if ( !is_pda )
{
mainsizer->Fit( this );
}
m_list->GoToDir( m_dir );
UpdateControls();
m_text->SetValue( m_fileName );
m_ignoreChanges = false;
// must be after m_ignoreChanges = false
SetFilename( defaultFileName );
return true;
}
// NB: there is an unfortunate mismatch between wxFileName and wxFileDialog
// method names but our GetDirectory() does correspond to wxFileName::
// GetPath() while our GetPath() is wxFileName::GetFullPath()
wxString wxGenericFileCtrl::GetPath() const
{
wxASSERT_MSG ( !(m_style & wxFC_MULTIPLE), "use GetPaths() instead" );
return DoGetFileName().GetFullPath();
}
wxString wxGenericFileCtrl::GetFilename() const
{
wxASSERT_MSG ( !(m_style & wxFC_MULTIPLE), "use GetFilenames() instead" );
return DoGetFileName().GetFullName();
}
wxString wxGenericFileCtrl::GetDirectory() const
{
// don't check for wxFC_MULTIPLE here, this one is probably safe to call in
// any case as it can be always taken to mean "current directory"
return DoGetFileName().GetPath();
}
wxFileName wxGenericFileCtrl::DoGetFileName() const
{
wxFileName fn;
wxString value = m_text->GetValue();
if ( value.empty() )
{
// nothing in the text control, get the selected file from the list
wxListItem item;
item.m_itemId = m_list->GetNextItem(-1, wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
m_list->GetItem(item);
fn.Assign(m_list->GetDir(), item.m_text);
}
else // user entered the value
{
// the path can be either absolute or relative
fn.Assign(value);
if ( fn.IsRelative() )
fn.MakeAbsolute(m_list->GetDir());
}
return fn;
}
// helper used in DoGetFilenames() and needed because Borland can't compile
// operator?: inline
static inline wxString GetFileNameOrPath(const wxFileName& fn, bool fullPath)
{
return fullPath ? fn.GetFullPath() : fn.GetFullName();
}
void
wxGenericFileCtrl::DoGetFilenames(wxArrayString& filenames, bool fullPath) const
{
filenames.clear();
const wxString dir = m_list->GetDir();
const wxString value = m_text->GetValue();
if ( !value.empty() )
{
wxFileName fn(value);
if ( fn.IsRelative() )
fn.MakeAbsolute(dir);
filenames.push_back(GetFileNameOrPath(fn, fullPath));
return;
}
const int numSel = m_list->GetSelectedItemCount();
if ( !numSel )
return;
filenames.reserve(numSel);
wxListItem item;
item.m_mask = wxLIST_MASK_TEXT;
item.m_itemId = -1;
for ( ;; )
{
item.m_itemId = m_list->GetNextItem(item.m_itemId, wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
if ( item.m_itemId == -1 )
break;
m_list->GetItem(item);
const wxFileName fn(dir, item.m_text);
filenames.push_back(GetFileNameOrPath(fn, fullPath));
}
}
bool wxGenericFileCtrl::SetDirectory( const wxString& dir )
{
m_ignoreChanges = true;
m_list->GoToDir( dir );
UpdateControls();
m_ignoreChanges = false;
return wxFileName( dir ).SameAs( m_list->GetDir() );
}
bool wxGenericFileCtrl::SetFilename( const wxString& name )
{
const long item = m_list->FindItem( -1, name );
if ( item == -1 ) // file not found either because it doesn't exist or the
// current filter doesn't show it.
return false;
m_noSelChgEvent = true;
// Deselect selected items
{
const int numSelectedItems = m_list->GetSelectedItemCount();
if ( numSelectedItems > 0 )
{
long itemIndex = -1;
for ( ;; )
{
itemIndex = m_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
if ( itemIndex == -1 )
break;
m_list->SetItemState( itemIndex, 0, wxLIST_STATE_SELECTED );
}
}
}
m_list->SetItemState( item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
m_list->EnsureVisible( item );
m_noSelChgEvent = false;
return true;
}
void wxGenericFileCtrl::DoSetFilterIndex( int filterindex )
{
wxClientData *pcd = m_choice->GetClientObject( filterindex );
if ( !pcd )
return;
const wxString& str = ((static_cast<wxStringClientData *>(pcd))->GetData());
m_list->SetWild( str );
m_filterIndex = filterindex;
if ( str.Left( 2 ) == wxT( "*." ) )
{
m_filterExtension = str.Mid( 1 );
if ( m_filterExtension == wxT( ".*" ) )
m_filterExtension.clear();
}
else
{
m_filterExtension.clear();
}
GenerateFilterChangedEvent( this, this );
}
void wxGenericFileCtrl::SetWildcard( const wxString& wildCard )
{
if ( wildCard.empty() || wildCard == wxFileSelectorDefaultWildcardStr )
{
m_wildCard = wxString::Format( _( "All files (%s)|%s" ),
wxFileSelectorDefaultWildcardStr,
wxFileSelectorDefaultWildcardStr );
}
else
m_wildCard = wildCard;
wxArrayString wildDescriptions, wildFilters;
const size_t count = wxParseCommonDialogsFilter( m_wildCard,
wildDescriptions,
wildFilters );
wxCHECK_RET( count, wxT( "wxFileDialog: bad wildcard string" ) );
m_choice->Clear();
for ( size_t n = 0; n < count; n++ )
{
m_choice->Append(wildDescriptions[n], new wxStringClientData(wildFilters[n]));
}
SetFilterIndex( 0 );
}
void wxGenericFileCtrl::SetFilterIndex( int filterindex )
{
m_choice->SetSelection( filterindex );
DoSetFilterIndex( filterindex );
}
void wxGenericFileCtrl::OnChoiceFilter( wxCommandEvent &event )
{
DoSetFilterIndex( ( int )event.GetInt() );
}
void wxGenericFileCtrl::OnCheck( wxCommandEvent &event )
{
m_list->ShowHidden( event.GetInt() != 0 );
}
void wxGenericFileCtrl::OnActivated( wxListEvent &event )
{
HandleAction( event.m_item.m_text );
}
void wxGenericFileCtrl::OnTextEnter( wxCommandEvent &WXUNUSED( event ) )
{
HandleAction( m_text->GetValue() );
}
void wxGenericFileCtrl::OnTextChange( wxCommandEvent &WXUNUSED( event ) )
{
if ( !m_ignoreChanges )
{
// Clear selections. Otherwise when the user types in a value they may
// not get the file whose name they typed.
if ( m_list->GetSelectedItemCount() > 0 )
{
long item = m_list->GetNextItem( -1, wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED );
while ( item != -1 )
{
m_list->SetItemState( item, 0, wxLIST_STATE_SELECTED );
item = m_list->GetNextItem( item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
}
}
}
}
void wxGenericFileCtrl::OnSelected( wxListEvent &event )
{
if ( m_ignoreChanges )
return;
if ( m_inSelected )
return;
m_inSelected = true;
const wxString filename( event.m_item.m_text );
#ifdef __WXWINCE__
// No double-click on most WinCE devices, so do action immediately.
HandleAction( filename );
#else
if ( filename == wxT( ".." ) )
{
m_inSelected = false;
return;
}
wxString dir = m_list->GetDir();
if ( !IsTopMostDir( dir ) )
dir += wxFILE_SEP_PATH;
dir += filename;
if ( wxDirExists( dir ) )
{
m_inSelected = false;
return;
}
m_ignoreChanges = true;
m_text->SetValue( filename );
if ( m_list->GetSelectedItemCount() > 1 )
{
m_text->Clear();
}
if ( !m_noSelChgEvent )
GenerateSelectionChangedEvent( this, this );
m_ignoreChanges = false;
#endif
m_inSelected = false;
}
void wxGenericFileCtrl::HandleAction( const wxString &fn )
{
if ( m_ignoreChanges )
return;
wxString filename( fn );
if ( filename.empty() )
{
return;
}
if ( filename == wxT( "." ) ) return;
wxString dir = m_list->GetDir();
// "some/place/" means they want to chdir not try to load "place"
const bool want_dir = filename.Last() == wxFILE_SEP_PATH;
if ( want_dir )
filename = filename.RemoveLast();
if ( filename == wxT( ".." ) )
{
m_ignoreChanges = true;
m_list->GoToParentDir();
GenerateFolderChangedEvent( this, this );
UpdateControls();
m_ignoreChanges = false;
return;
}
#ifdef __UNIX__
if ( filename == wxT( "~" ) )
{
m_ignoreChanges = true;
m_list->GoToHomeDir();
GenerateFolderChangedEvent( this, this );
UpdateControls();
m_ignoreChanges = false;
return;
}
if ( filename.BeforeFirst( wxT( '/' ) ) == wxT( "~" ) )
{
filename = wxString( wxGetUserHome() ) + filename.Remove( 0, 1 );
}
#endif // __UNIX__
if ( !( m_style & wxFC_SAVE ) )
{
if ( ( filename.Find( wxT( '*' ) ) != wxNOT_FOUND ) ||
( filename.Find( wxT( '?' ) ) != wxNOT_FOUND ) )
{
if ( filename.Find( wxFILE_SEP_PATH ) != wxNOT_FOUND )
{
wxMessageBox( _( "Illegal file specification." ),
_( "Error" ), wxOK | wxICON_ERROR, this );
return;
}
m_list->SetWild( filename );
return;
}
}
if ( !IsTopMostDir( dir ) )
dir += wxFILE_SEP_PATH;
if ( !wxIsAbsolutePath( filename ) )
{
dir += filename;
filename = dir;
}
if ( wxDirExists( filename ) )
{
m_ignoreChanges = true;
m_list->GoToDir( filename );
UpdateControls();
GenerateFolderChangedEvent( this, this );
m_ignoreChanges = false;
return;
}
// they really wanted a dir, but it doesn't exist
if ( want_dir )
{
wxMessageBox( _( "Directory doesn't exist." ), _( "Error" ),
wxOK | wxICON_ERROR, this );
return;
}
// append the default extension to the filename if it doesn't have any
//
// VZ: the logic of testing for !wxFileExists() only for the open file
// dialog is not entirely clear to me, why don't we allow saving to a
// file without extension as well?
if ( !( m_style & wxFC_OPEN ) || !wxFileExists( filename ) )
{
filename = wxFileDialogBase::AppendExtension( filename, m_filterExtension );
GenerateFileActivatedEvent( this, this, wxFileName( filename ).GetFullName() );
return;
}
GenerateFileActivatedEvent( this, this );
}
bool wxGenericFileCtrl::SetPath( const wxString& path )
{
if ( !wxFileName::FileExists( ( path ) ) )
return false;
wxString ext;
wxFileName::SplitPath( path, &m_dir, &m_fileName, &ext );
if ( !ext.empty() )
{
m_fileName += wxT( "." );
m_fileName += ext;
}
SetDirectory( m_dir );
SetFilename( m_fileName );
return true;
}
void wxGenericFileCtrl::GetPaths( wxArrayString& paths ) const
{
DoGetFilenames( paths, true );
}
void wxGenericFileCtrl::GetFilenames( wxArrayString& files ) const
{
DoGetFilenames( files, false );
}
void wxGenericFileCtrl::UpdateControls()
{
const wxString dir = m_list->GetDir();
m_static->SetLabel( dir );
}
void wxGenericFileCtrl::GoToParentDir()
{
m_list->GoToParentDir();
UpdateControls();
}
void wxGenericFileCtrl::GoToHomeDir()
{
m_list->GoToHomeDir();
UpdateControls();
}
#endif // wxUSE_FILECTRL