///////////////////////////////////////////////////////////////////////////// // Name: src/generic/dirctrlg.cpp // Purpose: wxSpecificDirCtrl // Author: Harm van der Heijden, Robert Roebling, Julian Smart // Modified by: // Created: 12/12/98 // Copyright: (c) Harm van der Heijden, Robert Roebling and Julian Smart // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif //#if wxUSE_DIRDLG || wxUSE_FILEDLG #include "wx/generic/dirctrlg.h" #include "FileSelectorCtrl.h" #ifndef WX_PRECOMP #include "wx/hash.h" #include "wx/intl.h" #include "wx/log.h" #include "wx/utils.h" #include "wx/button.h" #include "wx/icon.h" #include "wx/settings.h" #include "wx/msgdlg.h" #include "wx/choice.h" #include "wx/textctrl.h" #include "wx/layout.h" #include "wx/sizer.h" #include "wx/textdlg.h" #include "wx/gdicmn.h" #include "wx/image.h" #include "wx/module.h" #endif #include "wx/filename.h" #include "wx/filefn.h" #include "wx/imaglist.h" #include "wx/tokenzr.h" #include "wx/dir.h" #include "wx/artprov.h" #include "wx/mimetype.h" #if wxUSE_STATLINE #include "wx/statline.h" #endif #if defined(__WXMAC__) #include "wx/osx/private.h" // includes mac headers #endif #ifdef __WINDOWS__ #include #include "wx/msw/winundef.h" #include "wx/volume.h" // MinGW has _getdrive() and _chdrive(), Cygwin doesn't. #if defined(__GNUWIN32__) && !defined(__CYGWIN__) #define wxHAS_DRIVE_FUNCTIONS #endif #ifdef wxHAS_DRIVE_FUNCTIONS #include #endif #endif // __WINDOWS__ #if defined(__WXMAC__) // #include "MoreFilesX.h" #endif #ifdef __BORLANDC__ #include "dos.h" #endif extern WXDLLEXPORT_DATA(const char) wxFileSelectorDefaultWildcardStr[]; // If compiled under Windows, this macro can cause problems #ifdef GetFirstChild #undef GetFirstChild #endif bool wxIsDriveAvailable(const wxString& dirName); // ---------------------------------------------------------------------------- // events // ---------------------------------------------------------------------------- wxDEFINE_EVENT( wxEVT_DIRCTRL_SELECTIONCHANGED, wxTreeEvent ); wxDEFINE_EVENT( wxEVT_DIRCTRL_FILEACTIVATED, wxTreeEvent ); // ---------------------------------------------------------------------------- // wxGetAvailableDrives, for WINDOWS, OSX, UNIX (returns "/") // ---------------------------------------------------------------------------- size_t wxGetAvailableDrives(wxArrayString &paths, wxArrayString &names, wxArrayInt &icon_ids) { #ifdef wxHAS_FILESYSTEM_VOLUMES #if defined(__WIN32__) && wxUSE_FSVOLUME // TODO: this code (using wxFSVolumeBase) should be used for all platforms // but unfortunately wxFSVolumeBase is not implemented everywhere const wxArrayString as = wxFSVolumeBase::GetVolumes(); for (size_t i = 0; i < as.GetCount(); i++) { wxString path = as[i]; wxFSVolume vol(path); int imageId; switch (vol.GetKind()) { case wxFS_VOL_FLOPPY: if ( (path == wxT("a:\\")) || (path == wxT("b:\\")) ) imageId = wxFileIconsTable::floppy; else imageId = wxFileIconsTable::removeable; break; case wxFS_VOL_DVDROM: case wxFS_VOL_CDROM: imageId = wxFileIconsTable::cdrom; break; case wxFS_VOL_NETWORK: if (path[0] == wxT('\\')) continue; // skip "\\computer\folder" imageId = wxFileIconsTable::drive; break; case wxFS_VOL_DISK: case wxFS_VOL_OTHER: default: imageId = wxFileIconsTable::drive; break; } paths.Add(path); names.Add(vol.GetDisplayName()); icon_ids.Add(imageId); } #else // !__WIN32__ /* If we can switch to the drive, it exists. */ for ( char drive = 'A'; drive <= 'Z'; drive++ ) { const wxString path = wxFileName::GetVolumeString(drive, wxPATH_GET_SEPARATOR); if (wxIsDriveAvailable(path)) { paths.Add(path); names.Add(wxFileName::GetVolumeString(drive, wxPATH_NO_SEPARATOR)); icon_ids.Add(drive <= 2 ? wxFileIconsTable::floppy : wxFileIconsTable::drive); } } #endif // __WIN32__/!__WIN32__ #elif defined(__WXMAC__) && wxOSX_USE_COCOA_OR_CARBON ItemCount volumeIndex = 1; OSErr err = noErr ; while( noErr == err ) { HFSUniStr255 volumeName ; FSRef fsRef ; FSVolumeInfo volumeInfo ; err = FSGetVolumeInfo(0, volumeIndex, NULL, kFSVolInfoFlags , &volumeInfo , &volumeName, &fsRef); if( noErr == err ) { wxString path = wxMacFSRefToPath( &fsRef ) ; wxString name = wxMacHFSUniStrToString( &volumeName ) ; if ( (volumeInfo.flags & kFSVolFlagSoftwareLockedMask) || (volumeInfo.flags & kFSVolFlagHardwareLockedMask) ) { icon_ids.Add(wxFileIconsTable::cdrom); } else { icon_ids.Add(wxFileIconsTable::drive); } // todo other removable paths.Add(path); names.Add(name); volumeIndex++ ; } } #elif defined(__UNIX__) paths.Add(wxT("/")); names.Add(wxT("/")); icon_ids.Add(wxFileIconsTable::computer); #else #error "Unsupported platform in wxSpecificDirCtrl!" #endif wxASSERT_MSG( (paths.GetCount() == names.GetCount()), wxT("The number of paths and their human readable names should be equal in number.")); wxASSERT_MSG( (paths.GetCount() == icon_ids.GetCount()), wxT("Wrong number of icons for available drives.")); return paths.GetCount(); } // ---------------------------------------------------------------------------- // wxIsDriveAvailable // ---------------------------------------------------------------------------- #if defined(__WINDOWS__) int setdrive(int drive) { #if defined(wxHAS_DRIVE_FUNCTIONS) return _chdrive(drive); #else wxChar newdrive[4]; if (drive < 1 || drive > 31) return -1; newdrive[0] = (wxChar)(wxT('A') + drive - 1); newdrive[1] = wxT(':'); newdrive[2] = wxT('\0'); #if defined(__WINDOWS__) if (::SetCurrentDirectory(newdrive)) #else // VA doesn't know what LPSTR is and has its own set if (!DosSetCurrentDir((PSZ)newdrive)) #endif return 0; else return -1; #endif // !GNUWIN32 } bool wxIsDriveAvailable(const wxString& dirName) { #ifdef __WIN32__ UINT errorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); #endif bool success = true; // Check if this is a root directory and if so, // whether the drive is available. if (dirName.length() == 3 && dirName[(size_t)1] == wxT(':')) { wxString dirNameLower(dirName.Lower()); #ifndef wxHAS_DRIVE_FUNCTIONS success = wxDirExists(dirNameLower); #else int currentDrive = _getdrive(); int thisDrive = (int) (dirNameLower[(size_t)0] - 'a' + 1) ; int err = setdrive( thisDrive ) ; setdrive( currentDrive ); if (err == -1) { success = false; } #endif } #ifdef __WIN32__ (void) SetErrorMode(errorMode); #endif return success; } #endif // __WINDOWS__ //#endif // wxUSE_DIRDLG || wxUSE_FILEDLG // Function which is called by quick sort. We want to override the default wxArrayString behaviour, // and sort regardless of case. static int wxCMPFUNC_CONV wxDirCtrlStringCompareFunction(const wxString& strFirst, const wxString& strSecond) { return strFirst.CmpNoCase(strSecond); } //----------------------------------------------------------------------------- // wxDirItemData //----------------------------------------------------------------------------- wxDirItemData::wxDirItemData(const wxString& path, const wxString& name, bool isDir) { m_path = path; m_name = name; /* Insert logic to detect hidden files here * In UnixLand we just check whether the first char is a dot * For FileNameFromPath read LastDirNameInThisPath ;-) */ // m_isHidden = (bool)(wxFileNameFromPath(*m_path)[0] == '.'); m_isHidden = false; m_isExpanded = false; m_isDir = isDir; } void wxDirItemData::SetNewDirName(const wxString& path) { m_path = path; m_name = wxFileNameFromPath(path); } bool wxDirItemData::HasSubDirs() const { if (m_path.empty()) return false; wxDir dir; { wxLogNull nolog; if ( !dir.Open(m_path) ) return false; } return dir.HasSubDirs(); } bool wxDirItemData::HasFiles(const wxString& WXUNUSED(spec)) const { if (m_path.empty()) return false; wxDir dir; { wxLogNull nolog; if ( !dir.Open(m_path) ) return false; } return dir.HasFiles(); } //----------------------------------------------------------------------------- // wxSpecificDirCtrl //----------------------------------------------------------------------------- wxBEGIN_EVENT_TABLE(wxSpecificDirCtrl, wxControl) EVT_TREE_ITEM_EXPANDING (wxID_TREECTRL, wxSpecificDirCtrl::OnExpandItem) EVT_TREE_ITEM_COLLAPSED (wxID_TREECTRL, wxSpecificDirCtrl::OnCollapseItem) EVT_TREE_BEGIN_LABEL_EDIT (wxID_TREECTRL, wxSpecificDirCtrl::OnBeginEditItem) EVT_TREE_END_LABEL_EDIT (wxID_TREECTRL, wxSpecificDirCtrl::OnEndEditItem) EVT_TREE_SEL_CHANGED (wxID_TREECTRL, wxSpecificDirCtrl::OnTreeSelChange) EVT_TREE_ITEM_ACTIVATED (wxID_TREECTRL, wxSpecificDirCtrl::OnItemActivated) EVT_SIZE (wxSpecificDirCtrl::OnSize) wxEND_EVENT_TABLE() wxSpecificDirCtrl::wxSpecificDirCtrl(void) { Init(); } void wxSpecificDirCtrl::ExpandRoot() { // ALl to remove this line! ExpandDir(m_rootId); // automatically expand first level // Expand and select the default path if (!m_defaultPath.empty()) { ExpandPath(m_defaultPath); } #ifdef __UNIX__ else { // On Unix, there's only one node under the (hidden) root node. It // represents the / path, so the user would always have to expand it; // let's do it ourselves ExpandPath( wxT("/") ); } #endif } bool wxSpecificDirCtrl::Create(wxWindow *parent, wxWindowID treeid, const wxString& dir, const wxPoint& pos, const wxSize& size, long style, const wxString& filter, int defaultFilter, const wxString& name) { if (!wxControl::Create(parent, treeid, pos, size, style, wxDefaultValidator, name)) return false; SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)); SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); long treeStyle = wxTR_HAS_BUTTONS; treeStyle |= wxTR_HIDE_ROOT; #ifdef __WXGTK20__ treeStyle |= wxTR_NO_LINES; #endif if (style & wxDIRCTRL_EDIT_LABELS) treeStyle |= wxTR_EDIT_LABELS; if (style & wxDIRCTRL_MULTIPLE) treeStyle |= wxTR_MULTIPLE; if ((style & wxDIRCTRL_3D_INTERNAL) == 0) treeStyle |= wxNO_BORDER; m_treeCtrl = CreateTreeCtrl(this, wxID_TREECTRL, wxPoint(0,0), GetClientSize(), treeStyle); /* JIB if (!filter.empty() && (style & wxDIRCTRL_SHOW_FILTERS)) m_filterListCtrl = new wxDirFilterListCtrl(this, wxID_FILTERLISTCTRL); */ m_defaultPath = dir; m_filter = filter; if (m_filter.empty()) m_filter = wxFileSelectorDefaultWildcardStr; SetFilterIndex(defaultFilter); if (m_filterListCtrl) m_filterListCtrl->FillFilterList(filter, defaultFilter); // TODO: set the icon size according to current scaling for this window. // Currently, there's insufficient API in wxWidgets to determine what icons // are available and whether to take the nearest size according to a tolerance // instead of scaling. // if (!wxTheFileIconsTable->IsOk()) // wxTheFileIconsTable->SetSize(scaledSize); // Meanwhile, in your application initialisation, where you have better knowledge of what // icons are available and whether to scale, you can do this: // // wxTheFileIconsTable->SetSize(calculatedIconSizeForDPI); // // Obviously this can't take into account monitors with different DPI. m_treeCtrl->SetImageList(wxTheFileIconsTable->GetSmallImageList()); m_showHidden = false; // OLD: wxDirItemData* rootData = new wxDirItemData(wxEmptyString, wxEmptyString, true); wxDirItemData* rootData = new wxDirItemData(dir, dir, true); wxString rootName; #if defined(__WINDOWS__) rootName = _("Computer"); #else rootName = _("Sections"); #endif m_rootId = m_treeCtrl->AddRoot( rootName, 3, -1, rootData); m_treeCtrl->SetItemHasChildren(m_rootId); ExpandRoot(); SetInitialSize(size); DoResize(); return true; } wxSpecificDirCtrl::~wxSpecificDirCtrl() { } void wxSpecificDirCtrl::Init() { m_showHidden = false; m_currentFilter = 0; m_currentFilterStr = wxEmptyString; // Default: any file m_treeCtrl = NULL; m_filterListCtrl = NULL; } wxTreeCtrl* wxSpecificDirCtrl::CreateTreeCtrl(wxWindow *parent, wxWindowID treeid, const wxPoint& pos, const wxSize& size, long treeStyle) { return new wxTreeCtrl(parent, treeid, pos, size, treeStyle); } void wxSpecificDirCtrl::ShowHidden( bool show ) { if ( m_showHidden == show ) return; m_showHidden = show; if ( HasFlag(wxDIRCTRL_MULTIPLE) ) { wxArrayString paths; GetPaths(paths); ReCreateTree(); for ( unsigned n = 0; n < paths.size(); n++ ) { ExpandPath(paths[n]); } } else { wxString path = GetPath(); ReCreateTree(); SetPath(path); } } const wxTreeItemId wxSpecificDirCtrl::AddSection(const wxString& path, const wxString& name, int imageId) { wxDirItemData *dir_item = new wxDirItemData(path,name,true); wxTreeItemId treeid = AppendItem( m_rootId, name, imageId, -1, dir_item); m_treeCtrl->SetItemHasChildren(treeid); return treeid; } void wxSpecificDirCtrl::SetupSections() { // JIB: This was adding in all the drives etc. AddSection(m_defaultPath, m_defaultPath, 1); return ; wxArrayString paths, names; wxArrayInt icons; size_t n, count = wxGetAvailableDrives(paths, names, icons); #ifdef __WXGTK20__ wxString home = wxGetHomeDir(); AddSection( home, _("Home directory"), 1); home += wxT("/Desktop"); AddSection( home, _("Desktop"), 1); #endif for (n = 0; n < count; n++) AddSection(paths[n], names[n], icons[n]); } void wxSpecificDirCtrl::SetFocus() { // we don't need focus ourselves, give it to the tree so that the user // could navigate it if (m_treeCtrl) m_treeCtrl->SetFocus(); } void wxSpecificDirCtrl::OnBeginEditItem(wxTreeEvent &event) { // don't rename the main entry "Sections" if (event.GetItem() == m_rootId) { event.Veto(); return; } // don't rename the individual sections if (m_treeCtrl->GetItemParent( event.GetItem() ) == m_rootId) { event.Veto(); return; } } void wxSpecificDirCtrl::OnEndEditItem(wxTreeEvent &event) { if (event.IsEditCancelled()) return; if ((event.GetLabel().empty()) || (event.GetLabel() == wxT(".")) || (event.GetLabel() == wxT("..")) || (event.GetLabel().Find(wxT('/')) != wxNOT_FOUND) || (event.GetLabel().Find(wxT('\\')) != wxNOT_FOUND) || (event.GetLabel().Find(wxT('|')) != wxNOT_FOUND)) { wxMessageDialog dialog(this, _("Illegal directory name."), _("Error"), wxOK | wxICON_ERROR ); dialog.ShowModal(); event.Veto(); return; } wxTreeItemId treeid = event.GetItem(); wxDirItemData *data = GetItemData( treeid ); wxASSERT( data ); wxString new_name( wxPathOnly( data->m_path ) ); new_name += wxString(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(data->m_path,new_name)) { data->SetNewDirName( new_name ); } else { wxMessageDialog dialog(this, _("Operation not permitted."), _("Error"), wxOK | wxICON_ERROR ); dialog.ShowModal(); event.Veto(); } } void wxSpecificDirCtrl::OnTreeSelChange(wxTreeEvent &event) { wxTreeEvent changedEvent(wxEVT_DIRCTRL_SELECTIONCHANGED, GetId()); changedEvent.SetEventObject(this); changedEvent.SetItem(event.GetItem()); changedEvent.SetClientObject(m_treeCtrl->GetItemData(event.GetItem())); if (GetEventHandler()->SafelyProcessEvent(changedEvent) && !changedEvent.IsAllowed()) event.Veto(); else event.Skip(); } void wxSpecificDirCtrl::OnItemActivated(wxTreeEvent &event) { wxTreeItemId treeid = event.GetItem(); const wxDirItemData *data = GetItemData(treeid); if (data->m_isDir) { // is dir event.Skip(); } else { // is file wxTreeEvent changedEvent(wxEVT_DIRCTRL_FILEACTIVATED, GetId()); changedEvent.SetEventObject(this); changedEvent.SetItem(treeid); changedEvent.SetClientObject(m_treeCtrl->GetItemData(treeid)); if (GetEventHandler()->SafelyProcessEvent(changedEvent) && !changedEvent.IsAllowed()) event.Veto(); else event.Skip(); } } void wxSpecificDirCtrl::OnExpandItem(wxTreeEvent &event) { wxTreeItemId parentId = event.GetItem(); // VS: this is needed because the event handler is called from wxTreeCtrl // ctor when wxTR_HIDE_ROOT was specified if (!m_rootId.IsOk()) m_rootId = m_treeCtrl->GetRootItem(); ExpandDir(parentId); } void wxSpecificDirCtrl::OnCollapseItem(wxTreeEvent &event ) { CollapseDir(event.GetItem()); } void wxSpecificDirCtrl::CollapseDir(wxTreeItemId parentId) { wxTreeItemId child; wxDirItemData *data = GetItemData(parentId); if (!data->m_isExpanded) return; data->m_isExpanded = false; m_treeCtrl->Freeze(); if (parentId != m_treeCtrl->GetRootItem()) m_treeCtrl->CollapseAndReset(parentId); m_treeCtrl->DeleteChildren(parentId); m_treeCtrl->Thaw(); } void wxSpecificDirCtrl::PopulateNode(wxTreeItemId parentId) { wxDirItemData *data = GetItemData(parentId); if (data->m_isExpanded) return; data->m_isExpanded = true; if (parentId == m_treeCtrl->GetRootItem()) { SetupSections(); return; } wxASSERT(data); wxString search,path,filename; wxString dirName(data->m_path); #if defined(__WINDOWS__) // Check if this is a root directory and if so, // whether the drive is available. if (!wxIsDriveAvailable(dirName)) { data->m_isExpanded = false; //wxMessageBox(wxT("Sorry, this drive is not available.")); return; } #endif // This may take a longish time. Go to busy cursor wxBusyCursor busy; #if defined(__WINDOWS__) if (dirName.Last() == ':') dirName += wxString(wxFILE_SEP_PATH); #endif wxArrayString dirs; wxArrayString filenames; wxDir d; wxString eachFilename; wxLogNull log; d.Open(dirName); if (d.IsOpened()) { int style = wxDIR_DIRS; if (m_showHidden) style |= wxDIR_HIDDEN; if (d.GetFirst(& eachFilename, wxEmptyString, style)) { do { if ((eachFilename != wxT(".")) && (eachFilename != wxT(".."))) { dirs.Add(eachFilename); } } while (d.GetNext(&eachFilename)); } } dirs.Sort(wxDirCtrlStringCompareFunction); // Now do the filenames -- but only if we're allowed to if (!HasFlag(wxDIRCTRL_DIR_ONLY)) { d.Open(dirName); if (d.IsOpened()) { int style = wxDIR_FILES; if (m_showHidden) style |= wxDIR_HIDDEN; // Process each filter (ex: "JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg") wxStringTokenizer strTok; wxString curFilter; strTok.SetString(m_currentFilterStr,wxT(";")); while(strTok.HasMoreTokens()) { curFilter = strTok.GetNextToken(); if (d.GetFirst(& eachFilename, curFilter, style)) { do { if ((eachFilename != wxT(".")) && (eachFilename != wxT(".."))) { filenames.Add(eachFilename); } } while (d.GetNext(& eachFilename)); } } } filenames.Sort(wxDirCtrlStringCompareFunction); } // Now we really know whether we have any children so tell the tree control // about it. m_treeCtrl->SetItemHasChildren(parentId, !dirs.empty() || !filenames.empty()); // Add the sorted dirs size_t i; for (i = 0; i < dirs.GetCount(); i++) { eachFilename = dirs[i]; path = dirName; if (!wxEndsWithPathSeparator(path)) path += wxString(wxFILE_SEP_PATH); path += eachFilename; wxDirItemData *dir_item = new wxDirItemData(path,eachFilename,true); wxTreeItemId treeid = AppendItem( parentId, eachFilename, wxFileIconsTable::folder, -1, dir_item); m_treeCtrl->SetItemImage( treeid, wxFileIconsTable::folder_open, wxTreeItemIcon_Expanded ); // assume that it does have children by default as it can take a long // time to really check for this (think remote drives...) // // and if we're wrong, we'll correct the icon later if // the user really tries to open this item m_treeCtrl->SetItemHasChildren(treeid); } // Add the sorted filenames if (!HasFlag(wxDIRCTRL_DIR_ONLY)) { for (i = 0; i < filenames.GetCount(); i++) { eachFilename = filenames[i]; path = dirName; if (!wxEndsWithPathSeparator(path)) path += wxString(wxFILE_SEP_PATH); path += eachFilename; //path = dirName + wxString(wxT("/")) + eachFilename; wxDirItemData *dir_item = new wxDirItemData(path,eachFilename,false); int image_id = wxFileIconsTable::file; if (eachFilename.Find(wxT('.')) != wxNOT_FOUND) image_id = wxTheFileIconsTable->GetIconID(eachFilename.AfterLast(wxT('.'))); (void) AppendItem( parentId, eachFilename, image_id, -1, dir_item); } } } void wxSpecificDirCtrl::ExpandDir(wxTreeItemId parentId) { // ExpandDir() will not actually expand the tree node, just populate it PopulateNode(parentId); } void wxSpecificDirCtrl::ReCreateTree() { CollapseDir(m_treeCtrl->GetRootItem()); ExpandRoot(); } void wxSpecificDirCtrl::CollapseTree() { wxTreeItemIdValue cookie; wxTreeItemId child = m_treeCtrl->GetFirstChild(m_rootId, cookie); while (child.IsOk()) { CollapseDir(child); child = m_treeCtrl->GetNextChild(m_rootId, cookie); } } // Find the child that matches the first part of 'path'. // E.g. if a child path is "/usr" and 'path' is "/usr/include" // th en the child for /usr is returned. wxTreeItemId wxSpecificDirCtrl::FindChild(wxTreeItemId parentId, const wxString& path, bool& done) { wxString path2(path); // Make sure all separators are as per the current platform path2.Replace(wxT("\\"), wxString(wxFILE_SEP_PATH)); path2.Replace(wxT("/"), wxString(wxFILE_SEP_PATH)); // Append a separator to foil bogus substring matching path2 += wxString(wxFILE_SEP_PATH); // In MSW case is not significant #if defined(__WINDOWS__) path2.MakeLower(); #endif wxTreeItemIdValue cookie; wxTreeItemId childId = m_treeCtrl->GetFirstChild(parentId, cookie); while (childId.IsOk()) { wxDirItemData* data = GetItemData(childId); if (data && !data->m_path.empty()) { wxString childPath(data->m_path); if (!wxEndsWithPathSeparator(childPath)) childPath += wxString(wxFILE_SEP_PATH); // In MSW case is not significant #if defined(__WINDOWS__) childPath.MakeLower(); #endif if (childPath.length() <= path2.length()) { wxString path3 = path2.Mid(0, childPath.length()); if (childPath == path3) { if (path3.length() == path2.length()) done = true; else done = false; return childId; } } } childId = m_treeCtrl->GetNextChild(parentId, cookie); } wxTreeItemId invalid; return invalid; } // Try to expand as much of the given path as possible, // and select the given tree item. bool wxSpecificDirCtrl::ExpandPath(const wxString& path) { bool done = false; wxTreeItemId treeid = FindChild(m_rootId, path, done); wxTreeItemId lastId = treeid; // The last non-zero treeid while (treeid.IsOk() && !done) { ExpandDir(treeid); treeid = FindChild(treeid, path, done); if (treeid.IsOk()) lastId = treeid; } if (!lastId.IsOk()) return false; wxDirItemData *data = GetItemData(lastId); if (data->m_isDir) { m_treeCtrl->Expand(lastId); } if (HasFlag(wxDIRCTRL_SELECT_FIRST) && data->m_isDir) { // Find the first file in this directory wxTreeItemIdValue cookie; wxTreeItemId childId = m_treeCtrl->GetFirstChild(lastId, cookie); bool selectedChild = false; while (childId.IsOk()) { data = GetItemData(childId); if (data && data->m_path != wxEmptyString && !data->m_isDir) { m_treeCtrl->SelectItem(childId); m_treeCtrl->EnsureVisible(childId); selectedChild = true; break; } childId = m_treeCtrl->GetNextChild(lastId, cookie); } if (!selectedChild) { m_treeCtrl->SelectItem(lastId); m_treeCtrl->EnsureVisible(lastId); } } else { m_treeCtrl->SelectItem(lastId); m_treeCtrl->EnsureVisible(lastId); } return true; } bool wxSpecificDirCtrl::CollapsePath(const wxString& path) { bool done = false; wxTreeItemId treeid = FindChild(m_rootId, path, done); wxTreeItemId lastId = treeid; // The last non-zero treeid while ( treeid.IsOk() && !done ) { CollapseDir(treeid); treeid = FindChild(treeid, path, done); if ( treeid.IsOk() ) lastId = treeid; } if ( !lastId.IsOk() ) return false; m_treeCtrl->SelectItem(lastId); m_treeCtrl->EnsureVisible(lastId); return true; } wxDirItemData* wxSpecificDirCtrl::GetItemData(wxTreeItemId itemId) { return static_cast(m_treeCtrl->GetItemData(itemId)); } wxString wxSpecificDirCtrl::GetPath(wxTreeItemId itemId) const { const wxDirItemData* data = static_cast(m_treeCtrl->GetItemData(itemId)); return data->m_path; } wxString wxSpecificDirCtrl::GetPath() const { // Allow calling GetPath() in multiple selection from OnSelFilter if (m_treeCtrl->HasFlag(wxTR_MULTIPLE)) { wxArrayTreeItemIds items; m_treeCtrl->GetSelections(items); if (items.size() > 0) { // return first string only wxTreeItemId treeid = items[0]; return GetPath(treeid); } return wxEmptyString; } wxTreeItemId treeid = m_treeCtrl->GetSelection(); if (treeid) { return GetPath(treeid); } else return wxEmptyString; } void wxSpecificDirCtrl::GetPaths(wxArrayString& paths) const { paths.clear(); wxArrayTreeItemIds items; m_treeCtrl->GetSelections(items); for ( unsigned n = 0; n < items.size(); n++ ) { wxTreeItemId treeid = items[n]; paths.push_back(GetPath(treeid)); } } wxString wxSpecificDirCtrl::GetFilePath() const { wxTreeItemId treeid = m_treeCtrl->GetSelection(); if (treeid) { wxDirItemData* data = (wxDirItemData*) m_treeCtrl->GetItemData(treeid); if (data->m_isDir) return wxEmptyString; else return data->m_path; } else return wxEmptyString; } void wxSpecificDirCtrl::GetFilePaths(wxArrayString& paths) const { paths.clear(); wxArrayTreeItemIds items; m_treeCtrl->GetSelections(items); for ( unsigned n = 0; n < items.size(); n++ ) { wxTreeItemId treeid = items[n]; wxDirItemData* data = (wxDirItemData*) m_treeCtrl->GetItemData(treeid); if ( !data->m_isDir ) paths.Add(data->m_path); } } void wxSpecificDirCtrl::SetPath(const wxString& path) { m_defaultPath = path; if (m_rootId) ExpandPath(path); } void wxSpecificDirCtrl::SelectPath(const wxString& path, bool select) { bool done = false; wxTreeItemId treeid = FindChild(m_rootId, path, done); wxTreeItemId lastId = treeid; // The last non-zero treeid while ( treeid.IsOk() && !done ) { treeid = FindChild(treeid, path, done); if ( treeid.IsOk() ) lastId = treeid; } if ( !lastId.IsOk() ) return; if ( done ) { m_treeCtrl->SelectItem(treeid, select); } } void wxSpecificDirCtrl::SelectPaths(const wxArrayString& paths) { if ( HasFlag(wxDIRCTRL_MULTIPLE) ) { UnselectAll(); for ( unsigned n = 0; n < paths.size(); n++ ) { SelectPath(paths[n]); } } } void wxSpecificDirCtrl::UnselectAll() { m_treeCtrl->UnselectAll(); } // Not used #if 0 void wxSpecificDirCtrl::FindChildFiles(wxTreeItemId treeid, int dirFlags, wxArrayString& filenames) { wxDirItemData *data = (wxDirItemData *) m_treeCtrl->GetItemData(treeid); // This may take a longish time. Go to busy cursor wxBusyCursor busy; wxASSERT(data); wxString search,path,filename; wxString dirName(data->m_path); #if defined(__WINDOWS__) if (dirName.Last() == ':') dirName += wxString(wxFILE_SEP_PATH); #endif wxDir d; wxString eachFilename; wxLogNull log; d.Open(dirName); if (d.IsOpened()) { if (d.GetFirst(& eachFilename, m_currentFilterStr, dirFlags)) { do { if ((eachFilename != wxT(".")) && (eachFilename != wxT(".."))) { filenames.Add(eachFilename); } } while (d.GetNext(& eachFilename)) ; } } } #endif void wxSpecificDirCtrl::SetFilterIndex(int n) { m_currentFilter = n; wxString f, d; if (ExtractWildcard(m_filter, n, f, d)) m_currentFilterStr = f; else #ifdef __UNIX__ m_currentFilterStr = wxT("*"); #else m_currentFilterStr = wxT("*.*"); #endif if (!m_filter.empty()) { m_currentFilterStr = m_filter; } else { m_currentFilterStr = wxT("*.*"); } } void wxSpecificDirCtrl::SetFilter(const wxString& filter) { wxMessageBox(filter,_("Setting filter")); m_filter = filter; /* m_filter = filter; if (!filter.empty() && !m_filterListCtrl && HasFlag(wxDIRCTRL_SHOW_FILTERS)) m_filterListCtrl = new wxDirFilterListCtrl(this, wxID_FILTERLISTCTRL); else if (filter.empty() && m_filterListCtrl) { m_filterListCtrl->Destroy(); m_filterListCtrl = NULL; } wxString f, d; if (ExtractWildcard(m_filter, m_currentFilter, f, d)) m_currentFilterStr = f; else #ifdef __UNIX__ m_currentFilterStr = wxT("*"); #else m_currentFilterStr = wxT("*.*"); #endif // current filter index is meaningless after filter change, set it to zero SetFilterIndex(0); if (m_filterListCtrl) m_filterListCtrl->FillFilterList(m_filter, 0); */ } // Extract description and actual filter from overall filter string bool wxSpecificDirCtrl::ExtractWildcard(const wxString& filterStr, int n, wxString& filter, wxString& description) { wxArrayString filters, descriptions; int count = wxParseCommonDialogsFilter(filterStr, descriptions, filters); if (count > 0 && n < count) { filter = filters[n]; description = descriptions[n]; return true; } return false; } void wxSpecificDirCtrl::DoResize() { wxSize sz = GetClientSize(); int verticalSpacing = 3; if (m_treeCtrl) { wxSize filterSz ; if (m_filterListCtrl) { filterSz = m_filterListCtrl->GetSize(); sz.y -= (filterSz.y + verticalSpacing); } m_treeCtrl->SetSize(0, 0, sz.x, sz.y); if (m_filterListCtrl) { m_filterListCtrl->SetSize(0, sz.y + verticalSpacing, sz.x, filterSz.y); // Don't know why, but this needs refreshing after a resize (wxMSW) m_filterListCtrl->Refresh(); } } } void wxSpecificDirCtrl::OnSize(wxSizeEvent& WXUNUSED(event)) { DoResize(); } wxTreeItemId wxSpecificDirCtrl::AppendItem (const wxTreeItemId & parent, const wxString & text, int image, int selectedImage, wxTreeItemData * data) { wxTreeCtrl *treeCtrl = GetTreeCtrl (); wxASSERT (treeCtrl); if (treeCtrl) { return treeCtrl->AppendItem (parent, text, image, selectedImage, data); } else { return wxTreeItemId(); } }