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