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

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

Update new version: 3.15.02

File size: 11.9 KB
Line 
1#include <filezilla.h>
2#include "queue.h"
3#include "statuslinectrl.h"
4#include <wx/dcbuffer.h>
5#include "Options.h"
6#include "sizeformatting.h"
7
8#include <algorithm>
9
10BEGIN_EVENT_TABLE(CStatusLineCtrl, wxWindow)
11EVT_PAINT(CStatusLineCtrl::OnPaint)
12EVT_TIMER(wxID_ANY, CStatusLineCtrl::OnTimer)
13EVT_ERASE_BACKGROUND(CStatusLineCtrl::OnEraseBackground)
14END_EVENT_TABLE()
15
16int CStatusLineCtrl::m_fieldOffsets[4];
17wxCoord CStatusLineCtrl::m_textHeight;
18bool CStatusLineCtrl::m_initialized = false;
19
20#define PROGRESSBAR_WIDTH 102
21
22CStatusLineCtrl::CStatusLineCtrl(CQueueView* pParent, const t_EngineData* const pEngineData, const wxRect& initialPosition)
23        : m_pParent(pParent)
24        , m_pEngineData(pEngineData)
25        , m_gcLastTimeStamp(wxDateTime::Now())
26{
27        wxASSERT(pEngineData);
28
29        Create(pParent->GetMainWindow(), wxID_ANY, initialPosition.GetPosition(), initialPosition.GetSize());
30
31        SetOwnFont(pParent->GetFont());
32        SetForegroundColour(pParent->GetForegroundColour());
33        SetBackgroundStyle(wxBG_STYLE_CUSTOM);
34        SetBackgroundColour(pParent->GetBackgroundColour());
35
36        m_transferStatusTimer.SetOwner(this);
37
38        InitFieldOffsets();
39
40        ClearTransferStatus();
41}
42
43void CStatusLineCtrl::InitFieldOffsets()
44{
45        if (m_initialized)
46                return;
47        m_initialized = true;
48
49        // Calculate field widths so that the contents fit under every language.
50        wxClientDC dc(this);
51        dc.SetFont(GetFont());
52
53        wxCoord w, h;
54        wxTimeSpan elapsed(100, 0, 0);
55        // @translator: This is a date/time formatting specifier. See https://wiki.filezilla-project.org/Date_and_Time_formatting
56        dc.GetTextExtent(elapsed.Format(_("%H:%M:%S elapsed")), &w, &h);
57        m_textHeight = h;
58        m_fieldOffsets[0] = 50 + w;
59
60        // @translator: This is a date/time formatting specifier. See https://wiki.filezilla-project.org/Date_and_Time_formatting
61        dc.GetTextExtent(elapsed.Format(_("%H:%M:%S left")), &w, &h);
62        m_fieldOffsets[1] = m_fieldOffsets[0] + 20 + w;
63
64        m_fieldOffsets[2] = m_fieldOffsets[1] + 20;
65        m_fieldOffsets[3] = m_fieldOffsets[2] + PROGRESSBAR_WIDTH + 20;
66}
67
68CStatusLineCtrl::~CStatusLineCtrl()
69{
70        if (!status_.empty() && status_.totalSize >= 0)
71                m_pEngineData->pItem->SetSize(status_.totalSize);
72
73        if (m_transferStatusTimer.IsRunning())
74                m_transferStatusTimer.Stop();
75}
76
77void CStatusLineCtrl::OnPaint(wxPaintEvent&)
78{
79        wxPaintDC dc(this);
80
81        wxRect rect = GetRect();
82
83        int refresh = 0;
84        if (!m_data.IsOk() || rect.GetWidth() != m_data.GetWidth() || rect.GetHeight() != m_data.GetHeight()) {
85                m_mdc.reset();
86                m_data = wxBitmap(rect.GetWidth(), rect.GetHeight());
87                m_mdc = std::make_unique<wxMemoryDC>(m_data);
88                // Use same layout direction as the DC which bitmap is drawn on.
89                // This avoids problem with mirrored characters on RTL locales.
90                m_mdc->SetLayoutDirection(dc.GetLayoutDirection());
91                refresh = 31;
92        }
93
94        fz::duration elapsed;
95        int left = -1;
96        wxFileOffset rate;
97        wxString bytes_and_rate;
98        int bar_split = -1;
99        int permill = -1;
100
101        if (status_.empty()) {
102                if (m_previousStatusText != m_statusText) {
103                        // Clear background
104                        m_mdc->SetFont(GetFont());
105                        m_mdc->SetPen(GetBackgroundColour());
106                        m_mdc->SetBrush(GetBackgroundColour());
107                        m_mdc->SetTextForeground(GetForegroundColour());
108                        m_mdc->DrawRectangle(0, 0, rect.GetWidth(), rect.GetHeight());
109                        wxCoord h = (rect.GetHeight() - m_textHeight) / 2;
110                        m_mdc->DrawText(m_statusText, 50, h);
111                        m_previousStatusText = m_statusText;
112                        refresh = 0;
113                }
114        }
115        else {
116                if (!m_previousStatusText.empty()) {
117                        m_previousStatusText.clear();
118                        refresh = 31;
119                }
120
121                int elapsed_milli_seconds = 0;
122                if (status_.started.empty()) {
123                        elapsed = fz::datetime::now() - status_.started;
124                        elapsed_milli_seconds = static_cast<int>(elapsed.get_milliseconds()); // Assume it doesn't overflow
125                }
126
127                if (elapsed_milli_seconds / 1000 != m_last_elapsed_seconds) {
128                        refresh |= 1;
129                        m_last_elapsed_seconds = elapsed_milli_seconds / 1000;
130                }
131
132                if (COptions::Get()->GetOptionVal(OPTION_SPEED_DISPLAY))
133                        rate = GetCurrentSpeed();
134                else
135                        rate = GetSpeed(elapsed_milli_seconds);
136
137                if (status_.totalSize > 0 && elapsed_milli_seconds >= 1000 && rate > 0) {
138                        wxFileOffset r = status_.totalSize - status_.currentOffset;
139                        left = r / rate + 1;
140                        if (r)
141                                ++left;
142
143                        if (left < 0)
144                                left = 0;
145                }
146
147                if (m_last_left != left) {
148                        refresh |= 2;
149                        m_last_left = left;
150                }
151
152                const wxString bytestr = CSizeFormat::Format(status_.currentOffset, true, CSizeFormat::bytes, COptions::Get()->GetOptionVal(OPTION_SIZE_USETHOUSANDSEP) != 0, 0);
153                if (elapsed_milli_seconds >= 1000 && rate > -1) {
154                        CSizeFormat::_format format = static_cast<CSizeFormat::_format>(COptions::Get()->GetOptionVal(OPTION_SIZE_FORMAT));
155                        if (format == CSizeFormat::bytes)
156                                format = CSizeFormat::iec;
157                        const wxString ratestr = CSizeFormat::Format(rate, true,
158                                                                                                                 format,
159                                                                                                                 COptions::Get()->GetOptionVal(OPTION_SIZE_USETHOUSANDSEP) != 0,
160                                                                                                                 COptions::Get()->GetOptionVal(OPTION_SIZE_DECIMALPLACES));
161                        bytes_and_rate.Printf(_("%s (%s/s)"), bytestr, ratestr );
162                }
163                else
164                        bytes_and_rate.Printf(_("%s (? B/s)"), bytestr);
165
166                if (m_last_bytes_and_rate != bytes_and_rate) {
167                        refresh |= 8;
168                        m_last_bytes_and_rate = bytes_and_rate;
169                }
170
171                if (status_.totalSize > 0) {
172                        bar_split = static_cast<int>(status_.currentOffset * (PROGRESSBAR_WIDTH - 2) / status_.totalSize);
173                        if (bar_split > (PROGRESSBAR_WIDTH - 2))
174                                bar_split = PROGRESSBAR_WIDTH - 2;
175
176                        if (status_.currentOffset > status_.totalSize)
177                                permill = 1001;
178                        else
179                                permill = static_cast<int>(status_.currentOffset * 1000 / status_.totalSize);
180                }
181
182                if (m_last_bar_split != bar_split || m_last_permill != permill) {
183                        refresh |= 4;
184                        m_last_bar_split = bar_split;
185                        m_last_permill = permill;
186                }
187        }
188
189        if (refresh) {
190                m_mdc->SetFont(GetFont());
191                m_mdc->SetPen(GetBackgroundColour());
192                m_mdc->SetBrush(GetBackgroundColour());
193                m_mdc->SetTextForeground(GetForegroundColour());
194
195                // Get character height so that we can center the text vertically.
196                wxCoord h = (rect.GetHeight() - m_textHeight) / 2;
197
198                if (refresh & 1) {
199                        m_mdc->DrawRectangle(0, 0, m_fieldOffsets[0], rect.GetHeight());
200                        DrawRightAlignedText(*m_mdc, wxTimeSpan::Milliseconds(elapsed.get_milliseconds()).Format(_("%H:%M:%S elapsed")), m_fieldOffsets[0], h);
201                }
202                if (refresh & 2) {
203                        m_mdc->DrawRectangle(m_fieldOffsets[0], 0, m_fieldOffsets[1] - m_fieldOffsets[0], rect.GetHeight());
204                        if (left != -1) {
205                                wxTimeSpan timeLeft(0, 0, left);
206                                DrawRightAlignedText(*m_mdc, timeLeft.Format(_("%H:%M:%S left")), m_fieldOffsets[1], h);
207                        }
208                        else
209                                DrawRightAlignedText(*m_mdc, _("--:--:-- left"), m_fieldOffsets[1], h);
210                }
211                if (refresh & 8) {
212                        m_mdc->DrawRectangle(m_fieldOffsets[3], 0, rect.GetWidth() - m_fieldOffsets[3], rect.GetHeight());
213                        m_mdc->DrawText(bytes_and_rate, m_fieldOffsets[3], h);
214                }
215                if (refresh & 16) {
216                        m_mdc->DrawRectangle(m_fieldOffsets[1], 0, m_fieldOffsets[2] - m_fieldOffsets[1], rect.GetHeight());
217                }
218                if (refresh & 4) {
219                        m_mdc->DrawRectangle(m_fieldOffsets[2], 0, m_fieldOffsets[3] - m_fieldOffsets[2], rect.GetHeight());
220                        if (bar_split != -1)
221                                DrawProgressBar(*m_mdc, m_fieldOffsets[2], 1, rect.GetHeight() - 2, bar_split, permill);
222                }
223        }
224        dc.Blit(0, 0, rect.GetWidth(), rect.GetHeight(), m_mdc.get(), 0, 0);
225}
226
227void CStatusLineCtrl::ClearTransferStatus()
228{
229        if (!status_.empty() && status_.totalSize >= 0) {
230                m_pParent->UpdateItemSize(m_pEngineData->pItem, status_.totalSize);
231        }
232        status_.clear();
233
234        switch (m_pEngineData->state)
235        {
236        case t_EngineData::disconnect:
237                m_statusText = _("Disconnecting from previous server");
238                break;
239        case t_EngineData::cancel:
240                m_statusText = _("Waiting for transfer to be cancelled");
241                break;
242        case t_EngineData::connect:
243                m_statusText = wxString::Format(_("Connecting to %s"), m_pEngineData->lastServer.FormatServer());
244                break;
245        default:
246                m_statusText = _("Transferring");
247                break;
248        }
249
250        if (m_transferStatusTimer.IsRunning())
251                m_transferStatusTimer.Stop();
252
253        m_past_data_count = 0;
254        m_gcLastOffset = -1;
255        m_gcLastSpeed = -1;
256        Refresh(false);
257}
258
259void CStatusLineCtrl::SetTransferStatus(CTransferStatus const& status)
260{
261        if (!status) {
262                ClearTransferStatus();
263        }
264        else {
265                status_ = status;
266
267                m_lastOffset = status.currentOffset;
268
269                if (!m_transferStatusTimer.IsRunning())
270                        m_transferStatusTimer.Start(100);
271                Refresh(false);
272        }
273}
274
275void CStatusLineCtrl::OnTimer(wxTimerEvent&)
276{
277        if (!m_pEngineData || !m_pEngineData->pEngine) {
278                m_transferStatusTimer.Stop();
279                return;
280        }
281
282        bool changed;
283        CTransferStatus status = m_pEngineData->pEngine->GetTransferStatus(changed);
284
285        if (status.empty())
286                ClearTransferStatus();
287        else if (changed) {
288                if (status.madeProgress && !status.list &&
289                        m_pEngineData->pItem->GetType() == QueueItemType::File)
290                {
291                        CFileItem* pItem = (CFileItem*)m_pEngineData->pItem;
292                        pItem->set_made_progress(true);
293                }
294                SetTransferStatus(status);
295        }
296        else
297                m_transferStatusTimer.Stop();
298}
299
300void CStatusLineCtrl::DrawRightAlignedText(wxDC& dc, wxString text, int x, int y)
301{
302        wxCoord w, h;
303        dc.GetTextExtent(text, &w, &h);
304        x -= w;
305
306        dc.DrawText(text, x, y);
307}
308
309void CStatusLineCtrl::OnEraseBackground(wxEraseEvent&)
310{
311        // Don't erase background, only causes status line to flicker.
312}
313
314void CStatusLineCtrl::DrawProgressBar(wxDC& dc, int x, int y, int height, int bar_split, int permill)
315{
316        wxASSERT(bar_split != -1);
317        wxASSERT(permill != -1);
318
319        // Draw right part
320        dc.SetPen(*wxTRANSPARENT_PEN);
321        dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
322        dc.DrawRectangle(x + 1 + bar_split, y + 1, PROGRESSBAR_WIDTH - bar_split - 1, height - 2);
323
324        if (bar_split && height > 2) {
325                // Draw pretty gradient
326
327                int greenmin = 128;
328                int greenmax = 255;
329                int colourCount = ((height + 1) / 2);
330
331                for (int i = 0; i < colourCount; ++i) {
332                        int curGreen = greenmax - ((greenmax - greenmin) * i / (colourCount - 1));
333                        dc.SetPen(wxPen(wxColour(0, curGreen, 0)));
334                        dc.DrawLine(x + 1, y + colourCount - i, x + 1 + bar_split, y + colourCount - i);
335                        dc.DrawLine(x + 1, y + height - colourCount + i - 1, x + 1 + bar_split, y + height - colourCount + i - 1);
336                }
337        }
338
339        dc.SetPen(*wxBLACK_PEN);
340        dc.SetBrush(*wxTRANSPARENT_BRUSH);
341        dc.DrawRectangle(x, y, PROGRESSBAR_WIDTH, height);
342
343        // Draw percentage-done text
344        wxString text;
345        if (permill > 1000) {
346                text = _T("> 100.0%");
347        }
348        else {
349                text = wxString::Format(_T("%d.%d%%"), permill / 10, permill % 10);
350        }
351
352        wxCoord w, h;
353        dc.GetTextExtent(text, &w, &h);
354        dc.DrawText(text, x + PROGRESSBAR_WIDTH / 2 - w / 2, y + height / 2 - h / 2);
355}
356
357wxFileOffset CStatusLineCtrl::GetSpeed(int elapsed_milli_seconds)
358{
359        if (status_.empty())
360                return -1;
361
362        if (elapsed_milli_seconds <= 0) {
363                return -1;
364        }
365
366        int elapsed_seconds = elapsed_milli_seconds / 1000;
367        while (m_past_data_count < 10 && elapsed_seconds > m_past_data_count) {
368                m_past_data[m_past_data_count].elapsed = elapsed_milli_seconds;
369                m_past_data[m_past_data_count].offset = status_.currentOffset - status_.startOffset;
370                ++m_past_data_count;
371        }
372
373        _past_data forget;
374
375        int offset = (elapsed_seconds - 1) / 2;
376        if (offset > 0) {
377                forget = m_past_data[std::min(offset, m_past_data_count - 1)];
378        }
379
380        if (elapsed_milli_seconds <= forget.elapsed) {
381                return -1;
382        }
383
384        return ((status_.currentOffset - status_.startOffset - forget.offset) * 1000) / (elapsed_milli_seconds - forget.elapsed);
385}
386
387wxFileOffset CStatusLineCtrl::GetCurrentSpeed()
388{
389        if (status_.empty())
390                return -1;
391
392        const wxTimeSpan timeDiff( wxDateTime::UNow().Subtract(m_gcLastTimeStamp) );
393
394        if (timeDiff.GetMilliseconds().GetLo() <= 2000)
395                return m_gcLastSpeed;
396
397        m_gcLastTimeStamp = wxDateTime::UNow();
398
399        if (m_gcLastOffset < 0)
400                m_gcLastOffset = status_.startOffset;
401
402        const wxFileOffset fileOffsetDiff = status_.currentOffset - m_gcLastOffset;
403        m_gcLastOffset = status_.currentOffset;
404        if( fileOffsetDiff >= 0 ) {
405                m_gcLastSpeed = fileOffsetDiff * 1000 / timeDiff.GetMilliseconds().GetLo();
406        }
407
408        return m_gcLastSpeed;
409}
410
411bool CStatusLineCtrl::Show(bool show /*=true*/)
412{
413        if (show) {
414                if (!m_transferStatusTimer.IsRunning())
415                        m_transferStatusTimer.Start(100);
416        }
417        else
418                m_transferStatusTimer.Stop();
419
420        return wxWindow::Show(show);
421}
422
423void CStatusLineCtrl::SetEngineData(const t_EngineData* const pEngineData)
424{
425        wxASSERT(pEngineData);
426        m_pEngineData = pEngineData;
427}
Note: See TracBrowser for help on using the repository browser.