]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/win32/wintray.cpp
Fix typo
[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-2003) 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 void terminate_filed(int sig);
58 extern char *bac_status(int stat);
59 extern int bacstat;
60
61 // Implementation
62
63 bacMenu::bacMenu()
64 {
65    // Create a dummy window to handle tray icon messages
66    WNDCLASSEX wndclass;
67
68    wndclass.cbSize                 = sizeof(wndclass);
69    wndclass.style                  = 0;
70    wndclass.lpfnWndProc    = bacMenu::WndProc;
71    wndclass.cbClsExtra             = 0;
72    wndclass.cbWndExtra             = 0;
73    wndclass.hInstance              = hAppInstance;
74    wndclass.hIcon                  = LoadIcon(NULL, IDI_APPLICATION);
75    wndclass.hCursor                = LoadCursor(NULL, IDC_ARROW);
76    wndclass.hbrBackground  = (HBRUSH) GetStockObject(WHITE_BRUSH);
77    wndclass.lpszMenuName   = (const char *) NULL;
78    wndclass.lpszClassName  = MENU_CLASS_NAME;
79    wndclass.hIconSm                = LoadIcon(NULL, IDI_APPLICATION);
80
81    RegisterClassEx(&wndclass);
82
83    m_hwnd = CreateWindow(MENU_CLASS_NAME,
84                            MENU_CLASS_NAME,
85                            WS_OVERLAPPEDWINDOW,
86                            CW_USEDEFAULT,
87                            CW_USEDEFAULT,
88                            200, 200,
89                            NULL,
90                            NULL,
91                            hAppInstance,
92                            NULL);
93    if (m_hwnd == NULL) {
94       PostQuitMessage(0);
95       return;
96    }
97
98    // record which client created this window
99    SetWindowLong(m_hwnd, GWL_USERDATA, (LONG) this);
100
101    // Timer to trigger icon updating
102    SetTimer(m_hwnd, 1, 5000, NULL);
103
104    // Load the icons for the tray
105    m_idle_icon = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_IDLE));
106    m_running_icon = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_RUNNING));
107    m_error_icon = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_JOB_ERROR));
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(0);
143    SendTrayMsg(NIM_MODIFY, bacstat);
144 }
145
146 void
147 bacMenu::SendTrayMsg(DWORD msg, int bacstat)
148 {
149    // Create the tray icon message
150    m_nid.hWnd = m_hwnd;
151    m_nid.cbSize = sizeof(m_nid);
152    m_nid.uID = IDI_BACULA;                  // never changes after construction
153    if (bacstat == 0)
154       m_nid.hIcon = m_idle_icon;
155    else if (bacstat == 1)
156       m_nid.hIcon = m_running_icon;
157    else if (bacstat < 0)
158       m_nid.hIcon = m_error_icon;
159
160    m_nid.uFlags = NIF_ICON | NIF_MESSAGE;
161    m_nid.uCallbackMessage = WM_TRAYNOTIFY;
162
163
164    // Use resource string as tip if there is one
165    if (LoadString(hAppInstance, IDI_BACULA, m_nid.szTip, sizeof(m_nid.szTip))) {
166        m_nid.uFlags |= NIF_TIP;
167    }
168
169    // Try to add the Bacula status to the tip string, if possible
170    if (m_nid.uFlags & NIF_TIP) {
171        strncpy(m_nid.szTip, bac_status(0), (sizeof(m_nid.szTip)-1));
172    }
173
174    // Send the message
175    if (Shell_NotifyIcon(msg, &m_nid)) {
176       EnableMenuItem(m_hmenu, ID_CLOSE, MF_ENABLED);
177    } else {
178       if (!bacService::RunningAsService()) {
179          if (msg == NIM_ADD) {
180             // The tray icon couldn't be created, so use the Properties dialog
181             // as the main program window
182             PostQuitMessage(0);
183          }
184       }
185    }
186 }
187
188 // Process window messages
189 LRESULT CALLBACK bacMenu::WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
190 {
191    // This is a static method, so we don't know which instantiation we're 
192    // dealing with. We use Allen Hadden's (ahadden@taratec.com) suggestion 
193    // from a newsgroup to get the pseudo-this.
194    bacMenu *_this = (bacMenu *) GetWindowLong(hwnd, GWL_USERDATA);
195
196    switch (iMsg) {
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 #ifdef xxx_needed
213
214    // DEAL WITH NOTIFICATIONS FROM THE SERVER:
215    case WM_SRV_CLIENT_AUTHENTICATED:
216    case WM_SRV_CLIENT_DISCONNECT:
217       // Adjust the icon accordingly
218       _this->UpdateTrayIcon(bacstat);
219       return 0;
220 #endif
221
222    // STANDARD MESSAGE HANDLING
223    case WM_CREATE:
224       return 0;
225
226    case WM_COMMAND:
227       // User has clicked an item on the tray menu
228       switch (LOWORD(wParam)) {
229       case ID_STATUS:
230          // Show the status dialog
231          _this->m_status.Show(TRUE);
232          _this->UpdateTrayIcon(bacstat);
233          break;
234
235       case ID_EVENTS:
236          // Show the Events dialog
237          _this->m_events.Show(TRUE);
238          _this->UpdateTrayIcon(bacstat);
239          break;
240
241
242       case ID_KILLCLIENTS:
243          // Disconnect all currently connected clients
244          break;
245
246       case ID_ABOUT:
247          // Show the About box
248          _this->m_about.Show(TRUE);
249          break;
250
251       case ID_CLOSE:
252          // User selected Close from the tray menu
253          PostMessage(hwnd, WM_CLOSE, 0, 0);
254          break;
255
256       }
257       return 0;
258
259    case WM_TRAYNOTIFY:
260       // User has clicked on the tray icon or the menu
261       {
262          // Get the submenu to use as a pop-up menu
263          HMENU submenu = GetSubMenu(_this->m_hmenu, 0);
264
265          // What event are we responding to, RMB click?
266          if (lParam==WM_RBUTTONUP) {
267             if (submenu == NULL) {
268                     return 0;
269             }
270
271             // Make the first menu item the default (bold font)
272             SetMenuDefaultItem(submenu, 0, TRUE);
273             
274             // Get the current cursor position, to display the menu at
275             POINT mouse;
276             GetCursorPos(&mouse);
277
278             // There's a "bug"
279             // (Microsoft calls it a feature) in Windows 95 that requires calling
280             // SetForegroundWindow. To find out more, search for Q135788 in MSDN.
281             //
282             SetForegroundWindow(_this->m_nid.hWnd);
283
284             // Display the menu at the desired position
285             TrackPopupMenu(submenu,
286                             0, mouse.x, mouse.y, 0,
287                             _this->m_nid.hWnd, NULL);
288
289             return 0;
290          }
291          
292          // Or was there a LMB double click?
293          if (lParam==WM_LBUTTONDBLCLK) {
294              // double click: execute first menu item
295              SendMessage(_this->m_nid.hWnd,
296                          WM_COMMAND, 
297                          GetMenuItemID(submenu, 0),
298                          0);
299          }
300
301          return 0;
302       }
303
304    case WM_CLOSE:
305       terminate_filed(0);
306       break;
307
308    case WM_DESTROY:
309       // The user wants Bacula to quit cleanly...
310       PostQuitMessage(0);
311       return 0;
312
313    case WM_QUERYENDSESSION:
314            // Are we running as a system service?
315            // Or is the system shutting down (in which case we should check anyway!)
316            if ((!bacService::RunningAsService()) || (lParam == 0)) {
317                    // No, so we are about to be killed
318
319                    // If there are remote connections then we should verify
320                    // that the user is happy about killing them.
321
322                    // Finally, post a quit message, just in case
323                    PostQuitMessage(0);
324                    return TRUE;
325            }
326
327            // Tell the OS that we've handled it anyway
328 //         PostQuitMessage(0);
329            return TRUE;
330
331    
332    default:
333            if (iMsg == MENU_ABOUTBOX_SHOW) {
334                    // External request to show our About dialog
335                    PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_ABOUT, 0), 0);
336                    return 0;
337            }
338            if (iMsg == MENU_STATUS_SHOW) {
339                    // External request to show our status
340                    PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_STATUS, 0), 0);
341                    return 0;
342            }
343
344 #ifdef xxx_needed
345            if (iMsg == MENU_EVENTS_SHOW) {
346                    // External request to show our Events dialogue
347                    PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_EVENTS, 0), 0);
348                    return 0;
349            }
350
351            if (iMsg == MENU_SERVICEHELPER_MSG) {
352                    // External ServiceHelper message.
353                    // This message holds a process id which we can use to
354                    // impersonate a specific user.  In doing so, we can load their
355                    // preferences correctly
356                    bacService::ProcessUserHelperMessage(wParam, lParam);
357
358                    // - Trigger a check of the current user
359                    PostMessage(hwnd, WM_USERCHANGED, 0, 0);
360                    return 0;
361            }
362            if (iMsg == MENU_ADD_CLIENT_MSG) {
363                    // Add Client message.  This message includes an IP address
364                    // of a listening client, to which we should connect.
365
366                    return 0;
367            }
368 #endif
369    }
370
371    // Message not recognised
372    return DefWindowProc(hwnd, iMsg, wParam, lParam);
373 }