C:\temp\win\Tortoise1976.rev.1.1-JvTrayIcon.pas
C:\projects\winKeyLock\current\3rd-party\jvcl\JvTrayIcon.pas
  1|{-----------------------------------------------------------------------------
  2|The contents of this file are subject to the Mozilla Public License
  3|Version 1.1 (the "License"); you may not use this file except in compliance
  4|with the License. You may obtain a copy of the License at
  5|http://www.mozilla.org/MPL/MPL-1.1.html
  6|
  7|Software distributed under the License is distributed on an "AS IS" basis,
  8|WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
  9|the specific language governing rights and limitations under the License.
 10|
 11|The Original Code is: JvTrayIcon.PAS, released on 2001-02-28.
 12|
 13|The Initial Developer of the Original Code is Sébastien Buysse [sbuysse@buypin.com]
 14|Portions created by Sébastien Buysse are Copyright (C) 2001 Sébastien Buysse.
 15|All Rights Reserved.
 16|
 17|Contributor(s):
 18|Michael Beck [mbeck@bigfoot.com].
 19|Feng Mingyu(Winston Feng), [winstonf@tom.com]
 20|
 21|Last Modified: 2003-09-28
 22|
 23|You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
 24|located at http://jvcl.sourceforge.net
 25|
 26|Known Issues:
 27|-----------------------------------------------------------------------------}
 28|
 29|{
 30|History:
 31|  2/29/2004 12:02PM
 32|     VladS removed dependancy on JvComponent and so on all other jvcl units
 33|  2003-09-28 by Winston Feng
 34|    Add WM_SESSIONEND message handler, TaskbarRestart message handler to:
 35|      Clean the trayicon when session ends.
 36|      Restore the trayicon when session restart.
 37|    Remove the old unsuccessful DoCheckCrash method.
 38|}
 39|
 40|{$I jvcl.inc}
 41|
 42|unit JvTrayIcon;
 43|
 44|interface
 45|
 46|uses
 47|  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ExtCtrls,
 48|  Menus, ShellApi,
 49|{$IFDEF COMPILER6_UP}
 50|  DateUtils,
 51|{$ENDIF COMPILER6_UP}
 52|  JvConsts, JvTypes{, JvComponent};
 53|
 54|type
 55|  TBalloonType = (btNone, btError, btInfo, btWarning);
 56|  TNotifyIconDataXP = record
 57|    cbSize: DWORD;
 58|    Wnd: HWND;
 59|    uID: UINT;
 60|    uFlags: UINT;
 61|    uCallbackMessage: UINT;
 62|    hIcon: HICON;
 63|    szTip: array[0..127] of AnsiChar;
 64|    dwState: DWORD;
 65|    dwStateMask: DWORD;
 66|    szInfo: array[0..255] of AnsiChar;
 67|    uTimeOut: DWORD;
 68|    szInfoTitle: array[0..63] of AnsiChar;
 69|    dwInfoFlags: DWORD;
 70|  end;
 71|
 72|  TAnimateEvent = procedure(Sender: TObject; const ImageIndex: Integer) of object;
 73|  TRegisterServiceProcess = function(dwProcessID, dwType: Integer): Integer; stdcall;
 74|
 75|  TTrayVisibility = (tvVisibleTaskBar, tvVisibleTaskList, tvAutoHide, tvAutoHideIcon, tvVisibleDesign,
 76|    tvRestoreClick, tvRestoreDbClick, tvMinimizeClick, tvMinimizeDbClick);
 77|  TTrayVisibilities = set of TTrayVisibility;
 78|
 79|  TJvTrayIcon = class(TComponent)
 80|  private
 81|    FTaskbarRestartMsg: Cardinal;
 82|  protected
 83|    FActive: Boolean;
 84|    FIcon: TIcon;
 85|    FIconData: TNotifyIconDataXP;
 86|    FHandle: THandle;
 87|    FHint: string;
 88|    FBalloonCloser: TTimer;
 89|    FPopupMenu: TPopupMenu;
 90|    FOnClick: TMouseEvent;
 91|    FOnDblClick: TMouseEvent;
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
 92|    FOnMouseMove: TMouseMoveEvent;
 93|    FOnMouseDown: TMouseEvent;
 94|    FOnMouseUp: TMouseEvent;
 95|    FApplicationVisible: Boolean; { Used??}
 96|    FAnimated: Boolean;
 97|    FDelay: Cardinal;
 98|    FImgList: TImageList;
 99|    FTimer: TTimer;
