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