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