]>
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 | ||
4bf78aae JS |
37 | // If compiled under Windows, this macro can cause problems |
38 | #ifdef GetFirstChild | |
39 | #undef GetFirstChild | |
40 | #endif | |
41 | ||
d7a15103 JS |
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 | ||
0659e7ee RR |
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 | " "}; | |
d7a15103 JS |
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 | ||
0659e7ee RR |
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" ); | |
1e6d9499 JS |
141 | void OnExpandItem(wxTreeEvent &event ); |
142 | void OnCollapseItem(wxTreeEvent &event ); | |
0659e7ee RR |
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 | ||
d7a15103 JS |
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)); | |
0659e7ee | 204 | m_imageListNormal->Add(wxICON(icon2)); |
d7a15103 JS |
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(); | |
87138c52 | 223 | ADD_SECTION(_T("/"), _("The Computer") ) |
d7a15103 JS |
224 | wxGetHomeDir(&home); |
225 | ADD_SECTION(home, _("My Home") ) | |
87138c52 OK |
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") ) | |
d7a15103 JS |
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]); | |
0659e7ee | 245 | id = AppendItem( parent, m_names[i], 0, 1, dir_item); |
d7a15103 JS |
246 | if (dir_item->m_hasSubDirs) SetItemHasChildren(id); |
247 | } | |
248 | } | |
249 | ||
1e6d9499 | 250 | void wxDirCtrl::OnExpandItem(wxTreeEvent &event ) |
d7a15103 JS |
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(); | |
63852e78 RR |
283 | |
284 | SortChildren( event.GetItem() ); | |
d7a15103 JS |
285 | }; |
286 | ||
287 | ||
1e6d9499 | 288 | void wxDirCtrl::OnCollapseItem(wxTreeEvent &event ) |
d7a15103 JS |
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") ); | |
3502e687 | 341 | m_cancel = new wxButton( this, ID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize(75,-1) ); |
d7a15103 JS |
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; | |
3502e687 | 367 | h -= 30; |
d7a15103 JS |
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 (); | |
3502e687 RR |
393 | c->top.Below (m_input,10); |
394 | c->centreX.PercentOf (this, wxWidth, 20); | |
d7a15103 JS |
395 | m_ok->SetConstraints(c); |
396 | ||
397 | c = new wxLayoutConstraints; | |
398 | c->width.SameAs (m_cancel, wxWidth); | |
399 | c->height.AsIs (); | |
3502e687 | 400 | c->top.Below (m_input,10); |
d7a15103 JS |
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 (); | |
3502e687 RR |
408 | c->top.Below (m_input,10); |
409 | c->centreX.PercentOf (this, wxWidth, 80); | |
d7a15103 JS |
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 | ||
1e6d9499 | 429 | void wxDirDialog::OnTreeKeyDown( wxTreeEvent &WXUNUSED(event) ) |
d7a15103 JS |
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 | */ |