100|    FNumber: Integer;
101|    FDropDown: TPopupMenu;
102|    FTask: Boolean;
103|    FRegisterServiceProcess: TRegisterServiceProcess;
104|    FDllHandle: THandle;
105|    FOnBalloonHide: TNotifyEvent;
106|    FOnBalloonShow: TNotifyEvent;
107|    FOnBalloonClick: TNotifyEvent;
108|    FTime: TDateTime;
109|    FTimeDelay: Integer;
110|    FOldTray: HWND;
111|    FOnAnimate: TAnimateEvent;
112|    FVisibility: TTrayVisibilities;
113|    FOldHint: string;
114|    FSnap: Boolean;
115|    FHooked: Boolean;
116|
117|    function GetSystemMinimumBalloonDelay: integer;
118|    procedure OnAnimateTimer(Sender: TObject);
119|    procedure OnBalloonCloserTimer(Sender: TObject);
120|    procedure IconChanged(Sender: TObject);
121|    procedure SetActive(Value: Boolean);
122|    procedure SetHint(Value: string);
123|    procedure SetIcon(Icon: TIcon);
124|    procedure SetApplicationVisible(Value: Boolean);
125|    procedure SetAnimated(const Value: Boolean);
126|    procedure SetDelay(const Value: Cardinal);
127|    procedure SetImgList(const Value: TImageList);
128|    procedure SetTask(const Value: Boolean);
129|    procedure SetNumber(const Value: Integer);
130|    procedure SetVisibility(const Value: TTrayVisibilities);
131|    procedure Hook;
132|    procedure UnHook;
133|    procedure WndProc(var Mesg: TMessage);
134|    procedure DoMouseMove(Shift: TShiftState; X, Y: Integer);
135|    procedure DoMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
136|    procedure DoMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
137|    procedure DoDoubleClick(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
   
138|    function ApplicationHook(var Msg: TMessage): Boolean;
139|    function NotifyIcon(dwMessage: DWORD): boolean;
140|
141|    property ApplicationVisible: Boolean read FApplicationVisible write SetApplicationVisible default True;
142|    property VisibleInTaskList: Boolean read FTask write SetTask default True;
143|
144|  public
145|    constructor Create(AOwner: TComponent); override;
146|    destructor Destroy; override;
147|    procedure DoCheckCrash;
148|    procedure HideApplication;
149|    procedure ShowApplication;
150|    procedure BalloonHint(Title, Value: string; BalloonType:
151|      TBalloonType = btNone; Delay: Integer = 5000; CancelPrevious: boolean = false);
152|    function AcceptBalloons: Boolean;
153|  published
154|    { (rb) Active should be set in Loaded; Icon isn't set when Active is now
155|      set to True etc. }
156|    property Active: Boolean read FActive write SetActive default False;
157|    property Animated: Boolean read FAnimated write SetAnimated default False;
158|    property Icon: TIcon read FIcon write SetIcon;
159|    property IconIndex: Integer read FNumber write SetNumber;
160|    property Icons: TImageList read FImgList write SetImgList;
161|    property Hint: string read FHint write SetHint;
162|    property DropDownMenu: TPopupMenu read FDropDown write FDropDown;
163|    property PopupMenu: TPopupMenu read FPopupMenu write FPopupMenu;
164|    property Delay: Cardinal read FDelay write SetDelay default 100;
165|    property Snap: Boolean read FSnap write FSnap default False;
166|    property Visibility: TTrayVisibilities read FVisibility write SetVisibility
167|      default [tvVisibleTaskBar, tvVisibleTaskList, tvAutoHide];
168|    property OnAnimate: TAnimateEvent read FOnAnimate write FOnAnimate;
169|    property OnClick: TMouseEvent read FOnClick write FOnClick;
170|    property OnDblClick: TMouseEvent read FOnDblClick write FOnDblClick;
171|    property OnMouseMove: TMouseMoveEvent read FOnMouseMove write FOnMouseMove;
172|    property OnMouseDown: TMouseEvent read FOnMouseDown write FOnMouseDown;
173|    property OnMouseUp: TMouseEvent read FOnMouseUp write FOnMouseUp;
174|    property OnBalloonShow: TNotifyEvent read FOnBalloonShow write FOnBalloonShow;
175|    property OnBalloonHide: TNotifyEvent read FOnBalloonHide write FOnBalloonHide;
176|    property OnBalloonClick: TNotifyEvent read FOnBalloonClick write FOnBalloonClick;
177|  end;
178|
179|implementation
180|
181|uses
182|    JvJVCLUtils_m {VladS}
183|    {JvJCLUtils, JvJVCLUtils};
184|
185|const
186|  WM_CALLBACKMESSAGE = WM_USER + 1;
187|  NOTIFYICON_VERSION = 3;
188|  NIM_ADD = $00000000;
189|  NIM_MODIFY = $00000001;
190|  NIM_DELETE = $00000002;
191|  NIM_SETFOCUS = $00000003;
192|  NIM_SETVERSION = $00000004;
193|
194|  NIF_MESSAGE = $00000001;
195|  NIF_ICON = $00000002;
196|  NIF_TIP = $00000004;
197|  NIF_STATE = $00000008;
198|  NIF_INFO = $00000010;
199|
200|  NIS_HIDDEN = $00000001;
201|  NIS_SHAREDICON = $00000002;
202|
203|  NIIF_NONE = $00000000;
204|  NIIF_INFO = $00000001;
205|  NIIF_WARNING = $00000002;
206|  NIIF_ERROR = $00000003;
207|
208|  NIN_SELECT = (WM_USER + 0);
209|  NINF_KEY = 1;
210|  NIN_KEYSELECT = (NIN_SELECT or NINF_KEY);
211|
212|  NIN_BALLOONSHOW = WM_USER + 2;
213|  NIN_BALLOONHIDE = WM_USER + 3;
214|  NIN_BALLOONTIMEOUT = WM_USER + 4;
215|  NIN_BALLOONUSERCLICK = WM_USER + 5;
216|
217|{$IFNDEF COMPILER6_UP}
218|
219|function SecondsBetween(const Now: TDateTime; const FTime: TDateTime): Integer;
220|begin
221|  Result := Trunc(86400 * (FTime - Now));
222|end;
223|{$ENDIF COMPILER6_UP}
224|
225|constructor TJvTrayIcon.Create(AOwner: TComponent);
226|begin
227|  inherited Create(AOwner);
228|  FIcon := TIcon.Create;
229|  FIcon.OnChange := IconChanged;
230|  FApplicationVisible := True;
231|  FSnap := False;
232|  FHandle := AllocateHWndEx(WndProc);
233|
234|  FVisibility := [tvVisibleTaskBar, tvVisibleTaskList, tvAutoHide];
235|  FAnimated := False;
236|  FDelay := 100;
237|  FNumber := 0;
238|  FActive := False;
239|  FTask := True;
240|
241|  FBalloonCloser := TTimer.Create(self);
242|  FBalloonCloser.Enabled := false;
243|  FBalloonCloser.OnTimer := OnBalloonCloserTimer;
244|
   
   
   
   
245|  if not (csDesigning in ComponentState) then
246|  begin
247|    FDllHandle := LoadLibrary('KERNEL32.DLL');
248|    if FDllHandle <> 0 then
249|      @FRegisterServiceProcess := GetProcAddress(FDllHandle, 'RegisterServiceProcess')
250|    else
251|      @FRegisterServiceProcess := nil;
252|  end;
253|
254|  FTaskbarRestartMsg := RegisterWindowMessage('TaskbarCreated');
255|end;
256|
257|destructor TJvTrayIcon.Destroy;
258|begin
259|  UnHook;
260|  if not (csDestroying in Application.ComponentState) then
261|    SetTask(False);
262|
   
263|  FTimer.Free;
264|  SetActive(False);
265|  FBalloonCloser.Enabled := false;
266|  FBalloonCloser.Free;
267|  FIcon.Free;
268|  DeallocateHWndEx(FHandle);
269|
270|  if not (csDesigning in ComponentState) then
271|    if FDllHandle <> 0 then
272|      FreeLibrary(FDllHandle);
273|  inherited Destroy;
274|end;
275|
276|function TJvTrayIcon.AcceptBalloons: Boolean;
277|begin
278|  // Balloons are only accepted with shell32.dll 5.0+
279|  Result := GetShellVersion >= $00050000;
280|end;
281|
282|procedure TJvTrayIcon.SetTask(const Value: Boolean);
283|begin
284|  if FTask <> Value then
285|  begin
286|    FTask := Value;
287|    if not (csDesigning in ComponentState) then
288|      if Assigned(FRegisterServiceProcess) then
289|        if FTask then
290|          FRegisterServiceProcess(GetCurrentProcessID, 0)
291|        else
292|          FRegisterServiceProcess(GetCurrentProcessID, 1);
293|  end;
294|end;
295|
296|procedure TJvTrayIcon.WndProc(var Mesg: TMessage);
297|var
298|  I: Integer;
299|  Pt: TPoint;
300|  ShState: TShiftState;
301|begin
302|  try
303|    with Mesg do
304|    begin
305|      case Msg of
306|        WM_CALLBACKMESSAGE:
307|          begin
308|            GetCursorPos(Pt);
309|            ShState := [];
310|            if GetKeyState(VK_SHIFT) < 0 then Include(ShState, ssShift);
311|            if GetKeyState(VK_CONTROL) < 0 then Include(ShState, ssCtrl);
312|            if GetKeyState(VK_LBUTTON) < 0 then Include(ShState, ssLeft);
313|            if GetKeyState(VK_RBUTTON) < 0 then Include(ShState, ssRight);
314|            if GetKeyState(VK_MBUTTON) < 0 then Include(ShState, ssMiddle);
315|            if GetKeyState(VK_MENU) < 0 then Include(ShState, ssAlt);
316|            case LParam of
317|              WM_MOUSEMOVE:
318|                DoMouseMove(shState, Pt.X, Pt.Y);
319|              WM_LBUTTONDOWN:
320|                DoMouseDown(mbLeft, ShState, Pt.X, Pt.Y);
321|              WM_RBUTTONDOWN:
322|                DoMouseDown(mbRight, ShState, Pt.X, Pt.Y);
323|              WM_MBUTTONDOWN:
324|                DoMouseDown(mbMiddle, ShState, Pt.X, Pt.Y);
325|              WM_LBUTTONUP:
326|                DoMouseUp(mbLeft, ShState, Pt.X, Pt.Y);
327|              WM_MBUTTONUP:
328|                DoMouseUp(mbMiddle, ShState, Pt.X, Pt.Y);
329|              WM_RBUTTONUP, NIN_KEYSELECT: //Mimics previous versions of shell32.dll
330|                DoMouseUp(mbRight, ShState, Pt.X, Pt.Y);
331|              WM_LBUTTONDBLCLK:
332|                DoDoubleClick(mbLeft, ShState, Pt.X, Pt.Y);
333|              WM_RBUTTONDBLCLK:
334|                DoDoubleClick(mbRight, ShState, Pt.X, Pt.Y);
335|              WM_MBUTTONDBLCLK:
336|                DoDoubleClick(mbMiddle, ShState, Pt.X, Pt.Y);
337|              NIN_BALLOONHIDE: //sb
338|                begin
339|                  try
340|                    if Assigned(FOnBalloonHide) then
341|                      FOnBalloonHide(self);
342|                  except
343|                  end;
344|                  Result := Ord(True);
345|                end;
346|              NIN_BALLOONTIMEOUT: //sb
347|                begin
348|                  I := SecondsBetween(Now, FTime);
349|                  if I > FTimeDelay then
350|                    BalloonHint('', '');
351|                  Result := Ord(True);
352|                end;
353|              NIN_BALLOONUSERCLICK: //sb
354|                begin
355|                  try
356|                    if Assigned(FOnBalloonClick) then
357|                      FOnBalloonClick(self);
358|                  except
359|                  end;
360|                  Result := Ord(True);
361|                  //Result := DefWindowProc(FHandle, Msg, wParam, lParam);
362|                  BalloonHint('', '');
363|                end;
364|            end;
365|          end;
366|        // Add by Winston Feng 2003-9-28
367|        // Handle the QueryEndSesstion and TaskbarCreated message, so trayicon
368|        // will be deleted and restored correctly.
369|        WM_QUERYENDSESSION:
370|          Result := 1;
371|        WM_ENDSESSION:
372|          begin
373|            if FActive then
374|            begin
375|              SetActive(False);
376|              FActive := True;
377|            end
378|            else
379|            begin
380|              SetActive(False);
381|              FActive := False;
382|            end;
383|          end;
384|      else
385|        begin
386|          if (Msg = FTaskbarRestartMsg) and (FActive) then
387|            SetActive(True);
388|          Result := DefWindowProc(FHandle, Msg, wParam, lParam);
389|        end;
390|      end; // case
391|    end; // with
392|  except
393|    Application.HandleException(Self);
394|  end;
395|end;
396|
397|procedure TJvTrayIcon.IconChanged(Sender: TObject);
398|begin
399|  //DoCheckCrash;
400|  with FIconData do
401|    hIcon := FIcon.Handle;
402|  if FActive then
403|    NotifyIcon(NIM_MODIFY);
404|end;
405|
406|procedure TJvTrayIcon.SetHint(Value: string);
407|begin
408|  //DoCheckCrash;
409|  //Remove sLineBreak on w98/me as they are not supported
410|  if not AcceptBalloons then
411|    FHint := StringReplace(Value, sLineBreak, ' - ', [rfReplaceAll])
412|  else
413|    FHint := Value;
414|end;
415|
416|procedure TJvTrayIcon.SetIcon(Icon: TIcon);
417|begin
418|  //DoCheckCrash;
419|  FIcon.Assign(Icon);
420|  with FIconData do
421|    hIcon := FIcon.Handle;
422|  if FActive then
423|    NotifyIcon(NIM_MODIFY);
424|end;
425|
426|procedure TJvTrayIcon.SetActive(Value: Boolean);
427|//http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/Shell/Structures/NOTIFYICONDATA.asp
428|begin
429|  if Value then
430|    Hook
431|  else
432|    UnHook;
433|
434|  if Value and ((not (csDesigning in ComponentState)) or (tvVisibleDesign in Visibility)) then
435|  begin
436|    FOldTray := FindWindow('Shell_TrayWnd', nil);
437|
438|    if (FIcon = nil) or FIcon.Empty then
439|    { (rb) This triggers the IconChanged event, note that Active is loaded
440|    before Icon from the stream, thus FIcon is always empty at this
441|    point when loaded from the dfm stream }
442|      FIcon.Assign(Application.Icon);
443|    with FIconData do
444|    begin
445|      if AcceptBalloons then
446|      begin
447|        cbSize := SizeOf(FIconData);
448|        FIconData.uTimeOut := NOTIFYICON_VERSION;
449|      end
450|      else
451|        cbSize := SizeOf(TNotifyIconData);
452|      Wnd := FHandle;
453|      uId := 1;
454|      uCallBackMessage := WM_CALLBACKMESSAGE;
455|      if FIcon <> nil then
456|        hIcon := FIcon.Handle
457|      else
458|        FIcon := Application.Icon;
459|      StrPLCopy(szTip, GetShortHint(FHint), SizeOf(szTip) - 1);
460|      uFlags := NIF_MESSAGE or NIF_ICON or NIF_INFO or NIF_TIP;
461|    end;
462|    if not ((tvAutoHideIcon in Visibility) and (Application.MainForm <> nil) and Application.MainForm.Visible) then
463|      NotifyIcon(NIM_ADD);
464|    if AcceptBalloons then
465|      NotifyIcon(NIM_SETVERSION);
466|  end
467|  else
468|    NotifyIcon(NIM_DELETE);
469|
470|  FActive := Value;
471|end;
472|
473|procedure TJvTrayIcon.SetApplicationVisible(Value: Boolean);
474|begin
475|  FApplicationVisible := Value;
476|  if (csLoading in ComponentState) and not (csDesigning in ComponentState) then
477|    Application.ShowMainForm := FApplicationVisible;
478|  if not (csDesigning in ComponentState) and not (csLoading in ComponentState) then
479|    if Value then
480|      ShowApplication
481|    else
482|      HideApplication;
483|end;
484|
485|procedure TJvTrayIcon.HideApplication;
486|begin
487|  if (Application.MainForm <> nil) and (Application.MainForm.WindowState <> wsMinimized) then
488|  begin
489|    if Snap then
490|    begin
491|      Application.MainForm.Visible := False;
492|      Application.ShowMainForm := False;
493|    end;
494|    Application.Minimize;
495|  end;
496|  ShowWindow(Application.Handle, SW_HIDE);
497|  FApplicationVisible := False;
498|  if (tvAutoHideIcon in Visibility) then
499|    NotifyIcon(NIM_ADD);
500|end;
501|
502|procedure TJvTrayIcon.ShowApplication;
503|begin
504|  ShowWindow(Application.Handle, SW_SHOW);
505|  Application.Restore;
506|  if Application.MainForm <> nil then
507|    Application.MainForm.Visible := True;
508|  if tvAutoHideIcon in Visibility then
509|    NotifyIcon(NIM_DELETE);
510|end;
511|
512|procedure TJvTrayIcon.OnAnimateTimer(Sender: TObject);
513|begin
514|  if (FActive) and (FImgList <> nil) then
515|  begin
516|    if IconIndex < 0 then
517|      IconIndex := 0
518|    else
519|      IconIndex := (IconIndex + 1) mod FimgList.Count;
520|    if Assigned(FOnAnimate) then
521|      FOnAnimate(self, IconIndex);
522|  end;
523|end;
524|
525|procedure TJvTrayIcon.SetAnimated(const Value: Boolean);
526|begin
527|  FAnimated := Value;
528|  if (not (csDesigning in ComponentState)) or (tvVisibleDesign in Visibility) then
529|  begin
530|    if Value then
531|    begin
532|      if FTimer = nil then
533|      begin
534|        FTimer := TTimer.Create(Self);
535|        FTimer.Interval := FDelay;
536|        FTimer.OnTimer := OnAnimateTimer;
537|      end;
538|      FTimer.Enabled := True;
539|    end
540|    else if FTimer <> nil then
541|    begin
542|      FTimer.Free;
543|      FTimer := nil;
544|    end;
545|  end;
546|end;
547|
548|procedure TJvTrayIcon.SetDelay(const Value: Cardinal);
549|begin
550|  FDelay := Value;
551|  if FTimer <> nil then
552|    FTimer.Interval := FDelay;
553|end;
554|
555|procedure TJvTrayIcon.SetImgList(const Value: TImageList);
556|begin
557|  FImgList := Value;
558|end;
559|
560|function TJvTrayIcon.GetSystemMinimumBalloonDelay: integer;
561|begin
562|  // from Microsoft's documentation, a balloon is shown for at
563|  // least 10 seconds, but it is a system settings which must
564|  // be somewhere in the registry. The only question is : Where ?
565|  Result := 10000;
566|end;
567|
568|procedure TjvTrayIcon.OnBalloonCloserTimer(Sender: TObject);
569|begin
570|  // we stop the timer
571|  FBalloonCloser.Enabled := false;
572|  // then we call BalloonHint with title and info set to
573|  // empty strings which surprisingly will cancel any existing
574|  // balloon for the icon. This is clearly not documented by
575|  // microsoft and may not work in later releases of Windows
576|  // it has been tested on XP Home French
577|  BalloonHint('', '');
578|end;
579|
580|procedure TJvTrayIcon.BalloonHint(Title, Value: string;
581|  BalloonType: TBalloonType; Delay: Integer; CancelPrevious: boolean);
582|//http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/Shell/reference/functions/shell_notifyicon.asp
583|begin
584|  if AcceptBalloons then
585|  begin
586|    FTime := Now;
587|    FTimeDelay := Delay div 1000;
588|    FIconData.uFlags := NIF_INFO;
589|
590|    // if we must cancel an existing balloon
591|    if CancelPrevious then
592|    begin
593|      // then we call BalloonHint with title and info set to
594|      // empty strings which surprisingly will cancel any existing
595|      // balloon for the icon. This is clearly not documented by
596|      // microsoft and may not work in later releases of Windows
597|      // it has been tested on XP Home French
598|      BalloonHint('', '');
599|    end;
600|
601|    with FIconData do
602|      StrPLCopy(szInfoTitle, Title, SizeOf(szInfoTitle) - 1);
603|    with FIconData do
604|      StrPLCopy(szInfo, Value, SizeOf(szInfo) - 1);
605|    FIconData.uFlags := NIF_MESSAGE or NIF_ICON or NIF_INFO or NIF_TIP;
606|    FIconData.uTimeOut := Delay;
607|    case BalloonType of
608|      btError:
609|        FIconData.dwInfoFlags := NIIF_ERROR;
610|      btInfo:
611|        FIconData.dwInfoFlags := NIIF_INFO;
612|      btNone:
613|        FIconData.dwInfoFlags := NIIF_NONE;
614|      btWarning:
615|        FIconData.dwInfoFlags := NIIF_WARNING;
616|    end;
617|    NotifyIcon(NIM_MODIFY);
618|
619|    // if the delay is less than the system's minimum and the balloon
620|    // was really shown (title and value are not empty)
621|    if (Delay < GetSystemMinimumBalloonDelay) and
622|      (Title <> '') and
623|      (Value <> '') then
624|    begin
625|      // then we enable the ballon closer timer which will cancel
626|      // the balloon when the delay is elapsed
627|      FBalloonCloser.Interval := Delay;
628|      FBalloonCloser.Enabled := true;
629|    end;
630|
631|    if Assigned(FOnBalloonShow) then
632|      FOnBalloonShow(self);
633|  end;
634|end;
635|
636|procedure TJvTrayIcon.DoCheckCrash;
637|var
638|  HWndTray: HWND;
639|begin
640|  if Active then
641|  begin
642|    HWndTray := FindWindow('Shell_TrayWnd', nil);
643|    if FOldTray <> HWndTray then
644|    begin
645|      FOldTray := HWndTray;
646|      Active := False;
647|      Active := True;
648|    end;
649|  end;
650|end;
651|
652|procedure TJvTrayIcon.DoMouseMove(Shift: TShiftState; X, Y: Integer);
653|begin
654|  if FOldHint <> FHint then
655|  begin
656|    FOldHint := FHint;
657|    with FIconData do
658|      StrPLCopy(szTip, GetShortHint(FHint), SizeOf(szTip) - 1);
659|    FIconData.uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
660|    if FActive then
661|      NotifyIcon(NIM_MODIFY);
662|  end;
663|  if Assigned(FOnMouseMove) then
664|    FOnMouseMove(Self, Shift, X, Y);
665|end;
666|
667|procedure TJvTrayIcon.DoMouseDown(Button: TMouseButton; Shift: TShiftState;
668|  X, Y: Integer);
669|begin
   
670|  if Assigned(FOnMouseDown) then
671|    FOnMouseDown(Self, Button, Shift, X, Y);
672|  if (Button = mbLeft) and (FDropDown <> nil) then
673|  begin
674|    SetForegroundWindow(FHandle);
675|    FDropDown.Popup(X, Y);
676|    PostMessage(FHandle, WM_NULL, 0, 0);
677|  end;
678|end;
679|
680|procedure TJvTrayIcon.DoMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
681|var
682|    hasPopup: boolean;
683|begin
684|  hasPopup := false;
685|  if (Button = mbRight) and (FPopupMenu <> nil) then
686|  begin
687|    SetForegroundWindow(FHandle);
688|    FPopupMenu.Popup(X, Y);
689|    PostMessage(FHandle, WM_NULL, 0, 0);
690|    hasPopup := true;
691|  end
692|  else if Button = mbLeft then
693|    if not ApplicationVisible then
694|    begin
695|      if tvRestoreClick in Visibility then
696|        Visibility := Visibility + [tvVisibleTaskBar]
697|    end
698|    else
699|    begin
700|      if tvMinimizeClick in Visibility then
701|        Visibility := Visibility - [tvVisibleTaskBar]
702|    end;
703|
704|  if Assigned(FOnMouseUp) then
705|    FOnMouseUp(Self, Button, Shift, X, Y);
706|  if (not hasPopup) and Assigned(FOnClick) then
707|    FOnClick(Self, Button, Shift, X, Y);
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
708|end;
709|
710|procedure TJvTrayIcon.DoDoubleClick(Button: TMouseButton;
711|  Shift: TShiftState; X, Y: Integer);
712|var
713|  I: Integer;
714|begin
   
   
   
715|  if Assigned(FOnDblClick) then
716|    FOnDblClick(Self, Button, Shift, X, Y)
717|  else if Button = mbLeft then
718|  begin
719|    if FPopupMenu <> nil then
720|      for I := 0 to FPopupMenu.Items.Count - 1 do
721|        if FPopupMenu.Items[I].Default then
722|        begin
723|          FPopupMenu.Items[I].Click;
724|          Break;
725|        end;
726|    if ApplicationVisible then
727|    begin
728|      if tvMinimizeDbClick in Visibility then
729|        Visibility := Visibility - [tvVisibleTaskBar];
730|    end
731|    else
732|    begin
733|      if tvRestoreDbClick in Visibility then
734|        Visibility := Visibility + [tvVisibleTaskBar];
735|    end;
736|  end;
737|end;
738|
739|procedure TJvTrayIcon.SetNumber(const Value: Integer);
740|var
741|  Ico: TIcon;
742|begin
743|  FNumber := Value;
744|  if (FImglist <> nil) and (FNumber >= 0) and (FNumber < FImgList.Count) then
745|  begin
746|    Ico := TIcon.Create;
747|    try
748|      FIconData.uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
749|      FImgList.GetIcon(FNumber, Ico);
750|      SetIcon(Ico);
751|    finally
752|      Ico.Free;
753|    end;
754|  end
755|  else
756|    FNumber := -1;
757|end;
758|
759|procedure TJvTrayIcon.SetVisibility(const Value: TTrayVisibilities);
760|begin
761|  FVisibility := Value;
762|  VisibleInTaskList := tvVisibleTaskList in Value;
763|  ApplicationVisible := tvVisibleTaskBar in Value;
764|  SetActive(FActive);
765|  SetAnimated(FAnimated);
766|end;
767|
768|function TJvTrayIcon.ApplicationHook(var Msg: TMessage): Boolean;
769|begin
770|  if (Msg.Msg = WM_SYSCOMMAND) and (Msg.WParam = SC_MINIMIZE) and
771|    (tvAutoHide in Visibility) and Active then
772|    ApplicationVisible := False;
773|  Result := False;
774|end;
775|
776|procedure TJvTrayIcon.Hook;
777|begin
778|  if FHooked or (csDesigning in ComponentState) then
779|    Exit;
780|
781|  FHooked := True;
782|
783|  Application.HookMainWindow(ApplicationHook);
784|end;
785|
786|procedure TJvTrayIcon.UnHook;
787|begin
788|  if not FHooked then
789|    Exit;
790|
791|  FHooked := False;
792|
793|  Application.UnHookMainWindow(ApplicationHook);
794|end;
795|
796|function TJvTrayIcon.NotifyIcon(dwMessage: DWORD): boolean;
797|begin
798|  Result := Shell_NotifyIcon(dwMessage, @FIconData);
799|
800|{  if FActive and (tvAutoHideIcon in Visibility) then
801|  begin
802|    if Application.MainForm.Visible then
803|      Result := Shell_NotifyIcon(NIM_DELETE,@FIconData)
804|    else
805|      Result := Shell_NotifyIcon(NIM_ADD,@FIconData);
806|  end;
807|  }
808|end;
809|
810|end.
811|
812|
  1|{-----------------------------------------------------------------------------
  2|The contents of this file are subject to the Mozilla Public License
  3|Version 1.1 (the "License"); you may not use this file except in compliance
  4|with the License. You may obtain a copy of the License at
  5|http://www.mozilla.org/MPL/MPL-1.1.html
  6|
  7|Software distributed under the License is distributed on an "AS IS" basis,
  8|WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
  9|the specific language governing rights and limitations under the License.
 10|
 11|The Original Code is: JvTrayIcon.PAS, released on 2001-02-28.
 12|
 13|The Initial Developer of the Original Code is Sébastien Buysse [sbuysse@buypin.com]
 14|Portions created by Sébastien Buysse are Copyright (C) 2001 Sébastien Buysse.
 15|All Rights Reserved.
 16|
 17|Contributor(s):
 18|Michael Beck [mbeck@bigfoot.com].
 19|Feng Mingyu(Winston Feng), [winstonf@tom.com]
 20|
 21|Last Modified: 2003-09-28
 22|
 23|You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
 24|located at http://jvcl.sourceforge.net
 25|
 26|Known Issues:
 27|-----------------------------------------------------------------------------}
 28|
 29|{
 30|History:
 31|  2/29/2004 12:02PM
 32|     VladS removed dependancy on JvComponent and so on all other jvcl units
 33|  2003-09-28 by Winston Feng
 34|    Add WM_SESSIONEND message handler, TaskbarRestart message handler to:
 35|      Clean the trayicon when session ends.
 36|      Restore the trayicon when session restart.
 37|    Remove the old unsuccessful DoCheckCrash method.
 38|}
 39|
 40|{$I jvcl.inc}
 41|
 42|unit JvTrayIcon;
 43|
 44|interface
 45|
 46|uses
 47|  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ExtCtrls,
 48|  Menus, ShellApi,
 49|{$IFDEF COMPILER6_UP}
 50|  DateUtils,
 51|{$ENDIF COMPILER6_UP}
 52|  JvConsts, JvTypes{, JvComponent};
 53|
 54|type
 55|  TBalloonType = (btNone, btError, btInfo, btWarning);
 56|  TNotifyIconDataXP = record
 57|    cbSize: DWORD;
 58|    Wnd: HWND;
 59|    uID: UINT;
 60|    uFlags: UINT;
 61|    uCallbackMessage: UINT;
 62|    hIcon: HICON;
 63|    szTip: array[0..127] of AnsiChar;
 64|    dwState: DWORD;
 65|    dwStateMask: DWORD;
 66|    szInfo: array[0..255] of AnsiChar;
 67|    uTimeOut: DWORD;
 68|    szInfoTitle: array[0..63] of AnsiChar;
 69|    dwInfoFlags: DWORD;
 70|  end;
 71|
 72|  TAnimateEvent = procedure(Sender: TObject; const ImageIndex: Integer) of object;
 73|  TRegisterServiceProcess = function(dwProcessID, dwType: Integer): Integer; stdcall;
 74|
 75|  TTrayVisibility = (tvVisibleTaskBar, tvVisibleTaskList, tvAutoHide, tvAutoHideIcon, tvVisibleDesign,
 76|    tvRestoreClick, tvRestoreDbClick, tvMinimizeClick, tvMinimizeDbClick);
 77|  TTrayVisibilities = set of TTrayVisibility;
 78|
 79|  TJvTrayIcon = class(TComponent)
 80|  private
 81|    FTaskbarRestartMsg: Cardinal;
 82|  protected
 83|    FActive: Boolean;
 84|    FIcon: TIcon;
 85|    FIconData: TNotifyIconDataXP;
 86|    FHandle: THandle;
 87|    FHint: string;
 88|    FBalloonCloser: TTimer;
 89|    FPopupMenu: TPopupMenu;
 90|    FOnClick: TMouseEvent;
 91|    FOnDblClick: TMouseEvent;
 92|
 93|    { Vlad S}
 94|    {
 95|    distinguish single-click and a double-click
 96|    Create a timer which is started on the first click, set the timeout value to
 97|    something a bit longer than the double-click, then connect the timeout() signal
 98|    to a slot of your own. When a double click event is received you simply stop
 99|    the timer. If the custom slot is visited you know that a single click was
100|    done.
101|    }
102|    FDblClicked: boolean;
103|    FDblClickTimer: TTimer;
104|    FClickedButton: TMouseButton;
105|    FClickedShift: TShiftState;
106|    FClickedX: Integer;
107|    FClickedY: Integer;
108|    { Vlad S end.}
109|
110|    FOnMouseMove: TMouseMoveEvent;
111|    FOnMouseDown: TMouseEvent;
112|    FOnMouseUp: TMouseEvent;
113|    FApplicationVisible: Boolean; { Used??}
114|    FAnimated: Boolean;
115|    FDelay: Cardinal;
116|    FImgList: TImageList;
117|    FTimer: TTimer;
118|    FNumber: Integer;
119|    FDropDown: TPopupMenu;
120|    FTask: Boolean;
121|    FRegisterServiceProcess: TRegisterServiceProcess;
122|    FDllHandle: THandle;
123|    FOnBalloonHide: TNotifyEvent;
124|    FOnBalloonShow: TNotifyEvent;
125|    FOnBalloonClick: TNotifyEvent;
126|    FTime: TDateTime;
127|    FTimeDelay: Integer;
128|    FOldTray: HWND;
129|    FOnAnimate: TAnimateEvent;
130|    FVisibility: TTrayVisibilities;
131|    FOldHint: string;
132|    FSnap: Boolean;
133|    FHooked: Boolean;
134|
135|    function GetSystemMinimumBalloonDelay: integer;
136|    procedure OnAnimateTimer(Sender: TObject);
137|    procedure OnBalloonCloserTimer(Sender: TObject);
138|    procedure IconChanged(Sender: TObject);
139|    procedure SetActive(Value: Boolean);
140|    procedure SetHint(Value: string);
141|    procedure SetIcon(Icon: TIcon);
142|    procedure SetApplicationVisible(Value: Boolean);
143|    procedure SetAnimated(const Value: Boolean);
144|    procedure SetDelay(const Value: Cardinal);
145|    procedure SetImgList(const Value: TImageList);
146|    procedure SetTask(const Value: Boolean);
147|    procedure SetNumber(const Value: Integer);
148|    procedure SetVisibility(const Value: TTrayVisibilities);
149|    procedure Hook;
150|    procedure UnHook;
151|    procedure WndProc(var Mesg: TMessage);
152|    procedure DoMouseMove(Shift: TShiftState; X, Y: Integer);
153|    procedure DoMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
154|    procedure DoMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
155|    procedure DoDoubleClick(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
156|    procedure OnDblClickTimer(Sender: TObject); { Vlad S}
157|    function ApplicationHook(var Msg: TMessage): Boolean;
158|    function NotifyIcon(dwMessage: DWORD): boolean;
159|
160|    property ApplicationVisible: Boolean read FApplicationVisible write SetApplicationVisible default True;
161|    property VisibleInTaskList: Boolean read FTask write SetTask default True;
162|
163|  public
164|    constructor Create(AOwner: TComponent); override;
165|    destructor Destroy; override;
166|    procedure DoCheckCrash;
167|    procedure HideApplication;
168|    procedure ShowApplication;
169|    procedure BalloonHint(Title, Value: string; BalloonType:
170|      TBalloonType = btNone; Delay: Integer = 5000; CancelPrevious: boolean = false);
171|    function AcceptBalloons: Boolean;
172|  published
173|    { (rb) Active should be set in Loaded; Icon isn't set when Active is now
174|      set to True etc. }
175|    property Active: Boolean read FActive write SetActive default False;
176|    property Animated: Boolean read FAnimated write SetAnimated default False;
177|    property Icon: TIcon read FIcon write SetIcon;
178|    property IconIndex: Integer read FNumber write SetNumber;
179|    property Icons: TImageList read FImgList write SetImgList;
180|    property Hint: string read FHint write SetHint;
181|    property DropDownMenu: TPopupMenu read FDropDown write FDropDown;
182|    property PopupMenu: TPopupMenu read FPopupMenu write FPopupMenu;
183|    property Delay: Cardinal read FDelay write SetDelay default 100;
184|    property Snap: Boolean read FSnap write FSnap default False;
185|    property Visibility: TTrayVisibilities read FVisibility write SetVisibility
186|      default [tvVisibleTaskBar, tvVisibleTaskList, tvAutoHide];
187|    property OnAnimate: TAnimateEvent read FOnAnimate write FOnAnimate;
188|    property OnClick: TMouseEvent read FOnClick write FOnClick;
189|    property OnDblClick: TMouseEvent read FOnDblClick write FOnDblClick;
190|    property OnMouseMove: TMouseMoveEvent read FOnMouseMove write FOnMouseMove;
191|    property OnMouseDown: TMouseEvent read FOnMouseDown write FOnMouseDown;
192|    property OnMouseUp: TMouseEvent read FOnMouseUp write FOnMouseUp;
193|    property OnBalloonShow: TNotifyEvent read FOnBalloonShow write FOnBalloonShow;
194|    property OnBalloonHide: TNotifyEvent read FOnBalloonHide write FOnBalloonHide;
195|    property OnBalloonClick: TNotifyEvent read FOnBalloonClick write FOnBalloonClick;
196|  end;
197|
198|implementation
199|
200|uses
201|    JvJVCLUtils_m {VladS}
202|    {JvJCLUtils, JvJVCLUtils};
203|
204|const
205|  WM_CALLBACKMESSAGE = WM_USER + 1;
206|  NOTIFYICON_VERSION = 3;
207|  NIM_ADD = $00000000;
208|  NIM_MODIFY = $00000001;
209|  NIM_DELETE = $00000002;
210|  NIM_SETFOCUS = $00000003;
211|  NIM_SETVERSION = $00000004;
212|
213|  NIF_MESSAGE = $00000001;
214|  NIF_ICON = $00000002;
215|  NIF_TIP = $00000004;
216|  NIF_STATE = $00000008;
217|  NIF_INFO = $00000010;
218|
219|  NIS_HIDDEN = $00000001;
220|  NIS_SHAREDICON = $00000002;
221|
222|  NIIF_NONE = $00000000;
223|  NIIF_INFO = $00000001;
224|  NIIF_WARNING = $00000002;
225|  NIIF_ERROR = $00000003;
226|
227|  NIN_SELECT = (WM_USER + 0);
228|  NINF_KEY = 1;
229|  NIN_KEYSELECT = (NIN_SELECT or NINF_KEY);
230|
231|  NIN_BALLOONSHOW = WM_USER + 2;
232|  NIN_BALLOONHIDE = WM_USER + 3;
233|  NIN_BALLOONTIMEOUT = WM_USER + 4;
234|  NIN_BALLOONUSERCLICK = WM_USER + 5;
235|
236|{$IFNDEF COMPILER6_UP}
237|
238|function SecondsBetween(const Now: TDateTime; const FTime: TDateTime): Integer;
239|begin
240|  Result := Trunc(86400 * (FTime - Now));
241|end;
242|{$ENDIF COMPILER6_UP}
243|
244|constructor TJvTrayIcon.Create(AOwner: TComponent);
245|begin
246|  inherited Create(AOwner);
247|  FIcon := TIcon.Create;
248|  FIcon.OnChange := IconChanged;
249|  FApplicationVisible := True;
250|  FSnap := False;
251|  FHandle := AllocateHWndEx(WndProc);
252|
253|  FVisibility := [tvVisibleTaskBar, tvVisibleTaskList, tvAutoHide];
254|  FAnimated := False;
255|  FDelay := 100;
256|  FNumber := 0;
257|  FActive := False;
258|  FTask := True;
259|
260|  FBalloonCloser := TTimer.Create(self);
261|  FBalloonCloser.Enabled := false;
262|  FBalloonCloser.OnTimer := OnBalloonCloserTimer;
263|
264|  FDblClickTimer := TTimer.Create(self);  { Vlad S}
265|  FDblClickTimer.Enabled := false;
266|  FDblClickTimer.OnTimer := OnDblClickTimer; { Vlad S}
267|
268|  if not (csDesigning in ComponentState) then
269|  begin
270|    FDllHandle := LoadLibrary('KERNEL32.DLL');
271|    if FDllHandle <> 0 then
272|      @FRegisterServiceProcess := GetProcAddress(FDllHandle, 'RegisterServiceProcess')
273|    else
274|      @FRegisterServiceProcess := nil;
275|  end;
276|
277|  FTaskbarRestartMsg := RegisterWindowMessage('TaskbarCreated');
278|end;
279|
280|destructor TJvTrayIcon.Destroy;
281|begin
282|  UnHook;
283|  if not (csDestroying in Application.ComponentState) then
284|    SetTask(False);
285|
286|  FDblClickTimer.Free; { Vlad S}
287|  FTimer.Free;
288|  SetActive(False);
289|  FBalloonCloser.Enabled := false;
290|  FBalloonCloser.Free;
291|  FIcon.Free;
292|  DeallocateHWndEx(FHandle);
293|
294|  if not (csDesigning in ComponentState) then
295|    if FDllHandle <> 0 then
296|      FreeLibrary(FDllHandle);
297|  inherited Destroy;
298|end;
299|
300|function TJvTrayIcon.AcceptBalloons: Boolean;
301|begin
302|  // Balloons are only accepted with shell32.dll 5.0+
303|  Result := GetShellVersion >= $00050000;
304|end;
305|
306|procedure TJvTrayIcon.SetTask(const Value: Boolean);
307|begin
308|  if FTask <> Value then
309|  begin
310|    FTask := Value;
311|    if not (csDesigning in ComponentState) then
312|      if Assigned(FRegisterServiceProcess) then
313|        if FTask then
314|          FRegisterServiceProcess(GetCurrentProcessID, 0)
315|        else
316|          FRegisterServiceProcess(GetCurrentProcessID, 1);
317|  end;
318|end;
319|
320|procedure TJvTrayIcon.WndProc(var Mesg: TMessage);
321|var
322|  I: Integer;
323|  Pt: TPoint;
324|  ShState: TShiftState;
325|begin
326|  try
327|    with Mesg do
328|    begin
329|      case Msg of
330|        WM_CALLBACKMESSAGE:
331|          begin
332|            GetCursorPos(Pt);
333|            ShState := [];
334|            if GetKeyState(VK_SHIFT) < 0 then Include(ShState, ssShift);
335|            if GetKeyState(VK_CONTROL) < 0 then Include(ShState, ssCtrl);
336|            if GetKeyState(VK_LBUTTON) < 0 then Include(ShState, ssLeft);
337|            if GetKeyState(VK_RBUTTON) < 0 then Include(ShState, ssRight);
338|            if GetKeyState(VK_MBUTTON) < 0 then Include(ShState, ssMiddle);
339|            if GetKeyState(VK_MENU) < 0 then Include(ShState, ssAlt);
340|            case LParam of
341|              WM_MOUSEMOVE:
342|                DoMouseMove(shState, Pt.X, Pt.Y);
343|              WM_LBUTTONDOWN:
344|                DoMouseDown(mbLeft, ShState, Pt.X, Pt.Y);
345|              WM_RBUTTONDOWN:
346|                DoMouseDown(mbRight, ShState, Pt.X, Pt.Y);
347|              WM_MBUTTONDOWN:
348|                DoMouseDown(mbMiddle, ShState, Pt.X, Pt.Y);
349|              WM_LBUTTONUP:
350|                DoMouseUp(mbLeft, ShState, Pt.X, Pt.Y);
351|              WM_MBUTTONUP:
352|                DoMouseUp(mbMiddle, ShState, Pt.X, Pt.Y);
353|              WM_RBUTTONUP, NIN_KEYSELECT: //Mimics previous versions of shell32.dll
354|                DoMouseUp(mbRight, ShState, Pt.X, Pt.Y);
355|              WM_LBUTTONDBLCLK:
356|                DoDoubleClick(mbLeft, ShState, Pt.X, Pt.Y);
357|              WM_RBUTTONDBLCLK:
358|                DoDoubleClick(mbRight, ShState, Pt.X, Pt.Y);
359|              WM_MBUTTONDBLCLK:
360|                DoDoubleClick(mbMiddle, ShState, Pt.X, Pt.Y);
361|              NIN_BALLOONHIDE: //sb
362|                begin
363|                  try
364|                    if Assigned(FOnBalloonHide) then
365|                      FOnBalloonHide(self);
366|                  except
367|                  end;
368|                  Result := Ord(True);
369|                end;
370|              NIN_BALLOONTIMEOUT: //sb
371|                begin
372|                  I := SecondsBetween(Now, FTime);
373|                  if I > FTimeDelay then
374|                    BalloonHint('', '');
375|                  Result := Ord(True);
376|                end;
377|              NIN_BALLOONUSERCLICK: //sb
378|                begin
379|                  try
380|                    if Assigned(FOnBalloonClick) then
381|                      FOnBalloonClick(self);
382|                  except
383|                  end;
384|                  Result := Ord(True);
385|                  //Result := DefWindowProc(FHandle, Msg, wParam, lParam);
386|                  BalloonHint('', '');
387|                end;
388|            end;
389|          end;
390|        // Add by Winston Feng 2003-9-28
391|        // Handle the QueryEndSesstion and TaskbarCreated message, so trayicon
392|        // will be deleted and restored correctly.
393|        WM_QUERYENDSESSION:
394|          Result := 1;
395|        WM_ENDSESSION:
396|          begin
397|            if FActive then
398|            begin
399|              SetActive(False);
400|              FActive := True;
401|            end
402|            else
403|            begin
404|              SetActive(False);
405|              FActive := False;
406|            end;
407|          end;
408|      else
409|        begin
410|          if (Msg = FTaskbarRestartMsg) and (FActive) then
411|            SetActive(True);
412|          Result := DefWindowProc(FHandle, Msg, wParam, lParam);
413|        end;
414|      end; // case
415|    end; // with
416|  except
417|    Application.HandleException(Self);
418|  end;
419|end;
420|
421|procedure TJvTrayIcon.IconChanged(Sender: TObject);
422|begin
423|  //DoCheckCrash;
424|  with FIconData do
425|    hIcon := FIcon.Handle;
426|  if FActive then
427|    NotifyIcon(NIM_MODIFY);
428|end;
429|
430|procedure TJvTrayIcon.SetHint(Value: string);
431|begin
432|  //DoCheckCrash;
433|  //Remove sLineBreak on w98/me as they are not supported
434|  if not AcceptBalloons then
435|    FHint := StringReplace(Value, sLineBreak, ' - ', [rfReplaceAll])
436|  else
437|    FHint := Value;
438|end;
439|
440|procedure TJvTrayIcon.SetIcon(Icon: TIcon);
441|begin
442|  //DoCheckCrash;
443|  FIcon.Assign(Icon);
444|  with FIconData do
445|    hIcon := FIcon.Handle;
446|  if FActive then
447|    NotifyIcon(NIM_MODIFY);
448|end;
449|
450|procedure TJvTrayIcon.SetActive(Value: Boolean);
451|//http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/Shell/Structures/NOTIFYICONDATA.asp
452|begin
453|  if Value then
454|    Hook
455|  else
456|    UnHook;
457|
458|  if Value and ((not (csDesigning in ComponentState)) or (tvVisibleDesign in Visibility)) then
459|  begin
460|    FOldTray := FindWindow('Shell_TrayWnd', nil);
461|
462|    if (FIcon = nil) or FIcon.Empty then
463|    { (rb) This triggers the IconChanged event, note that Active is loaded
464|    before Icon from the stream, thus FIcon is always empty at this
465|    point when loaded from the dfm stream }
466|      FIcon.Assign(Application.Icon);
467|    with FIconData do
468|    begin
469|      if AcceptBalloons then
470|      begin
471|        cbSize := SizeOf(FIconData);
472|        FIconData.uTimeOut := NOTIFYICON_VERSION;
473|      end
474|      else
475|        cbSize := SizeOf(TNotifyIconData);
476|      Wnd := FHandle;
477|      uId := 1;
478|      uCallBackMessage := WM_CALLBACKMESSAGE;
479|      if FIcon <> nil then
480|        hIcon := FIcon.Handle
481|      else
482|        FIcon := Application.Icon;
483|      StrPLCopy(szTip, GetShortHint(FHint), SizeOf(szTip) - 1);
484|      uFlags := NIF_MESSAGE or NIF_ICON or NIF_INFO or NIF_TIP;
485|    end;
486|    if not ((tvAutoHideIcon in Visibility) and (Application.MainForm <> nil) and Application.MainForm.Visible) then
487|      NotifyIcon(NIM_ADD);
488|    if AcceptBalloons then
489|      NotifyIcon(NIM_SETVERSION);
490|  end
491|  else
492|    NotifyIcon(NIM_DELETE);
493|
494|  FActive := Value;
495|end;
496|
497|procedure TJvTrayIcon.SetApplicationVisible(Value: Boolean);
498|begin
499|  FApplicationVisible := Value;
500|  if (csLoading in ComponentState) and not (csDesigning in ComponentState) then
501|    Application.ShowMainForm := FApplicationVisible;
502|  if not (csDesigning in ComponentState) and not (csLoading in ComponentState) then
503|    if Value then
504|      ShowApplication
505|    else
506|      HideApplication;
507|end;
508|
509|procedure TJvTrayIcon.HideApplication;
510|begin
511|  if (Application.MainForm <> nil) and (Application.MainForm.WindowState <> wsMinimized) then
512|  begin
513|    if Snap then
514|    begin
515|      Application.MainForm.Visible := False;
516|      Application.ShowMainForm := False;
517|    end;
518|    Application.Minimize;
519|  end;
520|  ShowWindow(Application.Handle, SW_HIDE);
521|  FApplicationVisible := False;
522|  if (tvAutoHideIcon in Visibility) then
523|    NotifyIcon(NIM_ADD);
524|end;
525|
526|procedure TJvTrayIcon.ShowApplication;
527|begin
528|  ShowWindow(Application.Handle, SW_SHOW);
529|  Application.Restore;
530|  if Application.MainForm <> nil then
531|    Application.MainForm.Visible := True;
532|  if tvAutoHideIcon in Visibility then
533|    NotifyIcon(NIM_DELETE);
534|end;
535|
536|procedure TJvTrayIcon.OnAnimateTimer(Sender: TObject);
537|begin
538|  if (FActive) and (FImgList <> nil) then
539|  begin
540|    if IconIndex < 0 then
541|      IconIndex := 0
542|    else
543|      IconIndex := (IconIndex + 1) mod FimgList.Count;
544|    if Assigned(FOnAnimate) then
545|      FOnAnimate(self, IconIndex);
546|  end;
547|end;
548|
549|procedure TJvTrayIcon.SetAnimated(const Value: Boolean);
550|begin
551|  FAnimated := Value;
552|  if (not (csDesigning in ComponentState)) or (tvVisibleDesign in Visibility) then
553|  begin
554|    if Value then
555|    begin
556|      if FTimer = nil then
557|      begin
558|        FTimer := TTimer.Create(Self);
559|        FTimer.Interval := FDelay;
560|        FTimer.OnTimer := OnAnimateTimer;
561|      end;
562|      FTimer.Enabled := True;
563|    end
564|    else if FTimer <> nil then
565|    begin
566|      FTimer.Free;
567|      FTimer := nil;
568|    end;
569|  end;
570|end;
571|
572|procedure TJvTrayIcon.SetDelay(const Value: Cardinal);
573|begin
574|  FDelay := Value;
575|  if FTimer <> nil then
576|    FTimer.Interval := FDelay;
577|end;
578|
579|procedure TJvTrayIcon.SetImgList(const Value: TImageList);
580|begin
581|  FImgList := Value;
582|end;
583|
584|function TJvTrayIcon.GetSystemMinimumBalloonDelay: integer;
585|begin
586|  // from Microsoft's documentation, a balloon is shown for at
587|  // least 10 seconds, but it is a system settings which must
588|  // be somewhere in the registry. The only question is : Where ?
589|  Result := 10000;
590|end;
591|
592|procedure TjvTrayIcon.OnBalloonCloserTimer(Sender: TObject);
593|begin
594|  // we stop the timer
595|  FBalloonCloser.Enabled := false;
596|  // then we call BalloonHint with title and info set to
597|  // empty strings which surprisingly will cancel any existing
598|  // balloon for the icon. This is clearly not documented by
599|  // microsoft and may not work in later releases of Windows
600|  // it has been tested on XP Home French
601|  BalloonHint('', '');
602|end;
603|
604|procedure TJvTrayIcon.BalloonHint(Title, Value: string;
605|  BalloonType: TBalloonType; Delay: Integer; CancelPrevious: boolean);
606|//http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/Shell/reference/functions/shell_notifyicon.asp
607|begin
608|  if AcceptBalloons then
609|  begin
610|    FTime := Now;
611|    FTimeDelay := Delay div 1000;
612|    FIconData.uFlags := NIF_INFO;
613|
614|    // if we must cancel an existing balloon
615|    if CancelPrevious then
616|    begin
617|      // then we call BalloonHint with title and info set to
618|      // empty strings which surprisingly will cancel any existing
619|      // balloon for the icon. This is clearly not documented by
620|      // microsoft and may not work in later releases of Windows
621|      // it has been tested on XP Home French
622|      BalloonHint('', '');
623|    end;
624|
625|    with FIconData do
626|      StrPLCopy(szInfoTitle, Title, SizeOf(szInfoTitle) - 1);
627|    with FIconData do
628|      StrPLCopy(szInfo, Value, SizeOf(szInfo) - 1);
629|    FIconData.uFlags := NIF_MESSAGE or NIF_ICON or NIF_INFO or NIF_TIP;
630|    FIconData.uTimeOut := Delay;
631|    case BalloonType of
632|      btError:
633|        FIconData.dwInfoFlags := NIIF_ERROR;
634|      btInfo:
635|        FIconData.dwInfoFlags := NIIF_INFO;
636|      btNone:
637|        FIconData.dwInfoFlags := NIIF_NONE;
638|      btWarning:
639|        FIconData.dwInfoFlags := NIIF_WARNING;
640|    end;
641|    NotifyIcon(NIM_MODIFY);
642|
643|    // if the delay is less than the system's minimum and the balloon
644|    // was really shown (title and value are not empty)
645|    if (Delay < GetSystemMinimumBalloonDelay) and
646|      (Title <> '') and
647|      (Value <> '') then
648|    begin
649|      // then we enable the ballon closer timer which will cancel
650|      // the balloon when the delay is elapsed
651|      FBalloonCloser.Interval := Delay;
652|      FBalloonCloser.Enabled := true;
653|    end;
654|
655|    if Assigned(FOnBalloonShow) then
656|      FOnBalloonShow(self);
657|  end;
658|end;
659|
660|procedure TJvTrayIcon.DoCheckCrash;
661|var
662|  HWndTray: HWND;
663|begin
664|  if Active then
665|  begin
666|    HWndTray := FindWindow('Shell_TrayWnd', nil);
667|    if FOldTray <> HWndTray then
668|    begin
669|      FOldTray := HWndTray;
670|      Active := False;
671|      Active := True;
672|    end;
673|  end;
674|end;
675|
676|procedure TJvTrayIcon.DoMouseMove(Shift: TShiftState; X, Y: Integer);
677|begin
678|  if FOldHint <> FHint then
679|  begin
680|    FOldHint := FHint;
681|    with FIconData do
682|      StrPLCopy(szTip, GetShortHint(FHint), SizeOf(szTip) - 1);
683|    FIconData.uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
684|    if FActive then
685|      NotifyIcon(NIM_MODIFY);
686|  end;
687|  if Assigned(FOnMouseMove) then
688|    FOnMouseMove(Self, Shift, X, Y);
689|end;
690|
691|procedure TJvTrayIcon.DoMouseDown(Button: TMouseButton; Shift: TShiftState;
692|  X, Y: Integer);
693|begin
694|  FDblClicked := false;
695|  if Assigned(FOnMouseDown) then
696|    FOnMouseDown(Self, Button, Shift, X, Y);
697|  if (Button = mbLeft) and (FDropDown <> nil) then
698|  begin
699|    SetForegroundWindow(FHandle);
700|    FDropDown.Popup(X, Y);
701|    PostMessage(FHandle, WM_NULL, 0, 0);
702|  end;
703|end;
704|
705|procedure TJvTrayIcon.DoMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
706|var
707|    hasPopup: boolean;
708|begin
709|  hasPopup := false;
710|  if (Button = mbRight) and (FPopupMenu <> nil) then
711|  begin
712|    SetForegroundWindow(FHandle);
713|    FPopupMenu.Popup(X, Y);
714|    PostMessage(FHandle, WM_NULL, 0, 0);
715|    hasPopup := true;
716|  end
717|  else if Button = mbLeft then
718|    if not ApplicationVisible then
719|    begin
720|      if tvRestoreClick in Visibility then
721|        Visibility := Visibility + [tvVisibleTaskBar]
722|    end
723|    else
724|    begin
725|      if tvMinimizeClick in Visibility then
726|        Visibility := Visibility - [tvVisibleTaskBar]
727|    end;
728|
729|  if Assigned(FOnMouseUp) then
730|    FOnMouseUp(Self, Button, Shift, X, Y);
731|  if (not hasPopup) and Assigned(FOnClick) and NOT FDblClicked then
732|  begin
733|    { Vlad S}
734|    //FOnClick(Self, Button, Shift, X, Y);
735|    FClickedButton := Button;
736|    FClickedShift := Shift;
737|    FClickedX  := X;
738|    FClickedY := Y;
739|    FDblClickTimer.Interval := GetDoubleClickTime() + 10;
740|    FDblClickTimer.Enabled := true;
741|  end;
742|end;
743|
744|procedure TJvTrayIcon.OnDblClickTimer(Sender: TObject); { Vlad S}
745|begin
746|    FDblClickTimer.Enabled := false;
747|    // Double-clicking the right mouse button actually generates four messages:
748|    // WM_RBUTTONDOWN, WM_RBUTTONUP, WM_RBUTTONDBLCLK, and WM_RBUTTONUP again
749|    if (NOT FDblClicked) then
750|        FOnClick(Self, FClickedButton, FClickedShift, FClickedX, FClickedY);
751|end;
752|
753|procedure TJvTrayIcon.DoDoubleClick(Button: TMouseButton;
754|  Shift: TShiftState; X, Y: Integer);
755|var
756|  I: Integer;
757|begin
758|  FDblClicked := true; { Vlad S}
759|  FDblClickTimer.Enabled := false; { Vlad S}
760|
761|  if Assigned(FOnDblClick) then
762|    FOnDblClick(Self, Button, Shift, X, Y)
763|  else if Button = mbLeft then
764|  begin
765|    if FPopupMenu <> nil then
766|      for I := 0 to FPopupMenu.Items.Count - 1 do
767|        if FPopupMenu.Items[I].Default then
768|        begin
769|          FPopupMenu.Items[I].Click;
770|          Break;
771|        end;
772|    if ApplicationVisible then
773|    begin
774|      if tvMinimizeDbClick in Visibility then
775|        Visibility := Visibility - [tvVisibleTaskBar];
776|    end
777|    else
778|    begin
779|      if tvRestoreDbClick in Visibility then
780|        Visibility := Visibility + [tvVisibleTaskBar];
781|    end;
782|  end;
783|end;
784|
785|procedure TJvTrayIcon.SetNumber(const Value: Integer);
786|var
787|  Ico: TIcon;
788|begin
789|  FNumber := Value;
790|  if (FImglist <> nil) and (FNumber >= 0) and (FNumber < FImgList.Count) then
791|  begin
792|    Ico := TIcon.Create;
793|    try
794|      FIconData.uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
795|      FImgList.GetIcon(FNumber, Ico);
796|      SetIcon(Ico);
797|    finally
798|      Ico.Free;
799|    end;
800|  end
801|  else
802|    FNumber := -1;
803|end;
804|
805|procedure TJvTrayIcon.SetVisibility(const Value: TTrayVisibilities);
806|begin
807|  FVisibility := Value;
808|  VisibleInTaskList := tvVisibleTaskList in Value;
809|  ApplicationVisible := tvVisibleTaskBar in Value;
810|  SetActive(FActive);
811|  SetAnimated(FAnimated);
812|end;
813|
814|function TJvTrayIcon.ApplicationHook(var Msg: TMessage): Boolean;
815|begin
816|  if (Msg.Msg = WM_SYSCOMMAND) and (Msg.WParam = SC_MINIMIZE) and
817|    (tvAutoHide in Visibility) and Active then
818|    ApplicationVisible := False;
819|  Result := False;
820|end;
821|
822|procedure TJvTrayIcon.Hook;
823|begin
824|  if FHooked or (csDesigning in ComponentState) then
825|    Exit;
826|
827|  FHooked := True;
828|
829|  Application.HookMainWindow(ApplicationHook);
830|end;
831|
832|procedure TJvTrayIcon.UnHook;
833|begin
834|  if not FHooked then
835|    Exit;
836|
837|  FHooked := False;
838|
839|  Application.UnHookMainWindow(ApplicationHook);
840|end;
841|
842|function TJvTrayIcon.NotifyIcon(dwMessage: DWORD): boolean;
843|begin
844|  Result := Shell_NotifyIcon(dwMessage, @FIconData);
845|
846|{  if FActive and (tvAutoHideIcon in Visibility) then
847|  begin
848|    if Application.MainForm.Visible then
849|      Result := Shell_NotifyIcon(NIM_DELETE,@FIconData)
850|    else
851|      Result := Shell_NotifyIcon(NIM_ADD,@FIconData);
852|  end;
853|  }
854|end;
855|
856|end.
857|
858|
Legend:
Added(46)
Deleted(0)
Changed(2)