| 1 | ///////////////////////////////////////////////////////////////////////// |
| 2 | // File: src/unix/taskbarx11.cpp |
| 3 | // Purpose: wxTaskBarIcon class for common Unix desktops |
| 4 | // Author: Vaclav Slavik |
| 5 | // Modified by: |
| 6 | // Created: 04/04/2003 |
| 7 | // RCS-ID: $Id$ |
| 8 | // Copyright: (c) Vaclav Slavik, 2003 |
| 9 | // Licence: wxWindows licence |
| 10 | ///////////////////////////////////////////////////////////////////////// |
| 11 | |
| 12 | // NB: This implementation does *not* work with every X11 window manager. |
| 13 | // Currently only GNOME 1.2 and KDE 1,2,3 methods are implemented here. |
| 14 | // Freedesktop.org's System Tray specification is implemented in |
| 15 | // src/gtk/taskbar.cpp and used from here under wxGTK. |
| 16 | // |
| 17 | // Thanks to Ian Campbell, author of XMMS Status Docklet, for publishing |
| 18 | // KDE and GNOME 1.2 methods. |
| 19 | |
| 20 | |
| 21 | // For compilers that support precompilation, includes "wx.h". |
| 22 | #include "wx/wxprec.h" |
| 23 | |
| 24 | #if wxUSE_TASKBARICON && !defined(__WXGTK20__) |
| 25 | |
| 26 | #include "wx/taskbar.h" |
| 27 | |
| 28 | #ifndef WX_PRECOMP |
| 29 | #include "wx/log.h" |
| 30 | #include "wx/frame.h" |
| 31 | #include "wx/dcclient.h" |
| 32 | #include "wx/statbmp.h" |
| 33 | #include "wx/sizer.h" |
| 34 | #include "wx/bitmap.h" |
| 35 | #include "wx/image.h" |
| 36 | #endif |
| 37 | |
| 38 | #ifdef __VMS |
| 39 | #pragma message disable nosimpint |
| 40 | #endif |
| 41 | #include <X11/Xlib.h> |
| 42 | #include <X11/Xatom.h> |
| 43 | #ifdef __VMS |
| 44 | #pragma message enable nosimpint |
| 45 | #endif |
| 46 | |
| 47 | // ---------------------------------------------------------------------------- |
| 48 | // base class that implements toolkit-specific method: |
| 49 | // ---------------------------------------------------------------------------- |
| 50 | |
| 51 | class WXDLLIMPEXP_ADV wxTaskBarIconAreaBase : public wxFrame |
| 52 | { |
| 53 | public: |
| 54 | wxTaskBarIconAreaBase() |
| 55 | : wxFrame(NULL, wxID_ANY, wxT("systray icon"), |
| 56 | wxDefaultPosition, wxDefaultSize, |
| 57 | wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR | |
| 58 | wxSIMPLE_BORDER | wxFRAME_SHAPED) {} |
| 59 | |
| 60 | static bool IsProtocolSupported() { return false; } |
| 61 | }; |
| 62 | |
| 63 | // ---------------------------------------------------------------------------- |
| 64 | // toolkit dependent methods to set properties on helper window: |
| 65 | // ---------------------------------------------------------------------------- |
| 66 | |
| 67 | #if defined(__WXGTK__) |
| 68 | #include <gtk/gtk.h> |
| 69 | #include <gdk/gdkx.h> |
| 70 | #define GetDisplay() GDK_DISPLAY() |
| 71 | #define GetXWindow(wxwin) GDK_WINDOW_XWINDOW((wxwin)->m_widget->window) |
| 72 | #elif defined(__WXX11__) || defined(__WXMOTIF__) |
| 73 | #include "wx/x11/privx.h" |
| 74 | #define GetDisplay() ((Display*)wxGlobalDisplay()) |
| 75 | #define GetXWindow(wxwin) ((Window)(wxwin)->GetHandle()) |
| 76 | #else |
| 77 | #error "You must define X11 accessors for this port!" |
| 78 | #endif |
| 79 | |
| 80 | |
| 81 | // ---------------------------------------------------------------------------- |
| 82 | // wxTaskBarIconArea is the real window that shows the icon: |
| 83 | // ---------------------------------------------------------------------------- |
| 84 | |
| 85 | class WXDLLIMPEXP_ADV wxTaskBarIconArea : public wxTaskBarIconAreaBase |
| 86 | { |
| 87 | public: |
| 88 | wxTaskBarIconArea(wxTaskBarIcon *icon, const wxBitmap &bmp); |
| 89 | void SetTrayIcon(const wxBitmap& bmp); |
| 90 | bool IsOk() { return true; } |
| 91 | |
| 92 | protected: |
| 93 | void SetLegacyWMProperties(); |
| 94 | |
| 95 | void OnSizeChange(wxSizeEvent& event); |
| 96 | void OnPaint(wxPaintEvent& evt); |
| 97 | void OnMouseEvent(wxMouseEvent& event); |
| 98 | void OnMenuEvent(wxCommandEvent& event); |
| 99 | |
| 100 | wxTaskBarIcon *m_icon; |
| 101 | wxPoint m_pos; |
| 102 | wxBitmap m_bmp; |
| 103 | |
| 104 | DECLARE_EVENT_TABLE() |
| 105 | }; |
| 106 | |
| 107 | BEGIN_EVENT_TABLE(wxTaskBarIconArea, wxTaskBarIconAreaBase) |
| 108 | EVT_SIZE(wxTaskBarIconArea::OnSizeChange) |
| 109 | EVT_MOUSE_EVENTS(wxTaskBarIconArea::OnMouseEvent) |
| 110 | EVT_MENU(wxID_ANY, wxTaskBarIconArea::OnMenuEvent) |
| 111 | EVT_PAINT(wxTaskBarIconArea::OnPaint) |
| 112 | END_EVENT_TABLE() |
| 113 | |
| 114 | wxTaskBarIconArea::wxTaskBarIconArea(wxTaskBarIcon *icon, const wxBitmap &bmp) |
| 115 | : wxTaskBarIconAreaBase(), m_icon(icon), m_bmp(bmp) |
| 116 | { |
| 117 | // Set initial size to bitmap size (tray manager may and often will |
| 118 | // change it): |
| 119 | SetClientSize(wxSize(bmp.GetWidth(), bmp.GetHeight())); |
| 120 | |
| 121 | SetTrayIcon(bmp); |
| 122 | |
| 123 | if (!IsProtocolSupported()) |
| 124 | { |
| 125 | wxLogTrace(wxT("systray"), |
| 126 | wxT("using legacy KDE1,2 and GNOME 1.2 methods")); |
| 127 | SetLegacyWMProperties(); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | void wxTaskBarIconArea::SetTrayIcon(const wxBitmap& bmp) |
| 132 | { |
| 133 | m_bmp = bmp; |
| 134 | |
| 135 | // determine suitable bitmap size: |
| 136 | wxSize winsize(GetClientSize()); |
| 137 | wxSize bmpsize(m_bmp.GetWidth(), m_bmp.GetHeight()); |
| 138 | wxSize iconsize(wxMin(winsize.x, bmpsize.x), wxMin(winsize.y, bmpsize.y)); |
| 139 | |
| 140 | // rescale the bitmap to fit into the tray icon window: |
| 141 | if (bmpsize != iconsize) |
| 142 | { |
| 143 | wxImage img = m_bmp.ConvertToImage(); |
| 144 | img.Rescale(iconsize.x, iconsize.y); |
| 145 | m_bmp = wxBitmap(img); |
| 146 | } |
| 147 | |
| 148 | wxRegion region; |
| 149 | region.Union(m_bmp); |
| 150 | |
| 151 | // if the bitmap is smaller than the window, offset it: |
| 152 | if (winsize != iconsize) |
| 153 | { |
| 154 | m_pos.x = (winsize.x - iconsize.x) / 2; |
| 155 | m_pos.y = (winsize.y - iconsize.y) / 2; |
| 156 | region.Offset(m_pos.x, m_pos.y); |
| 157 | } |
| 158 | |
| 159 | // set frame's shape to correct value and redraw: |
| 160 | SetShape(region); |
| 161 | Refresh(); |
| 162 | } |
| 163 | |
| 164 | void wxTaskBarIconArea::SetLegacyWMProperties() |
| 165 | { |
| 166 | #ifdef __WXGTK__ |
| 167 | gtk_widget_realize(m_widget); |
| 168 | #endif |
| 169 | |
| 170 | long data[1]; |
| 171 | |
| 172 | // KDE 2 & KDE 3: |
| 173 | Atom _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR = |
| 174 | XInternAtom(GetDisplay(), "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False); |
| 175 | data[0] = 0; |
| 176 | XChangeProperty(GetDisplay(), GetXWindow(this), |
| 177 | _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR, |
| 178 | XA_WINDOW, 32, |
| 179 | PropModeReplace, (unsigned char*)data, 1); |
| 180 | |
| 181 | // GNOME 1.2 & KDE 1: |
| 182 | Atom KWM_DOCKWINDOW = |
| 183 | XInternAtom(GetDisplay(), "KWM_DOCKWINDOW", False); |
| 184 | data[0] = 1; |
| 185 | XChangeProperty(GetDisplay(), GetXWindow(this), |
| 186 | KWM_DOCKWINDOW, |
| 187 | KWM_DOCKWINDOW, 32, |
| 188 | PropModeReplace, (unsigned char*)data, 1); |
| 189 | } |
| 190 | |
| 191 | void wxTaskBarIconArea::OnSizeChange(wxSizeEvent& WXUNUSED(event)) |
| 192 | { |
| 193 | wxLogTrace(wxT("systray"), wxT("icon size changed to %i x %i"), |
| 194 | GetSize().x, GetSize().y); |
| 195 | // rescale or reposition the icon as needed: |
| 196 | wxBitmap bmp(m_bmp); |
| 197 | SetTrayIcon(bmp); |
| 198 | } |
| 199 | |
| 200 | void wxTaskBarIconArea::OnPaint(wxPaintEvent& WXUNUSED(event)) |
| 201 | { |
| 202 | wxPaintDC dc(this); |
| 203 | dc.DrawBitmap(m_bmp, m_pos.x, m_pos.y, true); |
| 204 | } |
| 205 | |
| 206 | void wxTaskBarIconArea::OnMouseEvent(wxMouseEvent& event) |
| 207 | { |
| 208 | wxEventType type = 0; |
| 209 | wxEventType mtype = event.GetEventType(); |
| 210 | |
| 211 | if (mtype == wxEVT_LEFT_DOWN) |
| 212 | type = wxEVT_TASKBAR_LEFT_DOWN; |
| 213 | else if (mtype == wxEVT_LEFT_UP) |
| 214 | type = wxEVT_TASKBAR_LEFT_UP; |
| 215 | else if (mtype == wxEVT_LEFT_DCLICK) |
| 216 | type = wxEVT_TASKBAR_LEFT_DCLICK; |
| 217 | else if (mtype == wxEVT_RIGHT_DOWN) |
| 218 | type = wxEVT_TASKBAR_RIGHT_DOWN; |
| 219 | else if (mtype == wxEVT_RIGHT_UP) |
| 220 | type = wxEVT_TASKBAR_RIGHT_UP; |
| 221 | else if (mtype == wxEVT_RIGHT_DCLICK) |
| 222 | type = wxEVT_TASKBAR_RIGHT_DCLICK; |
| 223 | else if (mtype == wxEVT_MOTION) |
| 224 | type = wxEVT_TASKBAR_MOVE; |
| 225 | else |
| 226 | return; |
| 227 | |
| 228 | wxTaskBarIconEvent e(type, m_icon); |
| 229 | m_icon->ProcessEvent(e); |
| 230 | } |
| 231 | |
| 232 | void wxTaskBarIconArea::OnMenuEvent(wxCommandEvent& event) |
| 233 | { |
| 234 | m_icon->ProcessEvent(event); |
| 235 | } |
| 236 | |
| 237 | // ---------------------------------------------------------------------------- |
| 238 | // wxTaskBarIconBase class: |
| 239 | // ---------------------------------------------------------------------------- |
| 240 | |
| 241 | bool wxTaskBarIconBase::IsAvailable() |
| 242 | { |
| 243 | return wxTaskBarIconArea::IsProtocolSupported(); |
| 244 | } |
| 245 | |
| 246 | // ---------------------------------------------------------------------------- |
| 247 | // wxTaskBarIcon class: |
| 248 | // ---------------------------------------------------------------------------- |
| 249 | |
| 250 | IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler) |
| 251 | |
| 252 | wxTaskBarIcon::wxTaskBarIcon() : m_iconWnd(NULL) |
| 253 | { |
| 254 | } |
| 255 | |
| 256 | wxTaskBarIcon::~wxTaskBarIcon() |
| 257 | { |
| 258 | if (m_iconWnd) |
| 259 | { |
| 260 | m_iconWnd->Disconnect(wxEVT_DESTROY, |
| 261 | wxWindowDestroyEventHandler(wxTaskBarIcon::OnDestroy), NULL, this); |
| 262 | RemoveIcon(); |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | bool wxTaskBarIcon::IsOk() const |
| 267 | { |
| 268 | return true; |
| 269 | } |
| 270 | |
| 271 | bool wxTaskBarIcon::IsIconInstalled() const |
| 272 | { |
| 273 | return m_iconWnd != NULL; |
| 274 | } |
| 275 | |
| 276 | // Destroy event from wxTaskBarIconArea |
| 277 | void wxTaskBarIcon::OnDestroy(wxWindowDestroyEvent&) |
| 278 | { |
| 279 | // prevent crash if wxTaskBarIconArea is destroyed by something else, |
| 280 | // for example if panel/kicker is killed |
| 281 | m_iconWnd = NULL; |
| 282 | } |
| 283 | |
| 284 | bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip) |
| 285 | { |
| 286 | wxBitmap bmp; |
| 287 | bmp.CopyFromIcon(icon); |
| 288 | |
| 289 | if (!m_iconWnd) |
| 290 | { |
| 291 | m_iconWnd = new wxTaskBarIconArea(this, bmp); |
| 292 | if (m_iconWnd->IsOk()) |
| 293 | { |
| 294 | m_iconWnd->Connect(wxEVT_DESTROY, |
| 295 | wxWindowDestroyEventHandler(wxTaskBarIcon::OnDestroy), |
| 296 | NULL, this); |
| 297 | m_iconWnd->Show(); |
| 298 | } |
| 299 | else |
| 300 | { |
| 301 | m_iconWnd->Destroy(); |
| 302 | m_iconWnd = NULL; |
| 303 | return false; |
| 304 | } |
| 305 | } |
| 306 | else |
| 307 | { |
| 308 | m_iconWnd->SetTrayIcon(bmp); |
| 309 | } |
| 310 | |
| 311 | #if wxUSE_TOOLTIPS |
| 312 | if (!tooltip.empty()) |
| 313 | m_iconWnd->SetToolTip(tooltip); |
| 314 | else |
| 315 | m_iconWnd->SetToolTip(NULL); |
| 316 | #else |
| 317 | wxUnusedVar(tooltip); |
| 318 | #endif |
| 319 | return true; |
| 320 | } |
| 321 | |
| 322 | bool wxTaskBarIcon::RemoveIcon() |
| 323 | { |
| 324 | if (!m_iconWnd) |
| 325 | return false; |
| 326 | m_iconWnd->Destroy(); |
| 327 | m_iconWnd = NULL; |
| 328 | return true; |
| 329 | } |
| 330 | |
| 331 | bool wxTaskBarIcon::PopupMenu(wxMenu *menu) |
| 332 | { |
| 333 | if (!m_iconWnd) |
| 334 | return false; |
| 335 | m_iconWnd->PopupMenu(menu); |
| 336 | return true; |
| 337 | } |
| 338 | |
| 339 | #endif // wxUSE_TASKBARICON |