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