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