| 56 | | HICON hIcon; |
| 57 | | HBITMAP hMask; |
| 58 | | TrayIcon *iconObject; |
| 59 | | |
| 60 | | TrayIconPrivate( TrayIcon *object ) |
| 61 | | : QWidget( 0 ), hIcon( 0 ), hMask( 0 ), iconObject( object ) |
| 62 | | { |
| 63 | | if ( !WM_TASKBARCREATED ) |
| 64 | | WM_TASKBARCREATED = RegisterWindowMessage( TEXT("TaskbarCreated") ); |
| 65 | | } |
| 66 | | |
| 67 | | ~TrayIconPrivate() |
| 68 | | { |
| 69 | | if ( hMask ) { |
| 70 | | DeleteObject( hMask ); |
| 71 | | hMask = 0; // michalj |
| 72 | | } |
| 73 | | if ( hIcon ) { |
| 74 | | DestroyIcon( hIcon ); |
| 75 | | hIcon = 0; // michalj |
| 76 | | } |
| 77 | | } |
| 78 | | |
| 79 | | // the unavoidable A/W versions. Don't forget to keep them in sync! |
| 80 | | bool trayMessageA( DWORD msg ) |
| 81 | | { |
| 82 | | NOTIFYICONDATAA tnd; |
| 83 | | ZeroMemory( &tnd, sizeof(NOTIFYICONDATAA) ); |
| 84 | | tnd.cbSize = sizeof(NOTIFYICONDATAA); |
| 85 | | tnd.hWnd = winId(); |
| 86 | | tnd.uID = 1; // michalj |
| 87 | | |
| 88 | | if ( msg != NIM_DELETE ) { |
| 89 | | tnd.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP; |
| 90 | | tnd.uCallbackMessage= WM_NOTIFYICON; |
| 91 | | tnd.hIcon = hIcon; |
| 92 | | if ( !iconObject->toolTip().isNull() ) { |
| 93 | | // Tip is limited to 63 + NULL; lstrcpyn appends a NULL terminator. |
| 94 | | QString tip = iconObject->toolTip().left( 63 ) + QChar(); |
| 95 | | lstrcpynA(tnd.szTip, (const char*)tip.local8Bit(), QMIN( tip.length()+1, 64 ) ); |
| 96 | | } |
| 97 | | } |
| 98 | | |
| 99 | | return Shell_NotifyIconA(msg, &tnd); |
| 100 | | } |
| | 57 | HICON hIcon; |
| | 58 | HBITMAP hMask; |
| | 59 | TrayIcon *iconObject; |
| | 60 | |
| | 61 | TrayIconPrivate(TrayIcon *object) |
| | 62 | : QWidget(0), hIcon(0), hMask(0), iconObject(object) |
| | 63 | { |
| | 64 | if (!WM_TASKBARCREATED) |
| | 65 | WM_TASKBARCREATED = RegisterWindowMessage(TEXT("TaskbarCreated")); |
| | 66 | } |
| | 67 | |
| | 68 | ~TrayIconPrivate() |
| | 69 | { |
| | 70 | if (hMask) { |
| | 71 | DeleteObject( hMask ); |
| | 72 | hMask = 0; // michalj |
| | 73 | } |
| | 74 | if (hIcon) { |
| | 75 | DestroyIcon(hIcon); |
| | 76 | hIcon = 0; // michalj |
| | 77 | } |
| | 78 | } |
| | 79 | |
| | 80 | // the unavoidable A/W versions. Don't forget to keep them in sync! |
| | 81 | bool trayMessageA(DWORD msg) |
| | 82 | { |
| | 83 | NOTIFYICONDATAA tnd; |
| | 84 | ZeroMemory(&tnd, sizeof(NOTIFYICONDATAA)); |
| | 85 | tnd.cbSize = sizeof(NOTIFYICONDATAA); |
| | 86 | tnd.hWnd = winId(); |
| | 87 | tnd.uID = 1; // michalj |
| | 88 | |
| | 89 | if (msg != NIM_DELETE) { |
| | 90 | tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; |
| | 91 | tnd.uCallbackMessage = WM_NOTIFYICON; |
| | 92 | tnd.hIcon = hIcon; |
| | 93 | if (!iconObject->toolTip().isNull()) { |
| | 94 | // Tip is limited to 63 + NULL; lstrcpyn appends a NULL terminator. |
| | 95 | QString tip = iconObject->toolTip().left(63) + QChar(); |
| | 96 | lstrcpynA(tnd.szTip, (const char*)tip.local8Bit(), QMIN(tip.length()+1, 64)); |
| | 97 | } |
| | 98 | } |
| | 99 | return Shell_NotifyIconA(msg, &tnd); |
| | 100 | } |
| 103 | | bool trayMessageW( DWORD msg ) |
| 104 | | { |
| 105 | | resolveLibs(); |
| 106 | | if ( ! (ptrShell_NotifyIcon && qWinVersion() & Qt::WV_NT_based) ) |
| 107 | | return trayMessageA( msg ); |
| 108 | | |
| 109 | | NOTIFYICONDATAW tnd; |
| 110 | | ZeroMemory( &tnd, sizeof(NOTIFYICONDATAW) ); |
| 111 | | tnd.cbSize = sizeof(NOTIFYICONDATAW); |
| 112 | | tnd.hWnd = winId(); |
| 113 | | tnd.uID = 1; // michalj |
| 114 | | |
| 115 | | if ( msg != NIM_DELETE ) { |
| 116 | | tnd.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP; |
| 117 | | tnd.uCallbackMessage= WM_NOTIFYICON; |
| 118 | | tnd.hIcon = hIcon; |
| 119 | | if ( !iconObject->toolTip().isNull() ) { |
| 120 | | // Tip is limited to 63 + NULL; lstrcpyn appends a NULL terminator. |
| 121 | | QString tip = iconObject->toolTip().left( 63 ) + QChar(); |
| 122 | | lstrcpynW(tnd.szTip, (TCHAR*)tip.unicode(), QMIN( tip.length()+1, 64 ) ); |
| 123 | | // lstrcpynW(tnd.szTip, (TCHAR*)qt_winTchar( tip, FALSE ), QMIN( tip.length()+1, 64 ) ); |
| 124 | | } |
| 125 | | } |
| 126 | | return ptrShell_NotifyIcon(msg, &tnd); |
| | 103 | bool trayMessageW(DWORD msg) |
| | 104 | { |
| | 105 | resolveLibs(); |
| | 106 | |
| | 107 | if (!(ptrShell_NotifyIcon && qWinVersion() & Qt::WV_NT_based)) |
| | 108 | return trayMessageA(msg); |
| | 109 | |
| | 110 | NOTIFYICONDATAW tnd; |
| | 111 | ZeroMemory(&tnd, sizeof(NOTIFYICONDATAW)); |
| | 112 | tnd.cbSize = sizeof(NOTIFYICONDATAW); |
| | 113 | tnd.hWnd = winId(); |
| | 114 | tnd.uID = 1; // michalj |
| | 115 | |
| | 116 | if (msg != NIM_DELETE) { |
| | 117 | tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; |
| | 118 | tnd.uCallbackMessage = WM_NOTIFYICON; |
| | 119 | tnd.hIcon = hIcon; |
| | 120 | |
| | 121 | if (!iconObject->toolTip().isNull()) { |
| | 122 | // Tip is limited to 63 + NULL; lstrcpyn appends a NULL terminator. |
| | 123 | QString tip = iconObject->toolTip().left(63) + QChar(); |
| | 124 | lstrcpynW(tnd.szTip, (TCHAR*)tip.unicode(), QMIN(tip.length()+1, 64)); |
| | 125 | //lstrcpynW(tnd.szTip, (TCHAR*)qt_winTchar( tip, FALSE ), QMIN( tip.length()+1, 64 ) ); |
| | 126 | } |
| | 127 | } |
| | 128 | return ptrShell_NotifyIcon(msg, &tnd); |
| 141 | | if (!hIcon) |
| 142 | | return FALSE; |
| 143 | | |
| 144 | | DrawIconEx(lpdi->hDC, lpdi->rcItem.left, lpdi->rcItem.top, hIcon, 0, 0, 0, NULL, DI_NORMAL ); |
| 145 | | return TRUE; |
| 146 | | } |
| 147 | | |
| 148 | | bool winEvent( MSG *m, long* result ) |
| 149 | | { |
| 150 | | switch(m->message) { |
| 151 | | case WM_DRAWITEM: |
| 152 | | return iconDrawItem( (LPDRAWITEMSTRUCT)m->lParam ); |
| 153 | | case WM_NOTIFYICON: |
| 154 | | { |
| 155 | | QMouseEvent *e = 0; |
| 156 | | QPoint gpos = QCursor::pos(); |
| 157 | | switch (m->lParam) { |
| 158 | | case WM_MOUSEMOVE: e = new QMouseEvent( QEvent::MouseMove, mapFromGlobal( gpos ), gpos, 0, 0 ); break; |
| 159 | | case WM_LBUTTONDOWN: e = new QMouseEvent( QEvent::MouseButtonPress, mapFromGlobal( gpos ), gpos, Qt::LeftButton, Qt::LeftButton ); break; |
| 160 | | case WM_LBUTTONUP: e = new QMouseEvent( QEvent::MouseButtonRelease, mapFromGlobal( gpos ), gpos, Qt::LeftButton, Qt::LeftButton ); break; |
| 161 | | case WM_LBUTTONDBLCLK: e = new QMouseEvent( QEvent::MouseButtonDblClick, mapFromGlobal( gpos ), gpos, Qt::LeftButton, Qt::LeftButton ); break; |
| 162 | | case WM_RBUTTONDOWN: e = new QMouseEvent( QEvent::MouseButtonPress, mapFromGlobal( gpos ), gpos, Qt::RightButton, Qt::RightButton ); break; |
| 163 | | case WM_RBUTTONUP: e = new QMouseEvent( QEvent::MouseButtonRelease, mapFromGlobal( gpos ), gpos, Qt::RightButton, Qt::RightButton ); break; |
| 164 | | case WM_RBUTTONDBLCLK: e = new QMouseEvent( QEvent::MouseButtonDblClick, mapFromGlobal( gpos ), gpos, Qt::RightButton, Qt::RightButton ); break; |
| 165 | | case WM_MBUTTONDOWN: e = new QMouseEvent( QEvent::MouseButtonPress, mapFromGlobal( gpos ), gpos, Qt::MidButton, Qt::MidButton ); break; |
| 166 | | case WM_MBUTTONUP: e = new QMouseEvent( QEvent::MouseButtonRelease, mapFromGlobal( gpos ), gpos, Qt::MidButton, Qt::MidButton ); break; |
| 167 | | case WM_MBUTTONDBLCLK: e = new QMouseEvent( QEvent::MouseButtonDblClick, mapFromGlobal( gpos ), gpos, Qt::MidButton, Qt::MidButton ); break; |
| 168 | | case WM_CONTEXTMENU: e = new QMouseEvent( QEvent::MouseButtonRelease, mapFromGlobal( gpos ), gpos, Qt::RightButton, Qt::RightButton ); break; |
| 169 | | } |
| 170 | | if ( e ) { |
| 171 | | bool res = QApplication::sendEvent( iconObject, e ); |
| 172 | | delete e; |
| 173 | | return res; |
| 174 | | } |
| 175 | | } |
| 176 | | break; |
| 177 | | default: |
| 178 | | if ( m->message == WM_TASKBARCREATED ) trayMessage( NIM_ADD ); |
| 179 | | } |
| 180 | | return QWidget::winEvent( m, result ); |
| 181 | | } |
| | 140 | if (!hIcon) |
| | 141 | return FALSE; |
| | 142 | |
| | 143 | DrawIconEx(lpdi->hDC, lpdi->rcItem.left, lpdi->rcItem.top, hIcon, 0, 0, 0, NULL, DI_NORMAL); |
| | 144 | return TRUE; |
| | 145 | } |
| | 146 | |
| | 147 | bool winEvent(MSG *m, long *result) |
| | 148 | { |
| | 149 | switch(m->message) { |
| | 150 | case WM_DRAWITEM: |
| | 151 | return iconDrawItem( (LPDRAWITEMSTRUCT)m->lParam ); |
| | 152 | case WM_NOTIFYICON: |
| | 153 | { |
| | 154 | QMouseEvent *e = 0; |
| | 155 | QPoint gpos = QCursor::pos(); |
| | 156 | switch (m->lParam) { |
| | 157 | case WM_MOUSEMOVE: |
| | 158 | e = new QMouseEvent(QEvent::MouseMove, mapFromGlobal(gpos), gpos, 0, 0); |
| | 159 | break; |
| | 160 | case WM_LBUTTONDOWN: |
| | 161 | e = new QMouseEvent(QEvent::MouseButtonPress, mapFromGlobal(gpos), gpos, Qt::LeftButton, Qt::LeftButton ); |
| | 162 | break; |
| | 163 | case WM_LBUTTONUP: |
| | 164 | e = new QMouseEvent(QEvent::MouseButtonRelease, mapFromGlobal(gpos), gpos, Qt::LeftButton, Qt::LeftButton); |
| | 165 | break; |
| | 166 | case WM_LBUTTONDBLCLK: |
| | 167 | e = new QMouseEvent(QEvent::MouseButtonDblClick, mapFromGlobal(gpos), gpos, Qt::LeftButton, Qt::LeftButton); |
| | 168 | break; |
| | 169 | case WM_RBUTTONDOWN: |
| | 170 | e = new QMouseEvent(QEvent::MouseButtonPress, mapFromGlobal(gpos), gpos, Qt::RightButton, Qt::RightButton); |
| | 171 | break; |
| | 172 | case WM_RBUTTONUP: |
| | 173 | e = new QMouseEvent(QEvent::MouseButtonRelease, mapFromGlobal(gpos), gpos, Qt::RightButton, Qt::RightButton); |
| | 174 | break; |
| | 175 | case WM_RBUTTONDBLCLK: |
| | 176 | e = new QMouseEvent(QEvent::MouseButtonDblClick, mapFromGlobal(gpos), gpos, Qt::RightButton, Qt::RightButton); |
| | 177 | break; |
| | 178 | case WM_MBUTTONDOWN: |
| | 179 | e = new QMouseEvent(QEvent::MouseButtonPress, mapFromGlobal(gpos), gpos, Qt::MidButton, Qt::MidButton); |
| | 180 | break; |
| | 181 | case WM_MBUTTONUP: |
| | 182 | e = new QMouseEvent(QEvent::MouseButtonRelease, mapFromGlobal(gpos), gpos, Qt::MidButton, Qt::MidButton); |
| | 183 | break; |
| | 184 | case WM_MBUTTONDBLCLK: |
| | 185 | e = new QMouseEvent(QEvent::MouseButtonDblClick, mapFromGlobal(gpos), gpos, Qt::MidButton, Qt::MidButton); |
| | 186 | break; |
| | 187 | case WM_CONTEXTMENU: |
| | 188 | e = new QMouseEvent(QEvent::MouseButtonRelease, mapFromGlobal(gpos), gpos, Qt::RightButton, Qt::RightButton); |
| | 189 | break; |
| | 190 | } |
| | 191 | if (e) { |
| | 192 | bool res = QApplication::sendEvent(iconObject, e); |
| | 193 | delete e; |
| | 194 | return res; |
| | 195 | } |
| | 196 | } |
| | 197 | break; |
| | 198 | default: |
| | 199 | if (m->message == WM_TASKBARCREATED) |
| | 200 | trayMessage(NIM_ADD); |
| | 201 | } |
| | 202 | return QWidget::winEvent(m, result); |
| | 203 | } |
| 184 | | static HBITMAP createIconMask( const QPixmap &qp ) |
| 185 | | { |
| 186 | | QImage bm = qp.convertToImage(); |
| 187 | | int w = bm.width(); |
| 188 | | int h = bm.height(); |
| 189 | | int bpl = ((w+15)/16)*2; // bpl, 16 bit alignment |
| 190 | | uchar *bits = new uchar[bpl*h]; |
| 191 | | bm.invertPixels(); |
| 192 | | for ( int y=0; y<h; y++ ) |
| 193 | | memcpy( bits+y*bpl, bm.scanLine(y), bpl ); |
| 194 | | HBITMAP hbm = CreateBitmap( w, h, 1, 1, bits ); |
| 195 | | delete [] bits; |
| 196 | | return hbm; |
| 197 | | } |
| 198 | | |
| 199 | | static HICON createIcon( const QPixmap &pm, HBITMAP &hbm ) |
| 200 | | { |
| 201 | | QPixmap maskpm( pm.size() ); |
| 202 | | QBitmap mask( pm.size() ); |
| 203 | | if ( !pm.mask().isNull() ) { |
| 204 | | maskpm.fill( Qt::black ); // make masked area black |
| 205 | | QPainter p(&mask); |
| 206 | | p.drawPixmap( 0, 0, pm.mask() ); |
| 207 | | } else |
| 208 | | maskpm.fill( Qt::color1 ); |
| 209 | | |
| 210 | | bitBlt( &maskpm, 0, 0, &pm); |
| 211 | | ICONINFO iconInfo; |
| 212 | | iconInfo.fIcon = TRUE; |
| 213 | | iconInfo.hbmMask = hbm = createIconMask(mask); |
| 214 | | iconInfo.hbmColor = maskpm.toWinHBITMAP(); |
| 215 | | |
| 216 | | HICON icon = CreateIconIndirect( &iconInfo ); |
| 217 | | DeleteObject(iconInfo.hbmMask); |
| 218 | | iconInfo.hbmMask = hbm = 0; // michalj |
| 219 | | return icon; |
| 220 | | } |
| 221 | | |
| 222 | | void TrayIcon::sysInstall() |
| 223 | | { |
| 224 | | if ( !d ) { |
| 225 | | d = new TrayIconPrivate( this ); |
| 226 | | d->hIcon = createIcon( pm, d->hMask ); |
| 227 | | |
| 228 | | d->trayMessage( NIM_ADD ); |
| 229 | | } |
| 230 | | } |
| 231 | | |
| 232 | | void TrayIcon::sysRemove() |
| 233 | | { |
| 234 | | if ( d ) { |
| 235 | | d->trayMessage( NIM_DELETE ); |
| 236 | | |
| 237 | | delete d; |
| 238 | | d = 0; |
| 239 | | } |
| 240 | | } |
| 241 | | |
| 242 | | void TrayIcon::sysUpdateIcon() |
| 243 | | { |
| 244 | | if ( d ) { |
| 245 | | if ( d->hMask ) { |
| 246 | | DeleteObject( d->hMask ); |
| 247 | | d->hMask = 0; // michalj |
| 248 | | } |
| 249 | | if ( d->hIcon ) { |
| 250 | | DestroyIcon( d->hIcon ); |
| 251 | | d->hIcon = 0; // michalj |
| 252 | | } |
| 253 | | |
| 254 | | d->hIcon = createIcon( pm, d->hMask ); |
| 255 | | d->trayMessage( NIM_MODIFY ); |
| 256 | | } |
| 257 | | } |
| 258 | | |
| 259 | | void TrayIcon::sysUpdateToolTip() |
| 260 | | { |
| 261 | | if ( d ) |
| 262 | | d->trayMessage( NIM_MODIFY ); |
| 263 | | } |
| | 206 | static HBITMAP |
| | 207 | createIconMask(const QPixmap &qp) |
| | 208 | { |
| | 209 | QImage bm = qp.convertToImage(); |
| | 210 | int w = bm.width(); |
| | 211 | int h = bm.height(); |
| | 212 | int bpl = ((w+15)/16)*2; /* bpl, 16 bit alignment */ |
| | 213 | |
| | 214 | uchar *bits = new uchar[bpl*h]; |
| | 215 | bm.invertPixels(); |
| | 216 | for (int y = 0; y < h; y++) { |
| | 217 | memcpy(bits+y*bpl, bm.scanLine(y), bpl); |
| | 218 | } |
| | 219 | HBITMAP hbm = CreateBitmap(w, h, 1, 1, bits); |
| | 220 | delete [] bits; |
| | 221 | |
| | 222 | return hbm; |
| | 223 | } |
| | 224 | |
| | 225 | static HICON |
| | 226 | createIcon(const QPixmap &pm, HBITMAP &hbm) |
| | 227 | { |
| | 228 | QPixmap maskpm(pm.size()); |
| | 229 | QBitmap mask(pm.size()); |
| | 230 | if (!pm.mask().isNull()) { |
| | 231 | /* Make masked area black */ |
| | 232 | maskpm.fill(Qt::black); |
| | 233 | QPainter p(&mask); |
| | 234 | p.drawPixmap(0, 0, pm.mask()); |
| | 235 | } else { |
| | 236 | maskpm.fill(Qt::color1); |
| | 237 | } |
| | 238 | |
| | 239 | bitBlt(&maskpm, 0, 0, &pm); |
| | 240 | ICONINFO iconInfo; |
| | 241 | iconInfo.fIcon = TRUE; |
| | 242 | iconInfo.hbmMask = hbm = createIconMask(mask); |
| | 243 | iconInfo.hbmColor = maskpm.toWinHBITMAP(); |
| | 244 | |
| | 245 | HICON icon = CreateIconIndirect(&iconInfo); |
| | 246 | DeleteObject(iconInfo.hbmMask); |
| | 247 | iconInfo.hbmMask = hbm = 0; // michalj |
| | 248 | return icon; |
| | 249 | } |
| | 250 | |
| | 251 | void |
| | 252 | TrayIcon::sysInstall() |
| | 253 | { |
| | 254 | if (!d) { |
| | 255 | d = new TrayIconPrivate(this); |
| | 256 | d->hIcon = createIcon(pm, d->hMask); |
| | 257 | |
| | 258 | d->trayMessage(NIM_ADD); |
| | 259 | } |
| | 260 | } |
| | 261 | |
| | 262 | void |
| | 263 | TrayIcon::sysRemove() |
| | 264 | { |
| | 265 | if (d) { |
| | 266 | d->trayMessage(NIM_DELETE); |
| | 267 | |
| | 268 | delete d; |
| | 269 | d = 0; |
| | 270 | } |
| | 271 | } |
| | 272 | |
| | 273 | void |
| | 274 | TrayIcon::sysUpdateIcon() |
| | 275 | { |
| | 276 | if (d) { |
| | 277 | if (d->hMask) { |
| | 278 | DeleteObject( d->hMask ); |
| | 279 | d->hMask = 0; // michalj |
| | 280 | } |
| | 281 | if (d->hIcon) { |
| | 282 | DestroyIcon(d->hIcon); |
| | 283 | d->hIcon = 0; // michalj |
| | 284 | } |
| | 285 | |
| | 286 | d->hIcon = createIcon(pm, d->hMask); |
| | 287 | d->trayMessage(NIM_MODIFY); |
| | 288 | } |
| | 289 | } |
| | 290 | |
| | 291 | void |
| | 292 | TrayIcon::sysUpdateToolTip() |
| | 293 | { |
| | 294 | if (d) |
| | 295 | d->trayMessage(NIM_MODIFY); |
| | 296 | } |