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

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

First release to xenial

File size: 14.3 KB
Line 
1#include <filezilla.h>
2#include "viewheader.h"
3#include "commandqueue.h"
4
5#ifdef __WXMSW__
6#include <wx/msw/uxtheme.h>
7#endif //__WXMSW__
8#ifdef __WXMAC__
9#include "textctrlex.h"
10#endif
11
12#include <wx/combobox.h>
13#include <wx/dcclient.h>
14
15#ifdef __WXMSW__
16const int border_offset = 0;
17#elif defined(__WXMAC__)
18const int border_offset = 6;
19#else
20const int border_offset = 10;
21#endif
22
23// wxComboBox derived class which captures WM_CANCELMODE under Windows
24class CComboBoxEx : public wxComboBox
25{
26public:
27        CComboBoxEx(CViewHeader* parent)
28                : wxComboBox(parent, wxID_ANY, _T(""), wxDefaultPosition, wxDefaultSize, wxArrayString(), wxCB_DROPDOWN | wxTE_PROCESS_ENTER)
29        {
30                m_parent = parent;
31        }
32#ifdef __WXMSW__
33protected:
34        CViewHeader* m_parent;
35        virtual WXLRESULT MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
36        {
37                if (nMsg == WM_CANCELMODE)
38                {
39                        m_parent->m_bLeftMousePressed = false;
40                        Refresh();
41                }
42                else if (nMsg == WM_CAPTURECHANGED && !lParam)
43                {
44                        WXLRESULT res = wxComboBox::MSWDefWindowProc(nMsg, wParam, lParam);
45
46                        if (!SendMessage((HWND)GetHandle(), CB_GETDROPPEDSTATE, 0, 0))
47                        {
48                                m_parent->m_bLeftMousePressed = false;
49                                Refresh();
50                        }
51                        return res;
52                }
53                return wxComboBox::MSWDefWindowProc(nMsg, wParam, lParam);
54        }
55#endif //__WXMSW__
56};
57
58BEGIN_EVENT_TABLE(CViewHeader, wxNavigationEnabled<wxWindow>)
59EVT_SIZE(CViewHeader::OnSize)
60EVT_PAINT(CViewHeader::OnPaint)
61END_EVENT_TABLE()
62
63CViewHeader::CViewHeader(wxWindow* pParent, const wxString& label)
64{
65        Create(pParent, wxID_ANY);
66
67        m_pComboBox = new CComboBoxEx(this);
68        m_pLabel = new wxStaticText(this, wxID_ANY, label, wxDefaultPosition, wxDefaultSize);
69        wxSize size = GetSize();
70        size.SetHeight(m_pComboBox->GetBestSize().GetHeight() + border_offset);
71
72        SetLabel(label);
73
74        SetSize(size);
75
76#ifdef __WXMSW__
77        m_pComboBox->Connect(wxID_ANY, wxEVT_PAINT, (wxObjectEventFunction)(wxEventFunction)(wxPaintEventFunction)&CViewHeader::OnComboPaint, 0, this);
78        m_pComboBox->Connect(wxID_ANY, wxEVT_LEFT_DOWN, (wxObjectEventFunction)(wxEventFunction)(wxMouseEventFunction)&CViewHeader::OnComboMouseEvent, 0, this);
79        m_pComboBox->Connect(wxID_ANY, wxEVT_LEFT_UP, (wxObjectEventFunction)(wxEventFunction)(wxMouseEventFunction)&CViewHeader::OnComboMouseEvent, 0, this);
80        m_bLeftMousePressed = false;
81#endif //__WXMSW__
82
83        SetBackgroundStyle(wxBG_STYLE_SYSTEM);
84        m_pComboBox->SetBackgroundStyle(wxBG_STYLE_SYSTEM);
85        m_pLabel->SetBackgroundStyle(wxBG_STYLE_SYSTEM);
86}
87
88void CViewHeader::OnSize(wxSizeEvent&)
89{
90        const wxRect client_rect = GetClientRect();
91
92        wxRect rect = client_rect;
93        rect.SetWidth(rect.GetWidth() - m_cbOffset + 2);
94        rect.SetX(m_cbOffset);
95        rect.Deflate(0, border_offset / 2);
96        rect.SetWidth(rect.GetWidth() - border_offset / 2);
97        if (m_pComboBox) {
98                m_pComboBox->SetSize(rect);
99        }
100
101        rect.SetX(5);
102        rect.SetWidth(m_cbOffset - 5);
103        rect.SetY((client_rect.GetHeight() - m_labelHeight) / 2 - 1);
104        rect.SetHeight(m_labelHeight);
105        if (m_pLabel) {
106                m_pLabel->SetSize(rect);
107        }
108
109        Refresh();
110}
111
112#ifdef __WXMSW__
113
114void CViewHeader::OnComboPaint(wxPaintEvent& event)
115{
116        // We do a small trick to let the control handle the event before we can paint
117        if (m_alreadyInPaint)
118        {
119                event.Skip();
120                return;
121        }
122
123        wxComboBox* box = m_pComboBox;
124
125        m_alreadyInPaint = true;
126        box->Refresh();
127        box->Update();
128        m_alreadyInPaint = false;
129
130        wxClientDC dc(box);
131        dc.SetBrush(*wxTRANSPARENT_BRUSH);
132
133        int thumbWidth = ::GetSystemMetrics(SM_CXHTHUMB);
134
135        if (m_bLeftMousePressed)
136        {
137                if (!SendMessage((HWND)box->GetHandle(), CB_GETDROPPEDSTATE, 0, 0))
138                        m_bLeftMousePressed = false;
139        }
140
141#if wxUSE_UXTHEME
142        wxUxThemeEngine *p = wxUxThemeEngine::Get();
143        if (p && p->IsThemeActive())
144        {
145        }
146        else
147#endif //wxUSE_UXTHEME
148        {
149                dc.SetPen(wxPen(wxSystemSettings::GetColour(IsEnabled() ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE)));
150                wxRect rect = box->GetClientRect();
151                rect.Deflate(1);
152                wxRect rect2 = rect;
153                rect2.SetWidth(rect.GetWidth() - thumbWidth);
154                dc.DrawRectangle(rect2);
155
156                if (!m_bLeftMousePressed || !IsEnabled())
157                {
158                        wxPoint topLeft = rect.GetTopLeft();
159                        wxPoint bottomRight = rect.GetBottomRight();
160                        bottomRight.x--;
161                        topLeft.x = bottomRight.x - thumbWidth + 1;
162
163                        dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)));
164                        dc.DrawLine(topLeft.x, topLeft.y, bottomRight.x + 1, topLeft.y);
165                        dc.DrawLine(topLeft.x, topLeft.y + 1, topLeft.x, bottomRight.y);
166                        dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW)));
167                        dc.DrawLine(bottomRight.x, topLeft.y + 1, bottomRight.x, bottomRight.y + 1);
168                        dc.DrawLine(topLeft.x, bottomRight.y, bottomRight.x, bottomRight.y);
169
170                        topLeft.x++;
171                        topLeft.y++;
172                        bottomRight.x--;
173                        bottomRight.y--;
174                        dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT)));
175                        dc.DrawLine(topLeft.x, topLeft.y, bottomRight.x + 1, topLeft.y);
176                        dc.DrawLine(topLeft.x, topLeft.y + 1, topLeft.x, bottomRight.y);
177                        dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW)));
178                        dc.DrawLine(bottomRight.x, topLeft.y + 1, bottomRight.x, bottomRight.y + 1);
179                        dc.DrawLine(topLeft.x, bottomRight.y, bottomRight.x, bottomRight.y);
180
181                        topLeft.x++;
182                        topLeft.y++;
183                        bottomRight.x--;
184                        bottomRight.y--;
185                        dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)));
186                        dc.DrawRectangle(wxRect(topLeft, bottomRight));
187                }
188                else
189                {
190                        wxPoint topLeft = rect.GetTopLeft();
191                        wxPoint bottomRight = rect.GetBottomRight();
192                        bottomRight.x--;
193                        topLeft.x = bottomRight.x - thumbWidth + 1;
194
195                        dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW)));
196                        dc.DrawRectangle(wxRect(topLeft, bottomRight));
197
198                        topLeft.x++;
199                        topLeft.y++;
200                        bottomRight.x--;
201                        bottomRight.y--;
202                        dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)));
203                        dc.DrawRectangle(wxRect(topLeft, bottomRight));
204                }
205        }
206
207        // Cover up dark 3D shadow.
208        wxRect rect = box->GetClientRect();
209        dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW)));
210        dc.DrawRectangle(rect);
211
212}
213
214void CViewHeader::OnComboMouseEvent(wxMouseEvent& event)
215{
216        if (event.GetEventType() == wxEVT_LEFT_UP)
217                m_bLeftMousePressed = false;
218        else if (event.GetEventType() == wxEVT_LEFT_DOWN)
219                m_bLeftMousePressed = true;
220
221        event.Skip();
222}
223
224#endif //__WXMSW__
225
226void CViewHeader::OnPaint(wxPaintEvent&)
227{
228        wxRect rect = GetClientRect();
229        wxPaintDC dc(this);
230        dc.SetPen(*wxBLACK_PEN);
231        dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
232
233#ifdef __WXMSW__
234        dc.DrawLine(rect.GetLeft(), rect.GetBottom(), m_cbOffset, rect.GetBottom());
235#else
236        dc.DrawLine(rect.GetLeft(), rect.GetBottom(), rect.GetRight(), rect.GetBottom());
237#endif
238}
239
240void CViewHeader::SetLabel(const wxString& label)
241{
242        m_pLabel->SetLabel(label);
243        int w;
244        GetTextExtent(label, &w, &m_labelHeight);
245        m_cbOffset = w + 10;
246}
247
248void CViewHeader::Reparent(CViewHeader** pViewHeader, wxWindow* parent)
249{
250#if defined __WXMSW__ || defined __WXGTK__ || \
251        (defined __WXMAC__ && !(defined __WXMAC_CLASSIC__))
252        ((wxWindow*)*pViewHeader)->Reparent(parent);
253#else
254#error CViewHeader::Reparent unimplemented
255#endif
256}
257
258wxString CViewHeader::GetLabel() const
259{
260        return m_pLabel->GetLabel();
261}
262
263void CViewHeader::AddRecentDirectory(const wxString &directory)
264{
265        const int len = directory.Len();
266
267        // Check if directory is already in the list
268        for (auto iter = m_recentDirectories.begin(); iter != m_recentDirectories.end(); ++iter) {
269                if (*iter == directory) {
270                        m_pComboBox->SetStringSelection(directory);
271                        m_pComboBox->SetSelection(len, len);
272                        return;
273                }
274        }
275
276        if (m_recentDirectories.size() == 20) {
277                wxASSERT(m_recentDirectories.front() != directory);
278
279                int pos = 0;
280                for (auto it = m_sortedRecentDirectories.begin(); it != m_sortedRecentDirectories.end(); ++pos, ++it) {
281                        if (*it == m_recentDirectories.front()) {
282                                m_sortedRecentDirectories.erase(it);
283                                break;
284                        }
285                }
286                wxASSERT(pos != 20);
287
288                wxASSERT(m_pComboBox->FindString(m_recentDirectories.front(), true) == pos);
289                m_pComboBox->Delete(pos);
290                m_recentDirectories.pop_front();
291        }
292
293        m_recentDirectories.push_back(directory);
294
295        // Find insertion position.
296        // Do a linear search, binary search not worth it for 20 items
297        int pos = 0;
298        auto it = m_sortedRecentDirectories.begin();
299        for (; it != m_sortedRecentDirectories.end(); ++pos, ++it) {
300                int cmp = directory.CmpNoCase(*it);
301                if (cmp < 0 || (!cmp && directory.Cmp(*it) < 0)) {
302                        break;
303                }
304        }
305        m_sortedRecentDirectories.insert(it, directory);
306
307        int item = m_pComboBox->Insert(directory, pos);
308        m_pComboBox->SetSelection(item);
309        m_pComboBox->SetSelection(len, len);
310
311        wxASSERT(m_sortedRecentDirectories.size() == m_recentDirectories.size());
312}
313
314void CViewHeader::SetFocus()
315{
316        m_pComboBox->SetFocus();
317}
318
319#ifdef __WXGTK__
320DECLARE_EVENT_TYPE(fzEVT_LOCALVIEWHEADERSETTEXTSEL, -1)
321DEFINE_EVENT_TYPE(fzEVT_LOCALVIEWHEADERSETTEXTSEL)
322#endif
323
324BEGIN_EVENT_TABLE(CLocalViewHeader, CViewHeader)
325EVT_TEXT(wxID_ANY, CLocalViewHeader::OnTextChanged)
326EVT_TEXT_ENTER(wxID_ANY, CLocalViewHeader::OnTextEnter)
327EVT_COMBOBOX(wxID_ANY, CLocalViewHeader::OnSelectionChanged)
328#ifdef __WXGTK__
329EVT_COMMAND(wxID_ANY, fzEVT_LOCALVIEWHEADERSETTEXTSEL, CLocalViewHeader::OnSelectTextEvent)
330#endif
331END_EVENT_TABLE()
332
333CLocalViewHeader::CLocalViewHeader(wxWindow* pParent, CState* pState)
334        : CViewHeader(pParent, _("Local site:")), CStateEventHandler(pState)
335{
336        pState->RegisterHandler(this, STATECHANGE_LOCAL_DIR);
337}
338
339void CLocalViewHeader::OnTextChanged(wxCommandEvent&)
340{
341        // This function handles auto-completion
342
343#ifdef __WXGTK__
344        m_autoCompletionText = _T("");
345#endif
346
347        wxString str = m_pComboBox->GetValue();
348        if (str.empty() || str.Right(1) == _T("/"))
349        {
350                m_oldValue = str;
351                return;
352        }
353#ifdef __WXMSW__
354        if (str.Right(1) == _T("\\"))
355        {
356                m_oldValue = str;
357                return;
358        }
359#endif
360
361        if (str == m_oldValue)
362                return;
363
364        if (str.Left(m_oldValue.Length()) != m_oldValue)
365        {
366                m_oldValue = str;
367                return;
368        }
369
370#ifdef __WXMSW__
371        if (str.Left(2) == _T("\\\\"))
372        {
373                int pos = str.Mid(2).Find('\\');
374                if (pos == -1)
375                {
376                        // Partial UNC path, no full server yet, skip further processing
377                        return;
378                }
379
380                pos = str.Mid(pos + 3).Find('\\');
381                if (pos == -1)
382                {
383                        // Partial UNC path, no full share yet, skip further processing
384                        return;
385                }
386        }
387#endif
388
389        wxFileName fn(str);
390        if (!fn.IsOk())
391        {
392                m_oldValue = str;
393                return;
394        }
395
396        wxString name = fn.GetFullName();
397        const wxString path = fn.GetPath();
398
399        wxDir dir;
400        if (name.empty() || path.empty()) {
401                m_oldValue = str;
402                return;
403        }
404        else {
405                wxLogNull log;
406                if (!dir.Open(path) || !dir.IsOpened())
407                {
408                        m_oldValue = str;
409                        return;
410                }
411        }
412        wxString found;
413
414        {
415                wxLogNull noLog;
416                if (!dir.GetFirst(&found, name + _T("*"), wxDIR_DIRS)) {
417                        m_oldValue = str;
418                        return;
419                }
420        }
421
422        wxString tmp;
423        if (dir.GetNext(&tmp))
424        {
425                m_oldValue = str;
426                return;
427        }
428
429#ifdef __WXMSW__
430        if (found.Left(name.Length()).CmpNoCase(name))
431#else
432        if (found.Left(name.Length()) != name)
433#endif
434        {
435                m_oldValue = str;
436                return;
437        }
438
439#ifdef __WXGTK__
440        m_autoCompletionText = found.Mid(name.Length()) + wxFileName::GetPathSeparator();
441        QueueEvent(new wxCommandEvent(fzEVT_LOCALVIEWHEADERSETTEXTSEL));
442#else
443        m_pComboBox->SetValue(str + found.Mid(name.Length()) + wxFileName::GetPathSeparator());
444        m_pComboBox->SetSelection(str.Length(), m_pComboBox->GetValue().Length() + 1);
445#endif
446
447        m_oldValue = str;
448}
449
450#ifdef __WXGTK__
451void CLocalViewHeader::OnSelectTextEvent(wxCommandEvent&)
452{
453        if (m_autoCompletionText.empty())
454                return;
455
456        const wxString& oldValue = m_pComboBox->GetValue();
457        const wxString completionText = m_autoCompletionText;
458        m_autoCompletionText = _T("");
459
460        if (m_pComboBox->GetInsertionPoint() != (int)oldValue.Len())
461                return;
462
463        m_pComboBox->SetValue(oldValue + completionText);
464        m_pComboBox->SetSelection(oldValue.Len(), oldValue.Len() + completionText.Len());
465}
466#endif
467
468void CLocalViewHeader::OnSelectionChanged(wxCommandEvent& event)
469{
470#ifdef __WXGTK__
471        m_autoCompletionText = _T("");
472#endif
473
474        wxString dir = event.GetString();
475        if (dir.empty())
476                return;
477
478        if (!wxDir::Exists(dir))
479        {
480                const wxString& current = m_pState->GetLocalDir().GetPath();
481                int item = m_pComboBox->FindString(current, true);
482                if (item != wxNOT_FOUND)
483                        m_pComboBox->SetSelection(item);
484
485                wxBell();
486                return;
487        }
488
489        m_pState->SetLocalDir(dir);
490}
491
492void CLocalViewHeader::OnTextEnter(wxCommandEvent&)
493{
494#ifdef __WXGTK__
495        m_autoCompletionText = _T("");
496#endif
497
498        wxString dir = m_pComboBox->GetValue();
499
500        wxString error;
501        if (!m_pState->SetLocalDir(dir, &error))
502        {
503                if (!error.empty())
504                        wxMessageBoxEx(error, _("Failed to change directory"), wxICON_INFORMATION);
505                else
506                        wxBell();
507                m_pComboBox->SetValue(m_pState->GetLocalDir().GetPath());
508        }
509}
510
511void CLocalViewHeader::OnStateChange(CState* pState, enum t_statechange_notifications notification, const wxString&, const void*)
512{
513        wxASSERT(notification == STATECHANGE_LOCAL_DIR);
514        (void)notification;
515
516#ifdef __WXGTK__
517        m_autoCompletionText = _T("");
518#endif
519
520        wxString dir = pState->GetLocalDir().GetPath();
521        AddRecentDirectory(dir);
522}
523
524BEGIN_EVENT_TABLE(CRemoteViewHeader, CViewHeader)
525EVT_TEXT_ENTER(wxID_ANY, CRemoteViewHeader::OnTextEnter)
526EVT_COMBOBOX(wxID_ANY, CRemoteViewHeader::OnSelectionChanged)
527END_EVENT_TABLE()
528
529CRemoteViewHeader::CRemoteViewHeader(wxWindow* pParent, CState* pState)
530        : CViewHeader(pParent, _("Remote site:")), CStateEventHandler(pState)
531{
532        pState->RegisterHandler(this, STATECHANGE_REMOTE_DIR);
533        Disable();
534}
535
536void CRemoteViewHeader::OnStateChange(CState* pState, enum t_statechange_notifications notification, const wxString&, const void*)
537{
538        wxASSERT(notification == STATECHANGE_REMOTE_DIR);
539        (void)notification;
540
541        m_path = pState->GetRemotePath();
542        if (m_path.empty()) {
543                m_pComboBox->SetValue(_T(""));
544                Disable();
545        }
546        else {
547                const CServer* const pServer = pState->GetServer();
548                if (pServer && *pServer != m_lastServer) {
549                        m_pComboBox->Clear();
550                        m_recentDirectories.clear();
551                        m_sortedRecentDirectories.clear();
552                        m_lastServer = *pServer;
553                }
554                Enable();
555#ifdef __WXGTK__
556                GetParent()->m_dirtyTabOrder = true;
557#endif
558                AddRecentDirectory(m_path.GetPath());
559        }
560}
561
562void CRemoteViewHeader::OnTextEnter(wxCommandEvent&)
563{
564        CServerPath path = m_path;
565        wxString value = m_pComboBox->GetValue();
566        if (value.empty() || !path.ChangePath(value)) {
567                wxBell();
568                return;
569        }
570
571        if (!m_pState->IsRemoteIdle(true)) {
572                wxBell();
573                return;
574        }
575
576        m_pState->ChangeRemoteDir(path);
577}
578
579void CRemoteViewHeader::OnSelectionChanged(wxCommandEvent& event)
580{
581        const wxString& dir = event.GetString();
582        if (dir.empty())
583                return;
584
585        CServerPath path = m_path;
586        if (!path.SetPath(dir)) {
587                wxBell();
588                return;
589        }
590
591        if (!m_pState->IsRemoteIdle(true)) {
592                wxBell();
593                return;
594        }
595
596        m_pState->ChangeRemoteDir(path);
597}
Note: See TracBrowser for help on using the repository browser.