source: filezilla/trunk/fuentes/src/interface/StatusView.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: 12.0 KB
Line 
1#include <filezilla.h>
2#include "StatusView.h"
3#include "Options.h"
4
5#include <wx/dcclient.h>
6
7#define MAX_LINECOUNT 1000
8#define LINECOUNT_REMOVAL 10
9
10BEGIN_EVENT_TABLE(CStatusView, wxNavigationEnabled<wxWindow>)
11EVT_SIZE(CStatusView::OnSize)
12EVT_MENU(XRCID("ID_CLEARALL"), CStatusView::OnClear)
13EVT_MENU(XRCID("ID_COPYTOCLIPBOARD"), CStatusView::OnCopy)
14END_EVENT_TABLE()
15
16class CFastTextCtrl final : public wxNavigationEnabled<wxTextCtrl>
17{
18public:
19        CFastTextCtrl(wxWindow* parent)
20        {
21                Create(parent, -1, wxString(), wxDefaultPosition, wxDefaultSize,
22                        wxNO_BORDER | wxVSCROLL | wxTE_MULTILINE |
23                        wxTE_READONLY | wxTE_RICH | wxTE_RICH2 | wxTE_NOHIDESEL |
24                        wxTAB_TRAVERSAL);
25
26                SetBackgroundStyle(wxBG_STYLE_SYSTEM);
27        }
28#ifdef __WXMSW__
29        // wxTextCtrl::Remove is somewhat slow, this is a faster version
30        virtual void Remove(long from, long to)
31        {
32                DoSetSelection(from, to, false);
33
34                m_updatesCount = -2; // suppress any update event
35                ::SendMessage((HWND)GetHandle(), EM_REPLACESEL, 0, (LPARAM)_T(""));
36        }
37
38        void AppendText(const wxString& text, int lineCount, const CHARFORMAT2& cf)
39        {
40                HWND hwnd = (HWND)GetHWND();
41
42                CHARRANGE range;
43                range.cpMin = GetLastPosition();
44                range.cpMax = range.cpMin;
45                ::SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&range);
46                ::SendMessage(hwnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
47                m_updatesCount = -2; // suppress any update event
48                ::SendMessage(hwnd, EM_REPLACESEL, 0, reinterpret_cast<LPARAM>(static_cast<wxChar const*>(text.c_str())));
49                ::SendMessage(hwnd, EM_LINESCROLL, (WPARAM)0, (LPARAM)lineCount);
50        }
51#endif
52
53#ifndef __WXMAC__
54        void SetDefaultColor(const wxColour& color)
55        {
56                m_defaultStyle.SetTextColour(color);
57        }
58#endif
59
60        DECLARE_EVENT_TABLE()
61
62        void OnText(wxCommandEvent&)
63        {
64                // Do nothing here.
65                // Having this event handler prevents the event from propagating up the
66                // window hierarchy which saves a few CPU cycles.
67        }
68
69#ifdef __WXMAC__
70        void OnChar(wxKeyEvent& event)
71        {
72                if (event.GetKeyCode() != WXK_TAB) {
73                        event.Skip();
74                        return;
75                }
76
77                HandleAsNavigationKey(event);
78        }
79#endif
80};
81
82BEGIN_EVENT_TABLE(CFastTextCtrl, wxNavigationEnabled<wxTextCtrl>)
83        EVT_TEXT(wxID_ANY, CFastTextCtrl::OnText)
84#ifdef __WXMAC__
85        EVT_CHAR_HOOK(CFastTextCtrl::OnChar)
86#endif
87END_EVENT_TABLE()
88
89
90CStatusView::CStatusView(wxWindow* parent, wxWindowID id)
91{
92        Create(parent, id, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
93        m_pTextCtrl = new CFastTextCtrl(this);
94
95#ifdef __WXMAC__
96        m_pTextCtrl->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
97#else
98        m_pTextCtrl->SetFont(GetFont());
99#endif
100
101        m_pTextCtrl->Connect(wxID_ANY, wxEVT_CONTEXT_MENU, wxContextMenuEventHandler(CStatusView::OnContextMenu), 0, this);
102#ifdef __WXMSW__
103        ::SendMessage((HWND)m_pTextCtrl->GetHandle(), EM_SETOLECALLBACK, 0, 0);
104#endif
105
106        InitDefAttr();
107
108        m_shown = IsShown();
109
110        SetBackgroundStyle(wxBG_STYLE_SYSTEM);
111
112        RegisterOption(OPTION_LANGUAGE);
113        RegisterOption(OPTION_MESSAGELOG_TIMESTAMP);
114}
115
116CStatusView::~CStatusView()
117{
118}
119
120void CStatusView::OnSize(wxSizeEvent &)
121{
122        if (m_pTextCtrl) {
123                wxSize s = GetClientSize();
124                m_pTextCtrl->SetSize(0, 0, s.GetWidth(), s.GetHeight());
125        }
126}
127
128void CStatusView::AddToLog(CLogmsgNotification const& notification)
129{
130        AddToLog(notification.msgType, notification.msg, fz::datetime::now());
131}
132
133void CStatusView::AddToLog(MessageType messagetype, const wxString& message, fz::datetime const& time)
134{
135        if (!m_shown) {
136                if (m_hiddenLines.size() >= MAX_LINECOUNT) {
137                        auto it = m_hiddenLines.begin();
138                        it->messagetype = messagetype;
139                        it->message = message;
140                        it->time = time;
141                        m_hiddenLines.splice(m_hiddenLines.end(), m_hiddenLines, it );
142                }
143                else {
144                        t_line line;
145                        line.messagetype = messagetype;
146                        line.message = message;
147                        line.time = time;
148                        m_hiddenLines.push_back(line);
149                }
150                return;
151        }
152
153        const int messageLength = message.Length();
154
155        wxString prefix;
156        prefix.Alloc(25 + messageLength);
157
158        if (m_nLineCount)
159#ifdef __WXMSW__
160                prefix = _T("\r\n");
161#else
162                prefix = _T("\n");
163#endif
164
165        if (m_nLineCount >= MAX_LINECOUNT) {
166#ifndef __WXGTK__
167                m_pTextCtrl->Freeze();
168#endif //__WXGTK__
169                int oldLength = 0;
170                auto it = m_lineLengths.begin();
171                for (int i = 0; i < LINECOUNT_REMOVAL; ++i) {
172                        oldLength += *(it++) + 1;
173                }
174                m_unusedLineLengths.splice(m_unusedLineLengths.end(), m_lineLengths, m_lineLengths.begin(), it);
175                m_pTextCtrl->Remove(0, oldLength);
176        }
177#ifdef __WXMAC__
178        if (m_pTextCtrl->GetInsertionPoint() != m_pTextCtrl->GetLastPosition()) {
179                m_pTextCtrl->SetInsertionPointEnd();
180        }
181#endif
182
183        int lineLength = m_attributeCache[static_cast<int>(messagetype)].len + messageLength;
184
185        if (m_showTimestamps) {
186                if (time != m_lastTime) {
187                        m_lastTime = time;
188#ifndef __WXMAC__
189                        m_lastTimeString = time.format(_T("%H:%M:%S\t"), fz::datetime::local);
190#else
191                        // Tabs on OS X cannot be freely positioned
192                        m_lastTimeString = time.format(_T("%H:%M:%S "), fz::datetime::local);
193#endif
194                }
195                prefix += m_lastTimeString;
196                lineLength += m_lastTimeString.Len();
197        }
198
199#ifdef __WXMAC__
200        m_pTextCtrl->SetDefaultStyle(m_attributeCache[static_cast<int>(messagetype)].attr);
201#elif __WXGTK__
202        m_pTextCtrl->SetDefaultColor(m_attributeCache[static_cast<int>(messagetype)].attr.GetTextColour());
203#endif
204
205        prefix += m_attributeCache[static_cast<int>(messagetype)].prefix;
206
207        if (m_rtl) {
208                // Unicode control characters that control reading direction
209                const wxChar LTR_MARK = 0x200e;
210                //const wxChar RTL_MARK = 0x200f;
211                const wxChar LTR_EMBED = 0x202A;
212                //const wxChar RTL_EMBED = 0x202B;
213                //const wxChar POP = 0x202c;
214                //const wxChar LTR_OVERRIDE = 0x202D;
215                //const wxChar RTL_OVERRIDE = 0x202E;
216
217                if (messagetype == MessageType::Command || messagetype == MessageType::Response || messagetype >= MessageType::Debug_Warning) {
218                        // Commands, responses and debug message contain English text,
219                        // set LTR reading order for them.
220                        prefix += LTR_MARK;
221                        prefix += LTR_EMBED;
222                        lineLength += 2;
223                }
224        }
225
226        prefix += message;
227#if defined(__WXGTK__)
228        // AppendText always calls SetInsertionPointEnd, which is very expensive.
229        // This check however is negligible.
230        if (m_pTextCtrl->GetInsertionPoint() != m_pTextCtrl->GetLastPosition())
231                m_pTextCtrl->AppendText(prefix);
232        else
233                m_pTextCtrl->WriteText(prefix);
234#elif defined(__WXMAC__)
235        m_pTextCtrl->WriteText(prefix);
236#else
237        m_pTextCtrl->AppendText(prefix, m_nLineCount, m_attributeCache[static_cast<int>(messagetype)].cf);
238#endif
239
240        if (m_nLineCount >= MAX_LINECOUNT) {
241                m_nLineCount -= LINECOUNT_REMOVAL - 1;
242#ifndef __WXGTK__
243                m_pTextCtrl->Thaw();
244#endif
245        }
246        else {
247                m_nLineCount++;
248        }
249        if (m_unusedLineLengths.empty()) {
250                m_lineLengths.push_back(lineLength);
251        }
252        else {
253                m_unusedLineLengths.front() = lineLength;
254                m_lineLengths.splice(m_lineLengths.end(), m_unusedLineLengths, m_unusedLineLengths.begin());
255        }
256}
257
258void CStatusView::InitDefAttr()
259{
260        m_showTimestamps = COptions::Get()->GetOptionVal(OPTION_MESSAGELOG_TIMESTAMP) != 0;
261        m_lastTime = fz::datetime::now();
262        m_lastTimeString = m_lastTime.format(_T("%H:%M:%S\t"), fz::datetime::local);
263
264        // Measure withs of all types
265        wxClientDC dc(this);
266
267        int timestampWidth = 0;
268        if (m_showTimestamps) {
269                wxCoord width = 0;
270                wxCoord height = 0;
271#ifndef __WXMAC__
272                dc.GetTextExtent(_T("88:88:88 "), &width, &height);
273#else
274                dc.GetTextExtent(_T("88:88:88 "), &width, &height);
275#endif
276                timestampWidth = width;
277        }
278
279        wxCoord width = 0;
280        wxCoord height = 0;
281        dc.GetTextExtent(_("Error:") + _T(" "), &width, &height);
282        int maxPrefixWidth = width;
283        dc.GetTextExtent(_("Command:") + _T(" "), &width, &height);
284        if (width > maxPrefixWidth)
285                maxPrefixWidth = width;
286        dc.GetTextExtent(_("Response:") + _T(" "), &width, &height);
287        if (width > maxPrefixWidth)
288                maxPrefixWidth = width;
289        dc.GetTextExtent(_("Trace:") + _T(" "), &width, &height);
290        if (width > maxPrefixWidth)
291                maxPrefixWidth = width;
292        dc.GetTextExtent(_("Listing:") + _T(" "), &width, &height);
293        if (width > maxPrefixWidth)
294                maxPrefixWidth = width;
295        dc.GetTextExtent(_("Status:") + _T(" "), &width, &height);
296        if (width > maxPrefixWidth)
297                maxPrefixWidth = width;
298
299#ifdef __WXMAC__
300        wxCoord spaceWidth;
301        dc.GetTextExtent(_T(" "), &spaceWidth, &height);
302#endif
303
304        dc.SetMapMode(wxMM_LOMETRIC);
305
306        int maxWidth = dc.DeviceToLogicalX(maxPrefixWidth) + 20;
307        if (timestampWidth != 0) {
308                timestampWidth = dc.DeviceToLogicalX(timestampWidth) + 20;
309                maxWidth += timestampWidth;
310        }
311        wxArrayInt array;
312#ifndef __WXMAC__
313        if (timestampWidth != 0)
314                array.Add(timestampWidth);
315#endif
316        array.Add(maxWidth);
317        wxTextAttr defAttr;
318        defAttr.SetTabs(array);
319        defAttr.SetLeftIndent(0, maxWidth);
320        m_pTextCtrl->SetDefaultStyle(defAttr);
321#ifdef __WXMSW__
322        m_pTextCtrl->SetStyle(0, 0, defAttr);
323#endif
324
325        const wxColour background = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX);
326        const bool is_dark = background.Red() + background.Green() + background.Blue() < 384;
327
328        for (int i = 0; i < static_cast<int>(MessageType::count); i++) {
329                t_attributeCache& entry = m_attributeCache[i];
330#ifndef __WXMAC__
331                entry.attr = defAttr;
332#endif
333                switch (static_cast<MessageType>(i)) {
334                case MessageType::Error:
335                        entry.prefix = _("Error:");
336                        entry.attr.SetTextColour(wxColour(255, 0, 0));
337                        break;
338                case MessageType::Command:
339                        entry.prefix = _("Command:");
340                        if (is_dark)
341                                entry.attr.SetTextColour(wxColour(128, 128, 255));
342                        else
343                                entry.attr.SetTextColour(wxColour(0, 0, 128));
344                        break;
345                case MessageType::Response:
346                        entry.prefix = _("Response:");
347                        if (is_dark)
348                                entry.attr.SetTextColour(wxColour(128, 255, 128));
349                        else
350                                entry.attr.SetTextColour(wxColour(0, 128, 0));
351                        break;
352                case MessageType::Debug_Warning:
353                case MessageType::Debug_Info:
354                case MessageType::Debug_Verbose:
355                case MessageType::Debug_Debug:
356                        entry.prefix = _("Trace:");
357                        if (is_dark)
358                                entry.attr.SetTextColour(wxColour(255, 128, 255));
359                        else
360                                entry.attr.SetTextColour(wxColour(128, 0, 128));
361                        break;
362                case MessageType::RawList:
363                        entry.prefix = _("Listing:");
364                        if (is_dark)
365                                entry.attr.SetTextColour(wxColour(128, 255, 255));
366                        else
367                                entry.attr.SetTextColour(wxColour(0, 128, 128));
368                        break;
369                default:
370                        entry.prefix = _("Status:");
371                        entry.attr.SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
372                        break;
373                }
374
375#ifdef __WXMAC__
376                // Fill with blanks to approach best size
377                dc.GetTextExtent(entry.prefix, &width, &height);
378                wxASSERT(width <= maxPrefixWidth);
379                wxCoord spaces = (maxPrefixWidth - width) / spaceWidth;
380                entry.prefix += wxString(spaces, ' ');
381#endif
382                entry.prefix += _T("\t");
383                entry.len = entry.prefix.Length();
384
385#ifdef __WXMSW__
386                m_pTextCtrl->SetStyle(0, 0, entry.attr);
387                entry.cf.cbSize = sizeof(CHARFORMAT2);
388                ::SendMessage((HWND)m_pTextCtrl->GetHWND(), EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&entry.cf);
389#endif
390        }
391
392        m_rtl = wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft;
393}
394
395void CStatusView::OnContextMenu(wxContextMenuEvent&)
396{
397        wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_LOG"));
398        if (!pMenu)
399                return;
400
401        pMenu->Check(XRCID("ID_SHOW_DETAILED_LOG"), COptions::Get()->GetOptionVal(OPTION_LOGGING_SHOW_DETAILED_LOGS) != 0);
402
403        PopupMenu(pMenu);
404
405        COptions::Get()->SetOption(OPTION_LOGGING_SHOW_DETAILED_LOGS, pMenu->IsChecked(XRCID("ID_SHOW_DETAILED_LOG")) ? 1 : 0);
406        delete pMenu;
407}
408
409void CStatusView::OnClear(wxCommandEvent&)
410{
411        if (m_pTextCtrl)
412                m_pTextCtrl->Clear();
413        m_nLineCount = 0;
414        m_lineLengths.clear();
415}
416
417void CStatusView::OnCopy(wxCommandEvent&)
418{
419        if (!m_pTextCtrl)
420                return;
421
422        long from, to;
423        m_pTextCtrl->GetSelection(&from, &to);
424        if (from != to)
425                m_pTextCtrl->Copy();
426        else {
427                m_pTextCtrl->Freeze();
428                m_pTextCtrl->SetSelection(-1, -1);
429                m_pTextCtrl->Copy();
430                m_pTextCtrl->SetSelection(from, to);
431                m_pTextCtrl->Thaw();
432        }
433}
434
435void CStatusView::SetFocus()
436{
437        m_pTextCtrl->SetFocus();
438}
439
440bool CStatusView::Show(bool show /*=true*/)
441{
442        m_shown = show;
443
444        if (show && m_pTextCtrl) {
445                if (m_hiddenLines.size() >= MAX_LINECOUNT) {
446                        m_pTextCtrl->Clear();
447                        m_nLineCount = 0;
448                        m_unusedLineLengths.splice(m_unusedLineLengths.end(), m_lineLengths, m_lineLengths.begin(), m_lineLengths.end());
449                }
450
451                for (auto const& line : m_hiddenLines) {
452                        AddToLog(line.messagetype, line.message, line.time);
453                }
454                m_hiddenLines.clear();
455        }
456
457        return wxWindow::Show(show);
458}
459
460void CStatusView::OnOptionsChanged(changed_options_t const&)
461{
462        InitDefAttr();
463}
Note: See TracBrowser for help on using the repository browser.