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