Tried to add sorting to wxTreeCtrl
[wxWidgets.git] / src / generic / dirdlgg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dirdlg.cpp
3 // Purpose: wxDirDialog
4 // Author: Harm van der Heijden and Robert Roebling
5 // Modified by:
6 // Created: 12/12/98
7 // Copyright: (c) Harm van der Heijden and Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #ifdef __GNUG__
12 #pragma implementation "dirdlgg.h"
13 #endif
14
15 #include "wx/defs.h"
16 #include "wx/utils.h"
17 #include "wx/dialog.h"
18 #include "wx/button.h"
19 #include "wx/layout.h"
20 #include "wx/msgdlg.h"
21 #include "wx/textdlg.h"
22 #include "wx/filefn.h"
23 #include "wx/cmndata.h"
24 #include "wx/gdicmn.h"
25 #include "wx/intl.h"
26 #include "wx/imaglist.h"
27
28 #include "wx/generic/dirdlgg.h"
29
30 /* XPM */
31 static char * icon1_xpm[] = {
32 /* width height ncolors chars_per_pixel */
33 "16 16 6 1",
34 /* colors */
35 " s None c None",
36 ". c #000000",
37 "+ c #c0c0c0",
38 "@ c #808080",
39 "# c #ffff00",
40 "$ c #ffffff",
41 /* pixels */
42 " ",
43 " @@@@@ ",
44 " @#+#+#@ ",
45 " @#+#+#+#@@@@@@ ",
46 " @$$$$$$$$$$$$@.",
47 " @$#+#+#+#+#+#@.",
48 " @$+#+#+#+#+#+@.",
49 " @$#+#+#+#+#+#@.",
50 " @$+#+#+#+#+#+@.",
51 " @$#+#+#+#+#+#@.",
52 " @$+#+#+#+#+#+@.",
53 " @$#+#+#+#+#+#@.",
54 " @@@@@@@@@@@@@@.",
55 " ..............",
56 " ",
57 " "};
58
59 /* XPM */
60 static char * icon2_xpm[] = {
61 /* width height ncolors chars_per_pixel */
62 "16 16 6 1",
63 /* colors */
64 " s None c None",
65 ". c #000000",
66 "+ c #c0c0c0",
67 "@ c #808080",
68 "# c #ffff00",
69 "$ c #ffffff",
70 /* pixels */
71 " ",
72 " @@@@@ ",
73 " @$$$$$@ ",
74 " @$#+#+#$@@@@@@ ",
75 " @$+#+#+$$$$$$@.",
76 " @$#+#+#+#+#+#@.",
77 "@@@@@@@@@@@@@#@.",
78 "@$$$$$$$$$$@@+@.",
79 "@$#+#+#+#+##.@@.",
80 " @$#+#+#+#+#+.@.",
81 " @$+#+#+#+#+#.@.",
82 " @$+#+#+#+##@..",
83 " @@@@@@@@@@@@@.",
84 " .............",
85 " ",
86 " "};
87
88 static const int ID_DIRCTRL = 1000;
89 static const int ID_TEXTCTRL = 1001;
90 static const int ID_OK = 1002;
91 static const int ID_CANCEL = 1003;
92 static const int ID_NEW = 1004;
93 //static const int ID_CHECK = 1005;
94
95 //-----------------------------------------------------------------------------
96 // wxDirItemData
97 //-----------------------------------------------------------------------------
98
99 class wxDirItemData : public wxTreeItemData
100 {
101 public:
102 wxDirItemData(wxString& path, wxString& name);
103 ~wxDirItemData();
104 bool HasSubDirs();
105 wxString *m_path, *m_name;
106 bool m_isHidden;
107 bool m_hasSubDirs;
108 };
109
110 //-----------------------------------------------------------------------------
111 // wxDirCtrl
112 //-----------------------------------------------------------------------------
113
114 class wxDirCtrl: public wxTreeCtrl
115 {
116 DECLARE_DYNAMIC_CLASS(wxDirCtrl)
117
118 public:
119 bool m_showHidden;
120 wxTreeItemId m_rootId;
121
122 wxDirCtrl(void);
123 wxDirCtrl(wxWindow *parent, const wxWindowID id = -1,
124 const wxString &dir = "/",
125 const wxPoint& pos = wxDefaultPosition,
126 const wxSize& size = wxDefaultSize,
127 const long style = wxTR_HAS_BUTTONS,
128 const wxString& name = "wxTreeCtrl" );
129 void OnExpandItem( const wxTreeEvent &event );
130 void OnCollapseItem( const wxTreeEvent &event );
131 void ShowHidden( const bool yesno );
132 DECLARE_EVENT_TABLE()
133 protected:
134 void CreateItems(const wxTreeItemId &parent);
135 void SetupSections(void);
136 wxArrayString m_paths, m_names;
137 };
138
139 //-----------------------------------------------------------------------------
140 // wxDirItemData
141 //-----------------------------------------------------------------------------
142
143 wxDirItemData::wxDirItemData(wxString& path, wxString& name)
144 {
145 m_path = new wxString(path);
146 m_name = new wxString(name);
147 /* Insert logic to detect hidden files here
148 * In UnixLand we just check whether the first char is a dot
149 * For FileNameFromPath read LastDirNameInThisPath ;-) */
150 // m_isHidden = (bool)(wxFileNameFromPath(*m_path)[0] == '.');
151 m_isHidden = FALSE;
152 m_hasSubDirs = HasSubDirs();
153 }
154
155 wxDirItemData:: ~wxDirItemData()
156 {
157 delete m_path;
158 delete m_name;
159 }
160
161 bool wxDirItemData::HasSubDirs()
162 {
163 wxString search = *m_path + "/*";
164 wxString path = wxFindFirstFile( search, wxDIR );
165 return (bool)(!path.IsNull());
166 }
167
168 //-----------------------------------------------------------------------------
169 // wxDirCtrl
170 //-----------------------------------------------------------------------------
171
172 IMPLEMENT_DYNAMIC_CLASS(wxDirCtrl,wxTreeCtrl)
173
174 BEGIN_EVENT_TABLE(wxDirCtrl,wxTreeCtrl)
175 EVT_TREE_ITEM_EXPANDING (-1, wxDirCtrl::OnExpandItem)
176 EVT_TREE_ITEM_COLLAPSED (-1, wxDirCtrl::OnCollapseItem)
177 END_EVENT_TABLE()
178
179 wxDirCtrl::wxDirCtrl(void)
180 {
181 m_showHidden = FALSE;
182 };
183
184 wxDirCtrl::wxDirCtrl(wxWindow *parent, const wxWindowID id, const wxString &WXUNUSED(dir),
185 const wxPoint& pos, const wxSize& size,
186 const long style, const wxString& name )
187 :
188 wxTreeCtrl( parent, id, pos, size, style, wxDefaultValidator, name )
189 {
190 m_imageListNormal = new wxImageList(16, 16, TRUE);
191 m_imageListNormal->Add(wxICON(icon1));
192 m_imageListNormal->Add(wxICON(icon2));
193 SetImageList(m_imageListNormal);
194
195 m_showHidden = FALSE;
196 m_rootId = AddRoot("Sections");
197 SetItemHasChildren(m_rootId);
198 Expand(m_rootId); // automatically expand first level
199 };
200
201 /* Quick macro. Don't worry, I'll #undef it later */
202 #define ADD_SECTION(a,b) \
203 if (wxPathExists((a))) { m_paths.Add( (a) ); m_names.Add( (b) ); };
204
205 void wxDirCtrl::SetupSections()
206 {
207 wxString home;
208
209 m_paths.Clear();
210 m_names.Clear();
211 ADD_SECTION("/", _("The Computer") )
212 wxGetHomeDir(&home);
213 ADD_SECTION(home, _("My Home") )
214 ADD_SECTION("/mnt", _("Mounted Devices") )
215 ADD_SECTION("/usr", _("User") )
216 ADD_SECTION("/usr/local", _("User Local") )
217 ADD_SECTION("/var", _("Variables") )
218 ADD_SECTION("/etc", _("Etcetera") )
219 ADD_SECTION("/tmp", _("Temporary") )
220 }
221 #undef ADD_SECTION
222
223 void wxDirCtrl::CreateItems(const wxTreeItemId &parent)
224 {
225 wxTreeItemId id;
226 wxDirItemData *dir_item;
227
228 // wxASSERT(m_paths.Count() == m_names.Count()); ?
229
230 for (unsigned int i=0; i<m_paths.Count(); i++)
231 {
232 dir_item = new wxDirItemData(m_paths[i],m_names[i]);
233 id = AppendItem( parent, m_names[i], 0, 1, dir_item);
234 if (dir_item->m_hasSubDirs) SetItemHasChildren(id);
235 }
236 }
237
238 void wxDirCtrl::OnExpandItem( const wxTreeEvent &event )
239 {
240 if (event.GetItem() == m_rootId)
241 {
242 SetupSections();
243 CreateItems(m_rootId);
244 return;
245 };
246
247 // This may take a longish time. Go to busy cursor
248 wxBeginBusyCursor();
249
250 wxDirItemData *data = (wxDirItemData *)GetItemData(event.GetItem());
251 wxASSERT(data);
252
253 wxString search,path,filename;
254
255 m_paths.Clear();
256 m_names.Clear();
257 search = *(data->m_path) + "/*";
258 for (path = wxFindFirstFile( search, wxDIR ); !path.IsNull();
259 path=wxFindNextFile() ) {
260 filename = wxFileNameFromPath( path );
261 /* Don't add "." and ".." to the tree. I think wxFindNextFile
262 * also checks this, but I don't quite understand what happens
263 * there. Also wxFindNextFile seems to swallow hidden dirs */
264 if ((filename != ".") && (filename != "..")) {
265 m_paths.Add(path);
266 m_names.Add(filename);
267 }
268 }
269 CreateItems(event.GetItem());
270 wxEndBusyCursor();
271 };
272
273
274 void wxDirCtrl::OnCollapseItem( const wxTreeEvent &event )
275 {
276 wxTreeItemId child, parent = event.GetItem();
277 long cookie;
278 /* Workaround because DeleteChildren has disapeared (why?) and
279 * CollapseAndReset doesn't work as advertised (deletes parent too) */
280 child = GetFirstChild(parent, cookie);
281 while (child.IsOk()) {
282 Delete(child);
283 /* Not GetNextChild below, because the cookie mechanism can't
284 * handle disappearing children! */
285 child = GetFirstChild(parent, cookie);
286 }
287 };
288
289 //-----------------------------------------------------------------------------
290 // wxDirDialog
291 //-----------------------------------------------------------------------------
292
293
294 #if !USE_SHARED_LIBRARY
295 IMPLEMENT_CLASS(wxDirDialog, wxDialog)
296 #else
297 IMPLEMENT_DYNAMIC_CLASS( wxDirDialog, wxDialog )
298 #endif
299
300 BEGIN_EVENT_TABLE( wxDirDialog, wxDialog )
301 EVT_TREE_KEY_DOWN (ID_DIRCTRL, wxDirDialog::OnTreeKeyDown)
302 EVT_TREE_SEL_CHANGED (ID_DIRCTRL, wxDirDialog::OnTreeSelected)
303 EVT_SIZE ( wxDirDialog::OnSize)
304 EVT_BUTTON (ID_OK, wxDirDialog::OnOK)
305 EVT_BUTTON (ID_CANCEL, wxDirDialog::OnCancel)
306 EVT_BUTTON (ID_NEW, wxDirDialog::OnNew)
307 EVT_TEXT_ENTER (ID_TEXTCTRL, wxDirDialog::OnOK)
308 // EVT_CHECKBOX (ID_CHECK, wxDirDialog::OnCheck)
309 END_EVENT_TABLE()
310
311 wxDirDialog::wxDirDialog(wxWindow *parent, const wxString& message,
312 const wxString& defaultPath, long style,
313 const wxPoint& pos) :
314 wxDialog(parent, -1, message, pos, wxSize(300,300),
315 wxDEFAULT_DIALOG_STYLE|wxDIALOG_MODAL)
316 {
317 m_message = message;
318 m_dialogStyle = style;
319 m_parent = parent;
320
321 m_path = defaultPath;
322
323 m_dir = new wxDirCtrl( this, ID_DIRCTRL, "/", wxDefaultPosition, wxDefaultSize, wxTR_HAS_BUTTONS | wxSUNKEN_BORDER );
324 m_input = new wxTextCtrl( this, ID_TEXTCTRL, m_path, wxDefaultPosition );
325 // m_check = new wxCheckBox( this, ID_CHECK, _("Show hidden") );
326 m_ok = new wxButton( this, ID_OK, _("OK") );
327 m_cancel = new wxButton( this, ID_CANCEL, _("Cancel") );
328 m_new = new wxButton( this, ID_NEW, _("New...") );
329
330 // m_check->SetValue(TRUE);
331 m_ok->SetDefault();
332 m_dir->SetFocus();
333
334 doSize();
335 }
336
337 void wxDirDialog::OnSize(wxSizeEvent& WXUNUSED(event))
338 {
339 doSize();
340 }
341
342 void wxDirDialog::doSize()
343 {
344 /* Figure out height of DirCtrl, which is what is left over by
345 * the textctrl and the buttons. Manually, because I can't seem
346 * to get the constraints stuff to do this */
347 int w,h,h2;
348
349 GetClientSize(&w, &h);
350 m_input->GetSize(&w,&h2); h -= h2;
351 m_ok->GetSize(&w, &h2); h -= h2;
352 //m_check->GetSize(&w, &h2); h -= h2;
353 h -= 20;
354
355 wxLayoutConstraints *c = new wxLayoutConstraints;
356 c->left.SameAs (this, wxLeft,5);
357 c->right.SameAs (this, wxRight,5);
358 c->height.Absolute (h);
359 c->top.SameAs (this, wxTop,5);
360 m_dir->SetConstraints(c);
361
362 c = new wxLayoutConstraints;
363 c->left.SameAs (this, wxLeft,5);
364 c->right.SameAs (this, wxRight,5);
365 c->height.AsIs ();
366 c->top.Below (m_dir,5);
367 m_input->SetConstraints(c);
368
369 /* c = new wxLayoutConstraints;
370 c->left.SameAs (this, wxLeft,5);
371 c->right.SameAs (this, wxRight,5);
372 c->height.AsIs ();
373 c->top.Below (m_input,5);
374 m_check->SetConstraints(c); */
375
376 c = new wxLayoutConstraints;
377 c->width.SameAs (m_cancel, wxWidth);
378 c->height.AsIs ();
379 c->top.Below (m_input,5);
380 c->centreX.PercentOf (this, wxWidth, 25);
381 m_ok->SetConstraints(c);
382
383 c = new wxLayoutConstraints;
384 c->width.SameAs (m_cancel, wxWidth);
385 c->height.AsIs ();
386 c->top.Below (m_input,5);
387 c->bottom.SameAs (this, wxBottom, 5);
388 c->centreX.PercentOf (this, wxWidth, 50);
389 m_new->SetConstraints(c);
390
391 c = new wxLayoutConstraints;
392 c->width.AsIs ();
393 c->height.AsIs ();
394 c->top.Below (m_input,5);
395 c->centreX.PercentOf (this, wxWidth, 75);
396 m_cancel->SetConstraints(c);
397
398 Layout();
399 }
400
401 int wxDirDialog::ShowModal()
402 {
403 m_input->SetValue( m_path );
404 return wxDialog::ShowModal();
405 }
406
407 void wxDirDialog::OnTreeSelected( wxTreeEvent &event )
408 {
409 wxDirItemData *data =
410 (wxDirItemData*)m_dir->GetItemData(event.GetItem());
411 if (data)
412 m_input->SetValue( *(data->m_path) );
413 };
414
415 void wxDirDialog::OnTreeKeyDown( wxKeyEvent &WXUNUSED(event) )
416 {
417 wxDirItemData *data =
418 (wxDirItemData*)m_dir->GetItemData(m_dir->GetSelection());
419 if (data)
420 m_input->SetValue( *(data->m_path) );
421 };
422
423 void wxDirDialog::OnOK( wxCommandEvent& WXUNUSED(event) )
424 {
425 m_path = m_input->GetValue();
426 // Does the path exist? (User may have typed anything in m_input)
427 if (wxPathExists(m_path)) {
428 // OK, path exists, we're done.
429 EndModal(wxID_OK);
430 return;
431 }
432 // Interact with user, find out if the dir is a typo or to be created
433 wxString msg( _("The directory ") );
434 msg = msg + m_path;
435 msg = msg + _("\ndoes not exist\nCreate it now?") ;
436 wxMessageDialog dialog(this, msg, _("Directory does not exist"), wxYES_NO);
437 if ( dialog.ShowModal() == wxID_YES ) {
438 // Okay, let's make it
439 if (wxMkdir(m_path)) {
440 // The new dir was created okay.
441 EndModal(wxID_OK);
442 return;
443 }
444 else {
445 // Trouble...
446 msg = _("Failed to create directory ")+m_path+
447 _("\n(Do you have the required permissions?)");
448 wxMessageDialog errmsg(this, msg, _("Error creating directory"), wxOK);
449 errmsg.ShowModal();
450 // We still don't have a valid dir. Back to the main dialog.
451 }
452 }
453 // User has answered NO to create dir.
454 }
455
456 void wxDirDialog::OnCancel( wxCommandEvent& WXUNUSED(event) )
457 {
458 EndModal(wxID_CANCEL);
459 }
460
461 void wxDirDialog::OnNew( wxCommandEvent& WXUNUSED(event) )
462 {
463 wxTextEntryDialog dialog(this, _("Enter the name of the directory to create"),
464 _("Create New Directory"), m_input->GetValue(), wxOK|wxCANCEL);
465
466 while (dialog.ShowModal() == wxID_OK)
467 {
468 // Okay, let's make it
469 if (wxMkdir(dialog.GetValue())) {
470 // The new dir was created okay.
471 m_path = dialog.GetValue();
472 return;
473 }
474 wxString msg = _("Failed to create directory ")+dialog.GetValue()+
475 _("\n(Do you have the required permissions?)") ;
476 wxMessageDialog errmsg(this, msg, _("Error creating directory"), wxOK);
477 errmsg.ShowModal();
478 // Show the create dialog again, until user clicks cancel or enters
479 // a valid dir.
480 }
481 }
482
483 /*
484 void wxDirDialog::OnCheck( wxCommandEvent& WXUNUSED(event) )
485 {
486 printf("Checkbox clicked: %s\n", ( m_check->GetValue() ? "on" : "off" ) );
487 }
488 */