source: filezilla/trunk/fuentes/src/interface/queue.cpp @ 3185

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 3 years ago

Update new version: 3.15.02

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