*--------------------------------------------------------------------------------------------------------------------------------------------------------
* (EN) AUTOGENERATED - ATTENTION!! - NOT INTENDED FOR EXECUTION!! USE ONLY FOR MERGING CHANGES AND STORING WITH SCM TOOLS!!
*--------------------------------------------------------------------------------------------------------------------------------------------------------
*< FOXBIN2PRG: Version="1.21" SourceFile="systray.vcx" CPID="1252" /> (Solo para binarios VFP 9 / Only for VFP 9 binaries)
*
*
DEFINE CLASS systray AS hyperlink 
 	*< CLASSDATA: Baseclass="hyperlink" Timestamp="" Scale="Pixels" Uniqueid="" />

	*<DefinedPropArrayMethod>
		*m: addicontoiconlist		&& Adds an icon file to list of icons.  Accepts a string parameter containing a filename.  Call the SwitchIcon method to switch to the next icon.
		*m: addicontosystray		&& Adds the icon to the Taskbar Notification Area ("systray").
		*m: balloonclickevent		&& Occurs when user clicks on balloon tip.
		*m: balloonhideevent		&& Occurs if the user clicks on the close button of the balloon tip.
		*m: balloonshowevent		&& Occurs when the BalloonTip is shown.  Balloon tips are queued by the system, so you balloon tip may not be displayed immediately after calling the DisplayBallloonTip method.
		*m: balloontimeoutevent		&& Occurs if the system closes the balloon tip due to reaching the timeout.
		*m: cleariconlist		&& Clear all icons from the current list.  If an icon is currently displayed in the taskbar notification area (systray), it is removed.
		*m: currenticonindex_assign
		*m: getshellversion		&& Determines the Major and Minor version of shell32.dll.  Used to determine if the shell supports balloon tips on taskbar icons.
		*m: gettaskbarposition		&& Retrieves coordinates of TaskBar.  Also returns 0 if docked on bottom, 1 if docked left, 2 if docked top, 3 if docked right.
		*m: iconclickevent		&& Occurs when the user clicks on the icon in the Taskbar Notification Area ("systray").
		*m: icondblclickevent		&& Occurs when the user double-clicks on the icon in the Taskbar Notification Area.
		*m: iconfile_assign
		*m: iconmiddleclickevent		&& Occurs when the user clicks the icon using the middle mouse button.
		*m: iconrightclickevent		&& Occurs when the user right-clicks on the icon in the Taskbar Notification Area ("systray").
		*m: integertostring		&& Converts an integer to its binary representation.
		*m: loadicon		&& Loads an Icon from a file, and returns an hIcon token.
		*m: processmenuevent		&& This event is called if user has selected an item from a menu defined by the MenuText property.  Add code here to act upon the user's selection.
		*m: receiveiconevent		&& Receives the events the Notify Icon.
		*m: removeiconfromsystray		&& Removes the icon from the Taskbar Notification Area ("systray").
		*m: setforegroundwindow		&& By default, brings the form that contains this object to the foreground.  You can also  specify a different window to bring forward by passing a numeric hWnd parameter.
		*m: showballoontip		&& Displays a balloon tip over the icon in the Taskbar Notification Area.  Parameters: cBalloonText [, cBalloonTitle [, nIcon [, nTimeoutInSeconds]]]
		*m: showmenu		&& Displays the menu defined by the MenuText property, or runs an MPR that defines a shortcut menu.  Accepts string parameter containing the filename of the MPR to run, and optional second parameter to be passed to the MPR startup code.
		*m: stringtointeger		&& Convert binary representation of integer into an integer data type.
		*m: switchicon		&& Changes the displayed icon to the next icon in the icon list.  Use AddIconToIconList method to populate the icon list, and ClearIconList to start over.
		*m: tiptext_assign
		*p: addicontosystrayatinit		&& If .T., the icon will be added to the Taskbar Notification Area ("systray") when this class is instantiated.  If .F., you must call the AddIconToSystray() method.
		*p: crightclickproc		&& Numele procedurii care se apeleaza la click dreapta pe iconita din tray.
		*p: currenticonindex		&& Determines which of the currently loaded icons is displayed in the Taskbar Notification Area ("systray").
		*p: enabled		&& Is .T. when we have an icon in the Taskbar Notification Area ("systray").
		*p: iconcount
		*p: iconfile		&& If only one icon is used, it can be specified here.  If multiple icons are to be used (animation), use the AddIconToIconList method.
		*p: iconidentifier		&& Used internally to store a unique id for our icon.  This way, multiple instances of this class can be used without interfering with each other.
		*p: menuoffsetfromright		&& Essentially the width of your menu in pixels.  This is an offset used when the TaskBar is docked on the right, to prevent the menu from being drawn underneath the taskbar.
		*p: menutext		&& Defines a menu to be displayed when user clicks on icon.  Specify a numeric ID and text for each item, separating each by a semicolon.  (e.g.  1;Activate; 2; Exit).  The ID is passed to the ProcessMenuEvent method after the user has made a menu selecti
		*p: menutextismpr		&& Set to true if the MenuText property contains the name of a MPR to run. The MPR must define a shortcut menu.  To pass parameters to the MPR, call ShowMenu method from the IconClick event.
		*p: nprevmwaccrual		&& Previous setting of SYS(2060).
		*p: shellversion		&& Contains the numeric version of the Shell communication method that we've established.
		*p: systrayiconid		&& Contains Unique ID for the systray token that this object creates.
		*p: tiptext		&& Text to be displayed in standard tooltip above the Taskbar Notification Area ("systray").
		*a: aiconlist[1,0]		&& Array containnig Icons and handles.
		*p: _memberdata		&& XML Metadata for customizable properties
	*</DefinedPropArrayMethod>

	HIDDEN aiconlist,enabled,GoBack,GoForward,iconcount,iconidentifier,NavigateTo,nprevmwaccrual
	*<PropValue>
		addicontosystrayatinit = .T.
		crightclickproc = 
		currenticonindex = 0
		Height = 23
		iconcount = 0
		iconfile =  
		iconidentifier = 0
		menuoffsetfromright = 200
		menutext = 
		Name = "systray"
		nprevmwaccrual = 0
		shellversion = 0
		systrayiconid = 0
		tiptext =  
		Width = 23
		_memberdata = <VFPData>

		</VFPData>
	*</PropValue>
	
	PROCEDURE addicontoiconlist		&& Adds an icon file to list of icons.  Accepts a string parameter containing a filename.  Call the SwitchIcon method to switch to the next icon.
		LPARAMETERS cIconFileName
		
		IF !FILE(cIconFileName)
			RETURN .f.
		ENDIF
		
		* We must load the icon as a Windows resource.
		nHandle = THIS.LoadIcon(m.cIconFileName)
		IF m.nHandle = 0
			RETURN .f.
		ENDIF
		
		
		* We have a resource handle for the icon. Now we store that
		* handle (integer) in our array of icon resource handles.
		WITH THIS
			.IconCount = .IconCount + 1
			DIMENSION .aIconList[.IconCount]
			.aIconList[.IconCount] = m.nHandle
			IF .CurrentIconIndex = 0
				.CurrentIconIndex = 1	&& Updates Systray immediately
			ENDIF
		ENDWITH
		
		RETURN .T.
		
		
		
		
		
	ENDPROC

	PROCEDURE addicontosystray		&& Adds the icon to the Taskbar Notification Area ("systray").
		*!*	This is where the magic happens.
		*!*	This is an unabashed hack to allow us to get the full functionality
		*!*	of the Taskbar Notification Area ("System Tray") without having to
		*!*	use an external C++ library.
		*!*	To communicate with the systray, we tell it to send us messages via
		*!*	the MouseWheel event. (This is the only VFP event that doesn't alter
		*!*	or discard event data before firing the corresponding internal event.)
		*!*	The second trick is that only the main VFP window will accept the events
		*!*	without checking to see if the event coordinates are invalid. (The screen
		*!* doesn't even need to be visible, so you can have SCREEN=OFF in your 
		*!* config.fpw file.)  So we use VFP8's BINDEVENT() function to bind to the 
		*!* _SCREEN.MouseWheel event.
		*!*	This method sets up that communication path.
		*!*	Returns 1 if successful.
		
		
		LOCAL lcNotifyIconData
		
		** NOTIFYICON struct defines
		#define NIF_MESSAGE     0x00000001
		#define NIF_ICON        0x00000002
		#define NIF_TIP         0x00000004
		#define NIF_STATE       0x00000008  && Win2k and later.
		#define NIF_INFO        0x00000010	&& Use balloon tip.  Win2k and later.
		
		
		&& Notify Icon Infotip flags
		#define NIIF_NONE       0x00000000
		&& icon flags are mutually exclusive
		&& and take only the lowest 2 bits
		#define NIIF_INFO       0x00000001
		#define NIIF_WARNING    0x00000002
		#define NIIF_ERROR      0x00000003
		#define NIIF_ICON_MASK  0x0000000F
		#define NIIF_NOSOUND    0x00000010	&& Windows XP and later.
		
		#define NIM_ADD         0x00000000
		#define NIM_MODIFY      0x00000001
		#define NIM_DELETE      0x00000002
		#define NIM_SETFOCUS    0x00000003	&& Windows 2000 and later.
		#define NIM_SETVERSION  0x00000004	&& Windows 2000 and later.
		
		#define NOTIFYICON_VERSION 3
		#define NIS_HIDDEN              0x00000001
		#define NIS_SHAREDICON          0x00000002
		
		
		WITH THIS
		
		*  If no icons loaded, do nothing.
		IF .IconCount < 1 OR .CurrentIconIndex = 0
			RETURN 0
		ENDIF
		
		
		IF NOT .Enabled
			*  Each SystemTray icon in this process requires a unique ID. We'll
			*  use an _screen property to make sure each instance of this class
			*  gets its own ID.  Properties on _screen aren't affected by CLEAR ALL. 
			*  Because VFP drops the least significant two bytes of the mousewheel event's
			*  WPARAM parameter, our unique SysTray Icon ID must use the most significant 
			*  two bytes:
			#DEFINE MIN_SYSTRAY_ICON_ID	0x4000
			IF TYPE("_screen.nSysTrayCount") == "U"
				*  We're the first one here.
				_screen.AddProperty("nSysTrayCount", 0)	
			ELSE
				IF _Screen.nSysTrayCount > 0x3FFF
					* User is apparently creating and destroying this object repeatedly.
					_Screen.nSysTrayCount = 0	
				ENDIF
			ENDIF
			_screen.nSysTrayCount = _screen.nSysTrayCount + 1
			this.SystrayIconID = _screen.nSysTrayCount + MIN_SYSTRAY_ICON_ID
		
		
			*  By default, VFP combines MouseWheel events. (That is, if we receive
			*  a MouseWheel event from the OS, we check our internal queue to see
			*  if there is already a MouseWheel event that hasn't been processed yet.
			*  If found, we just add the number of MouseWheel turns to the existing
			*  event.)  This behavior makes our communication with the SystemTray
			*  unstable, so we must disable this combining of events.  SYS(2060)
			*  accomplishes this:
			THIS.nPrevMWAccrual = VAL(SYS(2060))
			SYS(2060, 1)
		ENDIF
		
		
		
		* Declare the WinAPI function that lets us install the icon.
		* We redeclare the function every time, just in case CLEAR DLLS is called
		* elsewhere in the app. 	
		DECLARE INTEGER Shell_NotifyIcon IN shell32.dll AS WinAPI_Shell_NotifyIcon ;
			INTEGER dwMessage, ;
			STRING @ PNOTIFYICONDATA 
		
		IF THIS.ShellVersion = 0	&& If we haven't set the version yet.
			nTipTextMaxLength = 63	&& Default to version 4.
		ELSE
			nTipTextMaxLength = 127
		ENDIF
		
		
		*  Build NOTIFYICONDATA structure.
		lcNotifyIconData = .IntegerToString(_VFP.Hwnd)	&& Messages get sent to VFP's main window.
		lcNotifyIconData = lcNotifyIconData + .IntegerToString(.SystrayIconID * 0x10000)	
		lcNotifyIconData = lcNotifyIconData + .IntegerToString(BITOR(NIF_TIP,NIF_MESSAGE ,NIF_ICON))	&&NIF_TIP,NIF_INFO,NIF_STATE
		lcNotifyIconData = lcNotifyIconData + .IntegerToString(0x20A)	&& uCallback
		lcNotifyIconData = lcNotifyIconData + .IntegerToString(.aIconList[.CurrentIconIndex])	&& icon handle
		lcNotifyIconData = lcNotifyIconData + PADR(LEFT(TRANSFORM(.TipText), nTipTextMaxLength ), nTipTextMaxLength  + 1, CHR(0))	&& TipText
		IF .ShellVersion >= 5
			lcNotifyIconData = lcNotifyIconData + .IntegerToString(0)	&& dwState
			lcNotifyIconData = lcNotifyIconData + .IntegerToString(0)	&& dwStateMask
			lcNotifyIconData = lcNotifyIconData + REPLICATE(CHR(0),256)	&& balloon tip.
			lcNotifyIconData = lcNotifyIconData + .IntegerToSTring(NOTIFYICON_VERSION) && Timeout/Version
			lcNotifyIconData = lcNotifyIconData + REPLICATE(CHR(0),64)	&& balloon tip title.
			lcNotifyIconData = lcNotifyIconData + .IntegerToSTring(BITAND(0, 0x1F)) && Info flags
		ENDIF
		
		lcNotifyIconData = .IntegerToString(LEN(lcNotifyIconData) + 4) + lcNotifyIconData	&& length of structure
		
		IF NOT .Enabled		&& If not already in Taskbar Notification Area....
		
			*  Add icon to Taskbar Notification Area.
			nReturn = WinAPI_Shell_NotifyIcon( NIM_ADD, @lcNotifyIconData)
			IF nReturn <> 1
				*  Adding item to tray failed!!!
				IF _Screen.nSysTrayCount = 1
					* There has not been two simultaneous instances of this object
					* in the current session.  We can clean up after ourselves.
					_Screen.nSysTrayCount = 0
					SYS(2060, .nPrevMWAccrual)	&& Reset mousewheel behavior.
				ENDIF
				RETURN nReturn
			ENDIF
			*  Bind to the MouseWheel event on VFP's main window.
			nReturn = BINDEVENT(_screen,"MouseWheel",this,"receiveIconEvent",2)
			.Enabled = .t.
			
			* We've just added the icon.  Now we see if we can set to Version 5 shell behavior,
			* with larger tooltip lengths and support for balloon Tips.
			IF  .GetShellVersion() >= 5
				* This OS supports version 5 features.  Switch to version 5.
				
				* We have to inform the Shell that we want to use Version 5 features.
				* Build a version 5 structure for sending message to change version.
				lcNotifyIconData = .IntegerToString(_vfp.hwnd)
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(.SystrayIconID * 0x10000)	
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(BITOR(NIF_TIP,NIF_MESSAGE ,NIF_ICON))	&&NIF_TIP,NIF_INFO,NIF_STATE
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(0x20A)	&& uCallback
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(.aIconList[.CurrentIconIndex])	&& icon handle
				lcNotifyIconData = lcNotifyIconData + PADR(LEFT(TRANSFORM(.TipText), 127), 128, CHR(0))	&& TipText
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(0)	&& dwState
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(0)	&& dwStateMask
				lcNotifyIconData = lcNotifyIconData + REPLICATE(CHR(0),256)	&& balloon tip.
				lcNotifyIconData = lcNotifyIconData + .IntegerToSTring(NOTIFYICON_VERSION) && Timeout/Version
				lcNotifyIconData = lcNotifyIconData + REPLICATE(CHR(0),64)	&& balloon tip title.
				lcNotifyIconData = lcNotifyIconData + .IntegerToSTring(BITAND(0, 0x1F)) && Info flags
				lcNotifyIconData = .IntegerToString(LEN(lcNotifyIconData) + 4) + lcNotifyIconData	&& length of structure
				
				*  First we check to see if we can switch to using version 5 events.
				nReturn = WinAPI_Shell_NotifyIcon( NIM_SETVERSION, @lcNotifyIconData)
				IF nReturn <> 1
					*  Couldn't switch to version 5 events
					RETURN -1
				ENDIF
		
				.ShellVersion = 5
		
				*  Now that we've updated to Version 5, we have to modify the existing icon
				*  if the .TipText is longer than 63 characters.
				IF LEN(.TipText) > 63
					nReturn = WinAPI_Shell_NotifyIcon( NIM_MODIFY, @lcNotifyIconData)
					IF nReturn <> 1
						RETURN nReturn
					ENDIF
				ENDIF
			ELSE
				.ShellVersion = 4
			ENDIF
		
		ELSE
			*  Already in the Taskbar Notification Area.  Just update existing icon.
			nReturn = WinAPI_Shell_NotifyIcon( NIM_MODIFY, @lcNotifyIconData)
			IF nReturn <> 1
				MESSAGEBOX("		*  Modifying item in tray failed!!!")
				RETURN nReturn
			ENDIF
		ENDIF
		
		ENDWITH
		
		RETURN nReturn	
		
	ENDPROC

	PROCEDURE balloonclickevent		&& Occurs when user clicks on balloon tip.
	ENDPROC

	PROCEDURE balloonhideevent		&& Occurs if the user clicks on the close button of the balloon tip.
	ENDPROC

	PROCEDURE balloonshowevent		&& Occurs when the BalloonTip is shown.  Balloon tips are queued by the system, so you balloon tip may not be displayed immediately after calling the DisplayBallloonTip method.
	ENDPROC

	PROCEDURE balloontimeoutevent		&& Occurs if the system closes the balloon tip due to reaching the timeout.
	ENDPROC

	PROCEDURE cleariconlist		&& Clear all icons from the current list.  If an icon is currently displayed in the taskbar notification area (systray), it is removed.
		*  Removes all icons from the icon list.
		
		*  In detail:
		*  Releases the resource handles of all icons that have been loaded,
		*  clears the array and sets the count to zero.  This method is called
		*  by the Destroy event, to make sure all system resources are correctly
		*  released.
		
		WITH this
			IF .IconCount = 0
				* No icons in list
				RETURN
			ENDIF
			
			*  Have to release all icon resources
			DECLARE INTEGER DestroyIcon IN user32.dll AS WinAPI_DestroyIcon ;
				INTEGER hIcon
				
			FOR nCurrentIcon = 1 TO ALEN(.aIconList,1)
				WinAPI_DestroyIcon(.aIconList[m.nCurrentIcon])
			ENDFOR
			
			DIMENSION .aIconList[1]
			.IconCount = 0
			.CurrentIconIndex = 0
		ENDWITH
		
		
		
	ENDPROC

	PROCEDURE currenticonindex_assign
		LPARAMETERS vNewVal
		*  This is called when user changes the CurrentIconIndex property.
		
		THIS.currenticonindex = m.vNewVal
		IF this.Enabled
			* We're attached to systray.  Update immediately.
			this.AddIconToSystray
		ENDIF
		
	ENDPROC

	PROCEDURE Destroy
		WITH THIS
		IF .Enabled
			.RemoveIconFromSystray()
			.ClearIconList()	&& Release resources.
		ENDIF
		
		ENDWITH
		
		
	ENDPROC

	PROCEDURE getshellversion		&& Determines the Major and Minor version of shell32.dll.  Used to determine if the shell supports balloon tips on taskbar icons.
		DECLARE INTEGER DllGetVersion IN shell32.dll AS WinAPI_Shell32_DllGetVersion ;
			STRING @ DLLVERSIONINFO
		
		* Windows 98 typically returns 4.72, depending on version of IE installed.
		* Windows ME returns 5.5 or greater.
		* Windows 2000 returns 5.0
		* Windows XP returns 6.0
		
		LOCAL lcDLLVersionInfo, lnMajor, lnMinor
		
		WITH THIS
		lcDLLVersionInfo = .IntegerToString(20)
		lcDLLVersionInfo = m.lcDLLVersionInfo + REPLICATE(CHR(0), 16)
		
		WinAPI_Shell32_DllGetVersion(@lcDLLVersionInfo)
		
		lnMajor = .StringToInteger(SUBSTR(m.lcDLLVersionInfo, 5, 4))
		lnMinor = .StringToInteger(SUBSTR(m.lcDLLVersionInfo, 9, 4))
		
		RETURN VAL(STR(m.lnMajor, 2, 0) + "." + ALLTRIM(STR(m.lnMinor,3,0)))
		ENDWITH
		
	ENDPROC

	PROCEDURE gettaskbarposition		&& Retrieves coordinates of TaskBar.  Also returns 0 if docked on bottom, 1 if docked left, 2 if docked top, 3 if docked right.
		LPARAMETERS nLeft, nTop, nRight, nBottom
		
		* We position any menus near where the mouse just clicked. But we need to
		* fine-tune that position depending on which edge of the screen the 
		* user has docked the taskbar.  This method retrieves the absolute
		* coordinates of the taskbar, and derives from those coordinates which
		* edge the taskbar is on.
		
		#DEFINE ABM_GETTASKBARPOS 0x00000005
		
		DECLARE INTEGER SHAppBarMessage IN shell32.dll as WinAPI_SHAppBarMessage ;
			INTEGER dwMessage, STRING @ PAPPBARDATA
		
		LOCAL cAPPBARDATA, nReturn
		
		WITH THIS
			cAPPBARDATA = .IntegerToString(0)	&& hWnd
			cAPPBARDATA = cAPPBARDATA + .IntegerToString(0)	&& uCallBackMsg
			cAPPBARDATA = cAPPBARDATA + .IntegerToString(0)	&& Edge
			cAPPBARDATA = cAPPBARDATA + REPLICATE(CHR(0), 4*4)	&& RECT structure
			cAPPBARDATA = cAPPBARDATA + .IntegerToString(0)	&& lParam
			cAPPBARDATA = .IntegerToString(LEN(cAPPBARDATA) + 4) + cAPPBARDATA
		
			nReturn = WinAPI_SHAppBarMessage(ABM_GETTASKBARPOS, @cAPPBARDATA)
		
			*  Taskbar coordinates should be in RECT structure.
			nLeft = .StringToInteger(SUBSTR(cAPPBARDATA, 17, 4))
			IF nLeft > 0xFFFFFF	&& If the number is ridiculously large, assume it was a negative number.
				nLeft = 0 - (0xFFFFFFFF - nLeft)
			ENDIF
			
			nTop = .StringToInteger(SUBSTR(cAPPBARDATA, 21, 4))
			IF nTop > 0xFFFFFF
				nTop = 0 - (0xFFFFFFFF - nTop)
			ENDIF
			
			nRight = .StringToInteger(SUBSTR(cAPPBARDATA, 25, 4))
			IF nRight > 0xFFFFFF
				nRight = 0 - (0xFFFFFFFF - nRight)
			ENDIF
			
			nBottom = .StringToInteger(SUBSTR(cAPPBARDATA, 29, 4))
			IF nBottom > 0xFFFFFF
				nBottom = 0 - (0xFFFFFFFF - nBottom)
			ENDIF
			
		ENDWITH
		
		DO CASE
		*  Must use approximations for taskbar coordinates, because they oare slightly different
		*  on different versions of Windows.
		CASE nLeft < 2 AND nTop < 2 AND nRight < 479 && Roughly minimum width in portrait mode.
			RETURN 1	&& Left
		CASE nLeft < 2 AND nTop > 0
			RETURN 0	&& Bottom
		CASE nLeft < 2 AND nTop < 2	&& Must be top, otherwise first case would take it.
			RETURN 2	&& Top
		OTHERWISE 
			RETURN 3	&& Right
		ENDCASE
		
		
		*!*	typedef struct _AppBarData {
		*!*	    DWORD cbSize;
		*!*	    HWND hWnd;
		*!*	    UINT uCallbackMessage;
		*!*	    UINT uEdge;
		*!*	    RECT rc;
		*!*	    LPARAM lParam;
		*!*	} APPBARDATA, *PAPPBARDATA
		
		*!*	typedef struct _RECT {
		*!*	    LONG    left;
		*!*	    LONG    top;
		*!*	    LONG    right;
		*!*	    LONG    bottom;
		*!*	} RECT
				
		
	ENDPROC

	PROCEDURE iconclickevent		&& Occurs when the user clicks on the icon in the Taskbar Notification Area ("systray").
		* This "event" is called when the user left-clicks on the icon.
		
		* Default behavior is to display the menu:
		If Not Isblank(This.MenuText)
			This.ShowMenu()	&& The ShowMenu method will handle any errors.
		Endif
		
		
	ENDPROC

	PROCEDURE icondblclickevent		&& Occurs when the user double-clicks on the icon in the Taskbar Notification Area.
		_Screen.WindowState = 0
		_Screen.Show()
		*!*	modificare ROASTART v 2.0.29
		This.SetForegroundWindow(_Screen.HWnd)
		If Type('_Screen.nHeight') = 'N'
			_Screen.Height = _Screen.nHeight
			_Screen.Width = _Screen.nWidth
		Endif
		_Screen.AutoCenter = .T.
		*!*	modificare ROASTART v 2.0.29	^
	ENDPROC

	HIDDEN PROCEDURE iconfile_assign
		LPARAMETERS vNewVal
		*To do: Modify this routine for the Assign method
		THIS.ICONFILE = m.vNewVal
		
		* Updating this property will release all other icons.
		THIS.ClearIconList()
		THIS.AddIconToIconList(m.vNewVal)	&& Going from 0 to 1 icon updates systray immediately.
		
	ENDPROC

	PROCEDURE iconmiddleclickevent		&& Occurs when the user clicks the icon using the middle mouse button.
	ENDPROC

	PROCEDURE iconrightclickevent		&& Occurs when the user right-clicks on the icon in the Taskbar Notification Area ("systray").
		* This "event" is called when the user right-clicks on the icon.
		
		* Default behavior is to display the menu:
		If Not Isblank(This.MenuText)
			This.ShowMenu()	&& The ShowMenu method will handle any errors.
		*!*	modificare	02.05.2012
		Else
			If Not Isblank(This.crightclickproc)
				Local lcPoint,lnReturn,lnXCoord,lnYCoord
				Declare Integer GetCursorPos In user32.Dll As WinAPI_GetCursorPos String @
				lcPoint = Replicate(Chr(0), 8)
				lnReturn = WinAPI_GetCursorPos(@lcPoint)
				lnXCoord  = This.StringToInteger(Left(m.lcPoint,4))
				lnYCoord = This.StringToInteger(Substr(m.lcPoint, 5))
				Do (This.crightclickproc) With lnXCoord,lnYCoord
				Release lcPoint,lnReturn,lnXCoord,lnYCoord
			Endif
		*!*	modificare	02.05.2012	^
		Endif
	ENDPROC

	PROCEDURE Init
		*  If there is an initial icon specified, add it to the Icon List.
		IF TYPE("THIS.IconFile") = "C" AND LEN(THIS.IconFile) > 0
			THIS.AddIconToIconList(THIS.IconFile)
		ENDIF
		
		
		*  Add icon to systray if requested.
		IF this.AddIconToSystrayAtInit
			this.AddIconToSystray
		ENDIF
		
	ENDPROC

	HIDDEN PROCEDURE integertostring		&& Converts an integer to its binary representation.
		LPARAMETERS nInteger
			* Changes an integer into the Intel representation of that integer.
			* Used to build structures.
			RETURN CHR(BITAND(nInteger, 255)) + CHR(BITAND(BITRSHIFT(nInteger, 8), 255)) + ;
					CHR(BITAND(BITRSHIFT(nInteger, 16), 255)) + CHR(BITAND(BITRSHIFT(nInteger, 24), 255))
		
		
	ENDPROC

	HIDDEN PROCEDURE loadicon		&& Loads an Icon from a file, and returns an hIcon token.
		LPARAMETERS cIconFileName
		*  Loads an icon as a Windows resource. Returns the resource handle.
		
		LOCAL cOldTalk, cTempFileName, hImage, llFisierTemp
		
		DECLARE INTEGER LoadImage IN user32.dll AS WinAPI_LoadImage ;
			INTEGER hinst, ;
		    STRING lpszName, ;
		    INTEGER uType, ; 	&& uint
		    INTEGER cxDesired, ;
		    INTEGER cyDesired, ;
		    INTEGER fuLoad	&& uint
		
		
		** LoadImage defines ****
		#define IMAGE_ICON          1
		#define LR_LOADFROMFILE     0x0010
		
		*  Assume that the icon is contained in the app/exe file.  Copy to temp before loading.
		*!*	modificare	ROASTART v 2.1.6
		*!*	IF FILE(cIconFileName)
		*!*		cTempFileName = ADDBS(GETENV("temp")) + "systray_temp_" + SYS(3) + ".icn"
		*!*		COPY FILE (cIconFileName) TO (cTempFileName)
		*!*	ELSE
		*!*		RETURN
		*!*	ENDIF
		Do Case
		Case File(cIconFileName) AND !EMPTY(JUSTPATH(cIconFileName))		&&	daca in cIconFile am calea fizica
			cTempFileName = cIconFileName
			llFisierTemp = .F.
		Case File(cIconFileName)											&&	daca e inclus in app/exe
			cTempFileName = Addbs(Getenv("temp")) + "systray_temp_" + Sys(3) + ".icn"
			Copy File (cIconFileName) To (cTempFileName)
			llFisierTemp = .T.
		Otherwise
			Return
		Endcase
		*!*	modificare	ROASTART v 2.1.6	^
		
		m.hImage = WinAPI_LoadImage( 0, ;	&& Don't load from resource file
					cTempFileName, ;	
					IMAGE_ICON, ;
					0, ;
					0, ;
					LR_LOADFROMFILE)
		
		IF llFisierTemp		&&	modificare ROASTART v 2.1.6
		*  Now need to erase the temp file.  SET TALK OFF to avoid the "File has been erased" 
		*  message on the status bar.
			cOldTalk = SET("TALK")
			ERASE (cTempFileName)
			SET TALK &cOldTalk
		Endif				&&	modificare ROASTART v 2.1.6
		
		IF m.hImage = 0
			this.Error(0, "Failed to load icon '" + cIconFileName + "'. in " + PROGRAM(), LINENO())
		ENDIF
		
		RETURN m.hImage
	ENDPROC

	PROCEDURE processmenuevent		&& This event is called if user has selected an item from a menu defined by the MenuText property.  Add code here to act upon the user's selection.
		LPARAMETERS nMenuItemID
		*  This event is fired after a menu has been clicked or cleared.
		
	ENDPROC

	PROCEDURE receiveiconevent		&& Receives the events the Notify Icon.
		LPARAMETERS nDirection, nShift, nXCoord, nYCoord
			* This is the procedure that receives the Notify Icon events.  Since
			* we execute the BindEvent() function from within this class, this
			* procedure can be hidden.
			* nDirection contains our icon identifier.
		
		IF nDirection != this.SystrayIconID
			* Not our icon, or this a real MouseWheel event on the VFP window.
			RETURN .F.
		ENDIF
		
		*  These are the events that we receive from the Taskbar icon.
		#DEFINE WM_MOUSEMOVE		0x0200
		#DEFINE WM_LBUTTONDOWN		0x0201
		#DEFINE WM_LBUTTONUP		0x0202
		#DEFINE WM_LBUTTONDBLCLK	0x0203
		#DEFINE WM_RBUTTONDOWN		0x0204
		#DEFINE WM_RBUTTONUP		0x0205
		#DEFINE WM_RBUTTONDBLCLK	0x0206
		#DEFINE WM_MBUTTONDOWN		0x0207
		#DEFINE WM_MBUTTONUP		0x0208
		#DEFINE WM_MBUTTONDBLCLK	0x0209
		*  Mousewheel events also get passed, but are difficult to decipher.
		#define WM_CONTEXTMENU      0x007B	&& Same as RightClick, but used when 
											&& Version 5 events have been specified.
											&& (See the DisplayBalloonTip method.)
		
		#define WM_USER             0x0400
		#define NIN_SELECT          WM_USER + 0
		#define NINF_KEY            0x1
		#define NIN_KEYSELECT       BITOR(NIN_SELECT , NINF_KEY)
		
		* Balloon events supported on Windows ME and Windows XP, and later. Not supported on Win2k.
		#DEFINE NIN_BALLOONSHOW     (WM_USER + 2)
		#DEFINE NIN_BALLOONHIDE     (WM_USER + 3)
		#DEFINE NIN_BALLOONTIMEOUT  (WM_USER + 4)
		#DEFINE NIN_BALLOONUSERCLICK (WM_USER + 5)
		
		* Even though this isn't a real MouseWheel event on the VFP window,
		* VFP gives us the coordinates with respect to the location of the
		* main VFP window.  Must change back to global coordinates.
		nXCoord = nXCoord + _screen.Left
		
		IF nXCoord <> WM_MOUSEMOVE	&& Ignore mousemove events.
			* The Version 5 events are more difficult to deal with,
			* but Version 5 supports Balloon Tips.  So the extra work
			* is worth it.
			IF THIS.ShellVersion >= 5
				DO CASE
				CASE nXCoord = NIN_SELECT OR nXCoord = NIN_KEYSELECT
					THIS.IconClickEvent
				CASE nXCoord = WM_LBUTTONDBLCLK ;
						OR nXCoord = WM_RBUTTONDBLCLK ;
						OR nXCoord = WM_MBUTTONDBLCLK
					* We'll just use one DoubleClick event, and ignore what button was used.
					THIS.IconDblClickEvent
					RETURN
				CASE nXCoord = WM_CONTEXTMENU  && Shell version 5 and later
					THIS.IconRightClickEvent
					RETURN
				CASE nXCoord = WM_MBUTTONDOWN
					THIS.IconMiddleClickEvent
					RETURN
				CASE nXCoord = NIN_BALLOONSHOW
					THIS.BalloonShowEvent
					RETURN
				CASE nXCoord = NIN_BALLOONHIDE
					THIS.BalloonHideEvent
					RETURN
				CASE nXCoord = NIN_BALLOONTIMEOUT
					THIS.BalloonTimeoutEvent
					RETURN
				CASE nXCoord = NIN_BALLOONUSERCLICK
					This.BalloonClickEvent
					RETURN
				OTHERWISE
					*  Unknown event.  Just ignore it.
					RETURN
				ENDCASE
			ELSE
				*  Using Version 4 events.
				DO CASE
				CASE nXCoord = WM_LBUTTONDOWN 
					THIS.IconClickEvent
				CASE nXCoord = WM_LBUTTONDBLCLK ;
						OR nXCoord = WM_RBUTTONDBLCLK ;
						OR nXCoord = WM_MBUTTONDBLCLK
					* We'll just use one DoubleClick event, and ignore what button was used.
					THIS.IconDblClickEvent
					RETURN
				CASE nXCoord = WM_RBUTTONDOWN
					THIS.IconRightClickEvent
					RETURN
				CASE nXCoord = WM_MBUTTONDOWN 
					THIS.IconMiddleClickEvent
					RETURN
				OTHERWISE
					*  Unknown event.  Just ignore it.
					RETURN
				ENDCASE
			ENDIF
			
		ENDIF
		
		
	ENDPROC

	PROCEDURE removeiconfromsystray		&& Removes the icon from the Taskbar Notification Area ("systray").
		* Removes the icon from the Taskbar Notification Area ('System Tray').
		
		
		DECLARE INTEGER Shell_NotifyIcon IN shell32.dll AS WinAPI_Shell_NotifyIcon ;
			INTEGER dwMessage, ;
			string @ PNOTIFYICONDATA 
		
		#define NIM_ADD         0x00000000
		#define NIM_MODIFY      0x00000001
		#define NIM_DELETE      0x00000002
		#define NIF_MESSAGE     0x00000001
		#define NIF_ICON        0x00000002
		#define NIF_TIP         0x00000004
		
		*#define NIF_STATE       0x00000008
		#define NIF_INFO        0x00000010	
			
			
		LOCAL cNotifyIconData
		
		WITH THIS
		UNBINDEVENTS(_screen, "MouseWheel", this, "receiveIconEvent")
		.Enabled = .f.
		
		
		* Build structure.
		cNotifyIconData = .IntegerToString(_vfp.hwnd)
		cNotifyIconData = cNotifyIconData + .IntegerToString(.SystrayIconID * 0x10000)	
		cNotifyIconData = cNotifyIconData + .IntegerToString(BITOR(NIF_TIP,NIF_MESSAGE ,NIF_ICON))	&&NIF_TIP,NIF_INFO,NIF_STATE, ,NIF_TIP
		cNotifyIconData = cNotifyIconData + .IntegerToString(0x20A)	&& uCallback
		cNotifyIconData = cNotifyIconData + .IntegerToString(0)	&& icon handle
		cNotifyIconData = cNotifyIconData + PADR(LEFT(TRANSFORM(.TipText), 63), 64, CHR(0))
		cNotifyIconData = .integertostring(LEN(cNotifyIconData) + 4) + cNotifyIconData
		ENDWITH
		
		nReturn = WinAPI_Shell_NotifyIcon(NIM_DELETE, @cNotifyIconData)
		THIS.ShellVersion = 0
		
		
		IF TYPE("_Screen.nSysTrayCount")="N" AND _Screen.nSysTrayCount=1
			* There has not been two simultaneous instances of this object
			* in the current session.  We can clean up after ourselves.
			_Screen.nSysTrayCount = 0
			SYS(2060, THIS.nPrevMWAccrual)	&& Reset mousewheel behavior.
		ENDIF
		
	ENDPROC

	PROCEDURE setforegroundwindow		&& By default, brings the form that contains this object to the foreground.  You can also  specify a different window to bring forward by passing a numeric hWnd parameter.
		LPARAMETERS lhWnd
		
		* Brings the specified top-level window to the top of the Windows z-order.
		* If no parameter specified, then THISFORM is made the foreground window.
		
		IF TYPE("m.lhWnd") = "N"
			DECLARE INTEGER SetForegroundWindow IN user32.dll AS WinAPI_SetForegroundWindow INTEGER hWnd
			WinAPI_SetForegroundWindow(m.lhWnd)
			RETURN
		ENDIF
		
		RETURN
		
		*  If we're here, then the user called this method without an 
		*  hWnd parameter.  Assume it is thisform that is to be brought forward.
		IF VARTYPE(thisform) == "O"
			IF ! thisform.Visible 
				thisform.Visible = .t.
			ENDIF
			
			*  First make sure it isn't minimized.
			IF thisform.WindowState= 1
				thisform.WindowState = 0
			ENDIF
			
			*  If necessary, make sure the main VFP window is not minimized.
			IF _screen.Visible AND thisform.ShowWindow = 0	
				IF _screen.WindowState = 1
					_screen.WindowState = 0	&& Set back to normal
				ENDIF
			ENDIF
				
			DECLARE INTEGER SetForegroundWindow IN user32.dll AS WinAPI_SetForegroundWindow INTEGER hWnd
			WinAPI_SetForegroundWindow(thisform.HWnd)
		ENDIF
		
		
		
	ENDPROC

	PROCEDURE showballoontip		&& Displays a balloon tip over the icon in the Taskbar Notification Area.  Parameters: cBalloonText [, cBalloonTitle [, nIcon [, nTimeoutInSeconds]]]
		LPARAMETERS lcBalloonText, lcBalloonTitle, lnIcon, lnTimeout
		* Displays a balloon tip if the Operating System shell supports it.
		* The lnTimeout parameter is the number of SECONDS to display the balloon. This
		* timeout period is subject to minimum and maximum values set by the operating 
		* system (typically 10 and 30 seconds).
		
		* The lnIcon parameter specifies the icon to display in the
		* balloon.  The values are as follows:
		*    0 = No icon
		*    1 = Info. A lowercase "i" in a small balloon
		*    2 = Warning. An exclamation point (!) in a triangle.
		*    3 = Error. A red disk with an X through it.
		*    Add 16 (0x10) to the value to mute the sound that is played when
		*    the balloon is displayed (Windows XP and later).
		
		* This method uses #DEFINES that are set in the AddIconToSystray method.
		
		IF PCOUNT() < 1
			*  Must at least include the balloon Text.
			RETURN -1
		ENDIF
		
		IF THIS.ShellVersion < 5	&&GetShellVersion() < 5.00
			*  This shell doesn't support balloon tips.
			RETURN -1
		ENDIF
		
		IF TYPE("m.lcBalloonText") <> "C" 
			RETURN -1
		ENDIF
		
		IF TYPE("m.lcBalloonTitle") <> "C"
			lcBalloonTitle = ""
		ENDIF
		
		IF TYPE("m.lnIcon") <> "N"
			lnIcon = 0
		ENDIF
		
		IF TYPE("m.lnTimeout") <> "N"
			lnTimeout = 0
		ELSE
			lnTimeOut = m.lnTimeout * 1000	&& Convert seconds to Milliseconds.
		ENDIF
		
		LOCAL lcNotifyIconData, lnReturn
		
		WITH THIS
				*  Build NOTIFYICONDATA structure compatible with version 5 of the shell.
				lcNotifyIconData = .IntegerToString(_vfp.hwnd)
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(.SystrayIconID * 0x10000)	
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(BITOR(NIF_TIP,NIF_MESSAGE ,NIF_ICON, NIF_INFO))	&&NIF_TIP,NIF_INFO,NIF_STATE
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(0x20A)	&& uCallback
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(.aIconList[.CurrentIconIndex])	&& icon handle
				lcNotifyIconData = lcNotifyIconData + PADR(LEFT(TRANSFORM(.TipText), 127), 128, CHR(0))	&& TipText
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(0)	&& dwState
				lcNotifyIconData = lcNotifyIconData + .IntegerToString(0)	&& dwStateMask
				lcNotifyIconData = lcNotifyIconData + PADR(LEFT(TRANSFORM(m.lcBalloonText), 255),256, CHR(0))	&& balloon tip.
				lcNotifyIconData = lcNotifyIconData + .IntegerToSTring(m.lnTimeOut) && Timeout/Version
				lcNotifyIconData = lcNotifyIconData + PADR(LEFT(TRANSFORM(m.lcBalloonTitle), 63),64, CHR(0))	&& balloon tip title.
				lcNotifyIconData = lcNotifyIconData + .IntegerToSTring(BITAND(lnIcon, 0x1F)) && Info flags
				lcNotifyIconData = .IntegerToString(LEN(lcNotifyIconData) + 4) + lcNotifyIconData	&& length of structure
				
		
				*  Send request to display balloon.  This is asynchronous, so
				*  balloon may not be displayed immediately.
				lnReturn = WinAPI_Shell_NotifyIcon( NIM_MODIFY, @lcNotifyIconData)
		ENDWITH
			
	ENDPROC

	PROCEDURE showmenu		&& Displays the menu defined by the MenuText property, or runs an MPR that defines a shortcut menu.  Accepts string parameter containing the filename of the MPR to run, and optional second parameter to be passed to the MPR startup code.
		LPARAMETERS lcMPRFileName, p1, p2, p3, p4, p5, p6
		* Displays the shortcut menu near the taskbar icon.
		* If the menu is defined as a set of tokens and text strings, the 
		* menu is defined here.  Otherwise, the specified MPR is called.
		* If the first parameter is blank or not specified, the contents
		* of the MenuText property are used to define/determine the menu.
		* If the first parameter is not blank, it is assumed to specify
		* the MPR to run. The MenuText property is ignored.
		* 
		* The additional parameters are passed on to the MPR. They are only
		* used if the cMPRFileName parameter is specified and non-blank.
		*
		* In order to display the shortcut menu in the proper location,
		* we must activate it within a toplevel form (with .visible = .t.),
		* near the taskbar.
		* On WindowsME, Win2k and later, the form is made invisible by 
		* calling a Windows API function. (See the INIT of the form class.)
		* But on Win98 and NT4, that API function didn't exist, so we make it
		* a "Desktop" form instead. This allows us to remove the titlebar and
		* make the form so small it won't be noticed. But it causes slightly
		* incorrect behavior when trying to clear the menu.
		
		LOCAL lhPreviousWindow, lcPOINT, lnReturn, lnXCoord, lnYCoord, lcClassLib, llContinue
		LOCAL lnTaskBarLeft, lnTaskBarTop, lnTaskBarRight, lnTaskBarBottom, lnTaskBarLocation
		LOCAL loTempForm as Form
		
		* Our invisible form must be made the top window in order to make the shortcut 
		* menu get cleared properly if user clicks elsewhere. Therefore, we store the
		* handle of the current foreground window, so we can restore it later.
		DECLARE INTEGER GetForegroundWindow IN user32.dll AS WinAPI_GetForegroundWindow
		lhPreviousWindow = WinAPI_GetForegroundWindow()
		
		* Get location of the mouse pointer.
		DECLARE INTEGER GetCursorPos IN user32.dll AS WinAPI_GetCursorPos STRING @
		lcPOINT = REPLICATE(CHR(0), 8)
		lnReturn = WinAPI_GetCursorPos(@lcPOINT)
		lnXCoord  = THIS.StringToInteger(LEFT(m.lcPOINT,4))
		lnYCoord = THIS.StringToInteger(SUBSTR(m.lcPOINT, 5))
		
		* Get the position and coordinates of the taskbar.
		lnTaskBarLeft = 0
		lnTaskBarRight = 0
		lnTaskBarTop = 0
		lnTaskBarBottom = 0
		lnTaskBarLocation = THIS.GetTaskBarPosition(@lnTaskBarLeft, @lnTaskBarTop, @lnTaskBarRight, @lnTaskBarBottom)
		
		*  Need Top-Level form.
		*  It also has ShowInTaskBar=.F. so we don't get a large button on the taskbar.
		*  It is also invisible, using the SetWindowLong API function in the form's INIT.
		lcClassLib = "systray.vcx"
		llContinue = .T.
		DO WHILE llContinue
		TRY
			loTempForm = NEWOBJECT("x_frmInternalSystrayUseOnly", lcClassLib )	
			llContinue = .F.
		CATCH 	
			IF lcClassLib = "systray.vcx"
				* Try to create the form by looking in the same place as this object 
				lcClassLib = ADDBS(JUSTPATH(SYS(1271, this))) + "systray.vcx"
			ELSE
				loTempForm = .Null.
				llContinue = .F.
			ENDIF
		ENDTRY
		ENDDO
		
		* Now position form near mouse, but adjust for taskbar.
		DO CASE 
		CASE m.lnTaskBarLocation = 1	&& Left
			loTempForm.top = m.lnYCoord 	
			loTempForm.Left = m.lnTaskBarRight + 7 	
		CASE m.lnTaskBarLocation = 2	&& Top
			* Ideally would determine height of titlebar, and subtract that.
			* But this is sufficient.
			loTempForm.top = m.lnTaskBarBottom - 8  
			loTempForm.Left = m.lnXCoord 	
		CASE m.lnTaskBarLocation = 3	&& Right
			loTempForm.top = m.lnYCoord 
			loTempForm.Left = m.lnTaskBarLeft - this.MenuOffsetFromRight  && Roughly the width of a typical menu.
		OTHERWISE			&& Bottom, or coding error.   
			*  Just place the window near the mouse.
			loTempForm.top = m.lnYCoord 
			loTempForm.Left = m.lnXCoord	
		ENDCASE
		
		
		* Window must be the foreground window when shortcut menu displayed.  Otherwise,
		* clicking in another window won't release the shortcut menu.
		loTempForm.Show 
		THIS.SetForegroundWindow(loTempForm.hWnd)
		
		
		*  Now display the menu.
		DO CASE
		CASE (PCOUNT() > 0 AND TYPE("m.lcMPRFileName") = "C") 
			* Want to run the MPR specified in parameter
		
			IF PCOUNT() = 1
				DO (m.lcMPRFileName) 
				RELEASE loTempForm
			ELSE
				* Build parameter list
				LOCAL lcParamList, lnCount
				lcParamList = "p1"
				FOR nCount = 3 TO PCOUNT()	&& if PCOUNT() is only 2, don't enter loop.
					lcParamList = m.lcParamList + ", p" + TRANSFORM(m.lnCount - 1)
				ENDFOR
		
				DO (m.lcMPRFileName) WITH &lcParamList
				RELEASE loTempForm
			ENDIF
			
		CASE THIS.MenuTextIsMPR AND FILE(THIS.MenuText)
			* We want to run the MPR specified in MenuText property.
			DO (THIS.MenuText)
			RELEASE loTempForm
		
		OTHERWISE
		
			* Build menu from this.MenuText.
			LOCAL lnMenuItems, lcMenuName, laMenuItems[1], lnCurrentMenuItem
			LOCAL lnToken, lcText, lnBarNum
			lnMenuItems = ALINES(m.laMenuItems, THIS.MenuText, .T., ";")
			IF m.lnMenuItems = 0
				RELEASE loTempForm
				RETURN .f.
			ENDIF
			IF m.lnMenuItems % 2 != 0
				*  Incorrect number of items in MenuText property.
				IF m.lnMenuItems > 1
					lnMenuItems = lnMenuItems - 1
				ELSE
					RELEASE loTempForm
					RETURN .f.
				ENDIF
			ENDIF
			lcMenuName = SYS(2015)
			* Make private variable to receive the ID of the selected menu item.
			PRIVATE __systray_menu_return
			__systray_menu_return = 0
			
			*  Build the menu.
			DEFINE POPUP (m.lcMenuName) SHORTCUT RELATIVE FROM MROW(),MCOL()
			FOR lnCurrentMenuItem = 1 TO m.lnMenuItems STEP 2
				lnToken = VAL(m.laMenuItems[m.lnCurrentMenuItem])
				lcText = m.laMenuItems[m.lnCurrentMenuItem + 1]
				lnBarNum = FLOOR(m.lnCurrentMenuItem/2) + 1
				DEFINE BAR (lnBarNum) OF (m.lcMenuName) PROMPT (m.lcText)
				lcOnSelectionBar = "ON SELECTION BAR " + TRANSFORM(m.lnBarNum) + " OF " + ;
							TRANSFORM(m.lcMenuName) + " __systray_menu_return = " + TRANSFORM(m.lnToken)
				&lcOnSelectionBar
			ENDFOR
			SET ESCAPE off
		
			*  Display the menu and wait for user action.
			ACTIVATE POPUP (m.lcMenuName)
		
			RELEASE loTempForm
			THIS.ProcessMenuEvent(__systray_menu_return)
			RETURN __systray_menu_return
		
		ENDCASE
		
		* Menu has been deactivated.  Restore previous foreground window.
		This.SetForegroundWindow(m.lhPreviousWindow)
	ENDPROC

	HIDDEN PROCEDURE stringtointeger		&& Convert binary representation of integer into an integer data type.
		LPARAMETERS lcPDWORD, lnBytes
		
		*  Converts a string from the Intel int, DWORD,WORD, and BYTE formats 
		*  into a VFP integer.
		IF PCOUNT() < 2
			lnBytes = 4	&& No length provided, assume 4-byte integer.
		ENDIF
		LOCAL lnReturn, lnCurByte
		lnReturn = 0
		FOR lnCurByte = 1 to m.lnBytes
			lnReturn = m.lnReturn + ASC(SUBSTR(m.lcPDWord, m.lnCurByte, 1))*(256^(m.lnCurByte-1))
		ENDFOR
		RETURN lnReturn
	ENDPROC

	PROCEDURE switchicon		&& Changes the displayed icon to the next icon in the icon list.  Use AddIconToIconList method to populate the icon list, and ClearIconList to start over.
		* Switches to the next icon in the iconlist.
		* Typically called by a timer to regularly change the icon,
		* to create animation.
		
		WITH THIS
		IF .IconCount > 1
			IF .CurrentIconIndex = .iconCount
				.CurrentIconIndex = 1
			ELSE
				.CurrentIconIndex = .CurrentIconIndex + 1
			ENDIF
		ENDIF
		RETURN .CurrentIconIndex
		ENDWITH
		
		
	ENDPROC

	HIDDEN PROCEDURE tiptext_assign
		LPARAMETERS vNewVal
		* The text for the tooltip is being changed.
		THIS.tiptext = ALLTRIM(m.vNewVal)
		
		IF THIS.Enabled
			*  This icon is already displayed in the Taskbar Notification Area,
			*  so we must update the systray immediately:
			this.AddIconToSystray()
		endif
	ENDPROC

