Small fix for wx bug.
[amule.git] / src / DirectoryTreeCtrl.cpp
blob005aab2d9a0762dcee549cf9e0fb5902e7b633dc
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2003-2011 Robert Rostek ( tecxx@rrs.at )
6 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
7 //
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "DirectoryTreeCtrl.h" // Interface declarations
29 #include <wx/app.h>
30 #include <wx/filename.h>
31 #include <wx/imaglist.h>
33 #include <common/StringFunctions.h>
34 #include <common/FileFunctions.h>
35 #include "amule.h" // Needed for theApp
36 #include "muuli_wdr.h" // Needed for amuleSpecial
39 BEGIN_EVENT_TABLE(CDirectoryTreeCtrl, wxTreeCtrl)
40 EVT_TREE_ITEM_RIGHT_CLICK(wxID_ANY, CDirectoryTreeCtrl::OnRButtonDown)
41 EVT_TREE_ITEM_ACTIVATED(wxID_ANY, CDirectoryTreeCtrl::OnItemActivated)
42 EVT_TREE_ITEM_EXPANDED(wxID_ANY, CDirectoryTreeCtrl::OnItemExpanding)
43 END_EVENT_TABLE()
46 class CItemData : public wxTreeItemData
48 public:
49 CItemData(const CPath& pathComponent)
50 : m_path(pathComponent)
54 ~CItemData() {}
56 const CPath& GetPathComponent() const { return m_path; }
57 private:
58 CPath m_path;
62 CDirectoryTreeCtrl::CDirectoryTreeCtrl(wxWindow* parent, int id, const wxPoint& pos, wxSize siz, int flags)
63 : wxTreeCtrl(parent,id,pos,siz,flags,wxDefaultValidator,wxT("ShareTree"))
65 m_IsInit = false;
66 HasChanged = false;
67 #ifdef CLIENT_GUI
68 m_IsRemote = !theApp->m_connect->IsConnectedToLocalHost();
69 #else
70 m_IsRemote = false;
71 #endif
75 CDirectoryTreeCtrl::~CDirectoryTreeCtrl()
79 enum {
80 IMAGE_FOLDER = 0,
81 IMAGE_FOLDER_SUB_SHARED
85 void CDirectoryTreeCtrl::Init()
87 // already done ?
88 if (m_IsInit) {
89 return;
91 m_IsInit = true;
93 // init image(s)
94 wxImageList* images = new wxImageList(16, 16);
95 images->Add(wxBitmap(amuleSpecial(1)));
96 images->Add(wxBitmap(amuleSpecial(2)));
97 // Gives wxTreeCtrl ownership of the list
98 AssignImageList(images);
101 // Create an empty root item, which we can
102 // safely append when creating a full path.
103 m_root = AddRoot(wxEmptyString, IMAGE_FOLDER, -1,
104 new CItemData(CPath()));
106 if (!m_IsRemote) {
107 #ifndef __WXMSW__
108 AddChildItem(m_root, CPath(wxT("/")));
109 #else
110 // this might take awhile, so change the cursor
111 ::wxSetCursor(*wxHOURGLASS_CURSOR);
112 // retrieve bitmask of all drives available
113 uint32 drives = GetLogicalDrives();
114 drives >>= 1;
115 for (char drive = 'C'; drive <= 'Z'; drive++) {
116 drives >>= 1;
117 if (! (drives & 1)) { // skip non existant drives
118 continue;
120 wxString driveStr = CFormat(wxT("%c:")) % drive;
121 uint32 type = GetDriveType(driveStr + wxT("\\"));
123 // skip removable/undefined drives, share only fixed or remote drives
124 if ((type == 3 || type == 4) // fixed drive / remote drive
125 && CPath::DirExists(driveStr)) {
126 AddChildItem(m_root, CPath(driveStr));
129 ::wxSetCursor(*wxSTANDARD_CURSOR);
130 #endif
133 HasChanged = false;
135 UpdateSharedDirectories();
139 void CDirectoryTreeCtrl::OnItemExpanding(wxTreeEvent& evt)
141 wxTreeItemId hItem = evt.GetItem();
143 // Force reloading of the path
144 DeleteChildren(hItem);
145 AddSubdirectories(hItem, GetFullPath(hItem));
147 SortChildren(hItem);
151 void CDirectoryTreeCtrl::OnItemActivated(wxTreeEvent& evt)
153 if (!m_IsRemote) {
154 CheckChanged(evt.GetItem(), !IsBold(evt.GetItem()), false);
155 HasChanged = true;
160 void CDirectoryTreeCtrl::OnRButtonDown(wxTreeEvent& evt)
162 if (m_IsRemote) {
163 SelectItem(evt.GetItem()); // looks weird otherwise
164 } else {
165 // this might take awhile, so change the cursor
166 ::wxSetCursor(*wxHOURGLASS_CURSOR);
167 MarkChildren(evt.GetItem(), !IsBold(evt.GetItem()), false);
168 ::wxSetCursor(*wxSTANDARD_CURSOR);
169 HasChanged = true;
174 void CDirectoryTreeCtrl::MarkChildren(wxTreeItemId hChild, bool mark, bool recursed)
176 // Ensure that children are added, otherwise we might only get a "." entry.
177 if (!IsExpanded(hChild) && ItemHasChildren(hChild)) {
178 DeleteChildren(hChild);
179 AddSubdirectories(hChild, GetFullPath(hChild));
180 SortChildren(hChild);
183 wxTreeItemIdValue cookie;
184 wxTreeItemId hChild2 = GetFirstChild(hChild, cookie);
185 if (hChild2.IsOk()) {
186 SetHasSharedSubdirectory(hChild, mark);
188 while (hChild2.IsOk()) {
189 MarkChildren(hChild2, mark, true);
191 hChild2 = GetNextSibling(hChild2);
194 CheckChanged(hChild, mark, recursed);
198 void CDirectoryTreeCtrl::AddChildItem(wxTreeItemId hBranch, const CPath& item)
200 wxCHECK_RET(hBranch.IsOk(), wxT("Attempted to add children to invalid item"));
202 CPath fullPath = GetFullPath(hBranch).JoinPaths(item);
203 wxTreeItemId treeItem = AppendItem(hBranch, item.GetPrintable(),
204 IMAGE_FOLDER, -1,
205 new CItemData(item));
207 // BUG: wxGenericTreeControl won't set text calculated sizes when the item is created in AppendItem.
208 // This causes asserts on Mac and possibly other systems, so we have to repeat setting the string here.
209 SetItemText(treeItem, item.GetPrintable());
211 if (IsShared(fullPath)) {
212 SetItemBold(treeItem, true);
215 if (HasSharedSubdirectory(fullPath)) {
216 SetHasSharedSubdirectory(treeItem, true);
219 if (HasSubdirectories(fullPath)) {
220 // Trick. will show + if it has subdirs
221 AppendItem(treeItem, wxT("."));
226 CPath CDirectoryTreeCtrl::GetFullPath(wxTreeItemId hItem)
228 { wxCHECK_MSG(hItem.IsOk(), CPath(), wxT("Invalid item in GetFullPath")); }
230 CPath result;
231 for (; hItem.IsOk(); hItem = GetItemParent(hItem)) {
232 CItemData* data = dynamic_cast<CItemData*>(GetItemData(hItem));
233 wxCHECK_MSG(data, CPath(), wxT("Missing data-item in GetFullPath"));
235 result = data->GetPathComponent().JoinPaths(result);
238 return result;
242 void CDirectoryTreeCtrl::AddSubdirectories(wxTreeItemId hBranch, const CPath& path)
244 wxCHECK_RET(path.IsOk(), wxT("Invalid path in AddSubdirectories"));
246 CDirIterator sharedDir(path);
248 CPath dirName = sharedDir.GetFirstFile(CDirIterator::Dir);
249 while (dirName.IsOk()) {
250 AddChildItem(hBranch, dirName);
252 dirName = sharedDir.GetNextFile();
257 bool CDirectoryTreeCtrl::HasSubdirectories(const CPath& folder)
259 // Prevent error-messages if we try to traverse somewhere we have no access.
260 wxLogNull logNo;
262 return CDirIterator(folder).HasSubDirs();
266 void CDirectoryTreeCtrl::GetSharedDirectories(PathList* list)
268 wxCHECK_RET(list, wxT("Invalid list in GetSharedDirectories"));
270 for (SharedMap::iterator it = m_lstShared.begin(); it != m_lstShared.end(); it++) {
271 list->push_back(it->second);
276 void CDirectoryTreeCtrl::SetSharedDirectories(PathList* list)
278 wxCHECK_RET(list, wxT("Invalid list in SetSharedDirectories"));
280 m_lstShared.clear();
281 for (PathList::iterator it = list->begin(); it != list->end(); it++) {
282 m_lstShared.insert(SharedMapItem(GetKey(*it), *it));
285 if (m_IsInit) {
286 UpdateSharedDirectories();
291 wxString CDirectoryTreeCtrl::GetKey(const CPath& path)
293 if (m_IsRemote) {
294 return path.GetRaw();
297 // Sanity check, see IsSameAs() in Path.cpp
298 const wxString cwd = wxGetCwd();
299 const int flags = (wxPATH_NORM_ALL | wxPATH_NORM_CASE) & ~wxPATH_NORM_ENV_VARS;
300 wxFileName fn(path.GetRaw());
301 fn.Normalize(flags, cwd);
302 return fn.GetFullPath();
306 void CDirectoryTreeCtrl::UpdateSharedDirectories()
308 // ugly hack to at least show shared dirs in remote gui
309 if (m_IsRemote) {
310 DeleteChildren(m_root);
311 for (SharedMap::iterator it = m_lstShared.begin(); it != m_lstShared.end(); it++) {
312 AppendItem(m_root, it->second.GetPrintable(), IMAGE_FOLDER, -1, new CItemData(it->second));
314 return;
317 // Mark all shared root items (on windows this can be multiple
318 // drives, on unix there is only the root dir).
319 wxTreeItemIdValue cookie;
320 wxTreeItemId hChild = GetFirstChild(GetRootItem(), cookie);
322 while (hChild.IsOk()) {
323 // Does this drive have shared subfolders?
324 if (HasSharedSubdirectory(GetFullPath(hChild))) {
325 SetHasSharedSubdirectory(hChild, true);
328 // Is this drive shared?
329 if (IsShared(GetFullPath(hChild))) {
330 SetItemBold(hChild, true);
333 hChild = GetNextSibling(hChild);
338 bool CDirectoryTreeCtrl::HasSharedSubdirectory(const CPath& path)
340 SharedMap::iterator it = m_lstShared.upper_bound(GetKey(path) + wxFileName::GetPathSeparator());
341 if (it == m_lstShared.end()) {
342 return false;
344 // upper_bound() doesn't find the directory itself, so no need to check for that.
345 return it->second.StartsWith(path);
349 void CDirectoryTreeCtrl::SetHasSharedSubdirectory(wxTreeItemId hItem, bool add)
351 SetItemImage(hItem, add ? IMAGE_FOLDER_SUB_SHARED : IMAGE_FOLDER);
355 void CDirectoryTreeCtrl::CheckChanged(wxTreeItemId hItem, bool bChecked, bool recursed)
357 if (IsBold(hItem) != bChecked) {
358 SetItemBold(hItem, bChecked);
360 if (bChecked) {
361 AddShare(GetFullPath(hItem));
362 } else {
363 DelShare(GetFullPath(hItem));
366 if (!recursed) {
367 UpdateParentItems(hItem, bChecked);
373 bool CDirectoryTreeCtrl::IsShared(const CPath& path)
375 wxCHECK_MSG(path.IsOk(), false, wxT("Invalid path in IsShared"));
377 return m_lstShared.find(GetKey(path)) != m_lstShared.end();
381 void CDirectoryTreeCtrl::AddShare(const CPath& path)
383 wxCHECK_RET(path.IsOk(), wxT("Invalid path in AddShare"));
385 if (IsShared(path)) {
386 return;
389 m_lstShared.insert(SharedMapItem(GetKey(path), path));
393 void CDirectoryTreeCtrl::DelShare(const CPath& path)
395 wxCHECK_RET(path.IsOk(), wxT("Invalid path in DelShare"));
397 m_lstShared.erase(GetKey(path));
401 void CDirectoryTreeCtrl::UpdateParentItems(wxTreeItemId hChild, bool add)
403 wxTreeItemId parent = hChild;
404 while (parent != GetRootItem()) {
405 parent = GetItemParent(parent);
406 if (add) {
407 if (GetItemImage(parent) == IMAGE_FOLDER_SUB_SHARED) {
408 // parent already marked -> so are all its parents, finished
409 break;
410 } else {
411 SetHasSharedSubdirectory(parent, true);
413 } else {
414 if (GetItemImage(parent) == IMAGE_FOLDER_SUB_SHARED) {
415 // check if now there are still other shared dirs
416 if (HasSharedSubdirectory(GetFullPath(parent))) {
417 // yes, then further parents can stay red
418 break;
419 } else {
420 // no, further parents have to be checked too
421 SetHasSharedSubdirectory(parent, false);
423 } else { // should not happen (unmark child of which the parent is already unmarked
424 break;
429 // File_checked_for_headers