#include #include "Options.h" #include "sftp_crypt_info_dlg.h" #include "sizeformatting.h" #include "speedlimits_dialog.h" #include "statusbar.h" #include "themeprovider.h" #include "verifycertdialog.h" #include static const int statbarWidths[3] = { -3, 0, 25 }; #define FIELD_QUEUESIZE 1 BEGIN_EVENT_TABLE(wxStatusBarEx, wxStatusBar) EVT_SIZE(wxStatusBarEx::OnSize) END_EVENT_TABLE() wxStatusBarEx::wxStatusBarEx(wxTopLevelWindow* pParent) { m_pParent = pParent; m_columnWidths = 0; Create(pParent, wxID_ANY); SetBackgroundStyle(wxBG_STYLE_SYSTEM); #ifdef __WXMSW__ m_parentWasMaximized = false; if (GetLayoutDirection() != wxLayout_RightToLeft) SetDoubleBuffered(true); #endif } wxStatusBarEx::~wxStatusBarEx() { delete [] m_columnWidths; } void wxStatusBarEx::SetFieldsCount(int number /*=1*/, const int* widths /*=NULL*/) { wxASSERT(number > 0); int oldCount = GetFieldsCount(); int* oldWidths = m_columnWidths; m_columnWidths = new int[number]; if (!widths) { if (oldWidths) { const int min = wxMin(oldCount, number); for (int i = 0; i < min; i++) m_columnWidths[i] = oldWidths[i]; for (int i = min; i < number; i++) m_columnWidths[i] = -1; delete [] oldWidths; } else { for (int i = 0; i < number; i++) m_columnWidths[i] = -1; } } else { delete [] oldWidths; for (int i = 0; i < number; i++) m_columnWidths[i] = widths[i]; FixupFieldWidth(number - 1); } wxStatusBar::SetFieldsCount(number, m_columnWidths); } void wxStatusBarEx::SetStatusWidths(int n, const int *widths) { wxASSERT(n == GetFieldsCount()); wxASSERT(widths); for (int i = 0; i < n; i++) m_columnWidths[i] = widths[i]; FixupFieldWidth(n - 1); wxStatusBar::SetStatusWidths(n, m_columnWidths); } void wxStatusBarEx::FixupFieldWidth(int field) { if (field != GetFieldsCount() - 1) return; #if __WXGTK20__ // Gripper overlaps last all the time if (m_columnWidths[field] > 0) m_columnWidths[field] += 15; #endif } void wxStatusBarEx::SetFieldWidth(int field, int width) { field = GetFieldIndex(field); if( field < 0 ) { return; } m_columnWidths[field] = width; FixupFieldWidth(field); wxStatusBar::SetStatusWidths(GetFieldsCount(), m_columnWidths); } int wxStatusBarEx::GetFieldIndex(int field) { if (field >= 0) { wxCHECK(field <= GetFieldsCount(), -1); } else { field = GetFieldsCount() + field; wxCHECK(field >= 0, -1); } return field; } void wxStatusBarEx::OnSize(wxSizeEvent&) { #ifdef __WXMSW__ const int count = GetFieldsCount(); if (count && m_columnWidths && m_columnWidths[count - 1] > 0) { // No sizegrip on maximized windows bool isMaximized = m_pParent->IsMaximized(); if (isMaximized != m_parentWasMaximized) { m_parentWasMaximized = isMaximized; if (isMaximized) m_columnWidths[count - 1] -= 16; else m_columnWidths[count - 1] += 16; wxStatusBar::SetStatusWidths(count, m_columnWidths); Refresh(); } } #endif } #ifdef __WXGTK__ void wxStatusBarEx::SetStatusText(const wxString& text, int number /*=0*/) { wxString oldText = GetStatusText(number); if (oldText != text) { wxStatusBar::SetStatusText(text, number); } } #endif int wxStatusBarEx::GetGripperWidth() { #if defined(__WXMSW__) return m_pParent->IsMaximized() ? 0 : 6; #elif defined(__WXGTK__) return 15; #else return 0; #endif } BEGIN_EVENT_TABLE(CWidgetsStatusBar, wxStatusBarEx) EVT_SIZE(CWidgetsStatusBar::OnSize) END_EVENT_TABLE() CWidgetsStatusBar::CWidgetsStatusBar(wxTopLevelWindow* parent) : wxStatusBarEx(parent) { } CWidgetsStatusBar::~CWidgetsStatusBar() { } void CWidgetsStatusBar::OnSize(wxSizeEvent& event) { wxStatusBarEx::OnSize(event); for (int i = 0; i < GetFieldsCount(); i++) PositionChildren(i); #ifdef __WXMSW__ if (GetLayoutDirection() != wxLayout_RightToLeft) Update(); #endif } bool CWidgetsStatusBar::AddField(int field, int idx, wxWindow* pChild) { field = GetFieldIndex(field); if( field < 0 ) { return false; } t_statbar_child data; data.field = field; data.pChild = pChild; m_children[idx] = data; PositionChildren(field); return true; } void CWidgetsStatusBar::RemoveField(int idx) { auto iter = m_children.find(idx); if (iter != m_children.end()) { int field = iter->second.field; m_children.erase(iter); PositionChildren(field); } } void CWidgetsStatusBar::PositionChildren(int field) { wxRect rect; GetFieldRect(field, rect); int offset = 2; #ifndef __WXMSW__ if (field + 1 == GetFieldsCount()) { rect.SetWidth(m_columnWidths[field]); offset += 5 + GetGripperWidth(); } #endif for (auto iter = m_children.begin(); iter != m_children.end(); ++iter) { if (iter->second.field != field) continue; const wxSize size = iter->second.pChild->GetSize(); int position = rect.GetRight() - size.x - offset; iter->second.pChild->SetSize(position, rect.GetTop() + (rect.GetHeight() - size.y + 1) / 2, -1, -1); offset += size.x + 3; } } void CWidgetsStatusBar::SetFieldWidth(int field, int width) { wxStatusBarEx::SetFieldWidth(field, width); for (int i = 0; i < GetFieldsCount(); i++) PositionChildren(i); } class CIndicator : public wxStaticBitmap { public: CIndicator(CStatusBar* pStatusBar, const wxBitmap& bmp) : wxStaticBitmap(pStatusBar, wxID_ANY, bmp) { m_pStatusBar = pStatusBar; } protected: CStatusBar* m_pStatusBar; DECLARE_EVENT_TABLE() void OnLeftMouseUp(wxMouseEvent&) { m_pStatusBar->OnHandleLeftClick(this); } void OnRightMouseUp(wxMouseEvent&) { m_pStatusBar->OnHandleRightClick(this); } }; BEGIN_EVENT_TABLE(CIndicator, wxStaticBitmap) EVT_LEFT_UP(CIndicator::OnLeftMouseUp) EVT_RIGHT_UP(CIndicator::OnRightMouseUp) END_EVENT_TABLE() BEGIN_EVENT_TABLE(CStatusBar, CWidgetsStatusBar) EVT_MENU(XRCID("ID_SPEEDLIMITCONTEXT_ENABLE"), CStatusBar::OnSpeedLimitsEnable) EVT_MENU(XRCID("ID_SPEEDLIMITCONTEXT_CONFIGURE"), CStatusBar::OnSpeedLimitsConfigure) EVT_TIMER(wxID_ANY, CStatusBar::OnTimer) END_EVENT_TABLE() CStatusBar::CStatusBar(wxTopLevelWindow* pParent) : CWidgetsStatusBar(pParent), CStateEventHandler(0) { // Speedlimits RegisterOption(OPTION_SPEEDLIMIT_ENABLE); RegisterOption(OPTION_SPEEDLIMIT_INBOUND); RegisterOption(OPTION_SPEEDLIMIT_OUTBOUND); // Size format RegisterOption(OPTION_SIZE_FORMAT); RegisterOption(OPTION_SIZE_USETHOUSANDSEP); RegisterOption(OPTION_SIZE_DECIMALPLACES); RegisterOption(OPTION_ASCIIBINARY); // Reload icons RegisterOption(OPTION_THEME); CContextManager::Get()->RegisterHandler(this, STATECHANGE_SERVER, true); CContextManager::Get()->RegisterHandler(this, STATECHANGE_CHANGEDCONTEXT, false); CContextManager::Get()->RegisterHandler(this, STATECHANGE_ENCRYPTION, true); const int count = 3; SetFieldsCount(count); int array[count]; array[0] = wxSB_FLAT; array[1] = wxSB_NORMAL; array[2] = wxSB_FLAT; SetStatusStyles(count, array); SetStatusWidths(count, statbarWidths); UpdateSizeFormat(); UpdateSpeedLimitsIcon(); DisplayDataType(); DisplayEncrypted(); m_queue_size_timer.SetOwner(this); } CStatusBar::~CStatusBar() { } void CStatusBar::DisplayQueueSize(int64_t totalSize, bool hasUnknown) { m_size = totalSize; m_hasUnknownFiles = hasUnknown; if (m_queue_size_timer.IsRunning()) { m_queue_size_changed = true; } else { DoDisplayQueueSize(); m_queue_size_timer.Start(200, true); } } void CStatusBar::DoDisplayQueueSize() { m_queue_size_changed = false; if (m_size == 0 && !m_hasUnknownFiles) { SetStatusText(_("Queue: empty"), FIELD_QUEUESIZE); return; } wxString queueSize = wxString::Format(_("Queue: %s%s"), m_hasUnknownFiles ? _T(">") : _T(""), CSizeFormat::Format(m_size, true, m_sizeFormat, m_sizeFormatThousandsSep, m_sizeFormatDecimalPlaces)); SetStatusText(queueSize, FIELD_QUEUESIZE); } void CStatusBar::DisplayDataType() { const CServer* pServer = 0; const CState* pState = CContextManager::Get()->GetCurrentContext(); if (pState) pServer = pState->GetServer(); if (!pServer || !CServer::ProtocolHasDataTypeConcept(pServer->GetProtocol())) { if (m_pDataTypeIndicator) { RemoveField(widget_datatype); m_pDataTypeIndicator->Destroy(); m_pDataTypeIndicator = 0; } } else { wxString name; wxString desc; const int type = COptions::Get()->GetOptionVal(OPTION_ASCIIBINARY); if (type == 1) { name = _T("ART_ASCII"); desc = _("Current transfer type is set to ASCII."); } else if (type == 2) { name = _T("ART_BINARY"); desc = _("Current transfer type is set to binary."); } else { name = _T("ART_AUTO"); desc = _("Current transfer type is set to automatic detection."); } wxBitmap bmp = wxArtProvider::GetBitmap(name, wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall)); if (!m_pDataTypeIndicator) { m_pDataTypeIndicator = new CIndicator(this, bmp); AddField(0, widget_datatype, m_pDataTypeIndicator); } else m_pDataTypeIndicator->SetBitmap(bmp); m_pDataTypeIndicator->SetToolTip(desc); } } void CStatusBar::MeasureQueueSizeWidth() { wxClientDC dc(this); dc.SetFont(GetFont()); wxSize s = dc.GetTextExtent(_("Queue: empty")); wxString tmp = _T(">8888"); if (m_sizeFormatDecimalPlaces) { tmp += _T("."); for (int i = 0; i < m_sizeFormatDecimalPlaces; i++) tmp += _T("8"); } s.IncTo(dc.GetTextExtent(wxString::Format(_("Queue: %s MiB"), tmp))); SetFieldWidth(FIELD_QUEUESIZE, s.x + 10); } void CStatusBar::DisplayEncrypted() { const CServer* pServer = 0; CState* pState = CContextManager::Get()->GetCurrentContext(); if (pState) pServer = pState->GetServer(); bool encrypted = false; if (pServer) { CCertificateNotification* info; if (pServer->GetProtocol() == FTPS || pServer->GetProtocol() == FTPES || pServer->GetProtocol() == SFTP) { encrypted = true; } else if (pServer->GetProtocol() == FTP && pState->GetSecurityInfo(info)) { encrypted = true; } } if (!encrypted) { if (m_pEncryptionIndicator) { RemoveField(widget_encryption); m_pEncryptionIndicator->Destroy(); m_pEncryptionIndicator = 0; } } else { wxBitmap bmp = wxArtProvider::GetBitmap(_T("ART_LOCK"), wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall)); if (!m_pEncryptionIndicator) { m_pEncryptionIndicator = new CIndicator(this, bmp); AddField(0, widget_encryption, m_pEncryptionIndicator); m_pEncryptionIndicator->SetToolTip(_("The connection is encrypted. Click icon for details.")); } else m_pEncryptionIndicator->SetBitmap(bmp); } } void CStatusBar::UpdateSizeFormat() { // 0 equals bytes, however just use IEC binary prefixes instead, // exact byte counts for queue make no sense. m_sizeFormat = CSizeFormat::_format(COptions::Get()->GetOptionVal(OPTION_SIZE_FORMAT)); if (!m_sizeFormat) m_sizeFormat = CSizeFormat::iec; m_sizeFormatThousandsSep = COptions::Get()->GetOptionVal(OPTION_SIZE_USETHOUSANDSEP) != 0; m_sizeFormatDecimalPlaces = COptions::Get()->GetOptionVal(OPTION_SIZE_DECIMALPLACES); MeasureQueueSizeWidth(); DisplayQueueSize(m_size, m_hasUnknownFiles); } void CStatusBar::OnHandleLeftClick(wxWindow* pWnd) { if (pWnd == m_pEncryptionIndicator) { CState* pState = CContextManager::Get()->GetCurrentContext(); CCertificateNotification *pCertificateNotification = 0; CSftpEncryptionNotification *pSftpEncryptionNotification = 0; if (pState->GetSecurityInfo(pCertificateNotification)) { CVerifyCertDialog dlg; dlg.ShowVerificationDialog(*pCertificateNotification, true); } else if (pState->GetSecurityInfo(pSftpEncryptionNotification)) { CSftpEncryptioInfoDialog dlg; dlg.ShowDialog(pSftpEncryptionNotification); } else wxMessageBoxEx(_("Certificate and session data are not available yet."), _("Security information")); } else if (pWnd == m_pSpeedLimitsIndicator) { CSpeedLimitsDialog dlg; dlg.Run(m_pParent); } else if (pWnd == m_pDataTypeIndicator) { ShowDataTypeMenu(); } } void CStatusBar::OnHandleRightClick(wxWindow* pWnd) { if (pWnd == m_pDataTypeIndicator) { ShowDataTypeMenu(); } else if (pWnd == m_pSpeedLimitsIndicator) { wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_SPEEDLIMITCONTEXT")); if (!pMenu) return; int downloadlimit = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_INBOUND); int uploadlimit = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_OUTBOUND); bool enable = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_ENABLE) != 0; if (!downloadlimit && !uploadlimit) enable = false; pMenu->Check(XRCID("ID_SPEEDLIMITCONTEXT_ENABLE"), enable); PopupMenu(pMenu); delete pMenu; } } void CStatusBar::ShowDataTypeMenu() { wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_TRANSFER_TYPE_CONTEXT")); if (!pMenu) return; const int type = COptions::Get()->GetOptionVal(OPTION_ASCIIBINARY); switch (type) { case 1: pMenu->Check(XRCID("ID_MENU_TRANSFER_TYPE_ASCII"), true); break; case 2: pMenu->Check(XRCID("ID_MENU_TRANSFER_TYPE_BINARY"), true); break; default: pMenu->Check(XRCID("ID_MENU_TRANSFER_TYPE_AUTO"), true); break; } PopupMenu(pMenu); delete pMenu; } void CStatusBar::UpdateSpeedLimitsIcon() { bool enable = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_ENABLE) != 0; wxBitmap bmp = wxArtProvider::GetBitmap(_T("ART_SPEEDLIMITS"), wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall)); if (!bmp.Ok()) { return; } wxString tooltip; int downloadLimit = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_INBOUND); int uploadLimit = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_OUTBOUND); if (!enable || (!downloadLimit && !uploadLimit)) { wxImage img = bmp.ConvertToImage(); img = img.ConvertToGreyscale(); bmp = wxBitmap(img); tooltip = _("Speed limits are disabled, click to change."); } else { tooltip = _("Speed limits are enabled, click to change."); tooltip += _T("\n"); if (downloadLimit) tooltip += wxString::Format(_("Download limit: %s/s"), CSizeFormat::FormatUnit(downloadLimit, CSizeFormat::kilo)); else tooltip += _("Download limit: none"); tooltip += _T("\n"); if (uploadLimit) tooltip += wxString::Format(_("Upload limit: %s/s"), CSizeFormat::FormatUnit(uploadLimit, CSizeFormat::kilo)); else tooltip += _("Upload limit: none"); } if (!m_pSpeedLimitsIndicator) { m_pSpeedLimitsIndicator = new CIndicator(this, bmp); AddField(0, widget_speedlimit, m_pSpeedLimitsIndicator); } else m_pSpeedLimitsIndicator->SetBitmap(bmp); m_pSpeedLimitsIndicator->SetToolTip(tooltip); } void CStatusBar::OnOptionsChanged(changed_options_t const& options) { if (options.test(OPTION_SPEEDLIMIT_ENABLE) || options.test(OPTION_SPEEDLIMIT_INBOUND) || options.test(OPTION_SPEEDLIMIT_OUTBOUND)) { UpdateSpeedLimitsIcon(); } if (options.test(OPTION_SIZE_FORMAT) || options.test(OPTION_SIZE_USETHOUSANDSEP) || options.test(OPTION_SIZE_DECIMALPLACES)) { UpdateSizeFormat(); } if (options.test(OPTION_ASCIIBINARY)) { DisplayDataType(); } if (options.test(OPTION_THEME)) { DisplayDataType(); UpdateSpeedLimitsIcon(); DisplayEncrypted(); } } void CStatusBar::OnStateChange(CState*, enum t_statechange_notifications notification, const wxString&, const void*) { if (notification == STATECHANGE_SERVER || notification == STATECHANGE_CHANGEDCONTEXT) { DisplayDataType(); DisplayEncrypted(); } else if (notification == STATECHANGE_ENCRYPTION) { DisplayEncrypted(); } } void CStatusBar::OnSpeedLimitsEnable(wxCommandEvent&) { int downloadlimit = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_INBOUND); int uploadlimit = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_OUTBOUND); bool enable = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_ENABLE) == 0; if (enable) { if (!downloadlimit && !uploadlimit) { CSpeedLimitsDialog dlg; dlg.Run(m_pParent); } else COptions::Get()->SetOption(OPTION_SPEEDLIMIT_ENABLE, 1); } else COptions::Get()->SetOption(OPTION_SPEEDLIMIT_ENABLE, 0); } void CStatusBar::OnSpeedLimitsConfigure(wxCommandEvent&) { CSpeedLimitsDialog dlg; dlg.Run(m_pParent); } void CStatusBar::OnTimer(wxTimerEvent&) { if (m_queue_size_changed && !m_queue_size_timer.IsRunning()) { DoDisplayQueueSize(); m_queue_size_timer.Start(200, true); } }