]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/win32/wintray.cpp
Use dcr more in SD + int to bool conversions
[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-2004, 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 #include "bacula.h"
45 #include "jcr.h"
46
47 // Constants
48 #ifdef properties_implemented
49 const UINT MENU_PROPERTIES_SHOW = RegisterWindowMessage("Bacula.Properties.User.Show");
50 const UINT MENU_DEFAULT_PROPERTIES_SHOW = RegisterWindowMessage("Bacula.Properties.Default.Show");
51 #endif
52 const UINT MENU_ABOUTBOX_SHOW = RegisterWindowMessage("Bacula.AboutBox.Show");
53 const UINT MENU_STATUS_SHOW = RegisterWindowMessage("Bacula.Status.Show");
54 const UINT MENU_EVENTS_SHOW = RegisterWindowMessage("Bacula.Events.Show");
55 const UINT MENU_SERVICEHELPER_MSG = RegisterWindowMessage("Bacula.ServiceHelper.Message");
56 const UINT MENU_ADD_CLIENT_MSG = RegisterWindowMessage("Bacula.AddClient.Message");
57 const char *MENU_CLASS_NAME = "Bacula Tray Icon";
58
59 extern void terminate_filed(int sig);
60 extern char *bac_status(char *buf, int buf_len);
61 extern int bacstat;
62
63 // Implementation
64
65 bacMenu::bacMenu()
66 {
67    // Create a dummy window to handle tray icon messages
68    WNDCLASSEX wndclass;
69
70    wndclass.cbSize                 = sizeof(wndclass);
71    wndclass.style                  = 0;
72    wndclass.lpfnWndProc    = bacMenu::WndProc;
73    wndclass.cbClsExtra             = 0;
74    wndclass.cbWndExtra             = 0;
75    wndclass.hInstance              = hAppInstance;
76    wndclass.hIcon                  = LoadIcon(NULL, IDI_APPLICATION);
77    wndclass.hCursor                = LoadCursor(NULL, IDC_ARROW);
78    wndclass.hbrBackground  = (HBRUSH) GetStockObject(WHITE_BRUSH);
79    wndclass.lpszMenuName   = (const char *) NULL;
80    wndclass.lpszClassName  = MENU_CLASS_NAME;
81    wndclass.hIconSm                = LoadIcon(NULL, IDI_APPLICATION);
82
83    RegisterClassEx(&wndclass);
84
85    m_hwnd = CreateWindow(MENU_CLASS_NAME,
86                            MENU_CLASS_NAME,
87                            WS_OVERLAPPEDWINDOW,
88                            CW_USEDEFAULT,
89                            CW_USEDEFAULT,
90                            200, 200,
91                            NULL,
92                            NULL,
93                            hAppInstance,
94                            NULL);
95    if (m_hwnd == NULL) {
96       PostQuitMessage(0);
97       return;
98    }
99
100    // record which client created this window
101    SetWindowLong(m_hwnd, GWL_USERDATA, (LONG) this);
102
103    // Timer to trigger icon updating
104    SetTimer(m_hwnd, 1, 5000, NULL);
105
106    // Load the icons for the tray
107    m_idle_icon    = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_IDLE));
108    m_running_icon = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_RUNNING));
109    m_error_icon   = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_JOB_ERROR));
110    m_warn_icon    = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_JOB_WARNING));
111
112    // Load the popup menu
113    m_hmenu = LoadMenu(hAppInstance, MAKEINTRESOURCE(IDR_TRAYMENU));
114
115    // Install the tray icon!
116    AddTrayIcon();
117 }
118
119 bacMenu::~bacMenu()
120 {
121    // Remove the tray icon
122    SendTrayMsg(NIM_DELETE, 0);
123         
124    // Destroy the loaded menu
125    if (m_hmenu != NULL)
126       DestroyMenu(m_hmenu);
127 }
128
129 void
130 bacMenu::AddTrayIcon()
131 {
132    SendTrayMsg(NIM_ADD, bacstat);
133 }
134
135 void
136 bacMenu::DelTrayIcon()
137 {
138    SendTrayMsg(NIM_DELETE, 0);
139 }
140
141
142 void
143 bacMenu::UpdateTrayIcon(int bacstat)
144 {
145    (void *)bac_status(NULL, 0);
146    SendTrayMsg(NIM_MODIFY, bacstat);
147 }
148
149 void
150 bacMenu::SendTrayMsg(DWORD msg, int bacstat)
151 {
152    struct s_last_job *job;
153    
154    // Create the tray icon message
155    m_nid.hWnd = m_hwnd;
156    m_nid.cbSize = sizeof(m_nid);
157    m_nid.uID = IDI_BACULA;                  // never changes after construction
158    switch (bacstat) {
159    case 0:
160       m_nid.hIcon = m_idle_icon;
161       break;
162    case JS_Running:
163       m_nid.hIcon = m_running_icon;
164       break;
165    case JS_ErrorTerminated:
166       m_nid.hIcon = m_error_icon;
167       break;
168    default:
169       if (last_jobs->size() > 0) {
170          job = (struct s_last_job *)last_jobs->last();
171          if (job->Errors) {
172             m_nid.hIcon = m_warn_icon;
173          } else {
174             m_nid.hIcon = m_idle_icon;
175          }
176       } else {
177          m_nid.hIcon = m_idle_icon;
178       }
179       break;
180    }
181
182    m_nid.uFlags = NIF_ICON | NIF_MESSAGE;
183    m_nid.uCallbackMessage = WM_TRAYNOTIFY;
184
185
186    // Use resource string as tip if there is one
187    if (LoadString(hAppInstance, IDI_BACULA, m_nid.szTip, sizeof(m_nid.szTip))) {
188        m_nid.uFlags |= NIF_TIP;
189    }
190
191    // Try to add the Bacula status to the tip string, if possible
192    if (m_nid.uFlags & NIF_TIP) {
193        bac_status(m_nid.szTip, sizeof(m_nid.szTip));
194    }
195
196    // Send the message
197    if (Shell_NotifyIcon(msg, &m_nid)) {
198       EnableMenuItem(m_hmenu, ID_CLOSE, MF_ENABLED);
199    } else {
200       if (!bacService::RunningAsService()) {
201          if (msg == NIM_ADD) {
202             // The tray icon couldn't be created, so use the Properties dialog
203             // as the main program window
204             PostQuitMessage(0);
205          }
206       }
207    }
208 }
209
210 // Process window messages
211 LRESULT CALLBACK bacMenu::WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
212 {
213    // This is a static method, so we don't know which instantiation we're 
214    // dealing with. We use Allen Hadden's (ahadden@taratec.com) suggestion 
215    // from a newsgroup to get the pseudo-this.
216    bacMenu *_this = (bacMenu *) GetWindowLong(hwnd, GWL_USERDATA);
217
218    switch (iMsg) {
219
220    // Every five seconds, a timer message causes the icon to update
221    case WM_TIMER:
222       // *** HACK for running servicified
223       if (bacService::RunningAsService()) {
224           // Attempt to add the icon if it's not already there
225           _this->AddTrayIcon();
226       }
227
228       // Update the icon
229       _this->UpdateTrayIcon(bacstat);
230      break;
231
232    // STANDARD MESSAGE HANDLING
233    case WM_CREATE:
234       return 0;
235
236    case WM_COMMAND:
237       // User has clicked an item on the tray menu
238       switch (LOWORD(wParam)) {
239       case ID_STATUS:
240          // Show the status dialog
241          _this->m_status.Show(TRUE);
242          _this->UpdateTrayIcon(bacstat);
243          break;
244
245       case ID_EVENTS:
246          // Show the Events dialog
247          _this->m_events.Show(TRUE);
248          _this->UpdateTrayIcon(bacstat);
249          break;
250
251
252       case ID_KILLCLIENTS:
253          // Disconnect all currently connected clients
254          break;
255
256       case ID_ABOUT:
257          // Show the About box
258          _this->m_about.Show(TRUE);
259          break;
260
261       case ID_CLOSE:
262          // User selected Close from the tray menu
263          PostMessage(hwnd, WM_CLOSE, 0, 0);
264          break;
265
266       }
267       return 0;
268
269    case WM_TRAYNOTIFY:
270       // User has clicked on the tray icon or the menu
271       {
272          // Get the submenu to use as a pop-up menu
273          HMENU submenu = GetSubMenu(_this->m_hmenu, 0);
274
275          // What event are we responding to, RMB click?
276          if (lParam==WM_RBUTTONUP) {
277             if (submenu == NULL) {
278                     return 0;
279             }
280
281             // Make the first menu item the default (bold font)
282             SetMenuDefaultItem(submenu, 0, TRUE);
283             
284             // Get the current cursor position, to display the menu at
285             POINT mouse;
286             GetCursorPos(&mouse);
287
288             // There's a "bug"
289             // (Microsoft calls it a feature) in Windows 95 that requires calling
290             // SetForegroundWindow. To find out more, search for Q135788 in MSDN.
291             //
292             SetForegroundWindow(_this->m_nid.hWnd);
293
294             // Display the menu at the desired position
295             TrackPopupMenu(submenu,
296                             0, mouse.x, mouse.y, 0,
297                             _this->m_nid.hWnd, NULL);
298
299             return 0;
300          }
301          
302          // Or was there a LMB double click?
303          if (lParam==WM_LBUTTONDBLCLK) {
304              // double click: execute first menu item
305              SendMessage(_this->m_nid.hWnd,
306                          WM_COMMAND, 
307                          GetMenuItemID(submenu, 0),
308                          0);
309          }
310
311          return 0;
312       }
313
314    case WM_CLOSE:
315       terminate_filed(0);
316       break;
317
318    case WM_DESTROY:
319       // The user wants Bacula to quit cleanly...
320       PostQuitMessage(0);
321       return 0;
322
323    case WM_QUERYENDSESSION:
324       // Are we running as a system service?
325       // Or is the system shutting down (in which case we should check anyway!)
326       if ((!bacService::RunningAsService()) || (lParam == 0)) {
327          // No, so we are about to be killed
328
329          // If there are remote connections then we should verify
330          // that the user is happy about killing them.
331
332          // Finally, post a quit message, just in case
333          PostQuitMessage(0);
334          return TRUE;
335       }
336       return TRUE;
337
338    
339    default:
340       if (iMsg == MENU_ABOUTBOX_SHOW) {
341          // External request to show our About dialog
342          PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_ABOUT, 0), 0);
343          return 0;
344       }
345       if (iMsg == MENU_STATUS_SHOW) {
346          // External request to show our status
347          PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_STATUS, 0), 0);
348          return 0;
349       }
350    }
351
352    // Message not recognised
353    return DefWindowProc(hwnd, iMsg, wParam, lParam);
354 }