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