-
Notifications
You must be signed in to change notification settings - Fork 1
/
TrayIconEx.pas
executable file
·325 lines (298 loc) · 9.34 KB
/
TrayIconEx.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
unit TrayIconEx;
interface
uses
Windows, Messages, ShellApi, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, Menus;
const
WM_CALLBACK_MESSAGE = WM_USER + 55;
type
TTrayIconEx = class(TComponent)
private
fData: TNotifyIconData;
fIcon: TIcon;
fHint: string;
fPopupMenu: TPopupMenu;
fClicked: Boolean;
fOnClick: TNotifyEvent;
fOnDblClick: TNotifyEvent;
fOnMinimize: TNotifyEvent;
fOnMouseMove: TMouseMoveEvent;
fOnMouseDown: TMouseEvent;
fOnMouseUp: TMouseEvent;
fOnRestore: TNotifyEvent;
protected
procedure SetHint(const Hint: string); virtual;
procedure SetIcon(Icon: TIcon); virtual;
procedure AppMinimize(Sender: TObject);
procedure AppRestore(Sender: TObject);
procedure DoMenu; virtual;
procedure Click; virtual;
procedure DblClick; virtual;
procedure EndSession; virtual;
procedure DoMouseMove(Shift: TShiftState; X, Y: Integer); virtual;
procedure DoMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
virtual;
procedure DoMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
virtual;
procedure OnMessage(var Msg: TMessage); virtual;
procedure Changed; virtual;
property Data: TNotifyIconData read fData;
public
constructor Create(Owner: TComponent); override;
destructor Destroy; override;
procedure Minimize; virtual;
procedure Restore; virtual;
published
property Hint: string read fHint write SetHint;
property Icon: TIcon read fIcon write SetIcon;
property PopupMenu: TPopupMenu read fPopupMenu write fPopupMenu;
property OnClick: TNotifyEvent read fOnClick write fOnClick;
property OnDblClick: TNotifyEvent read fOnDblClick write fOnDblClick;
property OnMinimize: TNotifyEvent read fOnMinimize write fOnMinimize;
property OnMouseMove: TMouseMoveEvent read fOnMouseMove write fOnMouseMove;
property OnMouseDown: TMouseEvent read fOnMouseDown write fOnMouseDown;
property OnMouseUp: TMouseEvent read fOnMouseUp write fOnMouseUp;
property OnRestore: TNotifyEvent read fOnRestore write fOnRestore;
end;
implementation
{
Create the component. At run-time, automatically add a tray icon
with a callback to a hidden window. Use the application icon and title.
}
constructor TTrayIconEx.Create(Owner: TComponent);
begin
inherited Create(Owner);
fIcon := TIcon.Create;
fIcon.Assign(Application.Icon);
if not (csDesigning in ComponentState) then
begin
FillChar(fData, SizeOf(fData), 0);
fData.cbSize := SizeOf(fData);
fData.Wnd := AllocateHwnd(OnMessage); // handle to get notification message
fData.hIcon := Icon.Handle; // icon to display
StrPLCopy(fData.szTip, Application.Title, SizeOf(fData.szTip) - 1);
fData.uFlags := Nif_Icon or Nif_Message;
if Application.Title <> '' then
fData.uFlags := fData.uFlags or Nif_Tip;
fData.uCallbackMessage := WM_CALLBACK_MESSAGE;
if not Shell_NotifyIcon(NIM_ADD, @fData) then // add it
raise EOutOfResources.Create('Cannot create shell notification icon');
{
Replace the application's minimize and restore handlers with
special ones for the tray. The TrayIconEx component has its own
OnMinimize and OnRestore events that the user can set.
}
Application.OnMinimize := AppMinimize;
Application.OnRestore := AppRestore;
end;
end;
{ Remove the icon from the system tray.}
destructor TTrayIconEx.Destroy;
begin
fIcon.Free;
if not (csDesigning in ComponentState) then
Shell_NotifyIcon(Nim_Delete, @fData);
inherited Destroy;
end;
{ Whenever any information changes, update the system tray. }
procedure TTrayIconEx.Changed;
begin
if not (csDesigning in ComponentState) then
Shell_NotifyIcon(NIM_MODIFY, @fData);
end;
{ When the Application is minimized, minimize to the system tray.}
procedure TTrayIconEx.AppMinimize(Sender: TObject);
begin
Minimize
end;
{ When restoring from the system tray, restore the application. }
procedure TTrayIconEx.AppRestore(Sender: TObject);
begin
Restore
end;
{
Message handler for the hidden shell notification window.
Most messages use Wm_Callback_Message as the Msg ID, with
WParam as the ID of the shell notify icon data. LParam is
a message ID for the actual message, e.g., Wm_MouseMove.
Another important message is Wm_EndSession, telling the
shell notify icon to delete itself, so Windows can shut down.
Send the usual Delphi events for the mouse messages. Also
interpolate the OnClick event when the user clicks the
left button, and popup the menu, if there is one, for
right click events.
}
procedure TTrayIconEx.OnMessage(var Msg: TMessage);
{ Return the state of the shift keys. }
function ShiftState: TShiftState;
begin
Result := [];
if GetKeyState(VK_SHIFT) < 0 then
Include(Result, ssShift);
if GetKeyState(VK_CONTROL) < 0 then
Include(Result, ssCtrl);
if GetKeyState(VK_MENU) < 0 then
Include(Result, ssAlt);
end;
var
Pt: TPoint;
Shift: TShiftState;
begin
case Msg.Msg of
Wm_QueryEndSession:
Msg.Result := 1;
Wm_EndSession:
if TWmEndSession(Msg).EndSession then
EndSession;
Wm_Callback_Message:
case Msg.lParam of
WM_MOUSEMOVE:
begin
Shift := ShiftState;
GetCursorPos(Pt);
DoMouseMove(Shift, Pt.X, Pt.Y);
end;
WM_LBUTTONDOWN:
begin
Shift := ShiftState + [ssLeft];
GetCursorPos(Pt);
DoMouseDown(mbLeft, Shift, Pt.X, Pt.Y);
fClicked := True;
end;
WM_LBUTTONUP:
begin
Shift := ShiftState + [ssLeft];
GetCursorPos(Pt);
if fClicked then
begin
fClicked := False;
Click;
end;
DoMouseUp(mbLeft, Shift, Pt.X, Pt.Y);
end;
WM_LBUTTONDBLCLK:
DblClick;
WM_RBUTTONDOWN:
begin
Shift := ShiftState + [ssRight];
GetCursorPos(Pt);
DoMouseDown(mbRight, Shift, Pt.X, Pt.Y);
DoMenu;
end;
WM_RBUTTONUP:
begin
Shift := ShiftState + [ssRight];
GetCursorPos(Pt);
DoMouseUp(mbRight, Shift, Pt.X, Pt.Y);
end;
WM_RBUTTONDBLCLK:
DblClick;
WM_MBUTTONDOWN:
begin
Shift := ShiftState + [ssMiddle];
GetCursorPos(Pt);
DoMouseDown(mbMiddle, Shift, Pt.X, Pt.Y);
end;
WM_MBUTTONUP:
begin
Shift := ShiftState + [ssMiddle];
GetCursorPos(Pt);
DoMouseUp(mbMiddle, Shift, Pt.X, Pt.Y);
end;
WM_MBUTTONDBLCLK:
DblClick;
end;
end;
end;
{ Set a new hint, which is the tool tip for the shell icon. }
procedure TTrayIconEx.SetHint(const Hint: string);
begin
if fHint <> Hint then
begin
fHint := Hint;
StrPLCopy(fData.szTip, Hint, SizeOf(fData.szTip) - 1);
if Hint <> '' then
fData.uFlags := fData.uFlags or Nif_Tip
else
fData.uFlags := fData.uFlags and not Nif_Tip;
Changed;
end;
end;
{ Set a new icon. Update the system tray. }
procedure TTrayIconEx.SetIcon(Icon: TIcon);
begin
if fIcon <> Icon then
begin
fIcon.Assign(Icon);
fData.hIcon := Icon.Handle;
Changed;
end;
end;
{
When the user right clicks the icon, call DoMenu.
If there is a popup menu, and if the window is minimized,
then popup the menu.
}
procedure TTrayIconEx.DoMenu;
var
Pt: TPoint;
begin
if (fPopupMenu <> nil) and not IsWindowVisible(Application.Handle) then
begin
GetCursorPos(Pt);
fPopupMenu.Popup(Pt.X, Pt.Y);
end;
end;
procedure TTrayIconEx.Click;
begin
if Assigned(fOnClick) then
fOnClick(Self);
end;
procedure TTrayIconEx.DblClick;
begin
if Assigned(fOnDblClick) then
fOnDblClick(Self);
end;
procedure TTrayIconEx.DoMouseMove(Shift: TShiftState; X, Y: Integer);
begin
if Assigned(fOnMouseMove) then
fOnMouseMove(Self, Shift, X, Y);
end;
procedure TTrayIconEx.DoMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Assigned(fOnMouseDown) then
fOnMouseDown(Self, Button, Shift, X, Y);
end;
procedure TTrayIconEx.DoMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Assigned(fOnMouseUp) then
fOnMouseUp(Self, Button, Shift, X, Y);
end;
{
When the application minimizes, hide it, so only the icon
in the system tray is visible.
}
procedure TTrayIconEx.Minimize;
begin
ShowWindow(Application.Handle, SW_HIDE);
if Assigned(fOnMinimize) then
fOnMinimize(Self);
end;
{
Restore the application by making its window visible again,
which is a little weird since its window is invisible, having
no height or width, but that's what determines whether the button
appears on the taskbar.
}
procedure TTrayIconEx.Restore;
begin
ShowWindow(Application.Handle, SW_RESTORE);
if Assigned(fOnRestore) then
fOnRestore(Self);
end;
{ Allow Windows to exit by deleting the shell notify icon. }
procedure TTrayIconEx.EndSession;
begin
Shell_NotifyIcon(Nim_Delete, @fData);
end;
end.