1 #include "filezilla.h"
2 #include "Options.h"
3 #include "queue.h"
4 #include "queueview_failed.h"
5 #include "queueview_successful.h"
6 #include "sizeformatting.h"
7 #include "timeformatting.h"
8 #include "themeprovider.h"
9
10 #include <wx/filedlg.h>
11
CQueueItem(CQueueItem * parent)12 CQueueItem::CQueueItem(CQueueItem* parent)
13 : m_parent(parent)
14 {
15 }
16
~CQueueItem()17 CQueueItem::~CQueueItem()
18 {
19 for (auto iter = m_children.begin() + m_removed_at_front; iter != m_children.end(); ++iter) {
20 delete *iter;
21 }
22 }
23
SetPriority(QueuePriority priority)24 void CQueueItem::SetPriority(QueuePriority priority)
25 {
26 for (auto iter = m_children.begin() + m_removed_at_front; iter != m_children.end(); ++iter) {
27 (*iter)->SetPriority(priority);
28 }
29 }
30
AddChild(CQueueItem * item)31 void CQueueItem::AddChild(CQueueItem* item)
32 {
33 wxASSERT(GetType() != QueueItemType::Folder);
34 wxASSERT(GetType() != QueueItemType::Status);
35
36 if (m_removed_at_front) {
37 m_children.erase(m_children.begin(), m_children.begin() + m_removed_at_front);
38 m_removed_at_front = 0;
39 }
40 m_children.push_back(item);
41
42 CQueueItem* parent = GetParent();
43 while (parent) {
44 if (parent->GetType() == QueueItemType::Server) {
45 static_cast<CServerItem*>(parent)->m_visibleOffspring += 1 + item->GetChildrenCount(true);
46 static_cast<CServerItem*>(parent)->m_maxCachedIndex = -1;
47 }
48 parent = parent->GetParent();
49 }
50 }
51
52
GetChild(unsigned int item,bool recursive)53 CQueueItem* CQueueItem::GetChild(unsigned int item, bool recursive)
54 {
55 if (!recursive) {
56 if (item + m_removed_at_front >= m_children.size()) {
57 return 0;
58 }
59 return *(m_children.begin() + item + m_removed_at_front);
60 }
61
62 std::vector<CQueueItem*>::iterator iter = m_children.begin() + m_removed_at_front;
63 for (; iter != m_children.end(); ++iter) {
64 if (!item) {
65 return *iter;
66 }
67
68 unsigned int count = (*iter)->GetChildrenCount(true);
69 if (item > count) {
70 item -= count + 1;
71 continue;
72 }
73
74 return (*iter)->GetChild(item - 1);
75 }
76 return 0;
77 }
78
GetChildrenCount(bool recursive) const79 unsigned int CQueueItem::GetChildrenCount(bool recursive) const
80 {
81 unsigned int count{};
82 if (!recursive) {
83 count = m_children.size() - m_removed_at_front;
84 }
85 else {
86 for (auto iter = m_children.begin() + m_removed_at_front; iter != m_children.end(); ++iter) {
87 count += 1 + (*iter)->GetChildrenCount(true);
88 }
89 }
90
91 return count;
92 }
93
RemoveChild(CQueueItem * pItem,bool destroy,bool forward)94 bool CQueueItem::RemoveChild(CQueueItem* pItem, bool destroy, bool forward)
95 {
96 int const oldVisibleOffspring = GetChildrenCount(true);
97 int visibleOffspring = oldVisibleOffspring;
98
99 bool deleted = false;
100
101 auto doRemove = [&](std::vector<CQueueItem*>::iterator iter) {
102 if (*iter == pItem) {
103 visibleOffspring -= 1;
104 visibleOffspring -= pItem->GetChildrenCount(true);
105 if (destroy) {
106 delete pItem;
107 }
108
109 if (iter - m_children.begin() - m_removed_at_front <= 10) {
110 ++m_removed_at_front;
111 unsigned int end = iter - m_children.begin();
112 for (int i = end; i >= m_removed_at_front; --i) {
113 m_children[i] = m_children[i - 1];
114 }
115 }
116 else {
117 m_children.erase(iter);
118 }
119
120 deleted = true;
121 return;
122 }
123
124 int childVisibleOffspring = (*iter)->GetChildrenCount(true);
125 if ((*iter)->RemoveChild(pItem, destroy)) {
126 visibleOffspring -= childVisibleOffspring - (*iter)->GetChildrenCount(true);
127 if (!((*iter)->m_children.size() - (*iter)->m_removed_at_front)) {
128 visibleOffspring -= 1;
129 delete *iter;
130
131 if (iter - m_children.begin() - m_removed_at_front <= 10) {
132 ++m_removed_at_front;
133 unsigned int end = iter - m_children.begin();
134 for (int i = end; i >= m_removed_at_front; --i) {
135 m_children[i] = m_children[i - 1];
136 }
137 }
138 else {
139 m_children.erase(iter);
140 }
141 }
142
143 deleted = true;
144 }
145 };
146
147 if (forward) {
148 for (auto iter = m_children.begin() + m_removed_at_front; iter != m_children.end(); ++iter ) {
149 doRemove(iter);
150 if (deleted) {
151 break;
152 }
153 }
154 }
155 else {
156 for (auto iter = m_children.rbegin(); iter != m_children.rend() - m_removed_at_front; ++iter ) {
157 doRemove(iter.base() - 1);
158 if (deleted) {
159 break;
160 }
161 }
162 }
163
164 if (!deleted) {
165 return false;
166 }
167
168 if (GetType() == QueueItemType::Server) {
169 static_cast<CServerItem*>(this)->m_visibleOffspring = visibleOffspring;
170 }
171
172 // Propagate new children count to parent
173 CQueueItem* parent = GetParent();
174 while (parent) {
175 if (parent->GetType() == QueueItemType::Server) {
176 static_cast<CServerItem*>(parent)->m_maxCachedIndex = -1;
177 static_cast<CServerItem*>(parent)->m_visibleOffspring -= oldVisibleOffspring - visibleOffspring;
178 }
179 parent = parent->GetParent();
180 }
181
182 return true;
183 }
184
GetTopLevelItem()185 CQueueItem* CQueueItem::GetTopLevelItem()
186 {
187 if (!m_parent) {
188 return this;
189 }
190
191 CQueueItem* newParent = m_parent;
192 CQueueItem* parent = 0;
193 while (newParent) {
194 parent = newParent;
195 newParent = newParent->GetParent();
196 }
197
198 return parent;
199 }
200
GetTopLevelItem() const201 const CQueueItem* CQueueItem::GetTopLevelItem() const
202 {
203 if (!m_parent) {
204 return this;
205 }
206
207 const CQueueItem* newParent = m_parent;
208 const CQueueItem* parent = 0;
209 while (newParent) {
210 parent = newParent;
211 newParent = newParent->GetParent();
212 }
213
214 return parent;
215 }
216
GetItemIndex() const217 int CQueueItem::GetItemIndex() const
218 {
219 const CQueueItem* pParent = GetParent();
220 if (!pParent) {
221 return 0;
222 }
223
224 int index = 1;
225 for (std::vector<CQueueItem*>::const_iterator iter = pParent->m_children.begin() + pParent->m_removed_at_front; iter != pParent->m_children.end(); ++iter) {
226 if (*iter == this) {
227 break;
228 }
229
230 index += (*iter)->GetChildrenCount(true) + 1;
231 }
232
233 return index + pParent->GetItemIndex();
234 }
235
CFileItem(CServerItem * parent,transfer_flags const & flags,std::wstring const & sourceFile,std::wstring const & targetFile,CLocalPath const & localPath,CServerPath const & remotePath,int64_t size)236 CFileItem::CFileItem(CServerItem* parent, transfer_flags const& flags,
237 std::wstring const& sourceFile, std::wstring const& targetFile,
238 CLocalPath const& localPath, CServerPath const& remotePath, int64_t size)
239 : CQueueItem(parent)
240 , flags_(flags)
241 , m_sourceFile(sourceFile)
242 , m_targetFile(targetFile.empty() ? fz::sparse_optional<std::wstring>() : fz::sparse_optional<std::wstring>(targetFile))
243 , m_localPath(localPath)
244 , m_remotePath(remotePath)
245 , m_size(size)
246 {
247 }
248
~CFileItem()249 CFileItem::~CFileItem()
250 {
251 }
252
SetPriority(QueuePriority priority)253 void CFileItem::SetPriority(QueuePriority priority)
254 {
255 if (priority == m_priority) {
256 return;
257 }
258
259 if (m_parent) {
260 CServerItem* parent = static_cast<CServerItem*>(m_parent);
261 parent->SetChildPriority(this, m_priority, priority);
262 }
263 m_priority = priority;
264 }
265
SetPriorityRaw(QueuePriority priority)266 void CFileItem::SetPriorityRaw(QueuePriority priority)
267 {
268 m_priority = priority;
269 }
270
GetPriority() const271 QueuePriority CFileItem::GetPriority() const
272 {
273 return m_priority;
274 }
275
SetActive(const bool active)276 void CFileItem::SetActive(const bool active)
277 {
278 if (active && !IsActive()) {
279 wxASSERT(!GetChildrenCount(false));
280 AddChild(new CStatusItem);
281 flags_ |= queue_flags::active;
282 }
283 else if (!active && IsActive()) {
284 CQueueItem* pItem = GetChild(0, false);
285 RemoveChild(pItem);
286 flags_ -= queue_flags::active;
287 }
288 }
289
SaveItem(pugi::xml_node & element) const290 void CFileItem::SaveItem(pugi::xml_node& element) const
291 {
292 if (m_edit != CEditHandler::none || !element) {
293 return;
294 }
295
296 auto file = element.append_child("File");
297
298 AddTextElement(file, "LocalFile", m_localPath.GetPath() + GetLocalFile());
299 AddTextElement(file, "RemoteFile", GetRemoteFile());
300 AddTextElement(file, "RemotePath", m_remotePath.GetSafePath());
301 AddTextElement(file, "Flags", static_cast<int64_t>(flags_ - queue_flags::mask));
302 if (m_size != -1) {
303 AddTextElement(file, "Size", m_size);
304 }
305 if (m_errorCount) {
306 AddTextElement(file, "ErrorCount", m_errorCount);
307 }
308 if (m_priority != QueuePriority::normal) {
309 AddTextElement(file, "Priority", static_cast<int>(m_priority));
310 }
311 if (m_defaultFileExistsAction != CFileExistsNotification::unknown) {
312 AddTextElement(file, "OverwriteAction", m_defaultFileExistsAction);
313 }
314 }
315
TryRemoveAll()316 bool CFileItem::TryRemoveAll()
317 {
318 if (!IsActive()) {
319 return true;
320 }
321
322 set_pending_remove(true);
323 return false;
324 }
325
SetTargetFile(std::wstring const & file)326 void CFileItem::SetTargetFile(std::wstring const& file)
327 {
328 if (!file.empty() && file != m_sourceFile) {
329 m_targetFile = fz::sparse_optional<std::wstring>(file);
330 }
331 else {
332 m_targetFile.clear();
333 }
334 }
335
SetStatusMessage(CFileItem::Status status)336 void CFileItem::SetStatusMessage(CFileItem::Status status)
337 {
338 m_status = status;
339 }
340
GetStatusMessage() const341 wxString const& CFileItem::GetStatusMessage() const
342 {
343 static wxString statusTexts[] = {
344 wxString(),
345 _("Incorrect password"),
346 _("Timeout"),
347 _("Disconnecting from previous server"),
348 _("Disconnected from server"),
349 _("Connecting"),
350 _("Connection attempt failed"),
351 _("Interrupted by user"),
352 _("Waiting for browsing connection"),
353 _("Waiting for password"),
354 _("Could not write to local file"),
355 _("Could not start transfer"),
356 _("Transferring"),
357 _("Creating directory")
358 };
359
360 return statusTexts[std::underlying_type_t<Status>(m_status)];
361 }
362
CFolderItem(CServerItem * parent,bool queued,CLocalPath const & localPath)363 CFolderItem::CFolderItem(CServerItem* parent, bool queued, CLocalPath const& localPath)
364 : CFileItem(parent, transfer_flags::download | (queued ? queue_flags::queued : transfer_flags{}), std::wstring(), std::wstring(), localPath, CServerPath(), -1)
365 {
366 }
367
CFolderItem(CServerItem * parent,bool queued,CServerPath const & remotePath,std::wstring const & remoteFile)368 CFolderItem::CFolderItem(CServerItem* parent, bool queued, CServerPath const& remotePath, std::wstring const& remoteFile)
369 : CFileItem(parent, queued ? queue_flags::queued : transfer_flags{}, std::wstring(), remoteFile, CLocalPath(), remotePath, -1)
370 {
371 }
372
SaveItem(pugi::xml_node & element) const373 void CFolderItem::SaveItem(pugi::xml_node& element) const
374 {
375 auto file = element.append_child("Folder");
376
377 if (Download()) {
378 AddTextElement(file, "LocalFile", GetLocalPath().GetPath() + GetLocalFile());
379 }
380 else {
381 AddTextElement(file, "RemoteFile", GetRemoteFile());
382 AddTextElement(file, "RemotePath", m_remotePath.GetSafePath());
383 }
384 AddTextElement(file, "Flags", static_cast<int64_t>(flags_ - queue_flags::mask));
385 }
386
SetActive(bool const active)387 void CFolderItem::SetActive(bool const active)
388 {
389 if (active) {
390 flags_ |= queue_flags::active;
391 }
392 else {
393 flags_ -= queue_flags::active;
394 }
395 }
396
CServerItem(Site const & site)397 CServerItem::CServerItem(Site const& site)
398 : m_activeCount(0)
399 , site_(site)
400 {
401 }
402
~CServerItem()403 CServerItem::~CServerItem()
404 {
405 }
406
GetName() const407 wxString CServerItem::GetName() const
408 {
409 return site_.Format(ServerFormat::with_user_and_optional_port);
410 }
411
AddChild(CQueueItem * pItem)412 void CServerItem::AddChild(CQueueItem* pItem)
413 {
414 CQueueItem::AddChild(pItem);
415 m_maxCachedIndex = -1;
416 m_visibleOffspring += 1 + pItem->GetChildrenCount(true);
417 if (pItem->GetType() == QueueItemType::File ||
418 pItem->GetType() == QueueItemType::Folder)
419 AddFileItemToList((CFileItem*)pItem);
420
421 wxASSERT(m_visibleOffspring >= static_cast<int>(m_children.size()) - m_removed_at_front);
422 wxASSERT(((m_children.size() - m_removed_at_front) != 0) == (m_visibleOffspring != 0));
423 }
424
GetChildrenCount(bool recursive) const425 unsigned int CServerItem::GetChildrenCount(bool recursive) const
426 {
427 if (!recursive) {
428 return m_children.size() - m_removed_at_front;
429 }
430
431 return m_visibleOffspring;
432 }
433
AddFileItemToList(CFileItem * pItem)434 void CServerItem::AddFileItemToList(CFileItem* pItem)
435 {
436 if (!pItem) {
437 return;
438 }
439
440 m_fileList[pItem->queued() ? 0 : 1][static_cast<int>(pItem->GetPriority())].push_back(pItem);
441 }
442
RemoveFileItemFromList(CFileItem * pItem,bool forward)443 void CServerItem::RemoveFileItemFromList(CFileItem* pItem, bool forward)
444 {
445 std::deque<CFileItem*>& fileList = m_fileList[pItem->queued() ? 0 : 1][static_cast<int>(pItem->GetPriority())];
446 if (forward) {
447 for (auto iter = fileList.begin(); iter != fileList.end(); ++iter) {
448 if (*iter == pItem) {
449 fileList.erase(iter);
450 return;
451 }
452 }
453 }
454 else {
455 for (auto iter = fileList.rbegin(); iter != fileList.rend(); ++iter) {
456 if (*iter == pItem) {
457 fileList.erase(iter.base() - 1);
458 return;
459 }
460 }
461 }
462 wxFAIL_MSG(_T("File item not deleted from m_fileList"));
463 }
464
SetDefaultFileExistsAction(CFileExistsNotification::OverwriteAction action,const TransferDirection direction)465 void CServerItem::SetDefaultFileExistsAction(CFileExistsNotification::OverwriteAction action, const TransferDirection direction)
466 {
467 for (auto iter = m_children.begin() + m_removed_at_front; iter != m_children.end(); ++iter) {
468 CQueueItem *pItem = *iter;
469 if (pItem->GetType() == QueueItemType::File) {
470 CFileItem* pFileItem = ((CFileItem *)pItem);
471 if (direction == TransferDirection::upload && pFileItem->Download()) {
472 continue;
473 }
474 else if (direction == TransferDirection::download && !pFileItem->Download()) {
475 continue;
476 }
477 pFileItem->m_defaultFileExistsAction = action;
478 }
479 }
480 }
481
Sort(int col,bool reverse)482 void CServerItem::Sort(int col, bool reverse)
483 {
484 auto const cmpLocalName = [](CFileItem const& l, CFileItem const& r) {
485 return std::tie(l.GetLocalPath(), l.GetLocalFile()) < std::tie(r.GetLocalPath(), r.GetLocalFile());
486 };
487 auto const cmpRemoteName = [](CFileItem const& l, CFileItem const& r) {
488 return std::tie(l.GetRemotePath(), l.GetRemoteFile()) < std::tie(r.GetRemotePath(), r.GetRemoteFile());
489 };
490 auto const cmpSize = [](CFileItem const& l, CFileItem const& r) {
491 return l.GetSize() < r.GetSize();
492 };
493 auto const cmpDirection = [](CFileItem const& l, CFileItem const& r) {
494 return l.Download() < r.Download();
495 };
496 auto const cmpStatus = [](CFileItem const& l, CFileItem const& r) {
497 return l.GetStatusMessage() < r.GetStatusMessage();
498 };
499
500 bool (*cmp)(CFileItem const&, CFileItem const&);
501 switch (col) {
502 case 0:
503 cmp = cmpLocalName;
504 break;
505 case 1:
506 cmp = cmpDirection;
507 break;
508 case 2:
509 cmp = cmpRemoteName;
510 break;
511 case 3:
512 cmp = cmpSize;
513 break;
514 case 4:
515 cmp = cmpStatus;
516 break;
517 default:
518 return;
519 }
520
521 std::function<bool(CQueueItem *, CQueueItem *)> fn;
522 if (reverse) {
523 fn = [&cmp](CQueueItem * l, CQueueItem * r) { return cmp(static_cast<CFileItem const&>(*r), static_cast<CFileItem const&>(*l)); };
524 }
525 else {
526 fn = [&cmp](CQueueItem * l, CQueueItem * r) { return cmp(static_cast<CFileItem const&>(*l), static_cast<CFileItem const&>(*r)); };
527 }
528
529
530 std::stable_sort(m_children.begin() + m_removed_at_front, m_children.end(), fn);
531
532 m_lookupCache.clear();
533 m_maxCachedIndex = -1;
534
535 // Rebuild m_fileList
536 for (size_t i = 0; i < static_cast<size_t>(QueuePriority::count); ++i) {
537 m_fileList[0][i].clear();
538 m_fileList[1][i].clear();
539 }
540
541 for (auto it = m_children.cbegin() + m_removed_at_front; it != m_children.cend(); ++it) {
542 CFileItem *pItem = static_cast<CFileItem*>(*it);
543 m_fileList[pItem->queued() ? 0 : 1][static_cast<int>(pItem->GetPriority())].push_back(pItem);
544 }
545 }
546
GetChild(unsigned int item,bool recursive)547 CQueueItem* CServerItem::GetChild(unsigned int item, bool recursive)
548 {
549 std::vector<CQueueItem*>::iterator iter;
550 if (!recursive) {
551 if (item + m_removed_at_front >= m_children.size()) {
552 return 0;
553 }
554 iter = m_children.begin();
555 iter += item + m_removed_at_front;
556 return *iter;
557 }
558
559 if ((int)item <= m_maxCachedIndex) {
560 // Index is cached
561 iter = m_children.begin() + m_removed_at_front;
562 iter += m_lookupCache[item].child;
563 item -= m_lookupCache[item].index;
564 if (!item) {
565 return *iter;
566 }
567 else {
568 return (*iter)->GetChild(item - 1);
569 }
570 }
571
572 int index;
573 int child;
574 iter = m_children.begin() + m_removed_at_front;
575 if (m_maxCachedIndex == -1) {
576 child = 0;
577 index = 0;
578 }
579 else {
580 // Start with loop with the last cached item index
581 iter += m_lookupCache[m_maxCachedIndex].child + 1;
582 item -= m_maxCachedIndex + 1;
583 index = m_maxCachedIndex + 1;
584 child = m_lookupCache[m_maxCachedIndex].child + 1;
585 }
586
587 for (; iter != m_children.end(); ++iter, ++child) {
588 if (!item) {
589 return *iter;
590 }
591
592 unsigned int count = (*iter)->GetChildrenCount(true);
593 if (item > count) {
594 if (m_maxCachedIndex == -1 && m_lookupCache.size() < (unsigned int)m_visibleOffspring)
595 m_lookupCache.resize(m_visibleOffspring);
596 for (unsigned int k = index; k <= index + count; ++k) {
597 m_lookupCache[k].child = child;
598 m_lookupCache[k].index = index;
599 }
600 m_maxCachedIndex = index + count;
601 item -= count + 1;
602 index += count + 1;
603 continue;
604 }
605
606 return (*iter)->GetChild(item - 1);
607 }
608 return 0;
609 }
610
611 namespace {
DoGetIdleChild(std::deque<CFileItem * > const * fileList,TransferDirection direction)612 CFileItem* DoGetIdleChild(std::deque<CFileItem*> const* fileList, TransferDirection direction)
613 {
614 int i = 0;
615 for (i = static_cast<int>(QueuePriority::count) - 1; i >= 0; --i) {
616 for (auto const& item : fileList[i]) {
617 if (item->IsActive()) {
618 continue;
619 }
620
621 if (direction == TransferDirection::both) {
622 return item;
623 }
624
625 if (direction == TransferDirection::download) {
626 if (item->Download()) {
627 return item;
628 }
629 }
630 else if (!item->Download()) {
631 return item;
632 }
633 }
634 }
635 return 0;
636 }
637 }
638
GetIdleChild(bool immediateOnly,TransferDirection direction)639 CFileItem* CServerItem::GetIdleChild(bool immediateOnly, TransferDirection direction)
640 {
641 CFileItem* item = DoGetIdleChild(m_fileList[1], direction);
642 if ( !item && !immediateOnly ) {
643 item = DoGetIdleChild(m_fileList[0], direction);
644 }
645 return item;
646 }
647
RemoveChild(CQueueItem * pItem,bool destroy,bool forward)648 bool CServerItem::RemoveChild(CQueueItem* pItem, bool destroy, bool forward)
649 {
650 if (!pItem) {
651 return false;
652 }
653
654 if (pItem->GetType() == QueueItemType::File || pItem->GetType() == QueueItemType::Folder) {
655 CFileItem* pFileItem = static_cast<CFileItem*>(pItem);
656 RemoveFileItemFromList(pFileItem, forward);
657 }
658
659 bool removed = CQueueItem::RemoveChild(pItem, destroy, forward);
660 if (removed) {
661 m_maxCachedIndex = -1;
662 }
663
664 wxASSERT(m_visibleOffspring >= static_cast<int>(m_children.size()) - m_removed_at_front);
665 wxASSERT(((m_children.size() - m_removed_at_front) != 0) == (m_visibleOffspring != 0));
666
667 return removed;
668 }
669
QueueImmediateFiles()670 void CServerItem::QueueImmediateFiles()
671 {
672 for (int i = 0; i < static_cast<int>(QueuePriority::count); ++i) {
673 std::deque<CFileItem*> activeList;
674 std::deque<CFileItem*>& fileList = m_fileList[1][i];
675 for (auto iter = fileList.rbegin(); iter != fileList.rend(); ++iter) {
676 CFileItem* item = *iter;
677 wxASSERT(!item->queued());
678 if (item->IsActive()) {
679 activeList.push_front(item);
680 }
681 else {
682 item->set_queued(true);
683 m_fileList[0][i].push_front(item);
684 }
685 }
686 std::swap(fileList, activeList);
687 }
688 }
689
QueueImmediateFile(CFileItem * pItem)690 void CServerItem::QueueImmediateFile(CFileItem* pItem)
691 {
692 if (pItem->queued()) {
693 return;
694 }
695
696 std::deque<CFileItem*>& fileList = m_fileList[1][static_cast<int>(pItem->GetPriority())];
697 for (auto iter = fileList.begin(); iter != fileList.end(); ++iter) {
698 if (*iter != pItem) {
699 continue;
700 }
701
702 pItem->set_queued(true);
703 fileList.erase(iter);
704 m_fileList[0][static_cast<int>(pItem->GetPriority())].push_front(pItem);
705 return;
706 }
707 wxASSERT(false);
708 }
709
SaveItem(pugi::xml_node & element) const710 void CServerItem::SaveItem(pugi::xml_node& element) const
711 {
712 auto server_node = element.append_child("Server");
713 SetServer(server_node, site_);
714
715 for (auto iter = m_children.cbegin() + m_removed_at_front; iter != m_children.cend(); ++iter) {
716 (*iter)->SaveItem(server_node);
717 }
718 }
719
GetTotalSize(int & filesWithUnknownSize,int & queuedFiles) const720 int64_t CServerItem::GetTotalSize(int& filesWithUnknownSize, int& queuedFiles) const
721 {
722 int64_t totalSize = 0;
723 for (int i = 0; i < static_cast<int>(QueuePriority::count); ++i) {
724 for (int j = 0; j < 2; ++j) {
725 const std::deque<CFileItem*>& fileList = m_fileList[j][i];
726 for (auto const& item : fileList) {
727 int64_t size = item->GetSize();
728 if (size >= 0) {
729 totalSize += size;
730 }
731 else {
732 filesWithUnknownSize++;
733 }
734 }
735 }
736 }
737
738 for (std::vector<CQueueItem*>::const_iterator iter = m_children.begin() + m_removed_at_front; iter != m_children.end(); ++iter) {
739 if ((*iter)->GetType() == QueueItemType::File ||
740 (*iter)->GetType() == QueueItemType::Folder)
741 queuedFiles++;
742 }
743
744 return totalSize;
745 }
746
TryRemoveAll()747 bool CServerItem::TryRemoveAll()
748 {
749 wxASSERT(!GetParent());
750
751 const int oldVisibleOffspring = m_visibleOffspring;
752 std::vector<CQueueItem*>::iterator iter;
753 std::vector<CQueueItem*> keepChildren;
754 m_visibleOffspring = 0;
755 for (iter = m_children.begin() + m_removed_at_front; iter != m_children.end(); ++iter) {
756 CQueueItem* pItem = *iter;
757 if (pItem->TryRemoveAll()) {
758 if (pItem->GetType() == QueueItemType::File || pItem->GetType() == QueueItemType::Folder) {
759 CFileItem* pFileItem = static_cast<CFileItem*>(pItem);
760 RemoveFileItemFromList(pFileItem, true);
761 }
762 delete pItem;
763 }
764 else {
765 keepChildren.push_back(pItem);
766 m_visibleOffspring++;
767 m_visibleOffspring += pItem->GetChildrenCount(true);
768 }
769 }
770 std::swap(m_children, keepChildren);
771 m_removed_at_front = 0;
772
773 m_maxCachedIndex = -1;
774
775 wxASSERT(oldVisibleOffspring >= m_visibleOffspring);
776 wxASSERT(m_visibleOffspring >= static_cast<int>(m_children.size()));
777 (void)oldVisibleOffspring;
778
779 return m_children.empty();
780 }
781
DetachChildren()782 void CServerItem::DetachChildren()
783 {
784 wxASSERT(!m_activeCount);
785
786 m_children.clear();
787 m_visibleOffspring = 0;
788 m_maxCachedIndex = -1;
789 m_removed_at_front = 0;
790
791 for (int i = 0; i < 2; ++i) {
792 for (int j = 0; j < static_cast<int>(QueuePriority::count); ++j) {
793 m_fileList[i][j].clear();
794 }
795 }
796 }
797
SetPriority(QueuePriority priority)798 void CServerItem::SetPriority(QueuePriority priority)
799 {
800 std::vector<CQueueItem*>::iterator iter;
801 for (iter = m_children.begin() + m_removed_at_front; iter != m_children.end(); ++iter) {
802 if ((*iter)->GetType() == QueueItemType::File) {
803 ((CFileItem*)(*iter))->SetPriorityRaw(priority);
804 }
805 else {
806 (*iter)->SetPriority(priority);
807 }
808 }
809
810 for (int i = 0; i < 2; ++i)
811 for (int j = 0; j < static_cast<int>(QueuePriority::count); ++j) {
812 if (j != static_cast<int>(priority)) {
813 std::move(m_fileList[i][j].begin(), m_fileList[i][j].end(), std::back_inserter(m_fileList[i][static_cast<int>(priority)]));
814 m_fileList[i][j].clear();
815 }
816 }
817 }
818
SetChildPriority(CFileItem * pItem,QueuePriority oldPriority,QueuePriority newPriority)819 void CServerItem::SetChildPriority(CFileItem* pItem, QueuePriority oldPriority, QueuePriority newPriority)
820 {
821 int i = pItem->queued() ? 0 : 1;
822
823 for (auto iter = m_fileList[i][static_cast<int>(oldPriority)].begin(); iter != m_fileList[i][static_cast<int>(oldPriority)].end(); ++iter) {
824 if (*iter != pItem) {
825 continue;
826 }
827
828 m_fileList[i][static_cast<int>(oldPriority)].erase(iter);
829 m_fileList[i][static_cast<int>(newPriority)].push_back(pItem);
830 return;
831 }
832
833 wxFAIL;
834 }
835
836 // --------------
837 // CQueueViewBase
838 // --------------
839
BEGIN_EVENT_TABLE(CQueueViewBase,wxListCtrlEx)840 BEGIN_EVENT_TABLE(CQueueViewBase, wxListCtrlEx)
841 EVT_ERASE_BACKGROUND(CQueueViewBase::OnEraseBackground)
842 EVT_CHAR(CQueueViewBase::OnChar)
843 EVT_LIST_COL_END_DRAG(wxID_ANY, CQueueViewBase::OnEndColumnDrag)
844 EVT_TIMER(wxID_ANY, CQueueViewBase::OnTimer)
845 EVT_KEY_DOWN(CQueueViewBase::OnKeyDown)
846 EVT_MENU(XRCID("ID_EXPORT"), CQueueViewBase::OnExport)
847 END_EVENT_TABLE()
848
849 CQueueViewBase::CQueueViewBase(CQueue* parent, int index, const wxString& title)
850 : wxListCtrlEx(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxCLIP_CHILDREN | wxLC_REPORT | wxLC_VIRTUAL | wxSUNKEN_BORDER | wxTAB_TRAVERSAL)
851 , m_pageIndex(index)
852 , m_title(title)
853 {
854 m_pQueue = parent;
855
856 // Create and assign the image list for the queue
857 wxSize s = CThemeProvider::GetIconSize(iconSizeSmall);
858 wxImageList* pImageList = new wxImageList(s.x, s.y);
859
860 pImageList->Add(CThemeProvider::Get()->CreateBitmap(_T("ART_SERVER"), wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall)));
861 pImageList->Add(CThemeProvider::Get()->CreateBitmap(_T("ART_FILE"), wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall)));
862 pImageList->Add(CThemeProvider::Get()->CreateBitmap(_T("ART_FOLDER"), wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall)));
863
864 AssignImageList(pImageList, wxIMAGE_LIST_SMALL);
865
866 m_filecount_delay_timer.SetOwner(this);
867 }
868
~CQueueViewBase()869 CQueueViewBase::~CQueueViewBase()
870 {
871 for (auto server : m_serverList) {
872 delete server;
873 }
874 }
875
GetQueueItem(unsigned int item) const876 CQueueItem* CQueueViewBase::GetQueueItem(unsigned int item) const
877 {
878 for (auto iter = m_serverList.cbegin(); iter != m_serverList.cend(); ++iter) {
879 if (!item) {
880 return *iter;
881 }
882
883 unsigned int count = (*iter)->GetChildrenCount(true);
884 if (item > count) {
885 item -= count + 1;
886 continue;
887 }
888
889 return (*iter)->GetChild(item - 1);
890 }
891 return 0;
892 }
893
GetItemIndex(const CQueueItem * item)894 int CQueueViewBase::GetItemIndex(const CQueueItem* item)
895 {
896 const CQueueItem* pTopLevelItem = item->GetTopLevelItem();
897
898 int index = 0;
899 for (std::vector<CServerItem*>::const_iterator iter = m_serverList.begin(); iter != m_serverList.end(); ++iter) {
900 if (pTopLevelItem == *iter) {
901 break;
902 }
903
904 index += (*iter)->GetChildrenCount(true) + 1;
905 }
906
907 return index + item->GetItemIndex();
908 }
909
OnEraseBackground(wxEraseEvent & event)910 void CQueueViewBase::OnEraseBackground(wxEraseEvent& event)
911 {
912 if (m_allowBackgroundErase) {
913 event.Skip();
914 }
915 }
916
OnGetItemText(long item,long column) const917 wxString CQueueViewBase::OnGetItemText(long item, long column) const
918 {
919 if (column < 0 || static_cast<size_t>(column) >= m_columns.size()) {
920 return wxString();
921 }
922
923 CQueueViewBase* pThis = const_cast<CQueueViewBase*>(this);
924
925 CQueueItem* pItem = pThis->GetQueueItem(item);
926 if (!pItem) {
927 return wxString();
928 }
929
930 return OnGetItemText(pItem, m_columns[column]);
931 }
932
OnGetItemText(CQueueItem * pItem,ColumnId column) const933 wxString CQueueViewBase::OnGetItemText(CQueueItem* pItem, ColumnId column) const
934 {
935 switch (pItem->GetType())
936 {
937 case QueueItemType::Server:
938 {
939 CServerItem* pServerItem = static_cast<CServerItem*>(pItem);
940 if (!column) {
941 return pServerItem->GetName();
942 }
943 }
944 break;
945 case QueueItemType::File:
946 {
947 CFileItem* pFileItem = static_cast<CFileItem*>(pItem);
948 switch (column)
949 {
950 case colLocalName:
951 return _T(" ") + pFileItem->GetLocalPath().GetPath() + pFileItem->GetLocalFile();
952 case colDirection:
953 if (pFileItem->Download()) {
954 if (pFileItem->queued()) {
955 return _T("<--");
956 }
957 else {
958 return _T("<<--");
959 }
960 }
961 else {
962 if (pFileItem->queued()) {
963 return _T("-->");
964 }
965 else {
966 return _T("-->>");
967 }
968 }
969 break;
970 case colRemoteName:
971 return pFileItem->GetRemotePath().FormatFilename(pFileItem->GetRemoteFile());
972 case colSize:
973 {
974 auto const& size = pFileItem->GetSize();
975 if (size >= 0) {
976 return CSizeFormat::Format(size);
977 }
978 else {
979 return _T("?");
980 }
981 }
982 case colPriority:
983 switch (pFileItem->GetPriority())
984 {
985 case QueuePriority::lowest:
986 return _("Lowest");
987 case QueuePriority::low:
988 return _("Low");
989 default:
990 case QueuePriority::normal:
991 return _("Normal");
992 case QueuePriority::high:
993 return _("High");
994 case QueuePriority::highest:
995 return _("Highest");
996 }
997 break;
998 case colTransferStatus:
999 case colErrorReason:
1000 return pFileItem->GetStatusMessage();
1001 case colTime:
1002 return CTimeFormat::FormatDateTime(pItem->GetTime());
1003 default:
1004 break;
1005 }
1006 }
1007 break;
1008 case QueueItemType::Folder:
1009 {
1010 CFileItem* pFolderItem = static_cast<CFolderItem*>(pItem);
1011 switch (column)
1012 {
1013 case colLocalName:
1014 if (pFolderItem->Download()) {
1015 return _T(" ") + pFolderItem->GetLocalPath().GetPath() + pFolderItem->GetLocalFile();
1016 }
1017 break;
1018 case colDirection:
1019 if (pFolderItem->Download())
1020 if (pFolderItem->queued()) {
1021 return _T("<--");
1022 }
1023 else {
1024 return _T("<<--");
1025 }
1026 else
1027 if (pFolderItem->queued()) {
1028 return _T("-->");
1029 }
1030 else {
1031 return _T("-->>");
1032 }
1033 break;
1034 case colRemoteName:
1035 if (!pFolderItem->Download()) {
1036 if (pFolderItem->GetRemoteFile().empty()) {
1037 return pFolderItem->GetRemotePath().GetPath();
1038 }
1039 else {
1040 return pFolderItem->GetRemotePath().FormatFilename(pFolderItem->GetRemoteFile());
1041 }
1042 }
1043 break;
1044 case colPriority:
1045 switch (pFolderItem->GetPriority())
1046 {
1047 case QueuePriority::lowest:
1048 return _("Lowest");
1049 case QueuePriority::low:
1050 return _("Low");
1051 default:
1052 case QueuePriority::normal:
1053 return _("Normal");
1054 case QueuePriority::high:
1055 return _("High");
1056 case QueuePriority::highest:
1057 return _("Highest");
1058 }
1059 break;
1060 case colTransferStatus:
1061 case colErrorReason:
1062 return pFolderItem->GetStatusMessage();
1063 case colTime:
1064 return CTimeFormat::FormatDateTime(pItem->GetTime());
1065 default:
1066 break;
1067 }
1068 }
1069 break;
1070 default:
1071 break;
1072 }
1073
1074 return wxString();
1075 }
1076
OnGetItemImage(long item) const1077 int CQueueViewBase::OnGetItemImage(long item) const
1078 {
1079 CQueueViewBase* pThis = const_cast<CQueueViewBase*>(this);
1080
1081 CQueueItem* pItem = pThis->GetQueueItem(item);
1082 if (!pItem) {
1083 return -1;
1084 }
1085
1086 switch (pItem->GetType())
1087 {
1088 case QueueItemType::Server:
1089 return 0;
1090 case QueueItemType::File:
1091 return 1;
1092 case QueueItemType::Folder:
1093 return 2;
1094 default:
1095 return -1;
1096 }
1097
1098 return -1;
1099 }
1100
UpdateSelections_ItemAdded(int added)1101 void CQueueViewBase::UpdateSelections_ItemAdded(int added)
1102 {
1103 // This is the fastest algorithm I can think of to move all
1104 // selections. Though worst case is still O(n), as with every algorithm to
1105 // move selections.
1106
1107 #ifndef __WXMSW__
1108 // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
1109 const int selection_count = GetSelectedItemCount();
1110 if (!selection_count) {
1111 return;
1112 }
1113 #endif
1114
1115 // Go through all items, keep record of the previous selected item
1116 int item = GetNextItem(added - 1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1117
1118 int prevItem = -1;
1119 while (item != -1) {
1120 if (prevItem != -1) {
1121 if (prevItem + 1 != item) {
1122 // Previous selected item was not the direct predecessor
1123 // That means we have to select the successor of prevItem
1124 // and unselect current item
1125 SetItemState(prevItem + 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1126 SetItemState(item, 0, wxLIST_STATE_SELECTED);
1127 }
1128 }
1129 else {
1130 // First selected item, no predecessor yet. We have to unselect
1131 SetItemState(item, 0, wxLIST_STATE_SELECTED);
1132 }
1133 prevItem = item;
1134
1135 item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1136 }
1137 if (prevItem != -1 && prevItem < m_itemCount - 1) {
1138 // Move the very last selected item
1139 SetItemState(prevItem + 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1140 }
1141
1142 SetItemState(added, 0, wxLIST_STATE_SELECTED);
1143 }
1144
UpdateSelections_ItemRangeAdded(int added,int count)1145 void CQueueViewBase::UpdateSelections_ItemRangeAdded(int added, int count)
1146 {
1147 wxASSERT(GetItemCount() == m_itemCount);
1148 #ifndef __WXMSW__
1149 // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
1150 const int selection_count = GetSelectedItemCount();
1151 if (!selection_count) {
1152 return;
1153 }
1154 #endif
1155
1156 std::deque<int> itemsToSelect;
1157
1158 // Go through all selected items
1159 int item = GetNextItem(added - 1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1160 while (item != -1) {
1161 // Select new items preceding to current one
1162 while (!itemsToSelect.empty() && itemsToSelect.front() < item) {
1163 SetItemState(itemsToSelect.front(), wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1164 itemsToSelect.pop_front();
1165 }
1166 if (!itemsToSelect.empty() && itemsToSelect.front() == item) {
1167 itemsToSelect.pop_front();
1168 }
1169 else {
1170 SetItemState(item, 0, wxLIST_STATE_SELECTED);
1171 }
1172
1173 if (item + count < GetItemCount()) {
1174 // On generic list controls, new items may be selected by default after
1175 // increasing the item count: Internally it sometimes keeps track
1176 // of only unselected items.
1177 itemsToSelect.push_back(item + count);
1178 }
1179
1180 item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1181 }
1182 for (auto const& sel : itemsToSelect) {
1183 SetItemState(sel, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1184 }
1185 }
1186
UpdateSelections_ItemRemoved(int removed)1187 void CQueueViewBase::UpdateSelections_ItemRemoved(int removed)
1188 {
1189 #ifndef __WXMSW__
1190 // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
1191 const int selection_count = GetSelectedItemCount();
1192 if (!selection_count) {
1193 return;
1194 }
1195 #endif
1196
1197 SetItemState(removed, 0, wxLIST_STATE_SELECTED);
1198
1199 int item = GetNextItem(removed - 1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1200
1201 int prevItem = -1;
1202 while (item != -1) {
1203 if (prevItem != -1) {
1204 if (prevItem + 1 != item) {
1205 // Previous selected item was not the direct predecessor
1206 // That means we have to select our predecessor and unselect
1207 // prevItem
1208 SetItemState(prevItem, 0, wxLIST_STATE_SELECTED);
1209 SetItemState(item - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1210 }
1211 }
1212 else {
1213 // First selected item, no predecessor yet. We have to unselect
1214 SetItemState(item - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1215 }
1216 prevItem = item;
1217
1218 item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1219 }
1220 if (prevItem != -1) {
1221 SetItemState(prevItem, 0, wxLIST_STATE_SELECTED);
1222 }
1223 }
1224
UpdateSelections_ItemRangeRemoved(int removed,int count)1225 void CQueueViewBase::UpdateSelections_ItemRangeRemoved(int removed, int count)
1226 {
1227 #ifndef __WXMSW__
1228 // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
1229 const int selection_count = GetSelectedItemCount();
1230 if (!selection_count) {
1231 return;
1232 }
1233 #endif
1234
1235 SetItemState(removed, 0, wxLIST_STATE_SELECTED);
1236
1237 std::deque<int> itemsToUnselect;
1238
1239 int item = GetNextItem(removed - 1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1240
1241 while (item != -1) {
1242 // Unselect new items preceding to current one
1243 while (!itemsToUnselect.empty() && itemsToUnselect.front() < item - count) {
1244 SetItemState(itemsToUnselect.front(), 0, wxLIST_STATE_SELECTED);
1245 itemsToUnselect.pop_front();
1246 }
1247
1248 if (itemsToUnselect.empty()) {
1249 SetItemState(item - count, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1250 }
1251 else if (itemsToUnselect.front() == item - count) {
1252 itemsToUnselect.pop_front();
1253 }
1254 else {
1255 SetItemState(item - count, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1256 }
1257
1258 itemsToUnselect.push_back(item);
1259
1260 item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1261 }
1262 for (auto const& unsel : itemsToUnselect) {
1263 SetItemState(unsel, 0, wxLIST_STATE_SELECTED);
1264 }
1265 }
1266
AddQueueColumn(ColumnId id)1267 void CQueueViewBase::AddQueueColumn(ColumnId id)
1268 {
1269 const unsigned long widths[8] = { 180, 60, 180, 80, 60, 100, 150, 150 };
1270 const int alignment[8] = { wxLIST_FORMAT_LEFT, wxLIST_FORMAT_CENTER, wxLIST_FORMAT_LEFT, wxLIST_FORMAT_RIGHT, wxLIST_FORMAT_LEFT, wxLIST_FORMAT_LEFT, wxLIST_FORMAT_LEFT, wxLIST_FORMAT_LEFT };
1271 const wxString names[8] = { _("Server/Local file"), _("Direction"), _("Remote file"), _("Size"), _("Priority"), _("Time"), _("Status"), _("Reason") };
1272
1273 AddColumn(names[id], alignment[id], widths[id]);
1274 m_columns.push_back(id);
1275 }
1276
CreateColumns(std::vector<ColumnId> const & extraColumns)1277 void CQueueViewBase::CreateColumns(std::vector<ColumnId> const& extraColumns)
1278 {
1279 AddQueueColumn(colLocalName);
1280 AddQueueColumn(colDirection);
1281 AddQueueColumn(colRemoteName);
1282 AddQueueColumn(colSize);
1283 AddQueueColumn(colPriority);
1284
1285 for (auto id : extraColumns) {
1286 AddQueueColumn(id);
1287 }
1288
1289 LoadColumnSettings(OPTION_QUEUE_COLUMN_WIDTHS, OPTIONS_NUM, OPTIONS_NUM);
1290 }
1291
GetServerItem(Site const & site)1292 CServerItem* CQueueViewBase::GetServerItem(Site const& site)
1293 {
1294 for (auto iter = m_serverList.begin(); iter != m_serverList.end(); ++iter) {
1295 if ((*iter)->GetSite() == site) {
1296 return *iter;
1297 }
1298 }
1299 return NULL;
1300 }
1301
CreateServerItem(Site const & site)1302 CServerItem* CQueueViewBase::CreateServerItem(Site const& site)
1303 {
1304 CServerItem* pItem = GetServerItem(site);
1305
1306 if (!pItem) {
1307 pItem = new CServerItem(site);
1308 m_serverList.push_back(pItem);
1309 ++m_itemCount;
1310
1311 wxASSERT(m_insertionStart == -1);
1312 wxASSERT(m_insertionCount == 0);
1313
1314 m_insertionStart = GetItemIndex(pItem);
1315 m_insertionCount = 1;
1316 }
1317
1318 return pItem;
1319 }
1320
CommitChanges()1321 void CQueueViewBase::CommitChanges()
1322 {
1323 SaveSetItemCount(m_itemCount);
1324
1325 if (m_insertionStart != -1) {
1326 wxASSERT(m_insertionCount != 0);
1327 if (m_insertionCount == 1) {
1328 UpdateSelections_ItemAdded(m_insertionStart);
1329 }
1330 else {
1331 UpdateSelections_ItemRangeAdded(m_insertionStart, m_insertionCount);
1332 }
1333
1334 m_insertionStart = -1;
1335 m_insertionCount = 0;
1336 }
1337
1338 if (m_fileCountChanged) {
1339 DisplayNumberQueuedFiles();
1340 }
1341 }
1342
DisplayNumberQueuedFiles()1343 void CQueueViewBase::DisplayNumberQueuedFiles()
1344 {
1345 if (m_filecount_delay_timer.IsRunning()) {
1346 m_fileCountChanged = true;
1347 return;
1348 }
1349
1350 wxString str;
1351 if (m_fileCount > 0) {
1352 str.Printf(m_title + _T(" (%d)"), m_fileCount);
1353 }
1354 else {
1355 str = m_title;
1356 }
1357 m_pQueue->SetPageText(m_pageIndex, str);
1358
1359 m_fileCountChanged = false;
1360
1361 m_filecount_delay_timer.Start(200, true);
1362 }
1363
InsertItem(CServerItem * pServerItem,CQueueItem * pItem)1364 void CQueueViewBase::InsertItem(CServerItem* pServerItem, CQueueItem* pItem)
1365 {
1366 const int newIndex = GetItemIndex(pServerItem) + pServerItem->GetChildrenCount(true) + 1;
1367
1368 pServerItem->AddChild(pItem);
1369 m_itemCount++;
1370
1371 if (m_insertionStart == -1) {
1372 assert(!m_insertionCount);
1373 m_insertionStart = newIndex;
1374 }
1375 ++m_insertionCount;
1376
1377 if (pItem->GetType() == QueueItemType::File || pItem->GetType() == QueueItemType::Folder) {
1378 m_fileCount++;
1379 m_fileCountChanged = true;
1380 }
1381 }
1382
RemoveItem(CQueueItem * pItem,bool destroy,bool updateItemCount,bool updateSelections,bool forward)1383 bool CQueueViewBase::RemoveItem(CQueueItem* pItem, bool destroy, bool updateItemCount, bool updateSelections, bool forward)
1384 {
1385 if (pItem->GetType() == QueueItemType::File || pItem->GetType() == QueueItemType::Folder) {
1386 wxASSERT(m_fileCount > 0);
1387 m_fileCount--;
1388 m_fileCountChanged = true;
1389 }
1390
1391 int index = 0;
1392 if (updateSelections) {
1393 index = GetItemIndex(pItem);
1394 }
1395
1396 CQueueItem* topLevelItem = pItem->GetTopLevelItem();
1397
1398 int count = topLevelItem->GetChildrenCount(true);
1399 topLevelItem->RemoveChild(pItem, destroy, forward);
1400
1401 bool didRemoveParent;
1402
1403 int oldCount = m_itemCount;
1404 if (!topLevelItem->GetChild(0)) {
1405 std::vector<CServerItem*>::iterator iter;
1406 for (iter = m_serverList.begin(); iter != m_serverList.end(); ++iter) {
1407 if (*iter == topLevelItem) {
1408 break;
1409 }
1410 }
1411 if (iter != m_serverList.end()) {
1412 m_serverList.erase(iter);
1413 }
1414
1415 UpdateSelections_ItemRangeRemoved(GetItemIndex(topLevelItem), count + 1);
1416
1417 delete topLevelItem;
1418
1419 m_itemCount -= count + 1;
1420 if (updateItemCount) {
1421 SaveSetItemCount(m_itemCount);
1422 }
1423
1424 didRemoveParent = true;
1425 }
1426 else {
1427 count -= topLevelItem->GetChildrenCount(true);
1428
1429 if (updateSelections) {
1430 UpdateSelections_ItemRangeRemoved(index, count);
1431 }
1432
1433 m_itemCount -= count;
1434 if (updateItemCount) {
1435 SaveSetItemCount(m_itemCount);
1436 }
1437
1438 didRemoveParent = false;
1439 }
1440
1441 if (updateItemCount) {
1442 if (m_fileCountChanged) {
1443 DisplayNumberQueuedFiles();
1444 }
1445 if (oldCount > m_itemCount) {
1446 bool eraseBackground = GetTopItem() + GetCountPerPage() + 1 >= m_itemCount;
1447 RefreshListOnly(eraseBackground);
1448 if (eraseBackground) {
1449 Update();
1450 }
1451 }
1452 }
1453
1454 return didRemoveParent;
1455 }
1456
RefreshItem(const CQueueItem * pItem)1457 void CQueueViewBase::RefreshItem(const CQueueItem* pItem)
1458 {
1459 wxASSERT(pItem);
1460 int index = GetItemIndex(pItem);
1461
1462 #ifdef __WXMSW__
1463 wxRect rect;
1464 GetItemRect(index, rect);
1465 RefreshRect(rect, false);
1466 #else
1467 wxListCtrl::RefreshItem(index);
1468 #endif
1469 }
1470
OnChar(wxKeyEvent & event)1471 void CQueueViewBase::OnChar(wxKeyEvent& event)
1472 {
1473 const int code = event.GetKeyCode();
1474 if (code != WXK_LEFT && code != WXK_RIGHT) {
1475 event.Skip();
1476 return;
1477 }
1478
1479 bool forward;
1480 if (GetLayoutDirection() != wxLayout_RightToLeft) {
1481 forward = code == WXK_RIGHT;
1482 }
1483 else {
1484 forward = code == WXK_LEFT;
1485 }
1486
1487 int selection = m_pQueue->GetSelection();
1488 if (selection > 0 && !forward) {
1489 selection--;
1490 }
1491 else if (selection < (int)m_pQueue->GetPageCount() - 1 && forward) {
1492 selection++;
1493 }
1494 else {
1495 return;
1496 }
1497
1498 m_pQueue->SetSelection(selection);
1499 }
1500
OnEndColumnDrag(wxListEvent &)1501 void CQueueViewBase::OnEndColumnDrag(wxListEvent&)
1502 {
1503 for (unsigned int i = 0; i < m_pQueue->GetPageCount(); ++i) {
1504 wxWindow *page = m_pQueue->GetPage(i);
1505
1506 wxListCtrl* queue_page = dynamic_cast<wxListCtrl*>(page);
1507 if (!queue_page || queue_page == this) {
1508 continue;
1509 }
1510
1511 for (int col = 0; col < wxMin(GetColumnCount(), queue_page->GetColumnCount()); ++col) {
1512 queue_page->SetColumnWidth(col, GetColumnWidth(col));
1513 }
1514 }
1515 }
1516
OnTimer(wxTimerEvent & event)1517 void CQueueViewBase::OnTimer(wxTimerEvent& event)
1518 {
1519 if (event.GetId() != m_filecount_delay_timer.GetId()) {
1520 event.Skip();
1521 return;
1522 }
1523
1524 if (m_fileCountChanged) {
1525 DisplayNumberQueuedFiles();
1526 }
1527 }
1528
OnKeyDown(wxKeyEvent & event)1529 void CQueueViewBase::OnKeyDown(wxKeyEvent& event)
1530 {
1531 const int code = event.GetKeyCode();
1532 const int mods = event.GetModifiers();
1533 if (code == 'A' && (mods == wxMOD_CMD || mods == (wxMOD_CONTROL | wxMOD_META))) {
1534 for (unsigned int i = 0; i < (unsigned int)GetItemCount(); ++i) {
1535 SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1536 }
1537 }
1538 else {
1539 event.Skip();
1540 }
1541 }
1542
WriteToFile(pugi::xml_node element) const1543 void CQueueViewBase::WriteToFile(pugi::xml_node element) const
1544 {
1545 auto queue = element.child("Queue");
1546 if (!queue) {
1547 queue = element.append_child("Queue");
1548 }
1549
1550 for (std::vector<CServerItem*>::const_iterator iter = m_serverList.begin(); iter != m_serverList.end(); ++iter) {
1551 (*iter)->SaveItem(queue);
1552 }
1553 }
1554
OnExport(wxCommandEvent &)1555 void CQueueViewBase::OnExport(wxCommandEvent&)
1556 {
1557 wxFileDialog dlg(m_parent, _("Select file for exported queue"), wxString(),
1558 _T("FileZilla.xml"), _T("XML files (*.xml)|*.xml"),
1559 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
1560
1561 if (dlg.ShowModal() != wxID_OK) {
1562 return;
1563 }
1564
1565 CXmlFile xml(dlg.GetPath().ToStdWstring());
1566
1567 auto exportRoot = xml.CreateEmpty();
1568
1569 WriteToFile(exportRoot);
1570
1571 SaveWithErrorDialog(xml);
1572 }
1573
1574 // ------
1575 // CQueue
1576 // ------
1577
CQueue(wxWindow * parent,CMainFrame * pMainFrame,CAsyncRequestQueue * pAsyncRequestQueue,cert_store & certStore)1578 CQueue::CQueue(wxWindow* parent, CMainFrame *pMainFrame, CAsyncRequestQueue *pAsyncRequestQueue, cert_store & certStore)
1579 {
1580 Create(parent, -1, wxDefaultPosition, wxDefaultSize, wxNO_BORDER | wxAUI_NB_BOTTOM);
1581 SetExArtProvider();
1582
1583 m_pQueueView = new CQueueView(this, 0, pMainFrame, pAsyncRequestQueue, certStore);
1584 AddPage(m_pQueueView, m_pQueueView->GetTitle());
1585
1586 m_pQueueView_Failed = new CQueueViewFailed(this, 1);
1587 AddPage(m_pQueueView_Failed, m_pQueueView_Failed->GetTitle());
1588 m_pQueueView_Successful = new CQueueViewSuccessful(this, 2);
1589 AddPage(m_pQueueView_Successful, m_pQueueView_Successful->GetTitle());
1590
1591 RemoveExtraBorders();
1592
1593 m_pQueueView->LoadQueue();
1594 }
1595
SetFocus()1596 void CQueue::SetFocus()
1597 {
1598 GetPage(GetSelection())->SetFocus();
1599 }
1600