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