Upstream tarball 9690
[amule.git] / src / DirectoryTreeCtrl.cpp
blob4b60aa2acaae79731cfadb10dc5fd6e559233442
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2003-2008 Robert Rostek ( tecxx@rrs.at )
6 // Copyright (c) 2002-2008 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 "muuli_wdr.h" // Needed for amuleSpecial
38 BEGIN_EVENT_TABLE(CDirectoryTreeCtrl, wxTreeCtrl)
39 EVT_TREE_ITEM_RIGHT_CLICK(wxID_ANY, CDirectoryTreeCtrl::OnRButtonDown)
40 EVT_TREE_ITEM_ACTIVATED(wxID_ANY, CDirectoryTreeCtrl::OnItemActivated)
41 EVT_TREE_ITEM_EXPANDED(wxID_ANY, CDirectoryTreeCtrl::OnItemExpanding)
42 END_EVENT_TABLE()
45 class CItemData : public wxTreeItemData
47 public:
48 CItemData(const CPath& pathComponent)
49 : m_path(pathComponent)
53 ~CItemData() {}
55 const CPath& GetPathComponent() const { return m_path; }
56 private:
57 CPath m_path;
61 CDirectoryTreeCtrl::CDirectoryTreeCtrl(wxWindow* parent, int id, const wxPoint& pos, wxSize siz, int flags)
62 : wxTreeCtrl(parent,id,pos,siz,flags,wxDefaultValidator,wxT("ShareTree"))
64 m_IsInit = false;
68 CDirectoryTreeCtrl::~CDirectoryTreeCtrl()
72 enum {
73 IMAGE_FOLDER = 0,
74 IMAGE_FOLDER_SUB_SHARED
78 void CDirectoryTreeCtrl::Init()
80 // already done ?
81 if (m_IsInit) {
82 return;
84 m_IsInit = true;
86 // init image(s)
87 wxImageList* images = new wxImageList(16, 16);
88 images->Add(wxBitmap(amuleSpecial(1)));
89 images->Add(wxBitmap(amuleSpecial(2)));
90 // Gives wxTreeCtrl ownership of the list
91 AssignImageList(images);
94 // Create an empty root item, which we can
95 // safely append when creating a full path.
96 wxTreeItemId root = AddRoot(wxEmptyString, IMAGE_FOLDER, -1,
97 new CItemData(CPath()));
100 #ifndef __WXMSW__
101 AddChildItem(root, CPath(wxT("/")));
102 #else
103 // this might take awhile, so change the cursor
104 ::wxSetCursor(*wxHOURGLASS_CURSOR);
105 // retrieve bitmask of all drives available
106 uint32 drives = GetLogicalDrives();
107 drives >>= 1;
108 for (char drive = 'C'; drive <= 'Z'; drive++) {
109 drives >>= 1;
110 if (! (drives & 1)) { // skip non existant drives
111 continue;
113 wxString driveStr = wxString::Format(wxT("%c:"), drive);
114 uint32 type = GetDriveType(driveStr + wxT("\\"));
116 // skip removable/undefined drives, share only fixed or remote drives
117 if ((type == 3 || type == 4) // fixed drive / remote drive
118 && CPath::DirExists(driveStr)) {
119 AddChildItem(root, CPath(driveStr));
122 ::wxSetCursor(*wxSTANDARD_CURSOR);
123 #endif
125 HasChanged = false;
127 UpdateSharedDirectories();
131 void CDirectoryTreeCtrl::OnItemExpanding(wxTreeEvent& evt)
133 wxTreeItemId hItem = evt.GetItem();
135 // Force reloading of the path
136 DeleteChildren(hItem);
137 AddSubdirectories(hItem, GetFullPath(hItem));
139 SortChildren(hItem);
143 void CDirectoryTreeCtrl::OnItemActivated(wxTreeEvent& evt)
145 CheckChanged(evt.GetItem(), !IsBold(evt.GetItem()), false);
146 HasChanged = true;
150 void CDirectoryTreeCtrl::OnRButtonDown(wxTreeEvent& evt)
152 MarkChildren(evt.GetItem(), !IsBold(evt.GetItem()), false);
153 HasChanged = true;
157 void CDirectoryTreeCtrl::MarkChildren(wxTreeItemId hChild, bool mark, bool recursed)
159 // Ensure that children are added, otherwise we might only get a "." entry.
160 if (!IsExpanded(hChild) && ItemHasChildren(hChild)) {
161 DeleteChildren(hChild);
162 AddSubdirectories(hChild, GetFullPath(hChild));
163 SortChildren(hChild);
166 wxTreeItemIdValue cookie;
167 wxTreeItemId hChild2 = GetFirstChild(hChild, cookie);
168 if (hChild2.IsOk()) {
169 SetHasSharedSubdirectory(hChild, mark);
171 while (hChild2.IsOk()) {
172 MarkChildren(hChild2, mark, true);
174 hChild2 = GetNextSibling(hChild2);
177 CheckChanged(hChild, mark, recursed);
181 void CDirectoryTreeCtrl::AddChildItem(wxTreeItemId hBranch, const CPath& item)
183 wxCHECK_RET(hBranch.IsOk(), wxT("Attempted to add children to invalid item"));
185 CPath fullPath = GetFullPath(hBranch).JoinPaths(item);
186 wxTreeItemId treeItem = AppendItem(hBranch, item.GetPrintable(),
187 IMAGE_FOLDER, -1,
188 new CItemData(item));
190 if (IsShared(fullPath)) {
191 SetItemBold(treeItem, true);
194 if (HasSharedSubdirectory(fullPath)) {
195 SetHasSharedSubdirectory(treeItem, true);
198 if (HasSubdirectories(fullPath)) {
199 // Trick. will show + if it has subdirs
200 AppendItem(treeItem, wxT("."));
205 CPath CDirectoryTreeCtrl::GetFullPath(wxTreeItemId hItem)
207 wxCHECK_MSG(hItem.IsOk(), CPath(), wxT("Invalid item in GetFullPath"));
209 CPath result;
210 for (; hItem.IsOk(); hItem = GetItemParent(hItem)) {
211 CItemData* data = dynamic_cast<CItemData*>(GetItemData(hItem));
212 wxCHECK_MSG(data, CPath(), wxT("Missing data-item in GetFullPath"));
214 result = data->GetPathComponent().JoinPaths(result);
217 return result;
221 void CDirectoryTreeCtrl::AddSubdirectories(wxTreeItemId hBranch, const CPath& path)
223 wxCHECK_RET(path.IsOk(), wxT("Invalid path in AddSubdirectories"));
225 CDirIterator sharedDir(path);
227 CPath dirName = sharedDir.GetFirstFile(CDirIterator::Dir);
228 while (dirName.IsOk()) {
229 AddChildItem(hBranch, dirName);
231 dirName = sharedDir.GetNextFile();
236 bool CDirectoryTreeCtrl::HasSubdirectories(const CPath& folder)
238 // Prevent error-messages if we try to traverse somewhere we have no access.
239 wxLogNull logNo;
241 return CDirIterator(folder).HasSubDirs();
245 void CDirectoryTreeCtrl::GetSharedDirectories(PathList* list)
247 wxCHECK_RET(list, wxT("Invalid list in GetSharedDirectories"));
249 for (SharedMap::iterator it = m_lstShared.begin(); it != m_lstShared.end(); it++) {
250 list->push_back(it->second);
255 void CDirectoryTreeCtrl::SetSharedDirectories(PathList* list)
257 wxCHECK_RET(list, wxT("Invalid list in SetSharedDirectories"));
259 m_lstShared.clear();
260 for (PathList::iterator it = list->begin(); it != list->end(); it++) {
261 m_lstShared.insert(SharedMapItem(GetKey(*it), *it));
264 if (m_IsInit) {
265 UpdateSharedDirectories();
270 wxString CDirectoryTreeCtrl::GetKey(const CPath& path)
272 // Sanity check, see IsSameAs() in Path.cpp
273 const wxString cwd = wxGetCwd();
274 const int flags = (wxPATH_NORM_ALL | wxPATH_NORM_CASE) & ~wxPATH_NORM_ENV_VARS;
275 wxFileName fn(path.GetRaw());
276 fn.Normalize(flags, cwd);
277 return fn.GetFullPath();
281 void CDirectoryTreeCtrl::UpdateSharedDirectories()
283 // Mark all shared root items (on windows this can be multiple
284 // drives, on unix there is only the root dir).
285 wxTreeItemIdValue cookie;
286 wxTreeItemId hChild = GetFirstChild(GetRootItem(), cookie);
288 while (hChild.IsOk()) {
289 // Does this drive have shared subfolders?
290 if (HasSharedSubdirectory(GetFullPath(hChild))) {
291 SetHasSharedSubdirectory(hChild, true);
294 // Is this drive shared?
295 if (IsShared(GetFullPath(hChild))) {
296 SetItemBold(hChild, true);
299 hChild = GetNextSibling(hChild);
304 bool CDirectoryTreeCtrl::HasSharedSubdirectory(const CPath& path)
306 SharedMap::iterator it = m_lstShared.begin();
307 for (; it != m_lstShared.end(); ++it) {
308 // IsSameDir to avoid the case where 'path' itself is shared.
309 if (it->second.StartsWith(path) && (!it->second.IsSameDir(path))) {
310 return true;
314 return false;
318 void CDirectoryTreeCtrl::SetHasSharedSubdirectory(wxTreeItemId hItem, bool add)
320 SetItemImage(hItem, add ? IMAGE_FOLDER_SUB_SHARED : IMAGE_FOLDER);
324 void CDirectoryTreeCtrl::CheckChanged(wxTreeItemId hItem, bool bChecked, bool recursed)
326 if (IsBold(hItem) != bChecked) {
327 SetItemBold(hItem, bChecked);
329 if (bChecked) {
330 AddShare(GetFullPath(hItem));
331 } else {
332 DelShare(GetFullPath(hItem));
335 if (!recursed) {
336 UpdateParentItems(hItem, bChecked);
342 bool CDirectoryTreeCtrl::IsShared(const CPath& path)
344 wxCHECK_MSG(path.IsOk(), false, wxT("Invalid path in IsShared"));
346 return m_lstShared.find(GetKey(path)) != m_lstShared.end();
350 void CDirectoryTreeCtrl::AddShare(const CPath& path)
352 wxCHECK_RET(path.IsOk(), wxT("Invalid path in AddShare"));
354 if (IsShared(path)) {
355 return;
358 m_lstShared.insert(SharedMapItem(GetKey(path), path));
362 void CDirectoryTreeCtrl::DelShare(const CPath& path)
364 wxCHECK_RET(path.IsOk(), wxT("Invalid path in DelShare"));
366 m_lstShared.erase(GetKey(path));
370 void CDirectoryTreeCtrl::UpdateParentItems(wxTreeItemId hChild, bool add)
372 wxTreeItemId parent = hChild;
373 while (parent != GetRootItem()) {
374 parent = GetItemParent(parent);
375 if (add) {
376 if (GetItemImage(parent) == IMAGE_FOLDER_SUB_SHARED) {
377 // parent already marked -> so are all its parents, finished
378 break;
379 } else {
380 SetHasSharedSubdirectory(parent, true);
382 } else {
383 if (GetItemImage(parent) == IMAGE_FOLDER_SUB_SHARED) {
384 // check if now there are still other shared dirs
385 if (HasSharedSubdirectory(GetFullPath(parent))) {
386 // yes, then further parents can stay red
387 break;
388 } else {
389 // no, further parents have to be checked too
390 SetHasSharedSubdirectory(parent, false);
392 } else { // should not happen (unmark child of which the parent is already unmarked
393 break;
398 // File_checked_for_headers