summaryrefslogtreecommitdiff
path: root/ui/tray_icon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ui/tray_icon.cpp')
-rw-r--r--ui/tray_icon.cpp224
1 files changed, 100 insertions, 124 deletions
diff --git a/ui/tray_icon.cpp b/ui/tray_icon.cpp
index 6a9c640c..3826458f 100644
--- a/ui/tray_icon.cpp
+++ b/ui/tray_icon.cpp
@@ -14,8 +14,6 @@
#include "../lib/resources.h"
-const wxEventType FFS_REQUEST_RESUME_TRAY_EVENT = wxNewEventType();
-
namespace
{
void fillRange(wxImage& img, int pixelFirst, int pixelLast, const wxColor& col) //tolerant input range
@@ -52,90 +50,56 @@ void fillRange(wxImage& img, int pixelFirst, int pixelLast, const wxColor& col)
wxIcon generateProgressIcon(const wxImage& logo, double fraction) //generate icon with progress indicator
{
- if (!logo.IsOk())
+ if (!logo.IsOk() || logo.GetWidth() <= 0 || logo.GetHeight() <= 0)
return wxIcon();
const int pixelCount = logo.GetWidth() * logo.GetHeight();
- const int startFillPixel = std::min(numeric::round(fraction * pixelCount), pixelCount);
+ const int startFillPixel = numeric::confineCpy(numeric::round(fraction * pixelCount), 0, pixelCount);
//minor optimization
static std::pair<int, wxIcon> buffer = std::make_pair(-1, wxNullIcon);
if (buffer.first != startFillPixel)
{
- //progress bar
- if (logo.GetWidth() > 0 &&
- logo.GetHeight() > 0)
- {
- wxImage genImage(logo.Copy()); //workaround wxWidgets' screwed-up design from hell: their copy-construction implements reference-counting WITHOUT copy-on-write!
-
- //gradually make FFS icon brighter while nearing completion
- zen::brighten(genImage, -200 * (1 - fraction));
-
- //fill black border row
- if (startFillPixel <= pixelCount - genImage.GetWidth())
- {
- /*
- --------
- ---bbbbb
- bbbbSyyy S : start yellow remainder
- yyyyyyyy
- */
- int bStart = startFillPixel - genImage.GetWidth();
- if (bStart % genImage.GetWidth() != 0) //add one more black pixel, see ascii-art
- --bStart;
- fillRange(genImage, bStart, startFillPixel, *wxBLACK);
- }
- else if (startFillPixel < pixelCount)
- {
- //special handling for last row
- /*
- --------
- --------
- ---bbbbb
- ---bSyyy S : start yellow remainder
- */
- int bStart = startFillPixel - genImage.GetWidth() - 1;
- int bEnd = (bStart / genImage.GetWidth() + 1) * genImage.GetWidth();
-
- fillRange(genImage, bStart, bEnd, *wxBLACK);
- fillRange(genImage, startFillPixel - 1, startFillPixel, *wxBLACK);
- }
+ wxImage genImage(logo.Copy()); //workaround wxWidgets' screwed-up design from hell: their copy-construction implements reference-counting WITHOUT copy-on-write!
- //fill yellow remainder
- fillRange(genImage, startFillPixel, pixelCount, wxColour(240, 200, 0));
+ //gradually make FFS icon brighter while nearing completion
+ zen::brighten(genImage, -200 * (1 - fraction));
+ //fill black border row
+ if (startFillPixel <= pixelCount - genImage.GetWidth())
+ {
/*
- const int indicatorWidth = genImage.GetWidth() * .4;
- const int indicatorXBegin = std::ceil((genImage.GetWidth() - indicatorWidth) / 2.0);
- const int indicatorYBegin = genImage.GetHeight() - indicatorHeight;
-
- //draw progress indicator: do NOT use wxDC::DrawRectangle! Doesn't respect alpha in Windows, but does in Linux!
- //We need a simple, working solution:
-
- for (int row = indicatorYBegin; row < genImage.GetHeight(); ++row)
- {
- for (int col = indicatorXBegin; col < indicatorXBegin + indicatorWidth; ++col)
- {
- unsigned char* const pixelBegin = data + (row * genImage.GetWidth() + col) * 3;
- pixelBegin[0] = 240; //red
- pixelBegin[1] = 200; //green
- pixelBegin[2] = 0; //blue
- }
- }
-
- if (genImage.HasAlpha())
- {
- unsigned char* const alpha = genImage.GetAlpha();
- //make progress indicator fully opaque:
- for (int row = indicatorYBegin; row < genImage.GetHeight(); ++row)
- ::memset(alpha + row * genImage.GetWidth() + indicatorXBegin, wxIMAGE_ALPHA_OPAQUE, indicatorWidth);
- }
+ --------
+ ---bbbbb
+ bbbbSyyy S : start yellow remainder
+ yyyyyyyy
*/
- buffer.second.CopyFromBitmap(wxBitmap(genImage));
+ int bStart = startFillPixel - genImage.GetWidth();
+ if (bStart % genImage.GetWidth() != 0) //add one more black pixel, see ascii-art
+ --bStart;
+ fillRange(genImage, bStart, startFillPixel, *wxBLACK);
+ }
+ else if (startFillPixel < pixelCount)
+ {
+ //special handling for last row
+ /*
+ --------
+ --------
+ ---bbbbb
+ ---bSyyy S : start yellow remainder
+ */
+ int bStart = startFillPixel - genImage.GetWidth() - 1;
+ int bEnd = (bStart / genImage.GetWidth() + 1) * genImage.GetWidth();
+
+ fillRange(genImage, bStart, bEnd, *wxBLACK);
+ fillRange(genImage, startFillPixel - 1, startFillPixel, *wxBLACK);
}
- else
- buffer.second = wxIcon();
+
+ //fill yellow remainder
+ fillRange(genImage, startFillPixel, pixelCount, wxColour(240, 200, 0));
+
+ buffer.second.CopyFromBitmap(wxBitmap(genImage));
}
return buffer.second;
@@ -145,7 +109,7 @@ wxIcon generateProgressIcon(const wxImage& logo, double fraction) //generate ico
enum Selection
{
- CONTEXT_RESTORE = 1, //wxWidgets: "A MenuItem ID of Zero does not work under Mac"
+ CONTEXT_RESTORE = 1, //wxWidgets: "A MenuItem ID of zero does not work under Mac"
CONTEXT_ABOUT = wxID_ABOUT
};
}
@@ -154,95 +118,107 @@ enum Selection
class FfsTrayIcon::TaskBarImpl : public wxTaskBarIcon
{
public:
- TaskBarImpl(FfsTrayIcon& parent) : parent_(&parent) {}
+ TaskBarImpl(const std::function<void()>& onRequestResume) : onRequestResume_(onRequestResume)
+ {
+ Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(TaskBarImpl::OnDoubleClick), nullptr, this); //register double-click
+ }
- void parentHasDied() { parent_ = nullptr; }
+ //virtual ~TaskBarImpl(){}
+
+ void dontCallbackAnymore() { onRequestResume_ = nullptr; }
private:
virtual wxMenu* CreatePopupMenu()
{
- if (!parent_)
+ if (!onRequestResume_)
return nullptr;
wxMenu* contextMenu = new wxMenu;
- contextMenu->Append(CONTEXT_ABOUT, _("&About"));
- contextMenu->AppendSeparator();
contextMenu->Append(CONTEXT_RESTORE, _("&Restore"));
+ contextMenu->AppendSeparator();
+ contextMenu->Append(CONTEXT_ABOUT, _("&About"));
//event handling
- contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(FfsTrayIcon::OnContextMenuSelection), nullptr, parent_);
+ contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TaskBarImpl::OnContextMenuSelection), nullptr, this);
return contextMenu; //ownership transferred to caller
}
- FfsTrayIcon* parent_;
+ void OnContextMenuSelection(wxCommandEvent& event)
+ {
+ switch (static_cast<Selection>(event.GetId()))
+ {
+ case CONTEXT_ABOUT:
+ {
+ //ATTENTION: the modal dialog below does NOT disable all GUI input, e.g. user may still double-click on tray icon
+ //which will implicitly destroy the tray icon while still showing the modal dialog
+ SetEvtHandlerEnabled(false);
+ ZEN_ON_SCOPE_EXIT(SetEvtHandlerEnabled(true));
+
+ zen::showAboutDialog(nullptr);
+ }
+ break;
+
+ case CONTEXT_RESTORE:
+ if (onRequestResume_)
+ onRequestResume_();
+ break;
+ }
+ }
+
+ void OnDoubleClick(wxCommandEvent& event)
+ {
+ if (onRequestResume_)
+ onRequestResume_();
+ }
+
+ std::function<void()> onRequestResume_;
};
-FfsTrayIcon::FfsTrayIcon() :
- trayIcon(new TaskBarImpl(*this)),
- fractionLast(1), //show FFS logo by default
+FfsTrayIcon::FfsTrayIcon(const std::function<void()>& onRequestResume) :
+ trayIcon(new TaskBarImpl(onRequestResume)),
+ activeFraction(1), //show FFS logo by default
#if defined ZEN_WIN || defined ZEN_MAC //16x16 seems to be the only size that is shown correctly on OS X
logo(getResourceImage(L"FFS_tray_16x16").ConvertToImage())
#elif defined ZEN_LINUX
logo(getResourceImage(L"FFS_tray_24x24").ConvertToImage())
#endif
{
- trayIcon->SetIcon(generateProgressIcon(logo, fractionLast), L"FreeFileSync");
- trayIcon->Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(FfsTrayIcon::OnDoubleClick), nullptr, this); //register double-click
+ trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), L"FreeFileSync");
}
FfsTrayIcon::~FfsTrayIcon()
{
- trayIcon->RemoveIcon(); //hide icon until final deletion takes place
- trayIcon->Disconnect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(FfsTrayIcon::OnDoubleClick), nullptr, this);
- trayIcon->parentHasDied(); //TaskBarImpl (potentially) has longer lifetime than FfsTrayIcon: avoid callback!
-
- //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking)
- if (!wxPendingDelete.Member(trayIcon))
- wxPendingDelete.Append(trayIcon);
-}
+ trayIcon->dontCallbackAnymore(); //TaskBarImpl has longer lifetime than FfsTrayIcon: avoid callback!
+ /*
+ This is not working correctly on OS X! It seems both wxTaskBarIcon::RemoveIcon() and ~wxTaskBarIcon() are broken and do NOT immediately
+ remove the icon from the system tray! Only some time later in the event loop which called these functions they will be removed.
+ Maybe some system component has still shared ownership? Objective C auto release pools are freed at the end of the current event loop...
+ Anyway, wxWidgets fails to disconnect the wxTaskBarIcon event handlers before calling "[m_statusitem release]"!
-void FfsTrayIcon::setToolTip(const wxString& toolTip)
-{
- toolTipLast = toolTip;
- trayIcon->SetIcon(generateProgressIcon(logo, fractionLast), toolTip); //another wxWidgets design bug: non-orthogonal method!
-}
+ => !!!clicking on the icon after ~wxTaskBarIcon ran crashes the application!!!
+ - if ~wxTaskBarIcon() ran from the SyncProgressDialog::updateGui() event loop (e.g. user manually clicking the icon) => icon removed on return
+ - if ~wxTaskBarIcon() ran from SyncProgressDialog::closeWindowDirectly() => leaves the icon dangling until user closes this dialog and outter event loop runs!
+ */
-void FfsTrayIcon::setProgress(double fraction)
-{
- fractionLast = fraction;
- trayIcon->SetIcon(generateProgressIcon(logo, fraction), toolTipLast);
+ trayIcon->RemoveIcon(); //required on Windows: unlike on OS X, wxPendingDelete does not kick in before main event loop!
+ //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking)
+ wxPendingDelete.Append(trayIcon);
}
-void FfsTrayIcon::OnContextMenuSelection(wxCommandEvent& event)
+void FfsTrayIcon::setToolTip(const wxString& toolTip)
{
- switch (static_cast<Selection>(event.GetId()))
- {
- case CONTEXT_ABOUT:
- {
- //ATTENTION: the modal dialog below does NOT disable all GUI input, e.g. user may still double-click on tray icon
- //which will implicitly destroy the tray icon while still showing the modal dialog
- trayIcon->SetEvtHandlerEnabled(false);
- zen::showAboutDialog(nullptr);
- trayIcon->SetEvtHandlerEnabled(true);
- }
- break;
- case CONTEXT_RESTORE:
- {
- wxCommandEvent dummy(FFS_REQUEST_RESUME_TRAY_EVENT);
- ProcessEvent(dummy);
- }
- }
+ activeToolTip = toolTip;
+ trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), activeToolTip); //another wxWidgets design bug: non-orthogonal method!
}
-void FfsTrayIcon::OnDoubleClick(wxCommandEvent& event)
+void FfsTrayIcon::setProgress(double fraction)
{
- wxCommandEvent dummy(FFS_REQUEST_RESUME_TRAY_EVENT);
- ProcessEvent(dummy);
+ activeFraction = fraction;
+ trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), activeToolTip);
}
-
bgstack15