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

Last change on this file since 130 was 130, checked in by jrpelegrina, 4 years ago

First release to xenial

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