;;;;;;;
;;;;;  Notify() 0.45                made by gwarble - sept 09
;;;      multiple tray area notifications
;          thanks to Rhys/engunneer/HugoV/Forum Posters
;
;   Notify([Title,Message,Duration,Options,Image])
;
;     Title      [!!!]          "" to omit title line
;     Message      []           "" to omit message line
;     Duration    [15]          seconds to show notification
;                                 0  to flash until clicked
;                                <0  to ExitApp on click/timeout
;                               "-0" for permanent and exitOnClick
;     Options                   string of options... see function
;          [SI=999 GC=Blue...]  most options are remembered (static)
;                               "Reset" to restore default options
;                                 if you want to show as well, use NO=Reset
;                               "Wait" to wait for a notification  ***
;     Image []                  Image file name/library or
;                               number of icon in shell32.dll
;                               Gui Number to "Wait" for          ***
;
;     Return                    Gui Number used                   ***
;                               0 if failed (too many open)
;
Notify(Title="!!!",Message="",Duration=30,Options="",Image="")
{
 Static                ;Options string: "TS=12 GC=Blue..."
 static GF := 50       ;    Gui First Number  ;= override Gui: # used
 static GL := 74       ;    Gui Last Number   ;= between GF and GL
 static GC := "FFFFAA" ;    Gui Color         ;   ie: don't use GF<=Gui#<=GL
 static GR := 9        ;    Gui Radius        ;       elsewhere in your script
 static GT := "Off"    ;    Gui Transparency
 static TS := 8        ;  Title Font Size
 static TW := 625      ;  Title Font Weight
 static TC := "Black"  ;  Title Font Color
 static TF := "Arial"  ;  Title Font Face
 static MS := 8        ;Message Font Size
 static MW := 550      ;Message Font Weight
 static MC := "Black"  ;Message Font Color
 static MF := "Arial"  ;Message Font Face
 static BC := "000000"  ; Border Colors
 static BW := 2        ; Border Width/Thickness
 static BR := 13       ; Border Radius
 static BT := 105      ; Border Transpacency
 static BF := 150      ; Border Flash Speed
 static SI := 350      ;  Speed In (AnimateWindow)
 static SC := 100      ;  Speed Clicked (AnimateWindow)
 static ST := 450      ;  Speed TimeOut (AnimateWindow)
 static IW := 32       ;  Image Width
 static IH := 32       ;  Image Height
 static IN := 0        ;  Image Icon Number (from Image)
 static AX := 0        ; Action X Close Button (maybe add yes/no ok/cancel, etc...)
 local AC, AT          ;Actions Clicked/Timeout
 local _Notify_Action, ImageOptions, GY, OtherY

_Notify_:
 local NO := 0         ;NO is Notify Option [NO=Reset]
 If (Options)
 {
  IfInString, Options, =
  {
   Loop,Parse,Options,%A_Space%
    If (Option:= SubStr(A_LoopField,1,2))
     %Option%:= SubStr(A_LoopField,4)
   If NO = Reset
   {
    Options := "GF=50 GL=74 GC=FFFFAA GR=9 GT=Off "
     . "TS=8 TW=625 TC=Black TF=Arial MS=8 MW=550 MC=Black MF=Arial "
     . "BC=Black BW=2 BR=9 BT=105 BF=150 SC=300 SI=250 ST=100 "
     . "IW=32 IH=32 IN=0"
    Goto, _Notify_
   }
  }
  Else If Options = Wait
   Goto, _Notify_Wait_
 }


 GN := GF
 Loop
  IfNotInString, NotifyList, % "|" GN
   Break
  Else
  If (++GN > GL)
    Return 0              ;=== too many notifications open!
 NotifyList .= "|" GN
 GN2 := GN + GL - GF + 1
 If AC <>
  ActionList .= "|" GN "=" AC

 Prev_DetectHiddenWindows := A_DetectHiddenWindows
 Prev_TitleMatchMode := A_TitleMatchMode
 DetectHiddenWindows On
 SetTitleMatchMode 1
 If (WinExist("NotifyGui"))  ;=== find all Notifications from ALL scripts, for placement
  WinGetPos, OtherX, OtherY  ;=== change this to a loop for all open notifications?
 DetectHiddenWindows %Prev_DetectHiddenWindows%
 SetTitleMatchMode %Prev_TitleMatchMode%

 Gui, %GN%:-Caption +ToolWindow +AlwaysOnTop -Border
 Gui, %GN%:Color, %GC%
 Gui, %GN%:Font, w%TW% s%TS% c%TC%, %TF%
 If (Image)
 {
  If FileExist(Image)
   Gui, %GN%:Add, Picture, w%IW% h%IH% Icon%IN%, % Image
  Else
   Gui, %GN%:Add, Picture, w%IW% h%IH% Icon%Image%, c:\windows\system32\shell32.dll
  ImageOptions = x+10
 }
 If Title <>
  Gui, %GN%:Add, Text, % ImageOptions, % Title
 Gui, %GN%:Font, w%MW% s%MS% c%MC%, %MF%
 If ((Title) && (Message))
  Gui, %GN%:Margin, , -5
 If Message <>
  Gui, %GN%:Add, Text,, % Message
 If ((Title) && (Message))
  Gui, %GN%:Margin, , 8
 Gui, %GN%:Show, Hide, NotifyGui
 Gui  %GN%:+LastFound
 WinGetPos, GX, GY, GW, GH

 If AX =
 {
  GW += 10
  Gui, %GN%:Font, w800 s6 c%MC%
  Gui, %GN%:Add, Text, % "x" GW-11 " y-1 Border Center w12 h12 g_Notify_Kill_" GN - GF + 1, X
 }

 Gui, %GN%:Add, Text, x0 y0 w%GW% h%GH% g_Notify_Action BackgroundTrans
 If (GR)
  WinSet, Region, % "0-0 w" GW " h" GH " R" GR "-" GR
 If (GT)
  WinSet, Transparent, % GT

 SysGet, Workspace, MonitorWorkArea
 NewX := WorkSpaceRight-GW-5
 If (OtherY)
  NewY := OtherY-GH-5
 Else
  NewY := WorkspaceBottom-GH-5
 If NewY < % WorkspaceTop
  NewY := WorkspaceBottom-GH-5

 Gui, %GN2%:-Caption +ToolWindow +AlwaysOnTop -Border +E0x20
 Gui, %GN2%:Color, %BC%
 Gui  %GN2%:+LastFound
 If (BR)
  WinSet, Region, % "0-0 w" GW+(BW*2) " h" GH+(BW*2) " R" BR "-" BR
 If (BT)
  WinSet, Transparent, % BT

 Gui, %GN2%:Show, % "Hide x" NewX-BW " y" NewY-BW " w" GW+(BW*2) " h" GH+(BW*2)
 Gui, %GN%:Show,  % "Hide x" NewX " y" NewY " w" GW, NotifyGui
 Gui  %GN%:+LastFound
 If SI
  DllCall("AnimateWindow","UInt",WinExist(),"Int",SI,"UInt","0x00040008")
 Else
  Gui, %GN%:Show, NA, NotifyGui
 Gui, %GN2%:Show, NA
 WinSet, AlwaysOnTop, On

 If ((Duration < 0) OR (Duration = "-0"))
	Return % GN
 If (Duration)
   SetTimer, % "_Notify_Kill_" GN - GF + 1, % - Abs(Duration) * 1000
 Else
  SetTimer, % "_Notify_Flash_" GN - GF + 1, % BF

Return % GN

;==========================================================================
;========================================== when a notification is clicked:
_Notify_Action:
 ;Critical
 SetTimer, % "_Notify_Kill_" A_Gui - GF + 1, Off
 Gui, % A_Gui + GL - GF + 1 ":Destroy"
 Gui  %A_Gui%:+LastFound
 If SC
  DllCall("AnimateWindow","UInt",WinExist(),"Int",SC,"UInt", "0x00050001")
 Gui, %A_Gui%:Destroy
 If (ActionList)
  Loop,Parse,ActionList,|
   If ((Action := SubStr(A_LoopField,1,2)) = A_Gui)
   {
    Temp_Notify_Action:= SubStr(A_LoopField,4)
    StringReplace, ActionList, ActionList, % "|" A_Gui "=" Temp_Notify_Action, , All
    If IsLabel(_Notify_Action := Temp_Notify_Action)
     Gosub, %_Notify_Action%
    _Notify_Action =
    Break
   }
 StringReplace, NotifyList, NotifyList, % "|" GN, , All
 SetTimer, % "_Notify_Flash_" A_Gui - GF + 1, Off
 If (Exit = A_Gui)
  ExitApp
Return

;==========================================================================
;=========================================== when a notification times out:
_Notify_Kill_1:
_Notify_Kill_2:
_Notify_Kill_3:
_Notify_Kill_4:
_Notify_Kill_5:
_Notify_Kill_6:
_Notify_Kill_7:
_Notify_Kill_8:
_Notify_Kill_9:
_Notify_Kill_10:
_Notify_Kill_11:
_Notify_Kill_12:
_Notify_Kill_13:
_Notify_Kill_14:
_Notify_Kill_15:
_Notify_Kill_16:
_Notify_Kill_17:
_Notify_Kill_18:
_Notify_Kill_19:
_Notify_Kill_20:
_Notify_Kill_21:
_Notify_Kill_22:
_Notify_Kill_23:
_Notify_Kill_24:
_Notify_Kill_25:
 ;Critical
 StringReplace, GK, A_ThisLabel, _Notify_Kill_
 SetTimer, _Notify_Flash_%GK%, Off
 GK += GF - 1
 Gui, % GK + GL - GF + 1 ":Destroy"
 Gui  %GK%:+LastFound
 If ST
  DllCall("AnimateWindow","UInt",WinExist(),"Int",ST,"UInt", "0x00050001")
 Gui, %GK%:Destroy
 StringReplace, NotifyList, NotifyList, % "|" GK
 If (Exit = GK)
  ExitApp
Return

;==========================================================================
;======================================== flashes a permanent notification:
_Notify_Flash_1:
_Notify_Flash_2:
_Notify_Flash_3:
_Notify_Flash_4:
_Notify_Flash_5:
_Notify_Flash_6:
_Notify_Flash_7:
_Notify_Flash_8:
_Notify_Flash_9:
_Notify_Flash_10:
_Notify_Flash_11:
_Notify_Flash_12:
_Notify_Flash_13:
_Notify_Flash_14:
_Notify_Flash_15:
_Notify_Flash_16:
_Notify_Flash_17:
_Notify_Flash_18:
_Notify_Flash_19:
_Notify_Flash_20:
_Notify_Flash_21:
_Notify_Flash_22:
_Notify_Flash_23:
_Notify_Flash_24:
_Notify_Flash_25:
 StringReplace, FlashGN, A_ThisLabel, _Notify_Flash_
 FlashGN += GF - 1
 FlashGN2 := FlashGN + GL - GF + 1
 If Flashed%FlashGN2% := !Flashed%FlashGN2%
  Gui, %FlashGN2%:Color, Silver
 Else
  Gui, %FlashGN2%:Color, % BC
Return

;==========================================================================
;============================= wait for (or force) a notification to close:
_Notify_Wait_:
 ;Critical
 If (Image)
 {
  Gui %Image%:+LastFound
  If NotifyGuiID := WinExist()
  {
   WinWaitClose, , , % Abs(Duration)
   If (ErrorLevel && Duration < 1)
   {
    Gui, % Image + GL - GF + 1 ":Destroy"
    DllCall("AnimateWindow","UInt",NotifyGuiID,"Int",ST,"UInt","0x00050001")
    Gui, %Image%:Destroy
   }
  }
 }
 Else
 Loop, % GL-GF
 {
  Image := GL - (A_Index) ;+ GF - 1)
  Gui %Image%:+LastFound
  If NotifyGuiID := WinExist()
  {
   ;WinWaitClose, , , % Abs(Duration)
   ;If (ErrorLevel && Duration < 1)
   ;{
    Gui, % Image + GL - GF + 1 ":Destroy"
    DllCall("AnimateWindow","UInt",NotifyGuiID,"Int",ST,"UInt","0x00050001")
    Gui, %Image%:Destroy
   ;}
  }
 }
Return
}