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