1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/generic/dirctrlg.cpp
3 // Purpose:     wxGenericDirCtrl
4 // Author:      Harm van der Heijden, Robert Roebling, Julian Smart
5 // Modified by:
6 // Created:     12/12/98
7 // Copyright:   (c) Harm van der Heijden, Robert Roebling and Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13 
14 
15 #if wxUSE_DIRDLG || wxUSE_FILEDLG
16 
17 #include "wx/generic/dirctrlg.h"
18 
19 #ifndef WX_PRECOMP
20     #include "wx/hash.h"
21     #include "wx/intl.h"
22     #include "wx/log.h"
23     #include "wx/utils.h"
24     #include "wx/button.h"
25     #include "wx/icon.h"
26     #include "wx/settings.h"
27     #include "wx/msgdlg.h"
28     #include "wx/choice.h"
29     #include "wx/textctrl.h"
30     #include "wx/layout.h"
31     #include "wx/sizer.h"
32     #include "wx/textdlg.h"
33     #include "wx/gdicmn.h"
34     #include "wx/image.h"
35     #include "wx/module.h"
36 #endif
37 
38 #include "wx/filename.h"
39 #include "wx/filefn.h"
40 #include "wx/imaglist.h"
41 #include "wx/tokenzr.h"
42 #include "wx/dir.h"
43 #include "wx/artprov.h"
44 #include "wx/mimetype.h"
45 
46 #if wxUSE_STATLINE
47     #include "wx/statline.h"
48 #endif
49 
50 #if defined(__WXMAC__)
51     #include  "wx/osx/private.h"  // includes mac headers
52 #endif
53 
54 #ifdef __WINDOWS__
55 #include <windows.h>
56 #include "wx/msw/winundef.h"
57 #include "wx/volume.h"
58 
59 // MinGW has _getdrive() and _chdrive(), Cygwin doesn't.
60 #if defined(__GNUWIN32__) && !defined(__CYGWIN__)
61     #define wxHAS_DRIVE_FUNCTIONS
62 #endif
63 
64 #ifdef wxHAS_DRIVE_FUNCTIONS
65     #include <direct.h>
66 #endif
67 
68 #endif // __WINDOWS__
69 
70 #if defined(__WXMAC__)
71 //    #include "MoreFilesX.h"
72 #endif
73 
74 extern WXDLLEXPORT_DATA(const char) wxFileSelectorDefaultWildcardStr[];
75 
76 // If compiled under Windows, this macro can cause problems
77 #ifdef GetFirstChild
78 #undef GetFirstChild
79 #endif
80 
81 bool wxIsDriveAvailable(const wxString& dirName);
82 
83 // ----------------------------------------------------------------------------
84 // events
85 // ----------------------------------------------------------------------------
86 
87 wxDEFINE_EVENT( wxEVT_DIRCTRL_SELECTIONCHANGED, wxTreeEvent );
88 wxDEFINE_EVENT( wxEVT_DIRCTRL_FILEACTIVATED, wxTreeEvent );
89 
90 // ----------------------------------------------------------------------------
91 // wxGetAvailableDrives, for WINDOWS, OSX, UNIX (returns "/")
92 // ----------------------------------------------------------------------------
93 
94 // since the macOS implementation needs objective-C this is dirdlg.mm
95 #ifdef __WXOSX__
96 extern size_t wxGetAvailableDrives(wxArrayString &paths, wxArrayString &names, wxArrayInt &icon_ids);
97 #else
wxGetAvailableDrives(wxArrayString & paths,wxArrayString & names,wxArrayInt & icon_ids)98 size_t wxGetAvailableDrives(wxArrayString &paths, wxArrayString &names, wxArrayInt &icon_ids)
99 {
100 #ifdef wxHAS_FILESYSTEM_VOLUMES
101 
102 #if defined(__WIN32__) && wxUSE_FSVOLUME
103     // TODO: this code (using wxFSVolumeBase) should be used for all platforms
104     //       but unfortunately wxFSVolumeBase is not implemented everywhere
105     const wxArrayString as = wxFSVolumeBase::GetVolumes();
106 
107     for (size_t i = 0; i < as.GetCount(); i++)
108     {
109         wxString path = as[i];
110         wxFSVolume vol(path);
111         int imageId;
112         switch (vol.GetKind())
113         {
114             case wxFS_VOL_FLOPPY:
115                 if ( (path == wxT("a:\\")) || (path == wxT("b:\\")) )
116                     imageId = wxFileIconsTable::floppy;
117                 else
118                     imageId = wxFileIconsTable::removeable;
119                 break;
120             case wxFS_VOL_DVDROM:
121             case wxFS_VOL_CDROM:
122                 imageId = wxFileIconsTable::cdrom;
123                 break;
124             case wxFS_VOL_NETWORK:
125                 if (path[0] == wxT('\\'))
126                     continue; // skip "\\computer\folder"
127                 imageId = wxFileIconsTable::drive;
128                 break;
129             case wxFS_VOL_DISK:
130             case wxFS_VOL_OTHER:
131             default:
132                 imageId = wxFileIconsTable::drive;
133                 break;
134         }
135         paths.Add(path);
136         names.Add(vol.GetDisplayName());
137         icon_ids.Add(imageId);
138     }
139 #else // !__WIN32__
140     /* If we can switch to the drive, it exists. */
141     for ( char drive = 'A'; drive <= 'Z'; drive++ )
142     {
143         const wxString
144             path = wxFileName::GetVolumeString(drive, wxPATH_GET_SEPARATOR);
145 
146         if (wxIsDriveAvailable(path))
147         {
148             paths.Add(path);
149             names.Add(wxFileName::GetVolumeString(drive, wxPATH_NO_SEPARATOR));
150             icon_ids.Add(drive <= 2 ? wxFileIconsTable::floppy
151                                     : wxFileIconsTable::drive);
152         }
153     }
154 #endif // __WIN32__/!__WIN32__
155 
156 #elif defined(__UNIX__)
157     paths.Add(wxT("/"));
158     names.Add(wxT("/"));
159     icon_ids.Add(wxFileIconsTable::computer);
160 #else
161     #error "Unsupported platform in wxGenericDirCtrl!"
162 #endif
163     wxASSERT_MSG( (paths.GetCount() == names.GetCount()), wxT("The number of paths and their human readable names should be equal in number."));
164     wxASSERT_MSG( (paths.GetCount() == icon_ids.GetCount()), wxT("Wrong number of icons for available drives."));
165     return paths.GetCount();
166 }
167 #endif
168 
169 // ----------------------------------------------------------------------------
170 // wxIsDriveAvailable
171 // ----------------------------------------------------------------------------
172 
173 #if defined(__WINDOWS__)
174 
setdrive(int drive)175 int setdrive(int drive)
176 {
177 #if defined(wxHAS_DRIVE_FUNCTIONS)
178     return _chdrive(drive);
179 #else
180     wxChar  newdrive[4];
181 
182     if (drive < 1 || drive > 31)
183         return -1;
184     newdrive[0] = (wxChar)(wxT('A') + drive - 1);
185     newdrive[1] = wxT(':');
186     newdrive[2] = wxT('\0');
187 #if defined(__WINDOWS__)
188     if (::SetCurrentDirectory(newdrive))
189 #else
190     // VA doesn't know what LPSTR is and has its own set
191     if (!DosSetCurrentDir((PSZ)newdrive))
192 #endif
193         return 0;
194     else
195         return -1;
196 #endif // !GNUWIN32
197 }
198 
wxIsDriveAvailable(const wxString & dirName)199 bool wxIsDriveAvailable(const wxString& dirName)
200 {
201 #ifdef __WIN32__
202     UINT errorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
203 #endif
204     bool success = true;
205 
206     // Check if this is a root directory and if so,
207     // whether the drive is available.
208     if (dirName.length() == 3 && dirName[(size_t)1] == wxT(':'))
209     {
210         wxString dirNameLower(dirName.Lower());
211 #ifndef wxHAS_DRIVE_FUNCTIONS
212         success = wxDirExists(dirNameLower);
213 #else
214         int currentDrive = _getdrive();
215         int thisDrive = (int) (dirNameLower[(size_t)0] - 'a' + 1) ;
216         int err = setdrive( thisDrive ) ;
217         setdrive( currentDrive );
218 
219         if (err == -1)
220         {
221             success = false;
222         }
223 #endif
224     }
225 #ifdef __WIN32__
226     (void) SetErrorMode(errorMode);
227 #endif
228 
229     return success;
230 }
231 #endif // __WINDOWS__
232 
233 #endif // wxUSE_DIRDLG || wxUSE_FILEDLG
234 
235 
236 
237 #if wxUSE_DIRDLG
238 
239 // Function which is called by quick sort. We want to override the default wxArrayString behaviour,
240 // and sort regardless of case.
wxDirCtrlStringCompareFunction(const wxString & strFirst,const wxString & strSecond)241 static int wxCMPFUNC_CONV wxDirCtrlStringCompareFunction(const wxString& strFirst, const wxString& strSecond)
242 {
243     return strFirst.CmpNoCase(strSecond);
244 }
245 
246 //-----------------------------------------------------------------------------
247 // wxDirItemData
248 //-----------------------------------------------------------------------------
249 
wxDirItemData(const wxString & path,const wxString & name,bool isDir)250 wxDirItemData::wxDirItemData(const wxString& path, const wxString& name,
251                              bool isDir)
252     : m_path(path)
253     , m_name(name)
254 {
255     /* Insert logic to detect hidden files here
256      * In UnixLand we just check whether the first char is a dot
257      * For FileNameFromPath read LastDirNameInThisPath ;-) */
258     // m_isHidden = (bool)(wxFileNameFromPath(*m_path)[0] == '.');
259     m_isHidden = false;
260     m_isExpanded = false;
261     m_isDir = isDir;
262 }
263 
SetNewDirName(const wxString & path)264 void wxDirItemData::SetNewDirName(const wxString& path)
265 {
266     m_path = path;
267     m_name = wxFileNameFromPath(path);
268 }
269 
HasSubDirs() const270 bool wxDirItemData::HasSubDirs() const
271 {
272     if (m_path.empty())
273         return false;
274 
275     wxDir dir;
276     {
277         wxLogNull nolog;
278         if ( !dir.Open(m_path) )
279             return false;
280     }
281 
282     return dir.HasSubDirs();
283 }
284 
HasFiles(const wxString & WXUNUSED (spec)) const285 bool wxDirItemData::HasFiles(const wxString& WXUNUSED(spec)) const
286 {
287     if (m_path.empty())
288         return false;
289 
290     wxDir dir;
291     {
292         wxLogNull nolog;
293         if ( !dir.Open(m_path) )
294             return false;
295     }
296 
297     return dir.HasFiles();
298 }
299 
300 //-----------------------------------------------------------------------------
301 // wxGenericDirCtrl
302 //-----------------------------------------------------------------------------
303 
wxBEGIN_EVENT_TABLE(wxGenericDirCtrl,wxControl)304 wxBEGIN_EVENT_TABLE(wxGenericDirCtrl, wxControl)
305   EVT_TREE_ITEM_EXPANDING     (wxID_TREECTRL, wxGenericDirCtrl::OnExpandItem)
306   EVT_TREE_ITEM_COLLAPSED     (wxID_TREECTRL, wxGenericDirCtrl::OnCollapseItem)
307   EVT_TREE_BEGIN_LABEL_EDIT   (wxID_TREECTRL, wxGenericDirCtrl::OnBeginEditItem)
308   EVT_TREE_END_LABEL_EDIT     (wxID_TREECTRL, wxGenericDirCtrl::OnEndEditItem)
309   EVT_TREE_SEL_CHANGED        (wxID_TREECTRL, wxGenericDirCtrl::OnTreeSelChange)
310   EVT_TREE_ITEM_ACTIVATED     (wxID_TREECTRL, wxGenericDirCtrl::OnItemActivated)
311   EVT_SIZE                    (wxGenericDirCtrl::OnSize)
312 wxEND_EVENT_TABLE()
313 
314 wxGenericDirCtrl::wxGenericDirCtrl(void)
315 {
316     Init();
317 }
318 
ExpandRoot()319 void wxGenericDirCtrl::ExpandRoot()
320 {
321     ExpandDir(m_rootId); // automatically expand first level
322 
323     // Expand and select the default path
324     if (!m_defaultPath.empty())
325     {
326         ExpandPath(m_defaultPath);
327     }
328 #ifdef __UNIX__
329     else
330     {
331         // On Unix, there's only one node under the (hidden) root node. It
332         // represents the / path, so the user would always have to expand it;
333         // let's do it ourselves
334         ExpandPath( wxT("/") );
335     }
336 #endif
337 }
338 
Create(wxWindow * parent,wxWindowID treeid,const wxString & dir,const wxPoint & pos,const wxSize & size,long style,const wxString & filter,int defaultFilter,const wxString & name)339 bool wxGenericDirCtrl::Create(wxWindow *parent,
340                               wxWindowID treeid,
341                               const wxString& dir,
342                               const wxPoint& pos,
343                               const wxSize& size,
344                               long style,
345                               const wxString& filter,
346                               int defaultFilter,
347                               const wxString& name)
348 {
349     if (!wxControl::Create(parent, treeid, pos, size, style, wxDefaultValidator, name))
350         return false;
351 
352     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
353     SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
354 
355     long treeStyle = wxTR_HAS_BUTTONS;
356 
357     treeStyle |= wxTR_HIDE_ROOT;
358 
359 #ifdef __WXGTK20__
360     treeStyle |= wxTR_NO_LINES;
361 #endif
362 
363     if (style & wxDIRCTRL_EDIT_LABELS)
364         treeStyle |= wxTR_EDIT_LABELS;
365 
366     if (style & wxDIRCTRL_MULTIPLE)
367         treeStyle |= wxTR_MULTIPLE;
368 
369     if ((style & wxDIRCTRL_3D_INTERNAL) == 0)
370         treeStyle |= wxNO_BORDER;
371 
372     m_treeCtrl = CreateTreeCtrl(this, wxID_TREECTRL,
373                                 wxPoint(0,0), GetClientSize(), treeStyle);
374 
375     if (!filter.empty() && (style & wxDIRCTRL_SHOW_FILTERS))
376         m_filterListCtrl = new wxDirFilterListCtrl(this, wxID_FILTERLISTCTRL);
377 
378     m_defaultPath = dir;
379     m_filter = filter;
380 
381     if (m_filter.empty())
382         m_filter = wxFileSelectorDefaultWildcardStr;
383 
384     SetFilterIndex(defaultFilter);
385 
386     if (m_filterListCtrl)
387         m_filterListCtrl->FillFilterList(filter, defaultFilter);
388 
389     // TODO: set the icon size according to current scaling for this window.
390     // Currently, there's insufficient API in wxWidgets to determine what icons
391     // are available and whether to take the nearest size according to a tolerance
392     // instead of scaling.
393     // if (!wxTheFileIconsTable->IsOk())
394     //     wxTheFileIconsTable->SetSize(scaledSize);
395 
396     // Meanwhile, in your application initialisation, where you have better knowledge of what
397     // icons are available and whether to scale, you can do this:
398     //
399     // wxTheFileIconsTable->SetSize(calculatedIconSizeForDPI);
400     //
401     // Obviously this can't take into account monitors with different DPI.
402     m_treeCtrl->SetImageList(wxTheFileIconsTable->GetSmallImageList());
403 
404     m_showHidden = false;
405     wxDirItemData* rootData = new wxDirItemData(wxEmptyString, wxEmptyString, true);
406 
407     wxString rootName;
408 
409 #if defined(__WINDOWS__)
410     rootName = _("Computer");
411 #else
412     rootName = _("Sections");
413 #endif
414 
415     m_rootId = m_treeCtrl->AddRoot( rootName, 3, -1, rootData);
416     m_treeCtrl->SetItemHasChildren(m_rootId);
417 
418     ExpandRoot();
419 
420     SetInitialSize(size);
421     DoResize();
422 
423     return true;
424 }
425 
~wxGenericDirCtrl()426 wxGenericDirCtrl::~wxGenericDirCtrl()
427 {
428 }
429 
Init()430 void wxGenericDirCtrl::Init()
431 {
432     m_showHidden = false;
433     m_currentFilter = 0;
434     m_currentFilterStr.clear(); // Default: any file
435     m_treeCtrl = NULL;
436     m_filterListCtrl = NULL;
437 }
438 
CreateTreeCtrl(wxWindow * parent,wxWindowID treeid,const wxPoint & pos,const wxSize & size,long treeStyle)439 wxTreeCtrl* wxGenericDirCtrl::CreateTreeCtrl(wxWindow *parent, wxWindowID treeid, const wxPoint& pos, const wxSize& size, long treeStyle)
440 {
441     return new wxTreeCtrl(parent, treeid, pos, size, treeStyle);
442 }
443 
ShowHidden(bool show)444 void wxGenericDirCtrl::ShowHidden( bool show )
445 {
446     if ( m_showHidden == show )
447         return;
448 
449     m_showHidden = show;
450 
451     if ( HasFlag(wxDIRCTRL_MULTIPLE) )
452     {
453         wxArrayString paths;
454         GetPaths(paths);
455         ReCreateTree();
456         for ( unsigned n = 0; n < paths.size(); n++ )
457         {
458             ExpandPath(paths[n]);
459         }
460     }
461     else
462     {
463         wxString path = GetPath();
464         ReCreateTree();
465         SetPath(path);
466     }
467 }
468 
469 const wxTreeItemId
AddSection(const wxString & path,const wxString & name,int imageId)470 wxGenericDirCtrl::AddSection(const wxString& path, const wxString& name, int imageId)
471 {
472     wxDirItemData *dir_item = new wxDirItemData(path,name,true);
473 
474     wxTreeItemId treeid = AppendItem( m_rootId, name, imageId, -1, dir_item);
475 
476     m_treeCtrl->SetItemHasChildren(treeid);
477 
478     return treeid;
479 }
480 
SetupSections()481 void wxGenericDirCtrl::SetupSections()
482 {
483     wxArrayString paths, names;
484     wxArrayInt icons;
485 
486     size_t n, count = wxGetAvailableDrives(paths, names, icons);
487 
488 #ifdef __WXGTK20__
489     wxString home = wxGetHomeDir();
490     AddSection( home, _("Home directory"), 1);
491     home += wxT("/Desktop");
492     AddSection( home, _("Desktop"), 1);
493 #endif
494 
495     for (n = 0; n < count; n++)
496         AddSection(paths[n], names[n], icons[n]);
497 }
498 
SetFocus()499 void wxGenericDirCtrl::SetFocus()
500 {
501     // we don't need focus ourselves, give it to the tree so that the user
502     // could navigate it
503     if (m_treeCtrl)
504         m_treeCtrl->SetFocus();
505 }
506 
OnBeginEditItem(wxTreeEvent & event)507 void wxGenericDirCtrl::OnBeginEditItem(wxTreeEvent &event)
508 {
509     // don't rename the main entry "Sections"
510     if (event.GetItem() == m_rootId)
511     {
512         event.Veto();
513         return;
514     }
515 
516     // don't rename the individual sections
517     if (m_treeCtrl->GetItemParent( event.GetItem() ) == m_rootId)
518     {
519         event.Veto();
520         return;
521     }
522 }
523 
OnEndEditItem(wxTreeEvent & event)524 void wxGenericDirCtrl::OnEndEditItem(wxTreeEvent &event)
525 {
526     if (event.IsEditCancelled())
527         return;
528 
529     if ((event.GetLabel().empty()) ||
530         (event.GetLabel() == wxT(".")) ||
531         (event.GetLabel() == wxT("..")) ||
532         (event.GetLabel().Find(wxT('/')) != wxNOT_FOUND) ||
533         (event.GetLabel().Find(wxT('\\')) != wxNOT_FOUND) ||
534         (event.GetLabel().Find(wxT('|')) != wxNOT_FOUND))
535     {
536         wxMessageDialog dialog(this, _("Illegal directory name."), _("Error"), wxOK | wxICON_ERROR );
537         dialog.ShowModal();
538         event.Veto();
539         return;
540     }
541 
542     wxTreeItemId treeid = event.GetItem();
543     wxDirItemData *data = GetItemData( treeid );
544     wxASSERT( data );
545 
546     wxString new_name( wxPathOnly( data->m_path ) );
547     new_name += wxString(wxFILE_SEP_PATH);
548     new_name += event.GetLabel();
549 
550     wxLogNull log;
551 
552     if (wxFileExists(new_name))
553     {
554         wxMessageDialog dialog(this, _("File name exists already."), _("Error"), wxOK | wxICON_ERROR );
555         dialog.ShowModal();
556         event.Veto();
557     }
558 
559     if (wxRenameFile(data->m_path,new_name))
560     {
561         data->SetNewDirName( new_name );
562     }
563     else
564     {
565         wxMessageDialog dialog(this, _("Operation not permitted."), _("Error"), wxOK | wxICON_ERROR );
566         dialog.ShowModal();
567         event.Veto();
568     }
569 }
570 
OnTreeSelChange(wxTreeEvent & event)571 void wxGenericDirCtrl::OnTreeSelChange(wxTreeEvent &event)
572 {
573     wxTreeEvent changedEvent(wxEVT_DIRCTRL_SELECTIONCHANGED, GetId());
574 
575     changedEvent.SetEventObject(this);
576     changedEvent.SetItem(event.GetItem());
577     changedEvent.SetClientObject(m_treeCtrl->GetItemData(event.GetItem()));
578 
579     if (GetEventHandler()->SafelyProcessEvent(changedEvent) && !changedEvent.IsAllowed())
580         event.Veto();
581     else
582         event.Skip();
583 }
584 
OnItemActivated(wxTreeEvent & event)585 void wxGenericDirCtrl::OnItemActivated(wxTreeEvent &event)
586 {
587     wxTreeItemId treeid = event.GetItem();
588     const wxDirItemData *data = GetItemData(treeid);
589 
590     if (data->m_isDir)
591     {
592         // is dir
593         event.Skip();
594     }
595     else
596     {
597         // is file
598         wxTreeEvent changedEvent(wxEVT_DIRCTRL_FILEACTIVATED, GetId());
599 
600         changedEvent.SetEventObject(this);
601         changedEvent.SetItem(treeid);
602         changedEvent.SetClientObject(m_treeCtrl->GetItemData(treeid));
603 
604         if (GetEventHandler()->SafelyProcessEvent(changedEvent) && !changedEvent.IsAllowed())
605             event.Veto();
606         else
607             event.Skip();
608     }
609 }
610 
OnExpandItem(wxTreeEvent & event)611 void wxGenericDirCtrl::OnExpandItem(wxTreeEvent &event)
612 {
613     wxTreeItemId parentId = event.GetItem();
614 
615     // VS: this is needed because the event handler is called from wxTreeCtrl
616     //     ctor when wxTR_HIDE_ROOT was specified
617 
618     if (!m_rootId.IsOk())
619         m_rootId = m_treeCtrl->GetRootItem();
620 
621     ExpandDir(parentId);
622 }
623 
OnCollapseItem(wxTreeEvent & event)624 void wxGenericDirCtrl::OnCollapseItem(wxTreeEvent &event )
625 {
626     CollapseDir(event.GetItem());
627 }
628 
CollapseDir(wxTreeItemId parentId)629 void wxGenericDirCtrl::CollapseDir(wxTreeItemId parentId)
630 {
631     wxTreeItemId child;
632 
633     wxDirItemData *data = GetItemData(parentId);
634     if (!data->m_isExpanded)
635         return;
636 
637     data->m_isExpanded = false;
638 
639     m_treeCtrl->Freeze();
640     if (parentId != m_treeCtrl->GetRootItem())
641         m_treeCtrl->CollapseAndReset(parentId);
642     m_treeCtrl->DeleteChildren(parentId);
643     m_treeCtrl->Thaw();
644 }
645 
PopulateNode(wxTreeItemId parentId)646 void wxGenericDirCtrl::PopulateNode(wxTreeItemId parentId)
647 {
648     wxDirItemData *data = GetItemData(parentId);
649 
650     if (data->m_isExpanded)
651         return;
652 
653     data->m_isExpanded = true;
654 
655     if (parentId == m_treeCtrl->GetRootItem())
656     {
657         SetupSections();
658         return;
659     }
660 
661     wxASSERT(data);
662 
663     wxString path;
664 
665     wxString dirName(data->m_path);
666 
667 #if defined(__WINDOWS__)
668     // Check if this is a root directory and if so,
669     // whether the drive is available.
670     if (!wxIsDriveAvailable(dirName))
671     {
672         data->m_isExpanded = false;
673         //wxMessageBox(wxT("Sorry, this drive is not available."));
674         return;
675     }
676 #endif
677 
678     // This may take a longish time. Go to busy cursor
679     wxBusyCursor busy;
680 
681 #if defined(__WINDOWS__)
682     if (dirName.Last() == ':')
683         dirName += wxString(wxFILE_SEP_PATH);
684 #endif
685 
686     wxArrayString dirs;
687     wxArrayString filenames;
688 
689     wxDir d;
690     wxString eachFilename;
691 
692     wxLogNull log;
693     d.Open(dirName);
694 
695     if (d.IsOpened())
696     {
697         int style = wxDIR_DIRS;
698         if (m_showHidden) style |= wxDIR_HIDDEN;
699         if (d.GetFirst(& eachFilename, wxEmptyString, style))
700         {
701             do
702             {
703                 if ((eachFilename != wxT(".")) && (eachFilename != wxT("..")))
704                 {
705                     dirs.Add(eachFilename);
706                 }
707             }
708             while (d.GetNext(&eachFilename));
709         }
710     }
711     dirs.Sort(wxDirCtrlStringCompareFunction);
712 
713     // Now do the filenames -- but only if we're allowed to
714     if (!HasFlag(wxDIRCTRL_DIR_ONLY))
715     {
716         d.Open(dirName);
717 
718         if (d.IsOpened())
719         {
720             int style = wxDIR_FILES;
721             if (m_showHidden) style |= wxDIR_HIDDEN;
722             // Process each filter (ex: "JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg")
723             wxStringTokenizer strTok;
724             wxString curFilter;
725             strTok.SetString(m_currentFilterStr,wxT(";"));
726             while(strTok.HasMoreTokens())
727             {
728                 curFilter = strTok.GetNextToken();
729                 if (d.GetFirst(& eachFilename, curFilter, style))
730                 {
731                     do
732                     {
733                         if ((eachFilename != wxT(".")) && (eachFilename != wxT("..")))
734                         {
735                             filenames.Add(eachFilename);
736                         }
737                     }
738                     while (d.GetNext(& eachFilename));
739                 }
740             }
741         }
742         filenames.Sort(wxDirCtrlStringCompareFunction);
743     }
744 
745     // Now we really know whether we have any children so tell the tree control
746     // about it.
747     m_treeCtrl->SetItemHasChildren(parentId, !dirs.empty() || !filenames.empty());
748 
749     // Add the sorted dirs
750     size_t i;
751     for (i = 0; i < dirs.GetCount(); i++)
752     {
753         eachFilename = dirs[i];
754         path = dirName;
755         if (!wxEndsWithPathSeparator(path))
756             path += wxString(wxFILE_SEP_PATH);
757         path += eachFilename;
758 
759         wxDirItemData *dir_item = new wxDirItemData(path,eachFilename,true);
760         wxTreeItemId treeid = AppendItem( parentId, eachFilename,
761                                       wxFileIconsTable::folder, -1, dir_item);
762         m_treeCtrl->SetItemImage( treeid, wxFileIconsTable::folder_open,
763                                   wxTreeItemIcon_Expanded );
764 
765         // assume that it does have children by default as it can take a long
766         // time to really check for this (think remote drives...)
767         //
768         // and if we're wrong, we'll correct the icon later if
769         // the user really tries to open this item
770         m_treeCtrl->SetItemHasChildren(treeid);
771     }
772 
773     // Add the sorted filenames
774     if (!HasFlag(wxDIRCTRL_DIR_ONLY))
775     {
776         for (i = 0; i < filenames.GetCount(); i++)
777         {
778             eachFilename = filenames[i];
779             path = dirName;
780             if (!wxEndsWithPathSeparator(path))
781                 path += wxString(wxFILE_SEP_PATH);
782             path += eachFilename;
783             //path = dirName + wxString(wxT("/")) + eachFilename;
784             wxDirItemData *dir_item = new wxDirItemData(path,eachFilename,false);
785             int image_id = wxFileIconsTable::file;
786             if (eachFilename.Find(wxT('.')) != wxNOT_FOUND)
787                 image_id = wxTheFileIconsTable->GetIconID(eachFilename.AfterLast(wxT('.')));
788             (void) AppendItem( parentId, eachFilename, image_id, -1, dir_item);
789         }
790     }
791 }
792 
ExpandDir(wxTreeItemId parentId)793 void wxGenericDirCtrl::ExpandDir(wxTreeItemId parentId)
794 {
795     // ExpandDir() will not actually expand the tree node, just populate it
796     PopulateNode(parentId);
797 }
798 
ReCreateTree()799 void wxGenericDirCtrl::ReCreateTree()
800 {
801     CollapseDir(m_treeCtrl->GetRootItem());
802     ExpandRoot();
803 }
804 
CollapseTree()805 void wxGenericDirCtrl::CollapseTree()
806 {
807     wxTreeItemIdValue cookie;
808     wxTreeItemId child = m_treeCtrl->GetFirstChild(m_rootId, cookie);
809     while (child.IsOk())
810     {
811         CollapseDir(child);
812         child = m_treeCtrl->GetNextChild(m_rootId, cookie);
813     }
814 }
815 
816 // Find the child that matches the first part of 'path'.
817 // E.g. if a child path is "/usr" and 'path' is "/usr/include"
818 // then the child for /usr is returned.
FindChild(wxTreeItemId parentId,const wxString & path,bool & done)819 wxTreeItemId wxGenericDirCtrl::FindChild(wxTreeItemId parentId, const wxString& path, bool& done)
820 {
821     wxString path2(path);
822 
823     // Make sure all separators are as per the current platform
824     path2.Replace(wxT("\\"), wxString(wxFILE_SEP_PATH));
825     path2.Replace(wxT("/"), wxString(wxFILE_SEP_PATH));
826 
827     // Append a separator to foil bogus substring matching
828     path2 += wxString(wxFILE_SEP_PATH);
829 
830     // In MSW case is not significant
831 #if defined(__WINDOWS__)
832     path2.MakeLower();
833 #endif
834 
835     wxTreeItemIdValue cookie;
836     wxTreeItemId childId = m_treeCtrl->GetFirstChild(parentId, cookie);
837     while (childId.IsOk())
838     {
839         wxDirItemData* data = GetItemData(childId);
840 
841         if (data && !data->m_path.empty())
842         {
843             wxString childPath(data->m_path);
844             if (!wxEndsWithPathSeparator(childPath))
845                 childPath += wxString(wxFILE_SEP_PATH);
846 
847             // In MSW case is not significant
848 #if defined(__WINDOWS__)
849             childPath.MakeLower();
850 #endif
851 
852             if (childPath.length() <= path2.length())
853             {
854                 wxString path3 = path2.Mid(0, childPath.length());
855                 if (childPath == path3)
856                 {
857                     if (path3.length() == path2.length())
858                         done = true;
859                     else
860                         done = false;
861                     return childId;
862                 }
863             }
864         }
865 
866         childId = m_treeCtrl->GetNextChild(parentId, cookie);
867     }
868     wxTreeItemId invalid;
869     return invalid;
870 }
871 
872 // Try to expand as much of the given path as possible,
873 // and select the given tree item.
ExpandPath(const wxString & path)874 bool wxGenericDirCtrl::ExpandPath(const wxString& path)
875 {
876     bool done = false;
877     wxTreeItemId treeid = FindChild(m_rootId, path, done);
878     wxTreeItemId lastId = treeid; // The last non-zero treeid
879     while (treeid.IsOk() && !done)
880     {
881         ExpandDir(treeid);
882 
883         treeid = FindChild(treeid, path, done);
884         if (treeid.IsOk())
885             lastId = treeid;
886     }
887     if (!lastId.IsOk())
888         return false;
889 
890     wxDirItemData *data = GetItemData(lastId);
891     if (data->m_isDir)
892     {
893         m_treeCtrl->Expand(lastId);
894     }
895     if (HasFlag(wxDIRCTRL_SELECT_FIRST) && data->m_isDir)
896     {
897         // Find the first file in this directory
898         wxTreeItemIdValue cookie;
899         wxTreeItemId childId = m_treeCtrl->GetFirstChild(lastId, cookie);
900         bool selectedChild = false;
901         while (childId.IsOk())
902         {
903             data = GetItemData(childId);
904 
905             if (data && !data->m_path.empty() && !data->m_isDir)
906             {
907                 m_treeCtrl->SelectItem(childId);
908                 m_treeCtrl->EnsureVisible(childId);
909                 selectedChild = true;
910                 break;
911             }
912             childId = m_treeCtrl->GetNextChild(lastId, cookie);
913         }
914         if (!selectedChild)
915         {
916             m_treeCtrl->SelectItem(lastId);
917             m_treeCtrl->EnsureVisible(lastId);
918         }
919     }
920     else
921     {
922         m_treeCtrl->SelectItem(lastId);
923         m_treeCtrl->EnsureVisible(lastId);
924     }
925 
926     return true;
927 }
928 
929 
CollapsePath(const wxString & path)930 bool wxGenericDirCtrl::CollapsePath(const wxString& path)
931 {
932     bool done           = false;
933     wxTreeItemId treeid     = FindChild(m_rootId, path, done);
934     wxTreeItemId lastId = treeid; // The last non-zero treeid
935 
936     while ( treeid.IsOk() && !done )
937     {
938         CollapseDir(treeid);
939 
940         treeid = FindChild(treeid, path, done);
941 
942         if ( treeid.IsOk() )
943             lastId = treeid;
944     }
945 
946     if ( !lastId.IsOk() )
947         return false;
948 
949     m_treeCtrl->SelectItem(lastId);
950     m_treeCtrl->EnsureVisible(lastId);
951 
952     return true;
953 }
954 
GetItemData(wxTreeItemId itemId)955 wxDirItemData* wxGenericDirCtrl::GetItemData(wxTreeItemId itemId)
956 {
957     return static_cast<wxDirItemData*>(m_treeCtrl->GetItemData(itemId));
958 }
959 
GetPath(wxTreeItemId itemId) const960 wxString wxGenericDirCtrl::GetPath(wxTreeItemId itemId) const
961 {
962     const wxDirItemData*
963         data = static_cast<wxDirItemData*>(m_treeCtrl->GetItemData(itemId));
964 
965     return data ? data->m_path : wxString();
966 }
967 
GetPath() const968 wxString wxGenericDirCtrl::GetPath() const
969 {
970     // Allow calling GetPath() in multiple selection from OnSelFilter
971     if (m_treeCtrl->HasFlag(wxTR_MULTIPLE))
972     {
973         wxArrayTreeItemIds items;
974         m_treeCtrl->GetSelections(items);
975         if (items.size() > 0)
976         {
977             // return first string only
978             wxTreeItemId treeid = items[0];
979             return GetPath(treeid);
980         }
981 
982         return wxEmptyString;
983     }
984 
985     wxTreeItemId treeid = m_treeCtrl->GetSelection();
986     if (treeid)
987     {
988         return GetPath(treeid);
989     }
990     else
991         return wxEmptyString;
992 }
993 
GetPaths(wxArrayString & paths) const994 void wxGenericDirCtrl::GetPaths(wxArrayString& paths) const
995 {
996     paths.clear();
997 
998     wxArrayTreeItemIds items;
999     m_treeCtrl->GetSelections(items);
1000     for ( unsigned n = 0; n < items.size(); n++ )
1001     {
1002         wxTreeItemId treeid = items[n];
1003         paths.push_back(GetPath(treeid));
1004     }
1005 }
1006 
GetFilePath() const1007 wxString wxGenericDirCtrl::GetFilePath() const
1008 {
1009     wxTreeItemId treeid = m_treeCtrl->GetSelection();
1010     if (treeid)
1011     {
1012         wxDirItemData* data = (wxDirItemData*) m_treeCtrl->GetItemData(treeid);
1013         if (data->m_isDir)
1014             return wxEmptyString;
1015         else
1016             return data->m_path;
1017     }
1018     else
1019         return wxEmptyString;
1020 }
1021 
GetFilePaths(wxArrayString & paths) const1022 void wxGenericDirCtrl::GetFilePaths(wxArrayString& paths) const
1023 {
1024     paths.clear();
1025 
1026     wxArrayTreeItemIds items;
1027     m_treeCtrl->GetSelections(items);
1028     for ( unsigned n = 0; n < items.size(); n++ )
1029     {
1030         wxTreeItemId treeid = items[n];
1031         wxDirItemData* data = (wxDirItemData*) m_treeCtrl->GetItemData(treeid);
1032         if ( !data->m_isDir )
1033             paths.Add(data->m_path);
1034     }
1035 }
1036 
SetPath(const wxString & path)1037 void wxGenericDirCtrl::SetPath(const wxString& path)
1038 {
1039     m_defaultPath = path;
1040     if (m_rootId)
1041         ExpandPath(path);
1042 }
1043 
SelectPath(const wxString & path,bool select)1044 void wxGenericDirCtrl::SelectPath(const wxString& path, bool select)
1045 {
1046     bool done = false;
1047     wxTreeItemId treeid = FindChild(m_rootId, path, done);
1048     wxTreeItemId lastId = treeid; // The last non-zero treeid
1049     while ( treeid.IsOk() && !done )
1050     {
1051         treeid = FindChild(treeid, path, done);
1052         if ( treeid.IsOk() )
1053             lastId = treeid;
1054     }
1055     if ( !lastId.IsOk() )
1056         return;
1057 
1058     if ( done )
1059     {
1060         m_treeCtrl->SelectItem(treeid, select);
1061     }
1062 }
1063 
SelectPaths(const wxArrayString & paths)1064 void wxGenericDirCtrl::SelectPaths(const wxArrayString& paths)
1065 {
1066     if ( HasFlag(wxDIRCTRL_MULTIPLE) )
1067     {
1068         UnselectAll();
1069         for ( unsigned n = 0; n < paths.size(); n++ )
1070         {
1071             SelectPath(paths[n]);
1072         }
1073     }
1074 }
1075 
UnselectAll()1076 void wxGenericDirCtrl::UnselectAll()
1077 {
1078     m_treeCtrl->UnselectAll();
1079 }
1080 
1081 // Not used
1082 #if 0
1083 void wxGenericDirCtrl::FindChildFiles(wxTreeItemId treeid, int dirFlags, wxArrayString& filenames)
1084 {
1085     wxDirItemData *data = (wxDirItemData *) m_treeCtrl->GetItemData(treeid);
1086 
1087     // This may take a longish time. Go to busy cursor
1088     wxBusyCursor busy;
1089 
1090     wxASSERT(data);
1091 
1092     wxString search,path,filename;
1093 
1094     wxString dirName(data->m_path);
1095 
1096 #if defined(__WINDOWS__)
1097     if (dirName.Last() == ':')
1098         dirName += wxString(wxFILE_SEP_PATH);
1099 #endif
1100 
1101     wxDir d;
1102     wxString eachFilename;
1103 
1104     wxLogNull log;
1105     d.Open(dirName);
1106 
1107     if (d.IsOpened())
1108     {
1109         if (d.GetFirst(& eachFilename, m_currentFilterStr, dirFlags))
1110         {
1111             do
1112             {
1113                 if ((eachFilename != wxT(".")) && (eachFilename != wxT("..")))
1114                 {
1115                     filenames.Add(eachFilename);
1116                 }
1117             }
1118             while (d.GetNext(& eachFilename)) ;
1119         }
1120     }
1121 }
1122 #endif
1123 
SetFilterIndex(int n)1124 void wxGenericDirCtrl::SetFilterIndex(int n)
1125 {
1126     m_currentFilter = n;
1127 
1128     wxString f, d;
1129     if (ExtractWildcard(m_filter, n, f, d))
1130         m_currentFilterStr = f;
1131     else
1132 #ifdef __UNIX__
1133         m_currentFilterStr = wxT("*");
1134 #else
1135         m_currentFilterStr = wxT("*.*");
1136 #endif
1137 }
1138 
SetFilter(const wxString & filter)1139 void wxGenericDirCtrl::SetFilter(const wxString& filter)
1140 {
1141     m_filter = filter;
1142 
1143     if (!filter.empty() && !m_filterListCtrl && HasFlag(wxDIRCTRL_SHOW_FILTERS))
1144         m_filterListCtrl = new wxDirFilterListCtrl(this, wxID_FILTERLISTCTRL);
1145     else if (filter.empty() && m_filterListCtrl)
1146     {
1147         m_filterListCtrl->Destroy();
1148         m_filterListCtrl = NULL;
1149     }
1150 
1151     wxString f, d;
1152     if (ExtractWildcard(m_filter, m_currentFilter, f, d))
1153         m_currentFilterStr = f;
1154     else
1155 #ifdef __UNIX__
1156         m_currentFilterStr = wxT("*");
1157 #else
1158         m_currentFilterStr = wxT("*.*");
1159 #endif
1160     // current filter index is meaningless after filter change, set it to zero
1161     SetFilterIndex(0);
1162     if (m_filterListCtrl)
1163         m_filterListCtrl->FillFilterList(m_filter, 0);
1164 }
1165 
1166 // Extract description and actual filter from overall filter string
ExtractWildcard(const wxString & filterStr,int n,wxString & filter,wxString & description)1167 bool wxGenericDirCtrl::ExtractWildcard(const wxString& filterStr, int n, wxString& filter, wxString& description)
1168 {
1169     wxArrayString filters, descriptions;
1170     int count = wxParseCommonDialogsFilter(filterStr, descriptions, filters);
1171     if (count > 0 && n < count)
1172     {
1173         filter = filters[n];
1174         description = descriptions[n];
1175         return true;
1176     }
1177 
1178     return false;
1179 }
1180 
1181 
DoResize()1182 void wxGenericDirCtrl::DoResize()
1183 {
1184     wxSize sz = GetClientSize();
1185     int verticalSpacing = 3;
1186     if (m_treeCtrl)
1187     {
1188         wxSize filterSz ;
1189         if (m_filterListCtrl)
1190         {
1191             filterSz = m_filterListCtrl->GetBestSize();
1192             sz.y -= (filterSz.y + verticalSpacing);
1193         }
1194         m_treeCtrl->SetSize(0, 0, sz.x, sz.y);
1195         if (m_filterListCtrl)
1196         {
1197             m_filterListCtrl->SetSize(0, sz.y + verticalSpacing, sz.x, filterSz.y);
1198             // Don't know why, but this needs refreshing after a resize (wxMSW)
1199             m_filterListCtrl->Refresh();
1200         }
1201     }
1202 }
1203 
1204 
OnSize(wxSizeEvent & WXUNUSED (event))1205 void wxGenericDirCtrl::OnSize(wxSizeEvent& WXUNUSED(event))
1206 {
1207     DoResize();
1208 }
1209 
AppendItem(const wxTreeItemId & parent,const wxString & text,int image,int selectedImage,wxTreeItemData * data)1210 wxTreeItemId wxGenericDirCtrl::AppendItem (const wxTreeItemId & parent,
1211                                            const wxString & text,
1212                                            int image, int selectedImage,
1213                                            wxTreeItemData * data)
1214 {
1215   wxTreeCtrl *treeCtrl = GetTreeCtrl ();
1216 
1217   wxASSERT (treeCtrl);
1218 
1219   if (treeCtrl)
1220   {
1221     return treeCtrl->AppendItem (parent, text, image, selectedImage, data);
1222   }
1223   else
1224   {
1225     return wxTreeItemId();
1226   }
1227 }
1228 
1229 
1230 //-----------------------------------------------------------------------------
1231 // wxDirFilterListCtrl
1232 //-----------------------------------------------------------------------------
1233 
1234 wxIMPLEMENT_CLASS(wxDirFilterListCtrl, wxChoice);
1235 
wxBEGIN_EVENT_TABLE(wxDirFilterListCtrl,wxChoice)1236 wxBEGIN_EVENT_TABLE(wxDirFilterListCtrl, wxChoice)
1237     EVT_CHOICE(wxID_ANY, wxDirFilterListCtrl::OnSelFilter)
1238 wxEND_EVENT_TABLE()
1239 
1240 bool wxDirFilterListCtrl::Create(wxGenericDirCtrl* parent,
1241                                  wxWindowID treeid,
1242                                  const wxPoint& pos,
1243                                  const wxSize& size,
1244                                  long style)
1245 {
1246     m_dirCtrl = parent;
1247     return wxChoice::Create(parent, treeid, pos, size, 0, NULL, style);
1248 }
1249 
Init()1250 void wxDirFilterListCtrl::Init()
1251 {
1252     m_dirCtrl = NULL;
1253 }
1254 
OnSelFilter(wxCommandEvent & WXUNUSED (event))1255 void wxDirFilterListCtrl::OnSelFilter(wxCommandEvent& WXUNUSED(event))
1256 {
1257     int sel = GetSelection();
1258 
1259     if (m_dirCtrl->HasFlag(wxDIRCTRL_MULTIPLE))
1260     {
1261         wxArrayString paths;
1262         m_dirCtrl->GetPaths(paths);
1263 
1264         m_dirCtrl->SetFilterIndex(sel);
1265 
1266         // If the filter has changed, the view is out of date, so
1267         // collapse the tree.
1268         m_dirCtrl->ReCreateTree();
1269 
1270         // Expand and select the previously selected paths
1271         for (unsigned int i = 0; i < paths.GetCount(); i++)
1272         {
1273             m_dirCtrl->ExpandPath(paths.Item(i));
1274         }
1275     }
1276     else
1277     {
1278         wxString currentPath = m_dirCtrl->GetPath();
1279 
1280         m_dirCtrl->SetFilterIndex(sel);
1281         m_dirCtrl->ReCreateTree();
1282 
1283         // Try to restore the selection, or at least the directory
1284         m_dirCtrl->ExpandPath(currentPath);
1285     }
1286 }
1287 
FillFilterList(const wxString & filter,int defaultFilter)1288 void wxDirFilterListCtrl::FillFilterList(const wxString& filter, int defaultFilter)
1289 {
1290     Clear();
1291     wxArrayString descriptions, filters;
1292     size_t n = (size_t) wxParseCommonDialogsFilter(filter, descriptions, filters);
1293 
1294     if (n > 0 && defaultFilter < (int) n)
1295     {
1296         for (size_t i = 0; i < n; i++)
1297             Append(descriptions[i]);
1298         SetSelection(defaultFilter);
1299     }
1300 }
1301 #endif // wxUSE_DIRDLG
1302 
1303 #if wxUSE_DIRDLG || wxUSE_FILEDLG
1304 
1305 // ----------------------------------------------------------------------------
1306 // wxFileIconsTable icons
1307 // ----------------------------------------------------------------------------
1308 
1309 #if 0
1310 #ifndef __WXGTK20__
1311 /* Computer (c) Julian Smart */
1312 static const char* const file_icons_tbl_computer_xpm[] = {
1313 /* columns rows colors chars-per-pixel */
1314 "16 16 42 1",
1315 "r c #4E7FD0",
1316 "$ c #7198D9",
1317 "; c #DCE6F6",
1318 "q c #FFFFFF",
1319 "u c #4A7CCE",
1320 "# c #779DDB",
1321 "w c #95B2E3",
1322 "y c #7FA2DD",
1323 "f c #3263B4",
1324 "= c #EAF0FA",
1325 "< c #B1C7EB",
1326 "% c #6992D7",
1327 "9 c #D9E4F5",
1328 "o c #9BB7E5",
1329 "6 c #F7F9FD",
1330 ", c #BED0EE",
1331 "3 c #F0F5FC",
1332 "1 c #A8C0E8",
1333 "  c None",
1334 "0 c #FDFEFF",
1335 "4 c #C4D5F0",
1336 "@ c #81A4DD",
1337 "e c #4377CD",
1338 "- c #E2EAF8",
1339 "i c #9FB9E5",
1340 "> c #CCDAF2",
1341 "+ c #89A9DF",
1342 "s c #5584D1",
1343 "t c #5D89D3",
1344 ": c #D2DFF4",
1345 "5 c #FAFCFE",
1346 "2 c #F5F8FD",
1347 "8 c #DFE8F7",
1348 "& c #5E8AD4",
1349 "X c #638ED5",
1350 "a c #CEDCF2",
1351 "p c #90AFE2",
1352 "d c #2F5DA9",
1353 "* c #5282D0",
1354 "7 c #E5EDF9",
1355 ". c #A2BCE6",
1356 "O c #8CACE0",
1357 /* pixels */
1358 "                ",
1359 "  .XXXXXXXXXXX  ",
1360 "  oXO++@#$%&*X  ",
1361 "  oX=-;:>,<1%X  ",
1362 "  oX23=-;:4,$X  ",
1363 "  oX5633789:@X  ",
1364 "  oX05623=78+X  ",
1365 "  oXqq05623=OX  ",
1366 "  oX,,,,,<<<$X  ",
1367 "  wXXXXXXXXXXe  ",
1368 "  XrtX%$$y@+O,, ",
1369 "  uyiiiiiiiii@< ",
1370 " ouiiiiiiiiiip<a",
1371 " rustX%$$y@+Ow,,",
1372 " dfffffffffffffd",
1373 "                "
1374 };
1375 #endif // !GTK+ 2
1376 #endif
1377 
1378 // ----------------------------------------------------------------------------
1379 // wxFileIconsTable & friends
1380 // ----------------------------------------------------------------------------
1381 
1382 // global instance of a wxFileIconsTable
1383 wxFileIconsTable* wxTheFileIconsTable = NULL;
1384 
1385 // A module to allow icons table cleanup
1386 
1387 class wxFileIconsTableModule: public wxModule
1388 {
1389     wxDECLARE_DYNAMIC_CLASS(wxFileIconsTableModule);
1390 public:
wxFileIconsTableModule()1391     wxFileIconsTableModule() {}
OnInit()1392     bool OnInit() wxOVERRIDE { wxTheFileIconsTable = new wxFileIconsTable; return true; }
OnExit()1393     void OnExit() wxOVERRIDE
1394     {
1395         wxDELETE(wxTheFileIconsTable);
1396     }
1397 };
1398 
1399 wxIMPLEMENT_DYNAMIC_CLASS(wxFileIconsTableModule, wxModule);
1400 
1401 class wxFileIconEntry : public wxObject
1402 {
1403 public:
wxFileIconEntry(int i)1404     wxFileIconEntry(int i) { iconid = i; }
1405 
1406     int iconid;
1407 };
1408 
wxFileIconsTable()1409 wxFileIconsTable::wxFileIconsTable()
1410     : m_size(16, 16)
1411 {
1412     m_HashTable = NULL;
1413     m_smallImageList = NULL;
1414 }
1415 
~wxFileIconsTable()1416 wxFileIconsTable::~wxFileIconsTable()
1417 {
1418     if (m_HashTable)
1419     {
1420         WX_CLEAR_HASH_TABLE(*m_HashTable);
1421         delete m_HashTable;
1422     }
1423     delete m_smallImageList;
1424 }
1425 
1426 // delayed initialization - wait until first use (wxArtProv not created yet)
Create(const wxSize & sz)1427 void wxFileIconsTable::Create(const wxSize& sz)
1428 {
1429     wxCHECK_RET(!m_smallImageList && !m_HashTable, wxT("creating icons twice"));
1430     m_HashTable = new wxHashTable(wxKEY_STRING);
1431     m_smallImageList = new wxImageList(sz.x, sz.y);
1432 
1433     // folder:
1434     m_smallImageList->Add(wxArtProvider::GetBitmap(wxART_FOLDER,
1435                                                    wxART_CMN_DIALOG,
1436                                                    sz));
1437     // folder_open
1438     m_smallImageList->Add(wxArtProvider::GetBitmap(wxART_FOLDER_OPEN,
1439                                                    wxART_CMN_DIALOG,
1440                                                    sz));
1441     // computer
1442 #ifdef __WXGTK20__
1443     // GTK24 uses this icon in the file open dialog
1444     m_smallImageList->Add(wxArtProvider::GetBitmap(wxART_HARDDISK,
1445                                                    wxART_CMN_DIALOG,
1446                                                    sz));
1447 #else
1448     m_smallImageList->Add(wxArtProvider::GetBitmap(wxART_HARDDISK,
1449                                                    wxART_CMN_DIALOG,
1450                                                    sz));
1451     // TODO: add computer icon if really necessary
1452     //m_smallImageList->Add(wxIcon(file_icons_tbl_computer_xpm));
1453 #endif
1454     // drive
1455     m_smallImageList->Add(wxArtProvider::GetBitmap(wxART_HARDDISK,
1456                                                    wxART_CMN_DIALOG,
1457                                                    sz));
1458     // cdrom
1459     m_smallImageList->Add(wxArtProvider::GetBitmap(wxART_CDROM,
1460                                                    wxART_CMN_DIALOG,
1461                                                    sz));
1462     // floppy
1463     m_smallImageList->Add(wxArtProvider::GetBitmap(wxART_FLOPPY,
1464                                                    wxART_CMN_DIALOG,
1465                                                    sz));
1466     // removeable
1467     m_smallImageList->Add(wxArtProvider::GetBitmap(wxART_REMOVABLE,
1468                                                    wxART_CMN_DIALOG,
1469                                                    sz));
1470     // file
1471     m_smallImageList->Add(wxArtProvider::GetBitmap(wxART_NORMAL_FILE,
1472                                                    wxART_CMN_DIALOG,
1473                                                    sz));
1474     // executable
1475     if (GetIconID(wxEmptyString, wxT("application/x-executable")) == file)
1476     {
1477         m_smallImageList->Add(wxArtProvider::GetBitmap(wxART_EXECUTABLE_FILE,
1478                                                        wxART_CMN_DIALOG,
1479                                                        sz));
1480         delete m_HashTable->Get(wxT("exe"));
1481         m_HashTable->Delete(wxT("exe"));
1482         m_HashTable->Put(wxT("exe"), new wxFileIconEntry(executable));
1483     }
1484     /* else put into list by GetIconID
1485        (KDE defines application/x-executable for *.exe and has nice icon)
1486      */
1487 }
1488 
GetSmallImageList()1489 wxImageList *wxFileIconsTable::GetSmallImageList()
1490 {
1491     if (!m_smallImageList)
1492         Create(m_size);
1493 
1494     return m_smallImageList;
1495 }
1496 
GetIconID(const wxString & extension,const wxString & mime)1497 int wxFileIconsTable::GetIconID(const wxString& extension, const wxString& mime)
1498 {
1499     if (!m_smallImageList)
1500         Create(m_size);
1501 
1502 #if wxUSE_MIMETYPE
1503     if (!extension.empty())
1504     {
1505         wxFileIconEntry *entry = (wxFileIconEntry*) m_HashTable->Get(extension);
1506         if (entry) return (entry -> iconid);
1507     }
1508 
1509     wxFileType *ft = (mime.empty()) ?
1510                    wxTheMimeTypesManager -> GetFileTypeFromExtension(extension) :
1511                    wxTheMimeTypesManager -> GetFileTypeFromMimeType(mime);
1512 
1513     wxIconLocation iconLoc;
1514     wxIcon ic;
1515 
1516     {
1517         wxLogNull logNull;
1518         if ( ft && ft->GetIcon(&iconLoc) )
1519         {
1520             ic = wxIcon( iconLoc );
1521         }
1522     }
1523 
1524     delete ft;
1525 
1526     if ( !ic.IsOk() )
1527     {
1528         int newid = file;
1529         m_HashTable->Put(extension, new wxFileIconEntry(newid));
1530         return newid;
1531     }
1532 
1533     wxBitmap bmp;
1534     bmp.CopyFromIcon(ic);
1535 
1536     if ( !bmp.IsOk() )
1537     {
1538         int newid = file;
1539         m_HashTable->Put(extension, new wxFileIconEntry(newid));
1540         return newid;
1541     }
1542 
1543     int size = m_size.x;
1544 
1545     int treeid = m_smallImageList->GetImageCount();
1546     if ((bmp.GetWidth() == (int) size) && (bmp.GetHeight() == (int) size))
1547     {
1548         m_smallImageList->Add(bmp);
1549     }
1550 #if wxUSE_IMAGE && (!defined(__WINDOWS__) || wxUSE_WXDIB)
1551     else
1552     {
1553         wxImage img = bmp.ConvertToImage();
1554 
1555         if (img.HasMask())
1556             img.InitAlpha();
1557 
1558         wxBitmap bmp2;
1559         if ((img.GetWidth() != size) || (img.GetHeight() != size))
1560         {
1561             // TODO: replace with public API that gets the bitmap scale.
1562             // But this function may be called from code that doesn't pass a window,
1563             // so we will need to be able to get the scaling factor of the current
1564             // display, somehow. We could use wxTheApp->GetTopWindow() but sometimes
1565             // this won't be available.
1566 #if defined(__WXOSX_COCOA__)
1567             if (wxOSXGetMainScreenContentScaleFactor() > 1.0)
1568             {
1569                 img.Rescale(2*size, 2*size, wxIMAGE_QUALITY_HIGH);
1570                 bmp2 = wxBitmap(img, -1, 2.0);
1571             }
1572             else
1573 #endif
1574             {
1575                 // Double, using normal quality scaling.
1576                 img.Rescale(2*img.GetWidth(), 2*img.GetHeight());
1577 
1578                 // Then scale to the desired size. This gives the best quality,
1579                 // and better than CreateAntialiasedBitmap.
1580                 if ((img.GetWidth() != size) || (img.GetHeight() != size))
1581                     img.Rescale(size, size, wxIMAGE_QUALITY_HIGH);
1582 
1583                 bmp2 = wxBitmap(img);
1584             }
1585         }
1586         else
1587             bmp2 = wxBitmap(img);
1588 
1589         m_smallImageList->Add(bmp2);
1590     }
1591 #endif // wxUSE_IMAGE
1592 
1593     m_HashTable->Put(extension, new wxFileIconEntry(treeid));
1594     return treeid;
1595 
1596 #else // !wxUSE_MIMETYPE
1597 
1598     wxUnusedVar(mime);
1599     if (extension == wxT("exe"))
1600         return executable;
1601     else
1602         return file;
1603 #endif // wxUSE_MIMETYPE/!wxUSE_MIMETYPE
1604 }
1605 
1606 #endif // wxUSE_DIRDLG || wxUSE_FILEDLG
1607