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