]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/win32/wintray.cpp
.cvsignore
[bacula/bacula] / bacula / src / filed / win32 / wintray.cpp
1 //  Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
2 //
3 //  This file was part of the vnc system.
4 //
5 //  The vnc system is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation; either version 2 of the License, or
8 //  (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
18 //  USA.
19 //
20 // If the source code for the vnc system is not available from the place 
21 // whence you received this file, check http://www.uk.research.att.com/vnc or contact
22 // the authors on vnc@uk.research.att.com for information on obtaining it.
23 //
24 // This file has been adapted to the Win32 version of Bacula
25 // by Kern E. Sibbald.  Many thanks to ATT and James Weatherall,
26 // the original author, for providing an excellent template.
27 //
28 // Copyright (2000) Kern E. Sibbald
29 //
30
31
32
33 // Tray
34
35 // Implementation of a system tray icon & menu for Bacula
36
37 #include "winbacula.h"
38 #include "winservice.h"
39 #include <lmcons.h>
40
41 // Header
42
43 #include "wintray.h"
44
45 // Constants
46 #ifdef properties_implemented
47 const UINT MENU_PROPERTIES_SHOW = RegisterWindowMessage("Bacula.Properties.User.Show");
48 const UINT MENU_DEFAULT_PROPERTIES_SHOW = RegisterWindowMessage("Bacula.Properties.Default.Show");
49 #endif
50 const UINT MENU_ABOUTBOX_SHOW = RegisterWindowMessage("Bacula.AboutBox.Show");
51 const UINT MENU_STATUS_SHOW = RegisterWindowMessage("Bacula.Status.Show");
52 const UINT MENU_EVENTS_SHOW = RegisterWindowMessage("Bacula.Events.Show");
53 const UINT MENU_SERVICEHELPER_MSG = RegisterWindowMessage("Bacula.ServiceHelper.Message");
54 const UINT MENU_ADD_CLIENT_MSG = RegisterWindowMessage("Bacula.AddClient.Message");
55 const char *MENU_CLASS_NAME = "Bacula Tray Icon";
56
57 extern char *bac_status(int stat);
58 extern int bacstat;
59
60 // Implementation
61
62 bacMenu::bacMenu()
63 {
64    // Create a dummy window to handle tray icon messages
65    WNDCLASSEX wndclass;
66
67    wndclass.cbSize                 = sizeof(wndclass);
68    wndclass.style                  = 0;
69    wndclass.lpfnWndProc    = bacMenu::WndProc;
70    wndclass.cbClsExtra             = 0;
71    wndclass.cbWndExtra             = 0;
72    wndclass.hInstance              = hAppInstance;
73    wndclass.hIcon                  = LoadIcon(NULL, IDI_APPLICATION);
74    wndclass.hCursor                = LoadCursor(NULL, IDC_ARROW);
75    wndclass.hbrBackground  = (HBRUSH) GetStockObject(WHITE_BRUSH);
76    wndclass.lpszMenuName   = (const char *) NULL;
77    wndclass.lpszClassName  = MENU_CLASS_NAME;
78    wndclass.hIconSm                = LoadIcon(NULL, IDI_APPLICATION);
79
80    RegisterClassEx(&wndclass);
81
82    m_hwnd = CreateWindow(MENU_CLASS_NAME,
83                            MENU_CLASS_NAME,
84                            WS_OVERLAPPEDWINDOW,
85                            CW_USEDEFAULT,
86                            CW_USEDEFAULT,
87                            200, 200,
88                            NULL,
89                            NULL,
90                            hAppInstance,
91                            NULL);
92    if (m_hwnd == NULL) {
93       PostQuitMessage(0);
94       return;
95    }
96
97    // record which client created this window
98    SetWindowLong(m_hwnd, GWL_USERDATA, (LONG) this);
99
100    // Timer to trigger icon updating
101    SetTimer(m_hwnd, 1, 5000, NULL);
102
103    // Load the icons for the tray
104    m_idle_icon = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_IDLE));
105    m_running_icon = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_RUNNING));
106    m_error_icon = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_JOB_ERROR));
107
108    // Load the popup menu
109    m_hmenu = LoadMenu(hAppInstance, MAKEINTRESOURCE(IDR_TRAYMENU));
110
111    // Install the tray icon!
112    AddTrayIcon();
113 }
114
115 bacMenu::~bacMenu()
116 {
117    // Remove the tray icon
118    SendTrayMsg(NIM_DELETE, 0);
119         
120    // Destroy the loaded menu
121    if (m_hmenu != NULL)
122       DestroyMenu(m_hmenu);
123 }
124
125 void
126 bacMenu::AddTrayIcon()
127 {
128    SendTrayMsg(NIM_ADD, bacstat);
129 }
130
131 void
132 bacMenu::DelTrayIcon()
133 {
134    SendTrayMsg(NIM_DELETE, 0);
135 }
136
137
138 void
139 bacMenu::UpdateTrayIcon(int bacstat)
140 {
141    (void *)bac_status(0);
142    SendTrayMsg(NIM_MODIFY, bacstat);
143 }
144
145 void
146 bacMenu::SendTrayMsg(DWORD msg, int bacstat)
147 {
148    // Create the tray icon message
149    m_nid.hWnd = m_hwnd;
150    m_nid.cbSize = sizeof(m_nid);
151    m_nid.uID = IDI_BACULA;                  // never changes after construction
152    if (bacstat == 0)
153       m_nid.hIcon = m_idle_icon;
154    else if (bacstat == 1)
155       m_nid.hIcon = m_running_icon;
156    else if (bacstat < 0)
157       m_nid.hIcon = m_error_icon;
158
159    m_nid.uFlags = NIF_ICON | NIF_MESSAGE;
160    m_nid.uCallbackMessage = WM_TRAYNOTIFY;
161
162
163    // Use resource string as tip if there is one
164    if (LoadString(hAppInstance, IDI_BACULA, m_nid.szTip, sizeof(m_nid.szTip))) {
165        m_nid.uFlags |= NIF_TIP;
166    }
167
168    // Try to add the Bacula status to the tip string, if possible
169    if (m_nid.uFlags & NIF_TIP) {
170        strncpy(m_nid.szTip, bac_status(0), (sizeof(m_nid.szTip)-1));
171    }
172
173    // Send the message
174    if (Shell_NotifyIcon(msg, &m_nid)) {
175       EnableMenuItem(m_hmenu, ID_CLOSE, MF_ENABLED);
176    } else {
177       if (!bacService::RunningAsService()) {
178          if (msg == NIM_ADD) {
179             // The tray icon couldn't be created, so use the Properties dialog
180             // as the main program window
181             PostQuitMessage(0);
182          }
183       }
184    }
185 }
186
187 // Process window messages
188 LRESULT CALLBACK bacMenu::WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
189 {
190         // This is a static method, so we don't know which instantiation we're 
191         // dealing with. We use Allen Hadden's (ahadden@taratec.com) suggestion 
192         // from a newsgroup to get the pseudo-this.
193         bacMenu *_this = (bacMenu *) GetWindowLong(hwnd, GWL_USERDATA);
194
195         switch (iMsg)
196         {
197
198                 // Every five seconds, a timer message causes the icon to update
199         case WM_TIMER:
200                 // *** HACK for running servicified
201                 if (bacService::RunningAsService()) {
202                     // Attempt to add the icon if it's not already there
203                     _this->AddTrayIcon();
204                     // Trigger a check of the current user
205                     PostMessage(hwnd, WM_USERCHANGED, 0, 0);
206                 }
207
208                 // Update the icon
209                 _this->UpdateTrayIcon(bacstat);
210                 break;
211
212                 // DEAL WITH NOTIFICATIONS FROM THE SERVER:
213         case WM_SRV_CLIENT_AUTHENTICATED:
214         case WM_SRV_CLIENT_DISCONNECT:
215                 // Adjust the icon accordingly
216                 _this->UpdateTrayIcon(bacstat);
217                 return 0;
218
219                 // STANDARD MESSAGE HANDLING
220         case WM_CREATE:
221                 return 0;
222
223         case WM_COMMAND:
224                 // User has clicked an item on the tray menu
225                 switch (LOWORD(wParam))
226                 {
227                 case ID_STATUS:
228                         // Show the status dialog
229                         _this->m_status.Show(TRUE);
230                         _this->UpdateTrayIcon(bacstat);
231                         break;
232
233                 case ID_EVENTS:
234                         // Show the Events dialog
235                         _this->m_events.Show(TRUE);
236                         _this->UpdateTrayIcon(bacstat);
237                         break;
238
239
240                 case ID_KILLCLIENTS:
241                         // Disconnect all currently connected clients
242                         break;
243
244                 case ID_ABOUT:
245                         // Show the About box
246                         _this->m_about.Show(TRUE);
247                         break;
248
249                 case ID_CLOSE:
250                         // User selected Close from the tray menu
251                         PostMessage(hwnd, WM_CLOSE, 0, 0);
252                         break;
253
254                 }
255                 return 0;
256
257         case WM_TRAYNOTIFY:
258                 // User has clicked on the tray icon or the menu
259                 {
260                         // Get the submenu to use as a pop-up menu
261                         HMENU submenu = GetSubMenu(_this->m_hmenu, 0);
262
263                         // What event are we responding to, RMB click?
264                         if (lParam==WM_RBUTTONUP) {
265                                 if (submenu == NULL) {
266                                         return 0;
267                                 }
268
269                                 // Make the first menu item the default (bold font)
270                                 SetMenuDefaultItem(submenu, 0, TRUE);
271                                 
272                                 // Get the current cursor position, to display the menu at
273                                 POINT mouse;
274                                 GetCursorPos(&mouse);
275
276                                 // There's a "bug"
277                                 // (Microsoft calls it a feature) in Windows 95 that requires calling
278                                 // SetForegroundWindow. To find out more, search for Q135788 in MSDN.
279                                 //
280                                 SetForegroundWindow(_this->m_nid.hWnd);
281
282                                 // Display the menu at the desired position
283                                 TrackPopupMenu(submenu,
284                                                 0, mouse.x, mouse.y, 0,
285                                                 _this->m_nid.hWnd, NULL);
286
287                                 return 0;
288                         }
289                         
290                         // Or was there a LMB double click?
291                         if (lParam==WM_LBUTTONDBLCLK) {
292                                 // double click: execute first menu item
293                                 SendMessage(_this->m_nid.hWnd,
294                                                         WM_COMMAND, 
295                                                         GetMenuItemID(submenu, 0),
296                                                         0);
297                         }
298
299                         return 0;
300                 }
301
302         case WM_CLOSE:
303                 break;
304
305         case WM_DESTROY:
306                 // The user wants Bacula to quit cleanly...
307                 PostQuitMessage(0);
308                 return 0;
309
310         case WM_QUERYENDSESSION:
311                 // Are we running as a system service?
312                 // Or is the system shutting down (in which case we should check anyway!)
313                 if ((!bacService::RunningAsService()) || (lParam == 0)) {
314                         // No, so we are about to be killed
315
316                         // If there are remote connections then we should verify
317                         // that the user is happy about killing them.
318
319                         // Finally, post a quit message, just in case
320                         PostQuitMessage(0);
321                         return TRUE;
322                 }
323
324                 // Tell the OS that we've handled it anyway
325 //              PostQuitMessage(0);
326                 return TRUE;
327
328         
329         default:
330                 if (iMsg == MENU_ABOUTBOX_SHOW) {
331                         // External request to show our About dialog
332                         PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_ABOUT, 0), 0);
333                         return 0;
334                 }
335                 if (iMsg == MENU_STATUS_SHOW) {
336                         // External request to show our status
337                         PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_STATUS, 0), 0);
338                         return 0;
339                 }
340
341                 if (iMsg == MENU_EVENTS_SHOW) {
342                         // External request to show our Events dialogue
343                         PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_EVENTS, 0), 0);
344                         return 0;
345                 }
346
347                 if (iMsg == MENU_SERVICEHELPER_MSG) {
348                         // External ServiceHelper message.
349                         // This message holds a process id which we can use to
350                         // impersonate a specific user.  In doing so, we can load their
351                         // preferences correctly
352                         bacService::ProcessUserHelperMessage(wParam, lParam);
353
354                         // - Trigger a check of the current user
355                         PostMessage(hwnd, WM_USERCHANGED, 0, 0);
356                         return 0;
357                 }
358                 if (iMsg == MENU_ADD_CLIENT_MSG) {
359                         // Add Client message.  This message includes an IP address
360                         // of a listening client, to which we should connect.
361
362                         return 0;
363                 }
364         }
365
366         // Message not recognised
367         return DefWindowProc(hwnd, iMsg, wParam, lParam);
368 }