src/gui/util/qsystemtrayicon_win.cpp
changeset 33 3e2da88830cd
parent 30 5dc02b23752f
equal deleted inserted replaced
30:5dc02b23752f 33:3e2da88830cd
    39 **
    39 **
    40 ****************************************************************************/
    40 ****************************************************************************/
    41 
    41 
    42 #include "qsystemtrayicon_p.h"
    42 #include "qsystemtrayicon_p.h"
    43 #ifndef QT_NO_SYSTEMTRAYICON
    43 #ifndef QT_NO_SYSTEMTRAYICON
    44 #define _WIN32_IE 0x0600 //required for NOTIFYICONDATA_V2_SIZE
    44 
    45 
    45 #ifndef _WIN32_WINNT
    46 //missing defines for MINGW :
    46 #define _WIN32_WINNT 0x0600
    47 #ifndef NIN_BALLOONTIMEOUT
    47 #endif
    48 #define NIN_BALLOONTIMEOUT  (WM_USER + 4)
    48 
    49 #endif
    49 #ifndef _WIN32_IE
    50 #ifndef NIN_BALLOONUSERCLICK
    50 #define _WIN32_IE 0x600
    51 #define NIN_BALLOONUSERCLICK (WM_USER + 5)
       
    52 #endif
    51 #endif
    53 
    52 
    54 #include <qt_windows.h>
    53 #include <qt_windows.h>
       
    54 #include <windowsx.h>
    55 #include <commctrl.h>
    55 #include <commctrl.h>
    56 #include <QBitmap>
    56 
    57 #include <QLibrary>
    57 #include <QLibrary>
    58 #include <QApplication>
    58 #include <QApplication>
    59 #include <QToolTip>
       
    60 #include <QDesktopWidget>
       
    61 #include <QSettings>
    59 #include <QSettings>
    62 
    60 
    63 QT_BEGIN_NAMESPACE
    61 QT_BEGIN_NAMESPACE
    64 
    62 
    65 static const UINT q_uNOTIFYICONID = 0;
    63 static const UINT q_uNOTIFYICONID = 0;
    72     HWND hWnd;
    70     HWND hWnd;
    73     UINT uID;
    71     UINT uID;
    74     GUID guidItem;
    72     GUID guidItem;
    75 };
    73 };
    76 
    74 
       
    75 #ifndef NOTIFYICON_VERSION_4
       
    76 #define NOTIFYICON_VERSION_4 4
       
    77 #endif
       
    78 
       
    79 #ifndef NIN_SELECT
       
    80 #define NIN_SELECT (WM_USER + 0)
       
    81 #endif
       
    82 
       
    83 #ifndef NIN_KEYSELECT
       
    84 #define NIN_KEYSELECT (WM_USER + 1)
       
    85 #endif
       
    86 
       
    87 #ifndef NIN_BALLOONTIMEOUT
       
    88 #define NIN_BALLOONTIMEOUT (WM_USER + 4)
       
    89 #endif
       
    90 
       
    91 #ifndef NIN_BALLOONUSERCLICK
       
    92 #define NIN_BALLOONUSERCLICK (WM_USER + 5)
       
    93 #endif
       
    94 
       
    95 #ifndef NIF_SHOWTIP
       
    96 #define NIF_SHOWTIP 0x00000080
       
    97 #endif
       
    98 
    77 #define Q_MSGFLT_ALLOW 1
    99 #define Q_MSGFLT_ALLOW 1
    78 
   100 
    79 typedef HRESULT (WINAPI *PtrShell_NotifyIconGetRect)(const Q_NOTIFYICONIDENTIFIER* identifier, RECT* iconLocation);
   101 typedef HRESULT (WINAPI *PtrShell_NotifyIconGetRect)(const Q_NOTIFYICONIDENTIFIER* identifier, RECT* iconLocation);
    80 typedef BOOL (WINAPI *PtrChangeWindowMessageFilter)(UINT message, DWORD dwFlag);
   102 typedef BOOL (WINAPI *PtrChangeWindowMessageFilter)(UINT message, DWORD dwFlag);
    81 typedef BOOL (WINAPI *PtrChangeWindowMessageFilterEx)(HWND hWnd, UINT message, DWORD action, void* pChangeFilterStruct);
   103 typedef BOOL (WINAPI *PtrChangeWindowMessageFilterEx)(HWND hWnd, UINT message, DWORD action, void* pChangeFilterStruct);
    85 public:
   107 public:
    86     QSystemTrayIconSys(QSystemTrayIcon *object);
   108     QSystemTrayIconSys(QSystemTrayIcon *object);
    87     ~QSystemTrayIconSys();
   109     ~QSystemTrayIconSys();
    88     bool winEvent( MSG *m, long *result );
   110     bool winEvent( MSG *m, long *result );
    89     bool trayMessage(DWORD msg);
   111     bool trayMessage(DWORD msg);
    90     bool iconDrawItem(LPDRAWITEMSTRUCT lpdi);
       
    91     void setIconContents(NOTIFYICONDATA &data);
   112     void setIconContents(NOTIFYICONDATA &data);
    92     bool showMessage(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, uint uSecs);
   113     bool showMessage(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, uint uSecs);
    93     bool allowsMessages();
       
    94     bool supportsMessages();
       
    95     QRect findIconGeometry(const int a_iButtonID);
   114     QRect findIconGeometry(const int a_iButtonID);
    96     void createIcon();
   115     void createIcon();
    97     HICON hIcon;
   116     HICON hIcon;
    98     QPoint globalPos;
   117     QPoint globalPos;
    99     QSystemTrayIcon *q;
   118     QSystemTrayIcon *q;
   100 private:
   119 private:
   101     uint notifyIconSize;
   120     uint notifyIconSize;
   102     int maxTipLength;
   121     int maxTipLength;
       
   122     int version;
   103     bool ignoreNextMouseRelease;
   123     bool ignoreNextMouseRelease;
   104 };
   124 };
   105 
   125 
   106 bool QSystemTrayIconSys::allowsMessages()
   126 static bool allowsMessages()
   107 {
   127 {
   108 #ifndef QT_NO_SETTINGS
   128 #ifndef QT_NO_SETTINGS
   109     QSettings settings(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft"
   129     QSettings settings(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft"
   110                                       "\\Windows\\CurrentVersion\\Explorer\\Advanced"), QSettings::NativeFormat);
   130                                       "\\Windows\\CurrentVersion\\Explorer\\Advanced"), QSettings::NativeFormat);
   111     return settings.value(QLatin1String("EnableBalloonTips"), true).toBool();
   131     return settings.value(QLatin1String("EnableBalloonTips"), true).toBool();
   112 #else
   132 #else
   113     return false;
   133     return false;
   114 #endif
   134 #endif
   115 }
   135 }
   116 
   136 
   117 bool QSystemTrayIconSys::supportsMessages()
       
   118 {
       
   119     return allowsMessages();
       
   120 }
       
   121 
       
   122 QSystemTrayIconSys::QSystemTrayIconSys(QSystemTrayIcon *object)
   137 QSystemTrayIconSys::QSystemTrayIconSys(QSystemTrayIcon *object)
   123     : hIcon(0), q(object), ignoreNextMouseRelease(false)
   138     : hIcon(0), q(object), ignoreNextMouseRelease(false)
   124 
   139 
   125 {
   140 {
   126     notifyIconSize = FIELD_OFFSET(NOTIFYICONDATA, guidItem); // NOTIFYICONDATAW_V2_SIZE;
   141     if (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA) {
       
   142         notifyIconSize = sizeof(NOTIFYICONDATA);
       
   143         version = NOTIFYICON_VERSION_4;
       
   144     } else {
       
   145         notifyIconSize = NOTIFYICONDATA_V2_SIZE;
       
   146         version = NOTIFYICON_VERSION;
       
   147     }
       
   148 
   127     maxTipLength = 128;
   149     maxTipLength = 128;
   128 
   150 
   129     // For restoring the tray icon after explorer crashes
   151     // For restoring the tray icon after explorer crashes
   130     if (!MYWM_TASKBARCREATED) {
   152     if (!MYWM_TASKBARCREATED) {
   131         MYWM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated");
   153         MYWM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated");
   155         DestroyIcon(hIcon);
   177         DestroyIcon(hIcon);
   156 }
   178 }
   157 
   179 
   158 void QSystemTrayIconSys::setIconContents(NOTIFYICONDATA &tnd)
   180 void QSystemTrayIconSys::setIconContents(NOTIFYICONDATA &tnd)
   159 {
   181 {
   160     tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
   182     tnd.uFlags |= NIF_MESSAGE | NIF_ICON | NIF_TIP;
   161     tnd.uCallbackMessage = MYWM_NOTIFYICON;
   183     tnd.uCallbackMessage = MYWM_NOTIFYICON;
   162     tnd.hIcon = hIcon;
   184     tnd.hIcon = hIcon;
   163     QString tip = q->toolTip();
   185     QString tip = q->toolTip();
   164 
   186 
   165     if (!tip.isNull()) {
   187     if (!tip.isNull()) {
   168     }
   190     }
   169 }
   191 }
   170 
   192 
   171 static int iconFlag( QSystemTrayIcon::MessageIcon icon )
   193 static int iconFlag( QSystemTrayIcon::MessageIcon icon )
   172 {
   194 {
   173 #if NOTIFYICON_VERSION >= 3
       
   174     switch (icon) {
   195     switch (icon) {
   175         case QSystemTrayIcon::Information:
   196         case QSystemTrayIcon::Information:
   176             return NIIF_INFO;
   197             return NIIF_INFO;
   177         case QSystemTrayIcon::Warning:
   198         case QSystemTrayIcon::Warning:
   178             return NIIF_WARNING;
   199             return NIIF_WARNING;
   182             return NIIF_NONE;
   203             return NIIF_NONE;
   183         default:
   204         default:
   184             Q_ASSERT_X(false, "QSystemTrayIconSys::showMessage", "Invalid QSystemTrayIcon::MessageIcon value");
   205             Q_ASSERT_X(false, "QSystemTrayIconSys::showMessage", "Invalid QSystemTrayIcon::MessageIcon value");
   185             return NIIF_NONE;
   206             return NIIF_NONE;
   186     }
   207     }
   187 #else
       
   188     Q_UNUSED(icon);
       
   189     return 0;
       
   190 #endif
       
   191 }
   208 }
   192 
   209 
   193 bool QSystemTrayIconSys::showMessage(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, uint uSecs)
   210 bool QSystemTrayIconSys::showMessage(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, uint uSecs)
   194 {
   211 {
   195 #if NOTIFYICON_VERSION >= 3
       
   196     NOTIFYICONDATA tnd;
   212     NOTIFYICONDATA tnd;
   197     memset(&tnd, 0, notifyIconSize);
   213     memset(&tnd, 0, notifyIconSize);
   198     Q_ASSERT(testAttribute(Qt::WA_WState_Created));
   214 
   199 
       
   200     setIconContents(tnd);
       
   201     memcpy(tnd.szInfo, message.utf16(), qMin(message.length() + 1, 256) * sizeof(wchar_t));
   215     memcpy(tnd.szInfo, message.utf16(), qMin(message.length() + 1, 256) * sizeof(wchar_t));
   202     memcpy(tnd.szInfoTitle, title.utf16(), qMin(title.length() + 1, 64) * sizeof(wchar_t));
   216     memcpy(tnd.szInfoTitle, title.utf16(), qMin(title.length() + 1, 64) * sizeof(wchar_t));
   203 
   217 
   204     tnd.uID = q_uNOTIFYICONID;
   218     tnd.uID = q_uNOTIFYICONID;
   205     tnd.dwInfoFlags = iconFlag(type);
   219     tnd.dwInfoFlags = iconFlag(type);
   206     tnd.cbSize = notifyIconSize;
   220     tnd.cbSize = notifyIconSize;
   207     tnd.hWnd = winId();
   221     tnd.hWnd = winId();
   208     tnd.uTimeout = uSecs;
   222     tnd.uTimeout = uSecs;
   209     tnd.uFlags = NIF_INFO;
   223     tnd.uFlags = NIF_INFO | NIF_SHOWTIP;
       
   224 
       
   225     Q_ASSERT(testAttribute(Qt::WA_WState_Created));
   210 
   226 
   211     return Shell_NotifyIcon(NIM_MODIFY, &tnd);
   227     return Shell_NotifyIcon(NIM_MODIFY, &tnd);
   212 #else
       
   213     Q_UNUSED(title);
       
   214     Q_UNUSED(message);
       
   215     Q_UNUSED(type);
       
   216     Q_UNUSED(uSecs);
       
   217     return false;
       
   218 #endif
       
   219 }
   228 }
   220 
   229 
   221 bool QSystemTrayIconSys::trayMessage(DWORD msg)
   230 bool QSystemTrayIconSys::trayMessage(DWORD msg)
   222 {
   231 {
   223     NOTIFYICONDATA tnd;
   232     NOTIFYICONDATA tnd;
   224     memset(&tnd, 0, notifyIconSize);
   233     memset(&tnd, 0, notifyIconSize);
       
   234 
   225     tnd.uID = q_uNOTIFYICONID;
   235     tnd.uID = q_uNOTIFYICONID;
   226     tnd.cbSize = notifyIconSize;
   236     tnd.cbSize = notifyIconSize;
   227     tnd.hWnd = winId();
   237     tnd.hWnd = winId();
       
   238     tnd.uFlags = NIF_SHOWTIP;
       
   239     tnd.uVersion = version;
   228 
   240 
   229     Q_ASSERT(testAttribute(Qt::WA_WState_Created));
   241     Q_ASSERT(testAttribute(Qt::WA_WState_Created));
   230 
   242 
   231     if (msg != NIM_DELETE) {
   243     if (msg == NIM_ADD || msg == NIM_MODIFY) {
   232         setIconContents(tnd);
   244         setIconContents(tnd);
   233     }
   245     }
   234 
   246 
   235     return Shell_NotifyIcon(msg, &tnd);
   247     bool success = Shell_NotifyIcon(msg, &tnd);
   236 }
   248 
   237 
   249     if (msg == NIM_ADD)
   238 bool QSystemTrayIconSys::iconDrawItem(LPDRAWITEMSTRUCT lpdi)
   250         return success && Shell_NotifyIcon(NIM_SETVERSION, &tnd);
   239 {
   251     else
   240     if (!hIcon)
   252         return success;
   241         return false;
       
   242 
       
   243     DrawIconEx(lpdi->hDC, lpdi->rcItem.left, lpdi->rcItem.top, hIcon, 0, 0, 0, 0, DI_NORMAL);
       
   244     return true;
       
   245 }
   253 }
   246 
   254 
   247 void QSystemTrayIconSys::createIcon()
   255 void QSystemTrayIconSys::createIcon()
   248 {
   256 {
   249     hIcon = 0;
   257     hIcon = 0;
   262 }
   270 }
   263 
   271 
   264 bool QSystemTrayIconSys::winEvent( MSG *m, long *result )
   272 bool QSystemTrayIconSys::winEvent( MSG *m, long *result )
   265 {
   273 {
   266     switch(m->message) {
   274     switch(m->message) {
   267     case WM_CREATE:
       
   268 #ifdef GWLP_USERDATA
       
   269         SetWindowLongPtr(winId(), GWLP_USERDATA, (LONG_PTR)((CREATESTRUCTW*)m->lParam)->lpCreateParams);
       
   270 #else
       
   271         SetWindowLong(winId(), GWL_USERDATA, (LONG)((CREATESTRUCTW*)m->lParam)->lpCreateParams);
       
   272 #endif
       
   273         break;
       
   274 
       
   275     case WM_DRAWITEM:
       
   276         return iconDrawItem((LPDRAWITEMSTRUCT)m->lParam);
       
   277 
       
   278     case MYWM_NOTIFYICON:
   275     case MYWM_NOTIFYICON:
   279         {
   276         {
   280             RECT r;
   277             int message = 0;
   281             GetWindowRect(winId(), &r);
   278             QPoint gpos;
   282             QEvent *e = 0;
   279 
   283             Qt::KeyboardModifiers keys = QApplication::keyboardModifiers();
   280             if (version == NOTIFYICON_VERSION_4) {
   284             QPoint gpos = QCursor::pos();
   281                 Q_ASSERT(q_uNOTIFYICONID == HIWORD(m->lParam));
   285 
   282                 message = LOWORD(m->lParam);
   286             switch (m->lParam) {
   283                 gpos = QPoint(GET_X_LPARAM(m->wParam), GET_Y_LPARAM(m->wParam));
   287             case WM_LBUTTONUP:
   284             } else {
       
   285                 Q_ASSERT(q_uNOTIFYICONID == m->wParam);
       
   286                 message = m->lParam;
       
   287                 gpos = QCursor::pos();
       
   288             }
       
   289 
       
   290             switch (message) {
       
   291             case NIN_SELECT:
       
   292             case NIN_KEYSELECT:
   288                 if (ignoreNextMouseRelease)
   293                 if (ignoreNextMouseRelease)
   289                     ignoreNextMouseRelease = false;
   294                     ignoreNextMouseRelease = false;
   290                 else 
   295                 else 
   291                     emit q->activated(QSystemTrayIcon::Trigger);
   296                     emit q->activated(QSystemTrayIcon::Trigger);
   292                 break;
   297                 break;
   295                 ignoreNextMouseRelease = true; // Since DBLCLICK Generates a second mouse 
   300                 ignoreNextMouseRelease = true; // Since DBLCLICK Generates a second mouse 
   296                                                // release we must ignore it
   301                                                // release we must ignore it
   297                 emit q->activated(QSystemTrayIcon::DoubleClick);
   302                 emit q->activated(QSystemTrayIcon::DoubleClick);
   298                 break;
   303                 break;
   299 
   304 
   300             case WM_RBUTTONUP:
   305             case WM_CONTEXTMENU:
   301                 if (q->contextMenu()) {
   306                 if (q->contextMenu()) {
   302                     q->contextMenu()->popup(gpos);
   307                     q->contextMenu()->popup(gpos);
   303                 }
   308                 }
   304                 emit q->activated(QSystemTrayIcon::Context);
   309                 emit q->activated(QSystemTrayIcon::Context);
   305                 break;
   310                 break;
   309                 break;
   314                 break;
   310 
   315 
   311             case WM_MBUTTONUP:
   316             case WM_MBUTTONUP:
   312                 emit q->activated(QSystemTrayIcon::MiddleClick);
   317                 emit q->activated(QSystemTrayIcon::MiddleClick);
   313                 break;
   318                 break;
       
   319 
   314             default:
   320             default:
   315                         break;
   321                 break;
   316             }
       
   317             if (e) {
       
   318                 bool res = QApplication::sendEvent(q, e);
       
   319                 delete e;
       
   320                 return res;
       
   321             }
   322             }
   322             break;
   323             break;
   323         }
   324         }
   324     default:
   325     default:
   325         if (m->message == MYWM_TASKBARCREATED)
   326         if (m->message == MYWM_TASKBARCREATED)
   439     return ret;
   440     return ret;
   440 }
   441 }
   441 
   442 
   442 void QSystemTrayIconPrivate::showMessage_sys(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, int timeOut)
   443 void QSystemTrayIconPrivate::showMessage_sys(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, int timeOut)
   443 {
   444 {
   444     if (!sys || !sys->allowsMessages())
   445     if (!sys || !allowsMessages())
   445         return;
   446         return;
   446 
   447 
   447     uint uSecs = 0;
   448     uint uSecs = 0;
   448     if ( timeOut < 0)
   449     if ( timeOut < 0)
   449         uSecs = 10000; //10 sec default
   450         uSecs = 10000; //10 sec default
   457         messageString = message.left(255) + QChar();
   458         messageString = message.left(255) + QChar();
   458 
   459 
   459     //title is limited to 63 chars + NULL
   460     //title is limited to 63 chars + NULL
   460     QString titleString = title.left(63) + QChar();
   461     QString titleString = title.left(63) + QChar();
   461 
   462 
   462     if (sys->supportsMessages()) {
   463     sys->showMessage(titleString, messageString, type, uSecs);
   463         sys->showMessage(titleString, messageString, type, (unsigned int)uSecs);
       
   464     } else {
       
   465         //use fallback
       
   466         QRect iconPos = sys->findIconGeometry(q_uNOTIFYICONID);
       
   467         if (iconPos.isValid()) {
       
   468             QBalloonTip::showBalloon(type, title, message, sys->q, iconPos.center(), uSecs, true);
       
   469         }
       
   470     }
       
   471 }
   464 }
   472 
   465 
   473 QRect QSystemTrayIconPrivate::geometry_sys() const
   466 QRect QSystemTrayIconPrivate::geometry_sys() const
   474 {
   467 {
   475     if (!sys)
   468     if (!sys)
   476         return QRect();
   469         return QRect();
       
   470 
   477     return sys->findIconGeometry(q_uNOTIFYICONID);
   471     return sys->findIconGeometry(q_uNOTIFYICONID);
   478 }
   472 }
   479 
   473 
   480 void QSystemTrayIconPrivate::remove_sys()
   474 void QSystemTrayIconPrivate::remove_sys()
   481 {
   475 {
   517 bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys()
   511 bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys()
   518 {
   512 {
   519     return true;
   513     return true;
   520 }
   514 }
   521 
   515 
       
   516 bool QSystemTrayIconPrivate::supportsMessages_sys()
       
   517 {
       
   518     return allowsMessages();
       
   519 }
       
   520 
   522 QT_END_NAMESPACE
   521 QT_END_NAMESPACE
   523 
   522 
   524 #endif
   523 #endif