ENDDEFINE

DEFINE CLASS x_frminternalsystrayuseonly AS form 
 	*< CLASSDATA: Baseclass="form" Timestamp="" Scale="Pixels" Uniqueid="" />

	*<PropValue>
		BorderStyle = 0
		Caption = ""
		Closable = .F.
		ControlBox = .F.
		DoCreate = .T.
		Height = 34
		Left = 0
		MaxButton = .F.
		MaxHeight = 1
		MinButton = .F.
		Movable = .F.
		Name = "x_frminternalsystrayuseonly"
		ShowInTaskBar = .F.
		ShowWindow = 2
		Themes = .F.
		TitleBar = 0
		Top = 0
		Width = 123
	*</PropValue>
	
	PROCEDURE Init
		*  For proper menu behavior, this form must be a Top-Level form, not a Desktop form.
		*  But Top-Level forms are required to have a titlebar, which can be visible from
		*  beneath the menu.  Therefore, we'll make this window invisible, before it is 
		*  even displayed. 
		
		#DEFINE GWL_EXSTYLE         -20
		*#DEFINE WS_EX_LAYERED       0x00080000
		#DEFINE WS_EX_TRANSPARENT   0x00000020
		
		DECLARE SetWindowLong IN USER32.DLL AS WinAPI_SetWindowLong Integer, Integer, Integer 
		*DECLARE SetLayeredWindowAttributes In Win32Api Integer, String, Integer, Integer 
		WinAPI_SetWindowLong(THIS.hWnd, GWL_EXSTYLE, WS_EX_TRANSPARENT) 
		*SetLayeredWindowAttributes(THIS.hWnd, 0, 0, 2) 
		
		*  Make window as small as possible so menu position will be more accurate.
		THIS.Width = 1
		THIS.Height = 1
	ENDPROC

ENDDEFINE
