2 // This file is part of the aMule Project.
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 )
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.
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
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
)
46 class CItemData
: public wxTreeItemData
49 CItemData(const CPath
& pathComponent
)
50 : m_path(pathComponent
)
56 const CPath
& GetPathComponent() const { return 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"))
68 m_IsRemote
= !theApp
->m_connect
->IsConnectedToLocalHost();
75 CDirectoryTreeCtrl::~CDirectoryTreeCtrl()
81 IMAGE_FOLDER_SUB_SHARED
85 void CDirectoryTreeCtrl::Init()
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()));
108 AddChildItem(m_root
, CPath(wxT("/")));
110 // this might take awhile, so change the cursor
111 ::wxSetCursor(*wxHOURGLASS_CURSOR
);
112 // retrieve bitmask of all drives available
113 uint32 drives
= GetLogicalDrives();
115 for (char drive
= 'C'; drive
<= 'Z'; drive
++) {
117 if (! (drives
& 1)) { // skip non existant drives
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
);
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
));
151 void CDirectoryTreeCtrl::OnItemActivated(wxTreeEvent
& evt
)
154 CheckChanged(evt
.GetItem(), !IsBold(evt
.GetItem()), false);
160 void CDirectoryTreeCtrl::OnRButtonDown(wxTreeEvent
& evt
)
163 SelectItem(evt
.GetItem()); // looks weird otherwise
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
);
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(),
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")); }
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
);
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.
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"));
281 for (PathList::iterator it
= list
->begin(); it
!= list
->end(); it
++) {
282 m_lstShared
.insert(SharedMapItem(GetKey(*it
), *it
));
286 UpdateSharedDirectories();
291 wxString
CDirectoryTreeCtrl::GetKey(const CPath
& path
)
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
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
));
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()) {
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
);
361 AddShare(GetFullPath(hItem
));
363 DelShare(GetFullPath(hItem
));
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
)) {
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
);
407 if (GetItemImage(parent
) == IMAGE_FOLDER_SUB_SHARED
) {
408 // parent already marked -> so are all its parents, finished
411 SetHasSharedSubdirectory(parent
, true);
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
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
429 // File_checked_for_headers