]> git.saurik.com Git - wxWidgets.git/blob - src/generic/dirctrlg.cpp
Generic dirctrl corrections
[wxWidgets.git] / src / generic / dirctrlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dirctrlg.cpp
3 // Purpose: wxGenericDirCtrl
4 // Author: Harm van der Heijden, Robert Roebling, Julian Smart
5 // Modified by:
6 // Created: 12/12/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Harm van der Heijden, Robert Roebling and Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "dirctrlg.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #include "wx/defs.h"
24
25 #include "wx/utils.h"
26 #include "wx/dialog.h"
27 #include "wx/button.h"
28 #include "wx/layout.h"
29 #include "wx/msgdlg.h"
30 #include "wx/textdlg.h"
31 #include "wx/filefn.h"
32 #include "wx/cmndata.h"
33 #include "wx/gdicmn.h"
34 #include "wx/intl.h"
35 #include "wx/imaglist.h"
36 #include "wx/icon.h"
37 #include "wx/log.h"
38 #include "wx/sizer.h"
39 #include "wx/tokenzr.h"
40 #include "wx/dir.h"
41 #include "wx/settings.h"
42
43 #if wxUSE_STATLINE
44 #include "wx/statline.h"
45 #endif
46
47 #include "wx/generic/dirctrlg.h"
48
49 #ifdef __WXMSW__
50 #include <direct.h>
51 #include <stdlib.h>
52 #include <ctype.h>
53 #endif
54
55 // If compiled under Windows, this macro can cause problems
56 #ifdef GetFirstChild
57 #undef GetFirstChild
58 #endif
59
60 #if !defined(__WXMSW__) || wxUSE_XPM_IN_MSW
61 /* Closed folder */
62 static char * icon1_xpm[] = {
63 /* width height ncolors chars_per_pixel */
64 "16 16 6 1",
65 /* colors */
66 " s None c None",
67 ". c #000000",
68 "+ c #c0c0c0",
69 "@ c #808080",
70 "# c #ffff00",
71 "$ c #ffffff",
72 /* pixels */
73 " ",
74 " @@@@@ ",
75 " @#+#+#@ ",
76 " @#+#+#+#@@@@@@ ",
77 " @$$$$$$$$$$$$@.",
78 " @$#+#+#+#+#+#@.",
79 " @$+#+#+#+#+#+@.",
80 " @$#+#+#+#+#+#@.",
81 " @$+#+#+#+#+#+@.",
82 " @$#+#+#+#+#+#@.",
83 " @$+#+#+#+#+#+@.",
84 " @$#+#+#+#+#+#@.",
85 " @@@@@@@@@@@@@@.",
86 " ..............",
87 " ",
88 " "};
89
90 /* Open folder */
91 static char * icon2_xpm[] = {
92 /* width height ncolors chars_per_pixel */
93 "16 16 6 1",
94 /* colors */
95 " s None c None",
96 ". c #000000",
97 "+ c #c0c0c0",
98 "@ c #808080",
99 "# c #ffff00",
100 "$ c #ffffff",
101 /* pixels */
102 " ",
103 " @@@@@ ",
104 " @$$$$$@ ",
105 " @$#+#+#$@@@@@@ ",
106 " @$+#+#+$$$$$$@.",
107 " @$#+#+#+#+#+#@.",
108 "@@@@@@@@@@@@@#@.",
109 "@$$$$$$$$$$@@+@.",
110 "@$#+#+#+#+##.@@.",
111 " @$#+#+#+#+#+.@.",
112 " @$+#+#+#+#+#.@.",
113 " @$+#+#+#+##@..",
114 " @@@@@@@@@@@@@.",
115 " .............",
116 " ",
117 " "};
118
119 /* File */
120 static char * icon3_xpm[] = {
121 /* width height ncolors chars_per_pixel */
122 "16 16 3 1",
123 /* colors */
124 " s None c None",
125 ". c #000000",
126 "+ c #ffffff",
127 /* pixels */
128 " ",
129 " ........ ",
130 " .++++++.. ",
131 " .+.+.++.+. ",
132 " .++++++.... ",
133 " .+.+.+++++. ",
134 " .+++++++++. ",
135 " .+.+.+.+.+. ",
136 " .+++++++++. ",
137 " .+.+.+.+.+. ",
138 " .+++++++++. ",
139 " .+.+.+.+.+. ",
140 " .+++++++++. ",
141 " ........... ",
142 " ",
143 " "};
144
145 /* Computer */
146 static char * icon4_xpm[] = {
147 "16 16 7 1",
148 " s None c None",
149 ". c #808080",
150 "X c #c0c0c0",
151 "o c Black",
152 "O c Gray100",
153 "+ c #008080",
154 "@ c Blue",
155 " ........... ",
156 " .XXXXXXXXXX.o",
157 " .OOOOOOOOO..o",
158 " .OoooooooX..o",
159 " .Oo+...@+X..o",
160 " .Oo+XXX.+X..o",
161 " .Oo+....+X..o",
162 " .Oo++++++X..o",
163 " .OXXXXXXXX.oo",
164 " ..........o.o",
165 " ...........Xo",
166 " .XXXXXXXXXX.o",
167 " .o.o.o.o.o...o",
168 " .oXoXoXoXoXo.o ",
169 ".XOXXXXXXXXX.o ",
170 "............o "};
171
172 /* Drive */
173 static char * icon5_xpm[] = {
174 "16 16 7 1",
175 " s None c None",
176 ". c #808080",
177 "X c #c0c0c0",
178 "o c Black",
179 "O c Gray100",
180 "+ c Green",
181 "@ c #008000",
182 " ",
183 " ",
184 " ",
185 " ",
186 " ............. ",
187 " .XXXXXXXXXXXX.o",
188 ".OOOOOOOOOOOO..o",
189 ".XXXXXXXXX+@X..o",
190 ".XXXXXXXXXXXX..o",
191 ".X..........X..o",
192 ".XOOOOOOOOOOX..o",
193 "..............o ",
194 " ooooooooooooo ",
195 " ",
196 " ",
197 " "};
198
199 /* CD-ROM */
200 static char *icon6_xpm[] = {
201 "16 16 10 1",
202 " s None c None",
203 ". c #808080",
204 "X c #c0c0c0",
205 "o c Yellow",
206 "O c Blue",
207 "+ c Black",
208 "@ c Gray100",
209 "# c #008080",
210 "$ c Green",
211 "% c #008000",
212 " ... ",
213 " ..XoX.. ",
214 " .O.XoXXX+ ",
215 " ...O.oXXXX+ ",
216 " .O..X.XXXX+ ",
217 " ....X.+..XXX+",
218 " .XXX.+@+.XXX+",
219 " .X@XX.+.X@@X+",
220 " .....X...#XX@+ ",
221 ".@@@...XXo.O@X+ ",
222 ".@XXX..XXoXOO+ ",
223 ".@++++..XoX+++ ",
224 ".@$%@@XX+++X.+ ",
225 ".............+ ",
226 " ++++++++++++ ",
227 " "};
228
229 /* Floppy */
230 static char * icon7_xpm[] = {
231 "16 16 7 1",
232 " s None c None",
233 ". c #808080",
234 "X c Gray100",
235 "o c #c0c0c0",
236 "O c Black",
237 "+ c Cyan",
238 "@ c Red",
239 " ......X",
240 " .ooooooO",
241 " .+++++OO",
242 " .++++++O",
243 " .++++++O",
244 " .ooooooO",
245 " .......o....oO",
246 " .oooooo.o.O.XoO",
247 ".XXXXXXXXOOOOOO ",
248 ".ooooooooo@o..O ",
249 ".ooo....oooo..O ",
250 ".o..OOOO...o..O ",
251 ".oooXXXXoooo..O ",
252 ".............O ",
253 " OOOOOOOOOOOO ",
254 " "};
255
256 /* Removeable */
257 static char * icon8_xpm[] = {
258 "16 16 7 1",
259 " s None c None",
260 ". c #808080",
261 "X c #c0c0c0",
262 "o c Black",
263 "O c Gray100",
264 "+ c Red",
265 "@ c #800000",
266 " ",
267 " ",
268 " ",
269 " ............. ",
270 " .XXXXXXXXXXXX.o",
271 ".OOOOOOOOOOOO..o",
272 ".OXXXXXXXXXXX..o",
273 ".O+@.oooooo.X..o",
274 ".OXXOooooooOX..o",
275 ".OXXXOOOOOOXX..o",
276 ".OXXXXXXXXXXX..o",
277 ".O............o ",
278 " ooooooooooooo ",
279 " ",
280 " ",
281 " "};
282 #endif // !wxMSW
283
284 static const int ID_DIRCTRL = 1000;
285 static const int ID_TEXTCTRL = 1001;
286 static const int ID_OK = 1002;
287 static const int ID_CANCEL = 1003;
288 static const int ID_NEW = 1004;
289 //static const int ID_CHECK = 1005;
290
291 #ifdef __WXMSW__
292 static bool wxIsDriveAvailable(const wxString dirName)
293 {
294 #ifdef __WIN32__
295 UINT errorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
296 #endif
297 bool success = TRUE;
298
299 // Check if this is a root directory and if so,
300 // whether the drive is avaiable.
301 if (dirName.Len() == 3 && dirName[1] == wxT(':'))
302 {
303 wxString dirNameLower(dirName.Lower());
304 int currentDrive = _getdrive();
305 int thisDrive = (int) (dirNameLower[0] - 'a' + 1) ;
306 int err = _chdrive( thisDrive ) ;
307 _chdrive( currentDrive );
308
309 if (err == -1)
310 {
311 success = FALSE;
312 }
313 }
314 #ifdef __WIN32__
315 (void) SetErrorMode(errorMode);
316 #endif
317
318 return success;
319 }
320 #endif
321
322 //-----------------------------------------------------------------------------
323 // wxDirItemDataEx
324 //-----------------------------------------------------------------------------
325
326 wxDirItemDataEx::wxDirItemDataEx(const wxString& path, const wxString& name,
327 bool isDir)
328 {
329 m_path = path;
330 m_name = name;
331 /* Insert logic to detect hidden files here
332 * In UnixLand we just check whether the first char is a dot
333 * For FileNameFromPath read LastDirNameInThisPath ;-) */
334 // m_isHidden = (bool)(wxFileNameFromPath(*m_path)[0] == '.');
335 m_isHidden = FALSE;
336 m_hasSubDirs = HasSubDirs();
337 m_isExpanded = FALSE;
338 m_isDir = isDir;
339 }
340
341 wxDirItemDataEx::~wxDirItemDataEx()
342 {
343 }
344
345 void wxDirItemDataEx::SetNewDirName( wxString path )
346 {
347 m_path = path;
348 m_name = wxFileNameFromPath( path );
349 }
350
351 bool wxDirItemDataEx::HasSubDirs()
352 {
353 if (m_path.IsEmpty())
354 return TRUE;
355
356 // On WIN32, must check if this volume is mounted or
357 // we get an error dialog for e.g. drive a:
358 #ifdef __WIN32__
359 if (!wxIsDriveAvailable(m_path))
360 return FALSE;
361 #endif
362
363 wxString search = m_path;
364
365 if (m_path.Last() != wxFILE_SEP_PATH)
366 {
367 search += wxString(wxFILE_SEP_PATH);
368 }
369 search += wxT("*");
370
371 wxLogNull log;
372 wxString path = wxFindFirstFile( search, wxDIR );
373 return (bool)(!path.IsNull());
374 }
375
376 //-----------------------------------------------------------------------------
377 // wxGenericDirCtrl
378 //-----------------------------------------------------------------------------
379
380 IMPLEMENT_DYNAMIC_CLASS(wxGenericDirCtrl, wxControl)
381
382 BEGIN_EVENT_TABLE(wxGenericDirCtrl, wxControl)
383 EVT_TREE_ITEM_EXPANDING (-1, wxGenericDirCtrl::OnExpandItem)
384 EVT_TREE_ITEM_COLLAPSED (-1, wxGenericDirCtrl::OnCollapseItem)
385 EVT_TREE_BEGIN_LABEL_EDIT (-1, wxGenericDirCtrl::OnBeginEditItem)
386 EVT_TREE_END_LABEL_EDIT (-1, wxGenericDirCtrl::OnEndEditItem)
387 EVT_SIZE (wxGenericDirCtrl::OnSize)
388 END_EVENT_TABLE()
389
390 wxGenericDirCtrl::wxGenericDirCtrl(void)
391 {
392 Init();
393 }
394
395 bool wxGenericDirCtrl::Create(wxWindow *parent,
396 const wxWindowID id,
397 const wxString& dir,
398 const wxPoint& pos,
399 const wxSize& size,
400 long style,
401 const wxString& filter,
402 int defaultFilter,
403 const wxString& name )
404 {
405 if (!wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name))
406 return FALSE;
407
408 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
409
410 Init();
411
412 long treeStyle = wxTR_HAS_BUTTONS;
413 if ((style & wxDIRCTRL_3D_INTERNAL) == 0)
414 treeStyle |= wxNO_BORDER;
415
416 long filterStyle = 0;
417 if ((style & wxDIRCTRL_3D_INTERNAL) == 0)
418 filterStyle |= wxNO_BORDER;
419
420 m_treeCtrl = new wxTreeCtrl(this, wxID_TREECTRL, pos, size, treeStyle);
421
422 if (!filter.IsEmpty() && (style & wxDIRCTRL_SHOW_FILTERS))
423 m_filterListCtrl = new wxDirFilterListCtrl(this, wxID_FILTERLISTCTRL, wxDefaultPosition, wxDefaultSize, filterStyle);
424
425 m_defaultPath = dir;
426 m_filter = filter;
427
428 SetFilterIndex(defaultFilter);
429
430 if (m_filterListCtrl)
431 m_filterListCtrl->FillFilterList(filter, defaultFilter);
432
433 m_imageList = new wxImageList(16, 16, TRUE);
434 #if !defined(__WXMSW__) || wxUSE_XPM_IN_MSW
435 m_imageList->Add(wxIcon(icon1_xpm));
436 m_imageList->Add(wxIcon(icon2_xpm));
437 m_imageList->Add(wxIcon(icon3_xpm));
438 m_imageList->Add(wxIcon(icon4_xpm));
439 m_imageList->Add(wxIcon(icon5_xpm));
440 m_imageList->Add(wxIcon(icon6_xpm));
441 m_imageList->Add(wxIcon(icon7_xpm));
442 m_imageList->Add(wxIcon(icon8_xpm));
443 #elif defined(__WXMSW__)
444 m_imageList->Add(wxIcon(wxT("wxICON_SMALL_CLOSED_FOLDER"), wxBITMAP_TYPE_RESOURCE));
445 m_imageList->Add(wxIcon(wxT("wxICON_SMALL_OPEN_FOLDER"), wxBITMAP_TYPE_RESOURCE));
446 m_imageList->Add(wxIcon(wxT("wxICON_SMALL_FILE"), wxBITMAP_TYPE_RESOURCE));
447 m_imageList->Add(wxIcon(wxT("wxICON_SMALL_COMPUTER"), wxBITMAP_TYPE_RESOURCE));
448 m_imageList->Add(wxIcon(wxT("wxICON_SMALL_DRIVE"), wxBITMAP_TYPE_RESOURCE));
449 m_imageList->Add(wxIcon(wxT("wxICON_SMALL_CDROM"), wxBITMAP_TYPE_RESOURCE));
450 m_imageList->Add(wxIcon(wxT("wxICON_SMALL_FLOPPY"), wxBITMAP_TYPE_RESOURCE));
451 m_imageList->Add(wxIcon(wxT("wxICON_SMALL_REMOVEABLE"), wxBITMAP_TYPE_RESOURCE));
452 #else
453 #error "Sorry, we don't have icons available for this platforms."
454 #endif
455 m_treeCtrl->SetImageList(m_imageList);
456
457 m_showHidden = FALSE;
458 wxDirItemDataEx* rootData = new wxDirItemDataEx(wxT(""), wxT(""), TRUE);
459
460 wxString rootName;
461
462 #ifdef __WXMSW__
463 rootName = _("Computer");
464 #else
465 rootName = _("Sections");
466 #endif
467
468 m_rootId = m_treeCtrl->AddRoot( rootName, 3, -1, rootData);
469 m_treeCtrl->SetItemHasChildren(m_rootId);
470 m_treeCtrl->Expand(m_rootId); // automatically expand first level
471
472 // Expand and select the default path
473 if (!m_defaultPath.IsEmpty())
474 ExpandPath(m_defaultPath);
475
476 DoResize();
477
478 return TRUE;
479 }
480
481 wxGenericDirCtrl::~wxGenericDirCtrl()
482 {
483 m_treeCtrl->SetImageList(NULL);
484 delete m_imageList;
485 }
486
487 void wxGenericDirCtrl::Init()
488 {
489 m_showHidden = FALSE;
490 m_imageList = NULL;
491 m_currentFilter = 0;
492 m_currentFilterStr = wxEmptyString; // Default: any file
493 m_treeCtrl = NULL;
494 m_filterListCtrl = NULL;
495 }
496
497 void wxGenericDirCtrl::AddSection(const wxString& path, const wxString& name, int imageId)
498 {
499 wxDirItemDataEx *dir_item = new wxDirItemDataEx(path,name,TRUE);
500
501 #ifdef __WXMSW__
502 // Windows: sections are displayed as drives
503 wxTreeItemId id = m_treeCtrl->AppendItem( m_rootId, name, imageId, -1, dir_item);
504 #else
505 // Unix: sections are displayed as folders
506 wxTreeItemId id = m_treeCtrl->AppendItem( m_rootId, name, 0, -1, dir_item);
507 m_treeCtrl->SetItemImage( id, 1, wxTreeItemIcon_Expanded );
508 #endif
509 // TODO: other operating systems.
510
511 m_treeCtrl->SetItemHasChildren(id);
512 }
513
514 void wxGenericDirCtrl::SetupSections()
515 {
516 #ifdef __WXMSW__
517
518 #ifdef __WIN32__
519 wxChar driveBuffer[256];
520 size_t n = (size_t) GetLogicalDriveStrings(255, driveBuffer);
521 size_t i = 0;
522 while (i < n)
523 {
524 wxString path, name;
525 path.Printf(wxT("%c:\\"), driveBuffer[i]);
526 name.Printf(wxT("(%c:)"), driveBuffer[i]);
527
528 int imageId = 4;
529 int driveType = ::GetDriveType(path);
530 switch (driveType)
531 {
532 case DRIVE_REMOVABLE:
533 if (path == wxT("a:\\") || path == wxT("b:\\"))
534 imageId = 6; // Floppy
535 else
536 imageId = 7;
537 break;
538 case DRIVE_FIXED:
539 imageId = 4;
540 break;
541 case DRIVE_REMOTE:
542 imageId = 4;
543 break;
544 case DRIVE_CDROM:
545 imageId = 5;
546 break;
547 default:
548 imageId = 4;
549 break;
550 }
551
552 AddSection(path, name, imageId);
553
554 while (driveBuffer[i] != wxT('\0'))
555 i ++;
556 i ++;
557 if (driveBuffer[i] == wxT('\0'))
558 break;
559 }
560 #else
561 int drive;
562 int currentDrive;
563
564 /* If we can switch to the drive, it exists. */
565 for( drive = 1; drive <= 26; drive++ )
566 {
567 wxString path, name;
568 path.Printf(wxT("%c:\\"), (char) (drive + 'a' - 1));
569 name.Printf(wxT("(%c:)"), (char) (drive + 'a' - 1));
570
571 if (wxIsDriveAvailable(path))
572 {
573
574 AddSection(path, name);
575 }
576 }
577 #endif
578
579 #else
580 AddSection(wxT("/"), _("The Computer"), 0);
581 AddSection(wxGetHomeDir(), _("My Home"), 0 );
582 AddSection(wxT("/mnt"), _("Mounted Devices"), 0 );
583 AddSection(wxT("/usr/local"), _("User Local"), 0 );
584 AddSection(wxT("/usr"), _("User"), 0 );
585 AddSection(wxT("/var"), _("Variables"), 0 );
586 AddSection(wxT("/etc"), _("Etcetera"), 0 );
587 AddSection(wxT("/tmp"), _("Temporary"), 0 );
588 #endif
589 }
590
591 void wxGenericDirCtrl::OnBeginEditItem(wxTreeEvent &event)
592 {
593 // don't rename the main entry "Sections"
594 if (event.GetItem() == m_rootId)
595 {
596 event.Veto();
597 return;
598 }
599
600 // don't rename the individual sections
601 if (m_treeCtrl->GetParent( event.GetItem() ) == m_rootId)
602 {
603 event.Veto();
604 return;
605 }
606 }
607
608 void wxGenericDirCtrl::OnEndEditItem(wxTreeEvent &event)
609 {
610 if ((event.GetLabel().IsEmpty()) ||
611 (event.GetLabel() == _(".")) ||
612 (event.GetLabel() == _("..")) ||
613 (event.GetLabel().First( wxT("/") ) != wxNOT_FOUND))
614 {
615 wxMessageDialog dialog(this, _("Illegal directory name."), _("Error"), wxOK | wxICON_ERROR );
616 dialog.ShowModal();
617 event.Veto();
618 return;
619 }
620
621 wxTreeItemId id = event.GetItem();
622 wxDirItemDataEx *data = (wxDirItemDataEx*)m_treeCtrl->GetItemData( id );
623 wxASSERT( data );
624
625 wxString new_name( wxPathOnly( data->m_path ) );
626 new_name += wxString(wxFILE_SEP_PATH);
627 new_name += event.GetLabel();
628
629 wxLogNull log;
630
631 if (wxFileExists(new_name))
632 {
633 wxMessageDialog dialog(this, _("File name exists already."), _("Error"), wxOK | wxICON_ERROR );
634 dialog.ShowModal();
635 event.Veto();
636 }
637
638 if (wxRenameFile(data->m_path,new_name))
639 {
640 data->SetNewDirName( new_name );
641 }
642 else
643 {
644 wxMessageDialog dialog(this, _("Operation not permitted."), _("Error"), wxOK | wxICON_ERROR );
645 dialog.ShowModal();
646 event.Veto();
647 }
648 }
649
650 void wxGenericDirCtrl::OnExpandItem(wxTreeEvent &event)
651 {
652 wxTreeItemId parentId = event.GetItem();
653
654 ExpandDir(parentId);
655 }
656
657 void wxGenericDirCtrl::OnCollapseItem(wxTreeEvent &event )
658 {
659 wxTreeItemId child, parent = event.GetItem();
660
661 wxDirItemDataEx *data = (wxDirItemDataEx *) m_treeCtrl->GetItemData(event.GetItem());
662 if (!data->m_isExpanded)
663 return;
664
665 data->m_isExpanded = FALSE;
666 long cookie;
667 /* Workaround because DeleteChildren has disapeared (why?) and
668 * CollapseAndReset doesn't work as advertised (deletes parent too) */
669 child = m_treeCtrl->GetFirstChild(parent, cookie);
670 while (child.IsOk())
671 {
672 m_treeCtrl->Delete(child);
673 /* Not GetNextChild below, because the cookie mechanism can't
674 * handle disappearing children! */
675 child = m_treeCtrl->GetFirstChild(parent, cookie);
676 }
677 }
678
679 void wxGenericDirCtrl::ExpandDir(wxTreeItemId parentId)
680 {
681 wxDirItemDataEx *data = (wxDirItemDataEx *) m_treeCtrl->GetItemData(parentId);
682
683 if (data->m_isExpanded)
684 return;
685
686 data->m_isExpanded = TRUE;
687
688 if (parentId == m_rootId)
689 {
690 SetupSections();
691 return;
692 }
693
694 wxASSERT(data);
695
696 wxString search,path,filename;
697
698 wxString dirName(data->m_path);
699
700 #ifdef __WXMSW__
701 // Check if this is a root directory and if so,
702 // whether the drive is avaiable.
703 if (!wxIsDriveAvailable(dirName))
704 {
705 data->m_isExpanded = FALSE;
706 wxMessageBox(wxT("Sorry, this drive is not available."));
707 return;
708 }
709 #endif
710
711 // This may take a longish time. Go to busy cursor
712 wxBusyCursor busy;
713
714 #ifdef __WXMSW__
715 if (dirName.Last() == ':')
716 dirName += wxString(wxFILE_SEP_PATH);
717 #endif
718
719 wxArrayString dirs;
720 wxArrayString filenames;
721
722 wxDir d;
723 wxString eachFilename;
724
725 wxLogNull log;
726 d.Open(dirName);
727
728 if (d.IsOpened())
729 {
730 if (d.GetFirst(& eachFilename, wxEmptyString, wxDIR_DIRS))
731 {
732 do
733 {
734 if ((eachFilename != wxT(".")) && (eachFilename != wxT("..")))
735 {
736 dirs.Add(eachFilename);
737 }
738 }
739 while (d.GetNext(& eachFilename)) ;
740 }
741 }
742 dirs.Sort();
743
744 // Now do the filenames -- but only if we're allowed to
745 if ((GetWindowStyle() & wxDIRCTRL_DIR_ONLY) == 0)
746 {
747 wxLogNull log;
748
749 d.Open(dirName);
750
751 if (d.IsOpened())
752 {
753 if (d.GetFirst(& eachFilename, m_currentFilterStr, wxDIR_FILES))
754 {
755 do
756 {
757 if ((eachFilename != wxT(".")) && (eachFilename != wxT("..")))
758 {
759 filenames.Add(eachFilename);
760 }
761 }
762 while (d.GetNext(& eachFilename)) ;
763 }
764 }
765 filenames.Sort();
766 }
767
768 // Add the sorted dirs
769 size_t i;
770 for (i = 0; i < dirs.Count(); i++)
771 {
772 wxString eachFilename(dirs[i]);
773 path = dirName;
774 if (path.Last() != wxFILE_SEP_PATH)
775 path += wxString(wxFILE_SEP_PATH);
776 path += eachFilename;
777
778 wxDirItemDataEx *dir_item = new wxDirItemDataEx(path,eachFilename,TRUE);
779 wxTreeItemId id = m_treeCtrl->AppendItem( parentId, eachFilename, 0, -1, dir_item);
780 m_treeCtrl->SetItemImage( id, 1, wxTreeItemIcon_Expanded );
781
782 // Has this got any children? If so, make it expandable.
783 int options = wxDIR_DEFAULT;
784 if (GetWindowStyle() & wxDIRCTRL_DIR_ONLY) // If only showing dirs, then we specify dirs only here
785 {
786 options = wxDIR_DIRS;
787 }
788
789 wxLogNull log;
790 wxDir dir2;
791 if (dir2.Open(path))
792 {
793 wxString str;
794 // Have to test for wxDIR_DIRS separately in case m_currentFilterStr is non-empty and
795 // and filters out any directories
796 if (dir2.GetFirst(& str, m_currentFilterStr, options) || dir2.GetFirst(& str, wxEmptyString, wxDIR_DIRS))
797 {
798 m_treeCtrl->SetItemHasChildren(id);
799 }
800 }
801 }
802
803 // Add the sorted filenames
804 if ((GetWindowStyle() & wxDIRCTRL_DIR_ONLY) == 0)
805 {
806 for (i = 0; i < filenames.Count(); i++)
807 {
808 wxString eachFilename(filenames[i]);
809 path = dirName;
810 if (path.Last() != wxFILE_SEP_PATH)
811 path += wxString(wxFILE_SEP_PATH);
812 path += eachFilename;
813 //path = dirName + wxString(wxT("/")) + eachFilename;
814 wxDirItemDataEx *dir_item = new wxDirItemDataEx(path,eachFilename,FALSE);
815 (void)m_treeCtrl->AppendItem( parentId, eachFilename, 2, -1, dir_item);
816 }
817 }
818 }
819
820 // Find the child that matches the first part of 'path'.
821 // E.g. if a child path is "/usr" and 'path' is "/usr/include"
822 // then the child for /usr is returned.
823 wxTreeItemId wxGenericDirCtrl::FindChild(wxTreeItemId parentId, const wxString& path, bool& done)
824 {
825 wxString path2(path);
826
827 // Make sure all separators are as per the current platform
828 path2.Replace(wxT("\\"), wxString(wxFILE_SEP_PATH));
829 path2.Replace(wxT("/"), wxString(wxFILE_SEP_PATH));
830
831 // Append a separator to foil bogus substring matching
832 path2 += wxString(wxFILE_SEP_PATH);
833
834 // In MSW, case is not significant
835 #ifdef __WXMSW__
836 path2.MakeLower();
837 #endif
838
839 long cookie;
840 wxTreeItemId childId = m_treeCtrl->GetFirstChild(parentId, cookie);
841 while (childId != 0)
842 {
843 wxDirItemDataEx* data = (wxDirItemDataEx*) m_treeCtrl->GetItemData(childId);
844
845 if (data && data->m_path != "")
846 {
847 wxString childPath(data->m_path);
848 if (childPath.Last() != wxFILE_SEP_PATH)
849 childPath += wxString(wxFILE_SEP_PATH);
850
851 // In MSW, case is not significant
852 #ifdef __WXMSW__
853 childPath.MakeLower();
854 #endif
855
856 if (childPath.Len() <= path2.Len())
857 {
858 wxString path3 = path2.Mid(0, childPath.Len());
859 if (childPath == path3)
860 {
861 if (path3.Len() == path2.Len())
862 done = TRUE;
863 else
864 done = FALSE;
865 return childId;
866 }
867 }
868 }
869
870 childId = m_treeCtrl->GetNextChild(childId, cookie);
871 }
872 wxTreeItemId invalid;
873 return invalid;
874 }
875
876 // Try to expand as much of the given path as possible,
877 // and select the given tree item.
878 bool wxGenericDirCtrl::ExpandPath(const wxString& path)
879 {
880 bool done = FALSE;
881 wxTreeItemId id = FindChild(m_rootId, path, done);
882 wxTreeItemId lastId = id; // The last non-zero id
883 while ((id > 0) && !done)
884 {
885 ExpandDir(id);
886
887 id = FindChild(id, path, done);
888 if (id != 0)
889 lastId = id;
890 }
891 if (lastId > 0)
892 {
893 wxDirItemDataEx *data = (wxDirItemDataEx *) m_treeCtrl->GetItemData(lastId);
894 if (data->m_isDir)
895 {
896 m_treeCtrl->Expand(lastId);
897 }
898 if ((GetWindowStyle() & wxDIRCTRL_SELECT_FIRST) && data->m_isDir)
899 {
900 // Find the first file in this directory
901 long cookie;
902 wxTreeItemId childId = m_treeCtrl->GetFirstChild(lastId, cookie);
903 bool selectedChild = FALSE;
904 while (childId != 0)
905 {
906 wxDirItemDataEx* data = (wxDirItemDataEx*) m_treeCtrl->GetItemData(childId);
907
908 if (data && data->m_path != "" && !data->m_isDir)
909 {
910 m_treeCtrl->SelectItem(childId);
911 m_treeCtrl->EnsureVisible(childId);
912 selectedChild = TRUE;
913 break;
914 }
915 childId = m_treeCtrl->GetNextChild(lastId, cookie);
916 }
917 if (!selectedChild)
918 {
919 m_treeCtrl->SelectItem(lastId);
920 m_treeCtrl->EnsureVisible(lastId);
921 }
922 }
923 else
924 {
925 m_treeCtrl->SelectItem(lastId);
926 m_treeCtrl->EnsureVisible(lastId);
927 }
928
929 return TRUE;
930 }
931 else
932 return FALSE;
933 }
934
935 wxString wxGenericDirCtrl::GetPath() const
936 {
937 wxTreeItemId id = m_treeCtrl->GetSelection();
938 if (id)
939 {
940 wxDirItemDataEx* data = (wxDirItemDataEx*) m_treeCtrl->GetItemData(id);
941 return data->m_path;
942 }
943 else
944 return wxEmptyString;
945 }
946
947 wxString wxGenericDirCtrl::GetFilePath() const
948 {
949 wxTreeItemId id = m_treeCtrl->GetSelection();
950 if (id)
951 {
952 wxDirItemDataEx* data = (wxDirItemDataEx*) m_treeCtrl->GetItemData(id);
953 if (data->m_isDir)
954 return wxEmptyString;
955 else
956 return data->m_path;
957 }
958 else
959 return wxEmptyString;
960 }
961
962 void wxGenericDirCtrl::SetPath(const wxString& path)
963 {
964 m_defaultPath = path;
965 if (m_rootId)
966 ExpandPath(path);
967 }
968
969 // Not used
970 #if 0
971 void wxGenericDirCtrl::FindChildFiles(wxTreeItemId id, int dirFlags, wxArrayString& filenames)
972 {
973 wxDirItemDataEx *data = (wxDirItemDataEx *) m_treeCtrl->GetItemData(id);
974
975 // This may take a longish time. Go to busy cursor
976 wxBusyCursor busy;
977
978 wxASSERT(data);
979
980 wxString search,path,filename;
981
982 wxString dirName(data->m_path);
983
984 #ifdef __WXMSW__
985 if (dirName.Last() == ':')
986 dirName += wxString(wxFILE_SEP_PATH);
987 #endif
988
989 wxDir d;
990 wxString eachFilename;
991
992 wxLogNull log;
993 d.Open(dirName);
994
995 if (d.IsOpened())
996 {
997 if (d.GetFirst(& eachFilename, m_currentFilterStr, dirFlags))
998 {
999 do
1000 {
1001 if ((eachFilename != wxT(".")) && (eachFilename != wxT("..")))
1002 {
1003 filenames.Add(eachFilename);
1004 }
1005 }
1006 while (d.GetNext(& eachFilename)) ;
1007 }
1008 }
1009 }
1010 #endif
1011
1012 void wxGenericDirCtrl::SetFilterIndex(int n)
1013 {
1014 m_currentFilter = n;
1015
1016 wxString f, d;
1017 if (ExtractWildcard(m_filter, n, f, d))
1018 m_currentFilterStr = f;
1019 else
1020 m_currentFilterStr = wxT("*.*");
1021 }
1022
1023 void wxGenericDirCtrl::SetFilter(const wxString& filter)
1024 {
1025 m_filter = filter;
1026
1027 wxString f, d;
1028 if (ExtractWildcard(m_filter, m_currentFilter, f, d))
1029 m_currentFilterStr = f;
1030 else
1031 m_currentFilterStr = wxT("*.*");
1032 }
1033
1034 // Extract description and actual filter from overall filter string
1035 bool wxGenericDirCtrl::ExtractWildcard(const wxString& filterStr, int n, wxString& filter, wxString& description)
1036 {
1037 wxArrayString filters, descriptions;
1038 int count = ParseFilter(filterStr, filters, descriptions);
1039 if (count > 0 && n < count)
1040 {
1041 filter = filters[n];
1042 description = descriptions[n];
1043 return TRUE;
1044 }
1045 else
1046 return FALSE;
1047 }
1048
1049 // Parses the global filter, returning the number of filters.
1050 // Returns 0 if none or if there's a problem.
1051 // filterStr is in the form:
1052 //
1053 // "All files (*.*)|*.*|JPEG Files (*.jpeg)|*.jpg"
1054
1055 int wxGenericDirCtrl::ParseFilter(const wxString& filterStr, wxArrayString& filters, wxArrayString& descriptions)
1056 {
1057 wxString str(filterStr);
1058
1059 wxString description, filter;
1060 int pos;
1061 bool finished = FALSE;
1062 do
1063 {
1064 pos = str.Find(wxT('|'));
1065 if (pos == -1)
1066 return 0; // Problem
1067 description = str.Left(pos);
1068 str = str.Mid(pos+1);
1069 pos = str.Find(wxT('|'));
1070 if (pos == -1)
1071 {
1072 filter = str;
1073 finished = TRUE;
1074 }
1075 else
1076 {
1077 filter = str.Left(pos);
1078 str = str.Mid(pos+1);
1079 }
1080 descriptions.Add(description);
1081 filters.Add(filter);
1082 }
1083 while (!finished) ;
1084
1085 return filters.Count();
1086 }
1087
1088 void wxGenericDirCtrl::DoResize()
1089 {
1090 wxSize sz = GetClientSize();
1091 int verticalSpacing = 3;
1092 if (m_treeCtrl)
1093 {
1094 wxSize filterSz ;
1095 if (m_filterListCtrl)
1096 {
1097 filterSz = m_filterListCtrl->GetSize();
1098 sz.y -= (filterSz.y + verticalSpacing);
1099 }
1100 m_treeCtrl->SetSize(0, 0, sz.x, sz.y);
1101 if (m_filterListCtrl)
1102 {
1103 m_filterListCtrl->SetSize(0, sz.y + verticalSpacing, sz.x, filterSz.y);
1104 // Don't know why, but this needs refreshing after a resize (wxMSW)
1105 m_filterListCtrl->Refresh();
1106 }
1107 }
1108 }
1109
1110
1111 void wxGenericDirCtrl::OnSize(wxSizeEvent &event)
1112 {
1113 DoResize();
1114 }
1115
1116 //-----------------------------------------------------------------------------
1117 // wxDirFilterListCtrl
1118 //-----------------------------------------------------------------------------
1119
1120 IMPLEMENT_CLASS(wxDirFilterListCtrl, wxChoice)
1121
1122 BEGIN_EVENT_TABLE(wxDirFilterListCtrl, wxChoice)
1123 EVT_CHOICE(-1, wxDirFilterListCtrl::OnSelFilter)
1124 END_EVENT_TABLE()
1125
1126 bool wxDirFilterListCtrl::Create(wxGenericDirCtrl* parent, const wxWindowID id,
1127 const wxPoint& pos,
1128 const wxSize& size,
1129 long style)
1130 {
1131 m_dirCtrl = parent;
1132 return wxChoice::Create(parent, id, pos, size, 0, NULL, style);
1133 }
1134
1135 void wxDirFilterListCtrl::Init()
1136 {
1137 m_dirCtrl = NULL;
1138 }
1139
1140 void wxDirFilterListCtrl::OnSelFilter(wxCommandEvent& event)
1141 {
1142 int sel = GetSelection();
1143
1144 wxString currentPath = m_dirCtrl->GetPath();
1145
1146 m_dirCtrl->SetFilterIndex(sel);
1147
1148 // If the filter has changed, the view is out of date, so
1149 // collapse the tree.
1150 m_dirCtrl->GetTreeCtrl()->Collapse(m_dirCtrl->GetRootId());
1151 m_dirCtrl->GetTreeCtrl()->Expand(m_dirCtrl->GetRootId());
1152
1153 // Try to restore the selection, or at least the directory
1154 m_dirCtrl->ExpandPath(currentPath);
1155 }
1156
1157 void wxDirFilterListCtrl::FillFilterList(const wxString& filter, int defaultFilter)
1158 {
1159 Clear();
1160 wxArrayString descriptions, filters;
1161 size_t n = (size_t) m_dirCtrl->ParseFilter(filter, filters, descriptions);
1162
1163 if (n > 0 && defaultFilter < (int) n)
1164 {
1165 size_t i = 0;
1166 for (i = 0; i < n; i++)
1167 Append(descriptions[i]);
1168 SetSelection(defaultFilter);
1169 }
1170 }
1171
1172 // wxGenericDirDialog implementation
1173 // This should be moved into dirdlgg.cpp eventually
1174
1175 BEGIN_EVENT_TABLE(wxGenericDirDialog, wxDialog)
1176 EVT_BUTTON(wxID_OK, wxGenericDirDialog::OnOK)
1177 EVT_CLOSE(wxGenericDirDialog::OnCloseWindow)
1178 END_EVENT_TABLE()
1179
1180 wxGenericDirDialog::wxGenericDirDialog(wxWindow* parent, const wxString& title,
1181 const wxString& defaultPath, long style, const wxPoint& pos, const wxSize& sz, const wxString& name):
1182 wxDialog(parent, ID_DIRCTRL, title, pos, sz, style, name)
1183 {
1184 wxBusyCursor cursor;
1185
1186 wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL );
1187
1188 // 1) dir ctrl
1189 m_dirCtrl = new wxGenericDirCtrl(this, ID_DIRCTRL,
1190 defaultPath, wxPoint(5, 5),
1191 wxSize(300, 200), wxDIRCTRL_DIR_ONLY|wxSUNKEN_BORDER);
1192
1193 topsizer->Add( m_dirCtrl, 1, wxTOP|wxLEFT|wxRIGHT | wxEXPAND, 10 );
1194
1195 // 2) TODO: text control for entering path?
1196
1197 #if wxUSE_STATLINE
1198 // 3) Static line
1199 topsizer->Add( new wxStaticLine( this, -1 ), 0, wxEXPAND | wxLEFT|wxRIGHT|wxTOP, 10 );
1200 #endif
1201
1202 // 4) Buttons
1203 wxSizer* buttonsizer = new wxBoxSizer( wxHORIZONTAL );
1204 wxButton* okButton = new wxButton(this, wxID_OK, _("OK"));
1205 buttonsizer->Add( okButton, 0, wxLEFT|wxRIGHT, 10 );
1206 wxButton* cancelButton = new wxButton(this, wxID_CANCEL, _("Cancel"));
1207 buttonsizer->Add( cancelButton, 0, wxLEFT|wxRIGHT, 10 );
1208
1209 /* TODO: new directory button
1210 wxButton* newButton = new wxButton( this, ID_NEW, _("New...") );
1211 buttonsizer->Add( newButton, 0, wxLEFT|wxRIGHT, 10 );
1212 */
1213 topsizer->Add( buttonsizer, 0, wxALL | wxCENTER, 10 );
1214
1215 okButton->SetDefault();
1216 m_dirCtrl->SetFocus();
1217
1218 SetAutoLayout( TRUE );
1219 SetSizer( topsizer );
1220
1221 topsizer->SetSizeHints( this );
1222 topsizer->Fit( this );
1223
1224 Centre( wxBOTH );
1225 }
1226
1227 void wxGenericDirDialog::OnCloseWindow(wxCloseEvent& event)
1228 {
1229 EndModal(wxID_CANCEL);
1230 }
1231
1232 void wxGenericDirDialog::OnOK(wxCommandEvent& event)
1233 {
1234 EndModal(wxID_OK);
1235 }
1236
1237 void wxGenericDirDialog::SetPath(const wxString& path)
1238 {
1239 m_dirCtrl->SetPath(path);
1240 }
1241
1242 wxString wxGenericDirDialog::GetPath(void) const
1243 {
1244 return m_dirCtrl->GetPath();
1245 }