zoukankan      html  css  js  c++  java
  • vfp 智能感知拓展应用

    *========================================================================================
    *
    * Version: 2010-02Feb-20
    *
    *========================================================================================
    *
    * This program implements partial IntelliSense in VFP 6-9. To enable 
    * IntelliSenseX, simply execute this program at any time when using
    * Visual FoxPro or put it into your startup program.
    *
    * To configure ISX please see the section just below the comment block.
    *
    * To stop IntelliSenseX run this program again and pass "QUIT" as a
    * parameter. Alternatively, you can simply remove the ON KEY LABEL
    * macros for the ALT+I and the "." key.
    *
    * Currently only IntelliSense for variable names is implemented. This
    * means that whenever you enter "m." in a MODIFY COMMAND window or
    * in a Method edit window, you get a list of all variables declared
    * in the current procedure. ISX doesn't parse the entire sourcecode 
    * for memory variables, but only the current procedure or method and
    * only those variables listed in a LOCAL, PRIVATE, PUBLIC, LPARAMETER
    * and PARAMETER statement.  ALT+I can be used to trigger this list.
    *
    * ALT+RIGHTARROW triggers a universal autocomplete function that tries to determine the
    * type of what you have entered and offers a list of all possible values.
    *
    * Please note that I haven't written this program as an excercise for
    * good coding styles <g>, rather as an excercise to see if 
    * IntelliSense is possible within Visual FoxPro itself. Therefore
    * you won't find the Assertions you would otherwise find in my code.
    *
    *========================================================================================
    *
    * Acknowledgements
    *
    * Thanks to George Tasker for his really helpful documentation on the
    * FoxTools.Fll. You can download his ToolHelp.Hlp file from the
    * UniversalThread and the CompuServe MSDEVAPP forum. George also made
    * some suggestions to improve this program.
    *
    * Also thanks to Ken Levy, who couldn't implement an inline Intelli-
    * Sense feature in his SuperCls and thereby convinced me that there 
    * must be a way to do it, even only for the purpose of doing 
    * something that Ken Levy couldn't do. <bg>
    *
    * Thanks to all the folks that posted me bug reports, especially
    * Frank Cazabon. Thanks to Gerry Hughes for correcting the typos in 
    * my comments.
    *
    * Louis D. Zelus added a nifty feature to my version to make ISX 
    * even more useful. Thanks for that! The code based on his work is
    * marked with "LDZ:".
    *
    * Sietse Wijnkler added a lot of new cool features: He added the
    * ability to distinguish different types that all are triggered by
    * a period and the code to display variables, object properties and
    * field names. Code based on his work is marked with "SW:".
    *
    * J黵gen "wOOdy" Wondzinski pointed out that special characters like
    * "�" are valid variable names and IsAlpha() returns .T. for them.
    * Therefore any of these characters is detected by ISX, as well.
    *
    * Tamar E. Granor and Peter Steinke, both requested the list DEFINE 
    * features which is why I finally added it.
    *
    * Thanks to Eddy Maue for his contributions:
    *
    *   Ce qu'ile fait de plus maintenant
    *    -  Alt-Q pour arr阾er Isx
    *    -  Alt-Q pour redemarrer Isx
    *    - Ouvre automatiquements :
    *            -Les tables pr閟entes dans les r閜ertoires courants et de recherches
    *             (set path to)
    *            -Les vues pr閟entes dans le projet actif
    *            -Les query pr閟ents dans les r閜ertoires courants et de recherches
    *             (set path to)
    *              Petit point � ne pas n間liger. Le curseur produit par le fichier 
    *              MyQuery.qpr doit 阾re du m阭e nom que le fichier
    *
    * In English:
    * 
    *    - ALT+Q enables/disables ISX
    *    - files are opened automatically:
    *        - tables available in the current directory or the search path (SET PATH TO)
    *        - Views available in the current project
    *        - Queries available in the current directory or the search path (SET PATH TO)
    *          Minor, but important restriction: The cursor created by the query program
    *          must have the same alias as the filename.
    
    * Mike Yearwood added supported for maximized editing windows which caused a lot
    * of flickering everytime the popup came up.
    * 
    * Thanks to all those who pointed out bugs in ISX's releases: 
    * 
    *  - Nina Schwanzer
    *  - Del Lee
    *  - Pamela Thalacker
    *  - Christophe Chenavier
    *  - Aragorn Rockstroh
    *  - Claude Hebert 
    *  - Jens Kippnich
    *  - Stefan W黚be
    *
    *========================================================================================
    *
    * This program has been written in 1999-2005 by Christof Wollenhaupt
    * and is placed into Public Domain. You can use the entire
    * code or parts of it as you like in any private or commercial
    * application. None of the contributors to this programm can be hold 
    * liable for any damage or problems, using this program may cause.
    *
    * If you added a new feature, please let me know. If you want I add
    * your feature to my master copy of ISX to let others use your 
    * feature, as well. Please note that since the entire program is 
    * placed into Public Domain, this places your code into Public 
    * Domain, as well. Of course, your contributions are acknlowdeged in
    * the comment at the beginning of this file.
    *
    *========================================================================================
    *
    * Known problems:
    *
    * - So far ISX has not been tested with different Display appearance
    *   settings, like wider scrollbars or form borders, large fonts and
    *   the like. Some values are hardcoded and might be wrong for non-
    *   standard Windows settings.
    *
    * - When you enter a period into a textbox, the cursor is set to the first character of
    *   the textbox and then the period entered. If SelectOnEntry is true, everything is
    *   replaced by the period. This is caused by a bug in VFP that makes all ON KEY LABEL
    *   behave this way. You can disable this behavior by commenting out the lines starting 
    *   with "ON KEY LABEL .". In this case, you must use ALT+I or ALT+RIGHTARROW do expand
    *   the variable.
    *
    *========================================================================================
    
    
    *========================================================================================
    * Configuration. 
    *
    * Over the time I got many enhanced versions of ISX, many of which include new hotkeys.
    * To give everyone control over the hotkey assignment and to disable/enable particular
    * features, I added the following configuration section. By commenting out a #DEFINE, you
    * disable a particular feature. Changing the value changes the hotkey.
    *
    *========================================================================================
    
    #DEFINE EXPAND_VARIABLE ALT+I
    #DEFINE DOT_ACTIVATION  .
    #DEFINE LIST_ALL        ALT+RIGHTARROW
    #DEFINE TOGGLE_ISX      ALT+Q
    
    
    *========================================================================================
    * Main program
    *========================================================================================
    Lparameters tcAction, tcParam, tcParam2
    
    	Do Case
    	Case Vartype(m.tcAction) == "L"
    		InstallISX()
    	Case Upper(Alltrim(m.tcAction)) == "AUTOCOMPLETE"
    		Push Key Clear
    		AutoComplete( m.tcParam, m.tcParam2 )
    		Pop Key
    	Case Upper(Alltrim(m.tcAction)) == "QUIT"
    		UninstallISX()
    	Endcase
    	
    Return
    
    
    *========================================================================================
    * Activates the hotkeys.
    *========================================================================================
    Procedure InstallISX
    
    	Local lcISXProgram
    	lcISXProgram = ["] + Sys(16,Program(-1)-1) + ["]
    	#IFDEF EXPAND_VARIABLE 
    		On Key Label EXPAND_VARIABLE Do &lcISXProgram With "AUTOCOMPLETE", "VAR", ""
    	#ENDIF
    	#IFDEF DOT_ACTIVATION
    		On Key Label DOT_ACTIVATION Do &lcISXProgram With "AUTOCOMPLETE", "VAR,OBJ,TABLE", "."
    	#ENDIF
    	#IFDEF LIST_ALL
    		On Key Label LIST_ALL Do &lcISXProgram With "AUTOCOMPLETE", "", ""
    	#ENDIF
    	#IFDEF TOGGLE_ISX
       On Key Label TOGGLE_ISX Do &lcISXProgram With "QUIT"
       Wait Window Nowait [ISX up and running... TOGGLE_ISX to quit]
    	#ELSE
    		Wait Window nowait "ISX up and running..." 
    	#ENDIF
    EndProc
    
    
    *====================================================================
    * Deactivates the hotkeys.
    *====================================================================
    Procedure UninstallISX
    
    	Local lcISXProgram
    	lcISXProgram = ["] + Sys(16,Program(-1)-1) + ["]
    	
    	#IFDEF EXPAND_VARIABLE
    		On Key Label EXPAND_VARIABLE
    	#ENDIF
    	#IFDEF DOT_ACTIVATION
    		On Key Label DOT_ACTIVATION
    	#ENDIF
    	#IFDEF LIST_ALL
    		On Key Label LIST_ALL
    	#ENDIF
    	#IFDEF TOGGLE_ISX
    		On Key Label TOGGLE_ISX Do &lcISXProgram
    		Wait Window Nowait [ISX terminated... TOGGLE_ISX to restart]
    	#ELSE
    		Wait Window nowait "ISX terminated..." 
    	#ENDIF
    
    EndProc
    
    
    *========================================================================================
    * Provides a generic autocomplete function. AutoComplete checks all content providers 
    * if they have something to add to the global list and displays the list as a popup
    *========================================================================================
    Procedure AutoComplete
    Lparameters tcProviders, tcInvocation
    	
    	*--------------------------------------------------------------------------------------
    	* The list of providers can be limited. This speeds up program execution if one knows 
    	* from the context that only few content providers actually fit.
    	*--------------------------------------------------------------------------------------
    	Local lcProviders
    	If Empty(m.tcProviders)
    		lcProviders = "VAR,DEFINE,TABLE,OBJ"
    	Else
    		lcProviders = Upper(m.tcProviders)
    	EndIf 
    	
    	*-----------------------------------------------------------------
    	* Make sure, FoxTools.Fll is loaded.
    	*-----------------------------------------------------------------
    	If not "FOXTOOLS.FLL" $ Upper(Set("Library"))
    		Set Library to (Home()+"FoxTools.Fll") Additive
    	Endif
    	
    	*-----------------------------------------------------------------
    	* Get the current window and verify that it is a valid window.
    	*-----------------------------------------------------------------
    	Local lnWHandle
    	lnWHandle = GetCurrentWindow()
    	If lnWHandle == 0
    		If not Empty(m.tcInvocation)
    			Clear TypeAhead
    			Keyboard "{Mouse}{Mouse}"+m.tcInvocation Plain
    		Endif
    		Return
    	Endif
    
    	*-----------------------------------------------------------------
    	* Verify that the current window is indeed an edit window.
    	*-----------------------------------------------------------------
    	Local lnEditSource 
    	lnEditSource = GetEditSource(m.lnWHandle)
    	If not InList( m.lnEditSource, 1, 8, 10, 12 )
    		If not Empty(m.tcInvocation)
    			Clear TypeAhead
    			Keyboard "{Mouse}{Mouse}"+m.tcInvocation Plain
    		Endif
    		Return
    	EndIf
    	
    	*--------------------------------------------------------------------------------------
    	* Fill an object with details about the current context. We determine what the user 
    	* has entered so far and what's left from that Position. 
    	*--------------------------------------------------------------------------------------
    	Local loISX
    	loISX = CreateObject("Relation")
    	loISX.AddProperty("nWHandle",m.lnWHandle)
    	loISX.AddProperty("nEditSource",m.lnEditSource)
    	loISX.AddProperty("aList[1]")
    	loISX.AddProperty("nCount",0)
    	loISX.AddProperty("cTextLeft",GetLineLeftFromCursor(m.lnWHandle))
    	loISX.AddProperty("cName","")
    	loISX.AddProperty("cEntity","")
    	loISX.AddProperty("cInvocation",m.tcInvocation)
    	
    	*--------------------------------------------------------------------------------------
    	* Determine the part of the name that has been entered so far. This code has been 
    	* kindly provided by Louis D. Zelus.
    	*--------------------------------------------------------------------------------------
    	Local lcLine, lcChar
    	If Empty(m.tcInvocation)
    		Do While Len(m.loISX.cTextLeft) > 0
    			lcChar = Right( m.loISX.cTextLeft, 1 )
    			If IsAlpha(m.lcChar) or IsDigit(m.lcChar) or m.lcChar == "_"
    				loISX.cTextLeft = Left( m.loISX.cTextLeft, Len(m.loISX.cTextLeft)-1 )
    				loISX.cName = m.lcChar + m.loISX.cName
    			Else
    				Exit
    			Endif
    		Enddo
    	EndIf 
    
    	*--------------------------------------------------------------------------------------
    	* Determines the name of the entity. This code is courtesy of Sietse Wijnkler. 
    	*--------------------------------------------------------------------------------------
    	Do While Len(m.loISX.cTextLeft) > 0
    		lcChar = Right( m.loISX.cTextLeft, 1 )
    		If IsAlpha(m.lcChar) or IsDigit(m.lcChar) or m.lcChar == "_" or m.lcChar == "."
    			loISX.cTextLeft = Left( m.loISX.cTextLeft, Len(m.loISX.cTextLeft)-1 )
    			loISX.cEntity = m.lcChar + m.loISX.cEntity
    		Else
    			Exit
    		Endif
    	EndDo
    	If Right(loISX.cEntity,1) == "."
    		loISX.cEntity = Left( m.loISX.cEntity, Len(m.loISX.cEntity)-1 )
    	EndIf 
    	
    	*--------------------------------------------------------------------------------------
    	* This array lists all the providers
    	*--------------------------------------------------------------------------------------
    	Local laProvider[4,2]
    	laProvider = ""
    	laProvider[1,1] = "VAR"
    	laProvider[1,2] = "CP_Variables"
    	laProvider[2,1] = "DEFINE"
    	laProvider[2,2] = "CP_Defines"
    	laProvider[3,1] = "TABLE"
    	laProvider[3,2] = "CP_Tables"
    	laProvider[4,1] = "OBJ"
    	laProvider[4,2] = "CP_Objects"
    	
    	*--------------------------------------------------------------------------------------
    	* Get data from each provider and merge it into the list
    	*--------------------------------------------------------------------------------------
    	Local laAll[1], lnAll, laRequest[1], lnRequest, lnProvider 
    	lnAll = 0
    	For lnRequest=1 to ALines(laRequest,Chrtran(m.lcProviders,",",Chr(13)+Chr(10)),.T.)
    		For lnProvider=1 to Alen(laProvider,1)
    			If Upper(Alltrim(laRequest[m.lnRequest])) == laProvider[m.lnProvider,1]
    				loISX.nCount = 0
    				Dimension loISX.aList[1]
    				loISX.aList = ""
    				&laProvider[m.lnProvider,2](m.loISX)
    				If m.loISX.nCount > 0
    					Dimension laAll[m.lnAll+m.loISX.nCount]
    					Acopy(m.loISX.aList,laAll,1,m.loISX.nCount, m.lnAll+1)
    					lnAll = m.lnAll + m.loISX.nCount
    				EndIf 
    			EndIf 
    		EndFor 
    	EndFor 
    
    	*--------------------------------------------------------------------------------------
    	* If there's anything in the list, display the popup
    	*--------------------------------------------------------------------------------------
    	If m.lnAll == 0
    		If not Empty(m.tcInvocation)
    			Clear TypeAhead
    			Keyboard "{Mouse}{Mouse}"+m.tcInvocation Plain
    		Endif
    	Else 
    		If not Empty(m.tcInvocation)
    			InsertText( m.lnWHandle, m.tcInvocation )
    		EndIf
    		loISX.nCount = m.lnAll
    		Dimension loISX.aList[loISX.nCount]
    		Acopy(laAll,loISX.aList)
    		DisplayPopup(loISX)
    	EndIf
    	
    EndProc
    
    
    *========================================================================================
    * Determines all include files that fit in the current situation and adds them to the
    * list.
    *========================================================================================
    Procedure CP_Defines
    Lparameters toISX
    
    	Local loFile
    	If Type("_VFP.ActiveProject") == "O"
    		For each loFile in _VFP.ActiveProject.Files
    			If Upper(JustExt(loFile.Name)) == "H"
    				ReadDefines(m.toISX,loFile.Name)
    			EndIf 
    		EndFor 
    	Else
    		ReadDefines(m.toISX,Home()+"FoxPro.H")
    	EndIf 
    
    EndProc 
    
    
    *========================================================================================
    * Adds all constants from an include file to the array.
    *========================================================================================
    Procedure ReadDefines
    LParameter toISX, tcFile
    
    	*--------------------------------------------------------------------------------------
    	* File must exist.
    	*--------------------------------------------------------------------------------------
    	If not File(m.tcFile)
    		Return 
    	EndIf 
    	
    	*--------------------------------------------------------------------------------------
    	* To increase performance, we cache files if possible.
    	*--------------------------------------------------------------------------------------
    	Local laDefine[1], lnItem, lnCount
    	If not IsInCache( "DEFINE", m.toISX, m.tcFile )
    		If Version(4) >= "07.00"
    			lnCount = AProcInfo(laDefine,m.tcFile)
    		Else
    			lnCount = X6_AProcInfo(@laDefine,m.tcFile)
    		EndIf 
    		For lnItem=1 to m.lnCount
    			If laDefine[m.lnItem,3] == "Define"
    				toISX.nCount = toISX.nCount + 1
    				Dimension toISX.aList[toISX.nCount]
    				toISX.aList[toISX.nCount] = laDefine[m.lnItem,1]
    			EndIf 
    		EndFor 
    		AddToCache( "DEFINE", m.toISX, m.tcFile )
    	EndIf 
    
    EndProc
    
    
    *========================================================================================
    * The cache is an array in _SCREEN that holds the name of the file, the time stamp, the
    * provider ID and the contents of the array.
    *========================================================================================
    Procedure IsInCache
    LParameter tcProvider, toISX, tcFile
    
    	If Type("_Screen.ISXCache[1,1]") == "U"
    		Return .F.
    	EndIf
    
    	Local lnLine
    	If Version(4) >= "07.00"
    		lnLine = Ascan( _Screen.ISXCache, m.tcFile+"?"+m.tcProvider, -1, -1, 1, 1+2+4+8 )
    	Else
    		Local lnCurLine
    		lnLine = 0
    		For lnCurLine=1 to Alen(_Screen.ISXCache,1)
    			If Type(_Screen.ISXCache[m.lnCurLine]) == "C"
    				If Upper(m.tcFile+"?"+m.tcProvider) == Upper(_Screen.ISXCache[m.lnCurLine])
    					lnLine = lnCurLine
    					Exit
    				EndIf 
    			EndIf 
    		EndFor 
    	EndIf 
    	If m.lnLine == 0
    		Return .F.
    	EndIf 
    	
    	If Fdate(m.tcFile,1) # _Screen.ISXCache[m.lnLine,2]
    		Return .F.
    	EndIf
    	
    	toISX.nCount = _Screen.ISXCache[m.lnLine,3]
    	ALines( toISX.aList, _Screen.ISXCache[m.lnLine,4] )
    
    Return .T.
    
    
    *========================================================================================
    * Adds the current entry to the cache.
    *========================================================================================
    Procedure AddToCache
    LParameter tcProvider, toISX, tcFile
    
    	If Type("_Screen.ISXCache[1,1]") == "U"
    		_Screen.AddProperty("ISXCache[1,4]")
    	EndIf
    
    	Local lnLine
    	If Version(4) >= "07.00"
    		lnLine = Ascan( _Screen.ISXCache, m.tcFile+"?"+m.tcProvider, -1, -1, 1, 1+2+4+8 )
    	Else
    		Local lnCurLine
    		lnLine = 0
    		For lnCurLine=1 to Alen(_Screen.ISXCache)
    			If Upper(m.tcFile+"?"+m.tcProvider) == Upper(_Screen.ISXCache[m.lnCurLine])
    				lnLine = lnCurLine
    				Exit
    			EndIf 
    		EndFor 
    	EndIf 
    	If m.lnLine == 0
    		lnLine = Alen(_Screen.ISXCache,1) + 1
    		Dimension _Screen.ISXCache[m.lnLine,Alen(_Screen.ISXCache,2)]
    	EndIf 	
    
    	Local lnItem
    	_Screen.ISXCache[m.lnLine,1] = m.tcFile+"?"+m.tcProvider
    	_Screen.ISXCache[m.lnLine,2] = Fdate(m.tcFile,1)
    	_Screen.ISXCache[m.lnLine,3] = toISX.nCount
    	_Screen.ISXCache[m.lnLine,4] = ""
    	For lnItem=1 to toISX.nCount
    		_Screen.ISXCache[m.lnLine,4] = _Screen.ISXCache[m.lnLine,4] + ;
    			toISX.aList[m.lnItem] + Chr(13)+Chr(10)
    	EndFor 
    
    EndProc
    	
    
    *====================================================================
    * SW: Fills an array with all PEMs for the objectname typed in
    * Returns the number of PEMs. The object has to exist to work
    *====================================================================
    Procedure CP_Objects
    Lparameters toISX
    	
    	LOCAL lnVarCount
    	If TYPE(toISX.cEntity) = [O]
    		If Version(4) >= "07.00"
    			If    Upper(toISX.cEntity) == "_SCREEN" or Upper(toISX.cEntity) == "_VFP" ;
    			   OR Upper(toISX.cEntity) = "_VFP."
    				Return
    			EndIf 
    		EndIf 
    		Local laMembers[1]
    		toISX.nCount = AMEMBERS(laMembers, Evaluate(toISX.cEntity), 1)
    		Dimension toISX.aList[m.toISX.nCount]
    		FOR m.lnCount = 1 TO toISX.nCount
    			toISX.aList[m.lnCount] = PROPER(laMembers[m.lnCount,1])
    		NEXT
    	EndIf 
    
    EndProc 
    
    
    *====================================================================
    * SW: Fills an array with all Fields for the cursor typed in.
    * Returns the number of Fields. The cursor has to be open to work
    *====================================================================
    Procedure CP_Tables
    Lparameters toISX
    
    	LOCAL lnCount, lcName
    	lcName = JustStem(toISX.cEntity)
    	* November 11, 2004 Modified by Eddy Maue 
    	If Occurs(".",toISX.cEntity)==0 And !"m." == LOWER(toISX.cEntity+".") AND ;
             IIF(Used(m.lcName),.t.,;
             IIF(File(m.lcName+".dbf"),OpenTable(m.lcName),;
             IIF(File(m.lcName+".qpr"),ExecQuery(m.lcName),OpenView(m.lcName))))
     
    		toISX.nCount = FCOUNT(m.lcName)
    		DIMENSION toISX.aList[toISX.nCount]
    		FOR m.lnCount = 1 TO toISX.nCount
    			toISX.aList[m.lnCount] = PROPER(FIELD(m.lnCount, m.lcName))
    		NEXT
    	ENDIF
    
    EndProc 
    
    *====================================================================
    * Open the table
    * Eddy Maue
    * November 11, 2004
    *====================================================================
    Procedure OpenTable
       Lparameters lcName
       Use (m.lcName) In 0
       Return Used(m.lcName)
    ENDPROC
    
    *====================================================================
    * Open a query
    *====================================================================
    * Eddy Maue
    * November 11, 2004
    *====================================================================
    Procedure ExecQuery
       Lparameters lcName
       Do (lcName+".qpr")
       Return Used(lcName)
    ENDPROC
    
    *====================================================================
    * Open a view
    *====================================================================
    * Eddy Maue
    * November 11, 2004
    *====================================================================
    Procedure OpenView
       Lparameters lcName,lcSafety,lcConsol
       If Type("_vfp.ActiveProject")="U" .OR. EMPTY(DBC())
          Return .F.
       ENDIF
       m.lcSafety = "Set Safety "+Set("safety")
       Set Safety Off
       List Views To FILE _view.tmp NOCONSOLE 
       If ":"+ALLTRIM(Lower(lcName))+"(" $ STRTRAN(Lower(Filetostr("_view.tmp"))," ","")
          Use (lcName) In 0
       Endif
       &lcSafety
       RETURN USED(m.lcName)
    
    
    *========================================================================================
    * Displays a popup with all the values from taList, lets the user incrementally approach
    * the desired item and inserts it into the editor.
    *========================================================================================
    Procedure DisplayPopup
    LParameter toISX
    
    	Local loPopupForm
    	If toISX.nCount > 0
    		loPopupForm = CreateObject( "isxForm", toISX )
    		If VarType(m.loPopupForm) == "O"
    			loPopupForm.Show()
    		Endif
    		loPopupForm = NULL
    	EndIf 
    	Clear Class isxForm
    
    EndProc
    
    
    *====================================================================
    * Determines the source of the window identified by the passed 
    * WHandle. It returns the following values:
    *
    * -1     The window is not an edit window
    *  0     Command Window
    *  1     MODIFY COMMAND window
    *  2     MODIFY FILE window
    *  8     Menu Designer code window
    * 10     Method Edit Window in Class or Form Designer
    * 12     MODIFY PROCEDURE window
    *
    * This procedure uses _EdGetEnv() from the FoxTools.Fll to determine
    * the edit source. Passing an invalid handle causes an exception in
    * VFP 5 and VFP 3. In VFP 6 this raises an error 2028 (API function
    * caused an exception). Therefore we return -1 in this case, too.
    *====================================================================
    Procedure GetEditSource
    LParameter tnWHandle
    
    	Local laEnv[25], lnSource, lnOK, lcError
    	lcError = On( "Error" )
    	On Error lnOK = 0
    	lnOK = _EdGetEnv( m.tnWHandle, @laEnv )
    	On Error &lcError
    	If m.lnOK == 0
    		lnSource = -1
    	Else
    		lnSource = laEnv[25]
    	Endif
    	
    Return m.lnSource
    
    
    *====================================================================
    * Returns the WHandle of the current edit window or 0, if no edit
    * window is available.
    *====================================================================
    Procedure GetCurrentWindow
    
    	Local lnWindowOnTop
    	lnWindowOnTop = _WOnTop()
    	If m.lnWindowOnTop <= 0
    		Return 0
    	Endif
    	If GetEditSource( m.lnWindowOnTop ) == -1
    		lnWindowOnTop = 0
    	Endif
    	
    Return m.lnWindowOnTop
    
    
    *====================================================================
    * Returns the current cursor position in the edit window identified
    * by the WHandle. On error -1 is returned.
    *====================================================================
    Procedure GetFileCursorPos
    Lparameters tnWHandle
    
    	Local lnCursorPos
    	lnCursorPos = _EdGetPos( m.tnWHandle )
    	
    Return m.lnCursorPos
    
    
    *====================================================================
    * Changes the current cursor position in the edit window identified
    * by the WHandle.
    *====================================================================
    Procedure SetFileCursorPos
    LParameter tnWHandle, tnPosition
    
    	_EdSetPos( m.tnWHandle, m.tnPosition )
    
    EndProc
    
    
    *====================================================================
    * Returns the current line of the edit window identified by the
    * WHandle. The line number is zero based. On Error -1 is returned.
    *====================================================================
    Procedure GetCurrentLine
    LParameters tnWHandle
    
    	Local lnCursorPos, lnLineNo
    	lnCursorPos = GetFileCursorPos( m.tnWHandle )
    	If lnCursorPos < 0
    		lnLineNo = -1
    	Else
    		lnLineNo = _EdGetLNum( m.tnWhandle, m.lnCursorPos )
    	Endif
    	
    Return m.lnLineNo
    
    
    *====================================================================
    * Returns the cursor position within the current line of the edit
    * window identified by the WHandle. The cursor position is 0 based.
    * On error -1 is returned.
    *====================================================================
    Procedure GetCurrentCol
    Lparameters tnWHandle
    
    	Local lnCursorPos, lnLineNo, lnColumn, lnLineStart
    	lnCursorPos = GetFileCursorPos( m.tnWHandle )
    	If m.lnCursorPos < 0
    		Return -1
    	Endif
    	lnLineNo = GetCurrentLine( m.tnWHandle )
    	If m.lnLineNo < 0
    		Return -1
    	Endif
    	lnLineStart = GetLineStart( m.tnWHandle, m.lnLineNo )
    	lnColumn = m.lnCursorPos - m.lnLineStart
    
    Return m.lnColumn
    
    
    *====================================================================
    * Returns the beginning of the specific line in the edit window
    * identified by WHandle. Returns -1 on error.
    *====================================================================
    Procedure GetLineStart
    LParameter tnWHandle, tnLineNo
    
    	Local lnLineStart
    	lnLineStart = _EdGetLPos( m.tnWHandle, m.tnLineNo )
    	
    Return m.lnLineStart
    
    
    *====================================================================
    * Returns the text of the specified line in the edit window 
    * identified by the WHandle. A terminating carriage return is 
    * removed. Returns an empty string on error. The line must be zero
    * based.
    *====================================================================
    Procedure GetLine
    Lparameters tnWHandle, tnLine
    
    	Local lnStartPos, lnEndPos, lcString
    	lnStartPos = GetLineStart( m.tnWHandle, m.tnLine )
    	lnEndPos = GetLineStart( m.tnWHandle, m.tnLine+1 )
    	If m.lnStartPos == m.lnEndPos
    		lcString = ""
    	Else
    		lnEndPos = m.lnEndPos - 1
    		lcString = _EdGetStr( m.tnWHandle, m.lnStartPos, m.lnEndPos )
    		lcString = Chrtran( m.lcString, Chr(13), "" )
    	Endif
    
    Return m.lcString
    
    
    *====================================================================
    * Returns the text in the current line that is to the left of the 
    * cursor in the edit window identified by the WHandle. Returns "" on
    * error.
    *====================================================================
    Procedure GetLineLeftFromCursor
    Lparameters tnWHandle
    
    	Local lnCurLine, lnCurCol, lcLine
    	lnCurLine = GetCurrentLine( m.tnWHandle )
    	If m.lnCurLine < 0
    		Return ""
    	Endif
    	lnCurCol = GetCurrentCol( m.tnWHandle )
    	If m.lnCurCol < 0
    		Return ""
    	Endif
    	If m.lnCurCol == 0
    		lcLine = ""
    	Else
    		lcLine = GetLine( m.tnWHandle, m.lnCurLine )
    		lcLine = Left( m.lcLine, m.lnCurCol )
    	Endif
    
    Return m.lcLine
    
    
    *====================================================================
    * Inserts text in the edit window identified by WHandle. The text is
    * stored in tcText, the position is optional. tcOptions can contains
    * a combination of the following values:
    *
    *  R   The current selection is replaced
    *  B   The cursor is positioned at the beginning of the inserted
    *      text.
    *  E   (default) The cursor is positioned at the end of the inserted 
    *      text.
    *  H   The inserted text is highlighted.
    *====================================================================
    Procedure InsertText
    Lparameters tnWHandle, tcText, tnPosition, tcOptions
    
    	*-----------------------------------------------------------------
    	* Normalize options
    	*-----------------------------------------------------------------
    	Local lcOptions
    	If Vartype(m.tcOptions) == "C"
    		lcOptions = Upper( Alltrim(m.tcOptions) )
    	Else
    		lcOptions = ""
    	Endif
    	
    	*-----------------------------------------------------------------
    	* If a position is passed, Change the current cursor position
    	* accordingly.
    	*-----------------------------------------------------------------
    	If Vartype(m.tnPosition) == "N"
    		SetFileCursorPos( m.tnWHandle, m.tnPosition )
    	Endif
    	
    	*-----------------------------------------------------------------
    	* Insert the Text at the current position. If the "R" option is
    	* used, delete the current selection.
    	*-----------------------------------------------------------------
    	Local lnStartPosition, lnEndPosition
    	If "R" $ m.lcOptions
    		_EdDelete( m.tnWHandle )
    	Endif
    	lnStartPosition = GetFileCursorPos( m.tnWHandle )
    	_EdInsert( m.tnWHandle, m.tcText, Len(m.tcText) )
    	lnEndPosition = GetFileCursorPos( m.tnWHandle )
    	
    	*-----------------------------------------------------------------
    	* Set the cursor accordingly. "E" is the default of VFP. We don't
    	* need any action for that.
    	*-----------------------------------------------------------------
    	Do Case
    	Case "B" $ m.lcOptions
    		SetFileCursorPos( m.tnWHandle, m.lnStartPosition )
    	Case "H" $ m.lcOptions
    		_EdSelect( m.tnWHandle, m.lnStartPosition, m.lnEndPosition )
    	Endcase
    
    EndProc
    
    
    *========================================================================================
    * Fills an array with all variable declarations in the current procedure of the edit 
    * window identified by the WHandle. Variable declarations are only searched backward from
    * the current position. Returns the number of variables.
    *
    *! 2004-10Oct-19  ChrisW 
    *      Added support for variables with non-english characters such as "�".
    *      In VFP 9 the array limitation has been lifted.
    *========================================================================================
    Procedure CP_Variables
    Lparameters toISX
    
    	*--------------------------------------------------------------------------------------
    	* Check if the current entity is a variable
    	*--------------------------------------------------------------------------------------
    	Local llIsVariable
    	DO Case
    	Case Upper(toISX.cEntity)=="M"
    		llIsVariable = .T.
    	Case Empty(m.toISX.cEntity)
    		If Empty(toISX.cInvocation)
    			llIsVariable = .T.
    		Else
    			llIsVariable = .F.
    		EndIf 
    	Otherwise 
    		llIsVariable = .F.
    	EndCase 
    	If not m.llIsVariable 
    		Return
    	EndIf
    	
    	*-----------------------------------------------------------------
    	* Get the current line as a starting point. We start with the line
    	* before that line. 
    	*-----------------------------------------------------------------
    	Local lnEnd
    	lnEnd = GetCurrentLine( toISX.nWHandle )
    	If lnEnd <= 0
    		Return
    	Else
    		lnEnd = m.lnEnd - 1
    	Endif
    
    	*-----------------------------------------------------------------
    	* Because GetLine() is quite slow with large program files, we
    	* read the entire program up to the line before the current line
    	* into an array and parse that. Since an array can only contain
    	* up to 65000 lines, we make sure that we don't read more than 
    	* that into the laText array.
    	*-----------------------------------------------------------------
    	Local lnLineCount, laText[1], lnStart
    	If m.lnEnd >= 65000 and Version(4) < "09.00"
    		lnStart = m.lnEnd - 65000
    	Else
    		lnStart = 0
    	Endif
    	lnLineCount = AGetLines(m.toISX.nWHandle,@laText,m.lnStart,m.lnEnd)
    		
    	*--------------------------------------------------------------------------------------
    	* Parse all lines backwards for the following keywords: LOCAL,
    	* PUBLIC, PROCEDURE, FUNCTION. We add all variables in the
    	* LOCAL and PUBLIC lines and stop parsing when we find PROCEDURE
    	* or FUNCTION.
    	*--------------------------------------------------------------------------------------
    	Local lnCurrentLine, lcLine, lnPos, lcCommand, lcValidCmds
    	For lnCurrentLine = m.lnLineCount to 1 Step -1
    		lcLine = NormalizeLine( laText[m.lnCurrentLine] )
    		If Len(m.lcLine) < 4
    			Loop
    		EndIf
    		If Version(4) >= "07.00"
    			lcCommand = GetWordNum(m.lcLine,2)
    		Else
    			lcCommand = X6_GetWordNum(m.lcLine,2)
    		EndIf 
    		If m.lcCommand == "="
    			Loop
    		EndIf 
    		If Version(4) >= "07.00"
    			lcCommand = GetWordNum(m.lcLine,1)
    		Else
    			lcCommand = X6_GetWordNum(m.lcLine,1)
    		EndIf 
    		lcValidCmds = ;
    			"LOCAL,PUBLIC,LPARAMETERS,PARAMETERS,PRIVATE,PROCEDURE,FUNCTION,PROTECTED," + ;
    			"HIDDEN"
    		If not IsFoxProCommand(m.lcCommand,m.lcValidCmds)
    			Loop
    		EndIf
    		lnPos = At( " ", m.lcLine )
    		If m.lnPos == 0 or m.lnPos == Len(m.lcLine)
    			Loop
    		Endif
    		lcLine = Alltrim( Substr(m.lcLine,m.lnPos) )
    	  If IsFoxProCommand(m.lcCommand,"LOCAL")
    			If Version(4) >= "07.00"
    				lcCommand = GetWordNum(m.lcLine,1)
    			Else
    				lcCommand = X6_GetWordNum(m.lcLine,1)
    			EndIf 
    			If IsFoxProCommand(m.lcCommand,"ARRAY")
    				lnPos = At( " ", m.lcLine )
    				If m.lnPos == 0 or m.lnPos == Len(m.lcLine)
    					Loop
    				Endif
    				lcLine = Alltrim( Substr(m.lcLine,m.lnPos) )
    			EndIf 
    	  EndIf
    		If IsFoxProCommand( m.lcCommand, "PROCEDURE,FUNCTION,PROTECTED,HIDDEN" )
    			lnPos = At( "(", m.lcLine )
    			If m.lnPos == 0 or m.lnPos == Len(m.lcLine)
    				Exit
    			EndIf
    			lcLine = Substr(m.lcLine,m.lnPos+1)
    		EndIf
    	  lnCurrentLine = m.lnCurrentLine - ;
    	  	CP_VariablesAdd( m.toISX, m.lcLine, m.lnCurrentLine, @laText )
    		If IsFoxProCommand( m.lcCommand, "PROCEDURE,FUNCTION,PROTECTED,HIDDEN" )
    			Exit
    		Endif
    	Endfor
    	
    EndProc 
    
    
    *========================================================================================
    * 
    *========================================================================================
    Procedure CP_VariablesAdd
    LParameter toISX, tcLine, tnCurrentLine, taText
    
    	Local lcLine, lnLineOffset, lnCurrentVar, laDeclarations[1], lcCurrentVar, ;
    		lnPosInVar, lcChar, lnPos
    	lcLine = m.tcLine
    	lnLineOffset = 0
    	
    	Do While .T.
    		lcLine = Chrtran( m.lcLine, ",", Chr(13) )
    		For lnCurrentVar = 1 to ALines( laDeclarations, lcLine )
    			lcCurrentVar = Alltrim( laDeclarations[m.lnCurrentVar] )
    			If Empty( m.lcCurrentVar )
    				Loop
    			Endif
    			If     not IsAlpha( m.lcCurrentVar ) ;
    			   and not Left(m.lcCurrentVar,1) == "_"
    				Loop
    			Endif
    			lnPos = At( " ", m.lcCurrentVar )
    			If m.lnPos == 0
    				lnPos = Len( m.lcCurrentVar )
    			Else
    				lnPos = m.lnPos - 1
    			Endif
    			lcCurrentVar = Left( m.lcCurrentVar, m.lnPos )
    			If LEFT(LOWER(m.lcCurrentVar),2)=='m.'
    				lcCurrentVar = SUBSTR(m.lcCurrentVar,3)
    			EndIf
    			For m.lnPosInVar = 2 to Len(m.lcCurrentVar)
    				lcChar = SubStr(m.lcCurrentVar,m.lnPosInVar,1)
    				If not (IsAlpha(m.lcChar) or IsDigit(m.lcChar) or m.lcChar="_")
    					lcCurrentVar = Left( m.lcCurrentVar, m.lnPosInVar-1 )
    					Exit
    				Endif
    			Endfor
    			toISX.nCount = m.toISX.nCount + 1
    			Dimension toISX.aList[m.toISX.nCount]
    			toISX.aList[m.toISX.nCount] = m.lcCurrentVar
    		Endfor
    		If Right(m.lcLine,1) # ";"
    			Exit
    		Endif
    		lnLineOffset = m.lnLineOffset + 1
    		If m.tnCurrentLine + m.lnLineOffset > Alen(taText,1)
    			Exit
    		Endif
    		lcLine = NormalizeLine( ;
    			taText[m.tnCurrentLine+m.lnLineOffset] ;
    		)
    	Enddo
    
    Return m.lnLineOffset
    
    
    *========================================================================================
    * Returns .T., when the first string is a FoxPro command.
    *========================================================================================
    Procedure IsFoxProCommand
    LParameter tcCommand, tcCommandList
    
    	Local laList[1], lnLine, llFound
    	
    	llFound = .F.
    	For lnLine=1 to ALines(laList,Chrtran(m.tcCommandList,",",Chr(13)+Chr(10)))
    		If Left(Upper(laList[m.lnLine]),Len(m.tcCommand)) == Upper(m.tcCommand)
    			llFound = .T.
    			Exit
    		Endif
    	EndFor 
    
    Return m.llFound
    
    
    *====================================================================
    * Normalizes a line. This means: All tabs are converted to single
    * blanks, leading or trailing blanks are removed. Comments starting
    * with && are removed.
    *====================================================================
    Procedure NormalizeLine
    Lparameters tcLine
    
    	Local lcLine, lnPos
    	lcLine = Chrtran( m.tcLine, Chr(9), " " )
    	If "&"+"&" $ m.lcLine
    		lnPos = At( "&"+"&", m.lcLine )
    		lcLine = Left( m.lcLine, m.lnPos-1 )
    	Endif 
    	lcLine = Alltrim(m.lcLine)
    
    Return m.lcLine
    
    
    *====================================================================
    * GetKeyLabel takes the parameters passed to the KeyPress event and
    * returns the label name that can be used for KEYBOARD or ON KEY
    * LABEL, etc.
    *====================================================================
    Procedure GetKeyLabel
    LParameter tnKeyCode, tnSAC
    
    	Local lcLabel
    	Do Case
    	Case Between(m.tnKeyCode,33,126)
    		lcLabel = Chr(m.tnKeyCode)
    	Case Between(m.tnKeyCode,128,255)
    		lcLabel = Chr(m.tnKeyCode)
    	Case m.tnSAC == 2 and Between(m.tnKeyCode,1,26)
    		Do Case
    		Case m.tnKeyCode == 2
    			lcLabel = "CTRL+RIGHTARROW"
    		Case m.tnKeyCode == 8
    			lcLabel = ""
    		Case m.tnKeyCode == 10
    			lcLabel = "CTRL+ENTER"
    		Case m.tnKeyCode == 23
    			lcLabel = "CTRL+END"
    		Case m.tnKeyCode == 26
    			lcLabel = "CTRL+LEFTARROW"
    		Otherwise
    			lcLabel = "CTRL+" + Chr(m.tnKeyCode+64)
    		Endcase
    	Case m.tnSAC == 0 and m.tnKeyCode < 0
    		lcLabel = "F" + Alltrim(Str(Abs(m.tnKeyCode)+1))
    	Case m.tnSAC == 0 and m.tnKeyCode == 22
    		lcLabel = "INS"
    	Case m.tnSAC == 1 and m.tnKeyCode == 22
    		lcLabel = "SHIFT+INS"
    	Case m.tnSAC == 0 and m.tnKeyCode == 1
    		lcLabel = "HOME"
    	Case m.tnSAC == 0 and m.tnKeyCode == 7
    		lcLabel = "DEL"
    	Case m.tnSAC == 0 and m.tnKeyCode == 28
    		lcLabel = "F1"
    	Case m.tnSAC == 0 and m.tnKeyCode == 6
    		lcLabel = "END"
    	Case m.tnSAC == 0 and m.tnKeyCode == 18
    		lcLabel = "PGUP"
    	Case m.tnSAC == 0 and m.tnKeyCode == 3
    		lcLabel = "PGDN"
    	Case m.tnSAC == 0 and m.tnKeyCode == 5
    		lcLabel = "UPARROW"
    	Case m.tnSAC == 0 and m.tnKeyCode == 28
    		lcLabel = "F1"
    	Case m.tnSAC == 0 and m.tnKeyCode == 24
    		lcLabel = "DNARROW"
    	Case m.tnSAC == 0 and m.tnKeyCode == 4
    		lcLabel = "RIGHTARROW"
    	Case m.tnSAC == 0 and m.tnKeyCode == 19
    		lcLabel = "LEFTARROW"
    	Case m.tnSAC == 0 and m.tnKeyCode == 27
    		lcLabel = "ESC"
    	Case m.tnSAC == 0 and m.tnKeyCode == 13
    		lcLabel = "ENTER"
    	Case m.tnSAC == 0 and m.tnKeyCode == 127
    		lcLabel = "BACKSPACE"
    	Case m.tnSAC == 0 and m.tnKeyCode == 9
    		lcLabel = "TAB"
    	Case m.tnSAC == 0 and m.tnKeyCode == 32
    		lcLabel = "SPACEBAR"
    	Case m.tnSAC == 1 and m.tnKeyCode == 13
    		lcLabel = "SHIFT+ENTER"
    	Case m.tnSAC == 1 and m.tnKeyCode == 127
    		lcLabel = "SHIFT+BACKSPACE"
    	Case m.tnSAC == 1 and m.tnKeyCode == 15
    		lcLabel = "SHIFT+TAB"
    	Case m.tnSAC == 1 and m.tnKeyCode == 32
    		lcLabel = "SHIFT+SPACEBAR"
    	Case m.tnSAC == 2 and m.tnKeyCode == 29
    		lcLabel = "CTRL+HOME"
    	Case m.tnSAC == 2 and m.tnKeyCode == 31
    		lcLabel = "CTRL+PGUP"
    	Case m.tnSAC == 2 and m.tnKeyCode == 30
    		lcLabel = "CTRL+PGDN"
    	Case m.tnSAC == 2 and m.tnKeyCode == 128
    		lcLabel = "CTRL+BACKSPACE"
    	Case m.tnSAC == 2 and m.tnKeyCode == 32
    		lcLabel = "CTRL+SPACEBAR"
    	Otherwise
    		lcLabel = ""
    	Endcase
    
    Return m.lcLabel
    
    
    *====================================================================
    * Fills an array with all lines between nStart and nEnd. 
    *====================================================================
    Procedure AGetLines
    LParameter tnWHandle, raText, tnStart, tnEnd
    
    	*-----------------------------------------------------------------
    	* Copy the text between nStart and nEnd into a string variable.
    	*-----------------------------------------------------------------
    	Local lnStartPos, lnEndPos, lcString
    	lnStartPos = GetLineStart( m.tnWHandle, m.tnStart )
    	lnEndPos = GetLineStart( m.tnWHandle, m.tnEnd+1 ) - 1
    	lcString = _EdGetStr( m.tnWHandle, m.lnStartPos, m.lnEndPos )
    
    	*-----------------------------------------------------------------
    	* And parse this into an array
    	*-----------------------------------------------------------------
    	Local lnCount
    	lnCount = ALines( raText, m.lcString )
    	
    Return m.lnCount
    
    
    *====================================================================
    * The FoxTools function _AGetEnv() doesn't return proper font infor-
    * mation. Instead it claims that "MS Sans Serif", 8 pt. is the 
    * current font. This function returns font information for the speci-
    * fied window by accessing the GDI.
    *====================================================================
    Procedure WGetFontInfo
    LParameter tnWHandle, rcFontName, rnFontSize, rnStyle
    
    	*-----------------------------------------------------------------
    	* In addition to the window handle of this window we also need
    	* the HWND of the child window that contains the actual editor.
    	* The GetClientWindow() function retrieves this window handle.
    	*-----------------------------------------------------------------
    	Local lnHWND
    	lnHWND = GetClientWindow( m.tnWHandle )
    	If m.lnHWND == 0
    		Return .F.
    	Endif
    	
    	*-----------------------------------------------------------------
    	* Using this HWND we can then get a Device Context. 
    	*-----------------------------------------------------------------
    	Local lnHWND, lnHDC
    	Declare LONG GetDC in Win32API LONG
    	lnHDC = GetDC( m.lnHWND )
    	If m.lnHDC == 0
    		Return .F.
    	Endif
    	
    	*-----------------------------------------------------------------
    	* With this device context we can now get an object handle to the
    	* currently selected font.
    	*-----------------------------------------------------------------
    	Local lnHFONT
    	Declare LONG GetCurrentObject in Win32API LONG, LONG
    	lnHFONT = GetCurrentObject( m.lnHDC, 6 )  && OBJ_FONT
    	If m.lnHFONT == 0
    		Return .F.
    	Endif
    	
    	*-----------------------------------------------------------------
    	* The HFONT handle to the current font can be used to obtain more
    	* detailled information about the selected font. We need to rename
    	* the API function GetObject(), because it interferes with VFP's
    	* GETOBJECT() function
    	*-----------------------------------------------------------------
    	Local lcLogFont
    	Declare Integer GetObject in Win32API as GDI_GetObject ;
    		LONG, Integer, String@
    	lcLogFont = Replicate( Chr(0), 1024 )
    	If GDI_GetObject( m.lnHFONT, 1024, @lcLogFont ) == 0
    		Return .F.
    	Endif
    	
    	*-----------------------------------------------------------------
    	* Now to extract the font information from the LOGFONT structure.
    	*-----------------------------------------------------------------
    	Local lnSize, lcName, lnStyle
    	lnSize = Abs( FromInt(Left(m.lcLogFont,4)) - 2^32 )
    	lcName = SubStr( m.lcLogFont, 29 )
    	lcName = Left( m.lcName, At(Chr(0),m.lcName)-1 )
    	lnStyle = 0
    	If FromInt(SubStr(m.lcLogFont,17,4)) == 700
    		lnStyle = m.lnStyle + 1
    	Endif
    	If FromInt(SubStr(m.lcLogFont,21,4)) # 0
    		lnStyle = m.lnStyle + 2
    	Endif
    	
    	*-----------------------------------------------------------------
    	* We now have the height of the font in pixels but what we need 
    	* are points.
    	*-----------------------------------------------------------------
    	Local lnResolution
    	Declare Integer GetDeviceCaps in Win32API Integer, Integer
    	lnResolution = GetDeviceCaps( m.lnHDC, 90 ) && LOGPIXELSY
    	lnSize = m.lnSize / m.lnResolution * 72
    	lnSize = Round( m.lnSize, 0 )
    		
    	*-----------------------------------------------------------------
    	* Finally release the device context
    	*-----------------------------------------------------------------
    	Declare Integer ReleaseDC In Win32API LONG, LONG
    	ReleaseDC( m.lnHWND, m.lnHDC )
    	
    	*-----------------------------------------------------------------
    	* And pass the values pack as parameters
    	*-----------------------------------------------------------------
    	rcFontName = m.lcName
    	rnFontSize = m.lnSize
    	rnStyle = m.lnStyle
    	
    Return .T.
    
    
    *====================================================================
    * The editor only works on the editor window and you can only get the
    * HWND of this window using the Window Handle. For many Windows ope-
    * rations, however, you need the HWND of the child window that con-
    * tains the actual editor area. This function returns the HWND of 
    * this window. It's not that easy, because Method snippet windows
    * actually have two child windows, one for the text editor and one
    * with the method and object dropdown combos.
    *====================================================================
    Procedure GetClientWindow
    LParameter tnWHandle
    	
    	*-----------------------------------------------------------------
    	* Convert the Window Handle into a HWND
    	*-----------------------------------------------------------------
    	Local lnHWND
    	lnHWND = _WhToHWND( m.tnWHandle )
    
    	*-----------------------------------------------------------------
    	* FindWindowEx returns all child windows of a given parent window.
    	* We use it to find a child of the edit window that doesn't have
    	* another child window, because method edit windows have a second 
    	* which we can identify since it has another child window.
    	*-----------------------------------------------------------------
    	Local lnChild
    	Declare Integer FindWindowEx in Win32API ;
    		Integer, Integer, String, String
    	lnChild = 0
    	Do While .T.
    		lnChild = FindWindowEx( m.lnHWND, m.lnChild, NULL, NULL )
    		If m.lnChild == 0
    			Exit
    		Endif
    		If FindWindowEx( m.lnChild, 0, NULL, NULL ) == 0
    			Exit
    		Endif
    	Enddo
    		
    Return m.lnChild
    
    
    *====================================================================
    * Returns the position of the text cursor (caret) in _SCREEN coordi-
    * nates. If the window identified by the passed window handle doesn't
    * have the focus, or the position can't be determined, this function
    * returns .F.
    *====================================================================
    Procedure GetCaretPosition
    LParameter tnWHandle, rnTop, rnLeft
    
    	*-----------------------------------------------------------------
    	* Check whether this window has got the focus.
    	*-----------------------------------------------------------------
    	Declare Integer GetFocus in Win32API
    	If GetFocus() # _WhToHWND( m.tnWHandle )
    		Return .F.
    	Endif
    	
    	*-----------------------------------------------------------------
    	* Determine the cursor position. This position is relative to the
    	** OK
    	* client area of the editing subwindow of the actual editing win-
    	* dow.
    	*-----------------------------------------------------------------
    	Local lnLeft, lnTop, lcPOINT
    	Declare Integer GetCaretPos in Win32API String@
    	lcPOINT = Space(8)
    	If GetCaretPos( @lcPOINT ) == 0
    		lnLeft = MCol(3)
    		lnTop = MRow(3)
    	Else
    		lnLeft = Asc(Left(m.lcPOINT,1))+256*Asc(SubSTr(m.lcPOINT,2,1))
    		lnTop = Asc(SubSTr(m.lcPOINT,5,1))+256*Asc(SubStr(m.lcPOINT,6,1))
    	Endif
    	
    	*-----------------------------------------------------------------
    	* To convert this postion to _SCREEN coordinates, we have to 
    	* determine the position of the client window relative to the 
    	* desktop window and correlate this with the absolute position of
    	* the _SCREEN window. Hence, we need first the HWNDs of both 
    	* windows.
    	*-----------------------------------------------------------------
    	Local lnChild, lnScreen
    	Declare Integer GetParent in Win32API Integer
    	lnChild = GetClientWindow( m.tnWHandle )
    	If m.lnChild == 0
    		Return .F.
    	Endif
    	lnScreen = GetParent( _WhToHWND(m.tnWHandle) )
    	If m.lnScreen == 0
    		Return .F.
    	Endif
    
    	*-----------------------------------------------------------------
    	* Now we can determine the position of both windows.
    	*-----------------------------------------------------------------
    	Local lnChildTop, lnChildLeft, lnScreenTop, lnScreenLeft, lcRect
    	lcRect = Replicate( Chr(0), 16 )
    	Declare Integer GetWindowRect in Win32API Long, String@
    	GetWindowRect( m.lnChild, @lcRect )
    	lnChildLeft = FromInt( Left(m.lcRect,4) )
    	lnChildTop = FromInt( SubSTr(m.lcRect,5,4) )
    	GetWindowRect( m.lnScreen, @lcRect )
    	lnScreenLeft = FromInt( Left(m.lcRect,4) )
    	lnScreenTop = FromInt( SubSTr(m.lcRect,5,4) )
    	
    	*-----------------------------------------------------------------
    	* Now combine the position of the edit window and the cursor
    	* position.
    	*-----------------------------------------------------------------
    	rnLeft = m.lnLeft + m.lnChildLeft - m.lnScreenLeft
    	rnTop = m.lnTop + m.lnChildTop - m.lnScreenTop
    
    EndProc
    	
    	
    Procedure FromInt
    Parameter tcString
      Private nValue, nT
      nValue =0
      For nT = 1 to Len(tcString)
        nValue = nValue + Asc(SubStr(tcString,nT,1))*256^(nT-1)
      Endfor
    Return nValue
    
    
    *====================================================================
    * The following class displays a popup window at the current cursor
    * position and lets the user continue to type.
    *
    * The characters a-z, A-Z, 0-9 and _ are inserted into the active
    * edit window as the user types. The previous position is saved in
    * order to restore the text if necessary.
    *
    * ESC terminates the popup and doesn't change the text.
    * 
    * TAB inserts the current selection and terminates the popup.
    *
    * SPACEBAR inserts the current selection, adds a blank and terminates
    * the popup.
    *
    * Any other key terminates the popup and is repeated so it is handled
    * properly by VFP. If the user enters the first character that 
    * doesn't match an item in the list, or entered a full item where 
    * none exists that has the same name, but additional characters, the
    * list is terminated as well.
    *
    *====================================================================
    Define CLASS isxForm as Form
    
    	AlwaysOnTop = .T.
    	WindowType = 1
    	TitleBar = 0
    	BorderStyle = 0
    
    	nWHandle = 0
    	nCurrentPos = 0
    	cSearchString = ""
    	cVarString = ""
    	Dimension aItems[1,2]
    	lScrolled = .F.
    	*Mike Yearwood - these support reducing screen caption flicker
    	cScreenCaption = ""
    	cWindowCaption = ""
    	lMaximized = .F.
    	
    	Add Object isxList as Listbox with ;
    		ColumnCount = 2, ;
    		ColumnLines = .F., ;
    		IncrementalSearch = .F.
    
    PROCEDURE Load
    this.lMaximized = wmaximum()
    IF THIS.lMaximized
    	THIS.cWindowCaption = LOWER(WTITLE())
    	THIS.cScreenCaption = _screen.Caption
    ENDIF
    RETURN DODEFAULT()
    ENDPROC
    
    PROCEDURE Show
    *====================================================================
    * Mike Yearwood
    * When the edit window is maximized, the screen caption reads
    * currentedit.prg * - current vfp system window caption
    * When this window goes active, the screen caption changes
    * which causes a flicker. To stop that flicker, set the screen
    * caption to what it was before.
    *====================================================================
    
    IF THIS.lMaximized
    	_Screen.Caption = this.cWindowCaption + " * - " + this.cScreenCaption
    ENDIF
    ENDPROC
    
    PROCEDURE Destroy
    *Mike Yearwood
    *Prevent screen caption flicker.
    IF THIS.lMaximized
    	_Screen.Caption = this.cScreenCaption
    ENDIF
    ENDPROC
    
    *====================================================================
    * When the form is initialized, we have to determine its position
    * and get a handle to the current edit window. Pass an array to this
    * form that contains all possible values the user can enter.
    *====================================================================
    Procedure Init
    LParameter toISX
    With This
    
    	*-----------------------------------------------------------------
    	* Get the handle for the current window. 
    	*-----------------------------------------------------------------
    	.nWHandle = toISX.nWHandle
    	.nCurrentPos = GetFileCursorPos( .nWHandle )
    	
    	*-----------------------------------------------------------------
    	* Copy the array and sort it case-insensitive
    	*-----------------------------------------------------------------
    	Local laValues[1], lnValue
    	If Version(4) >= "07.00"
    		Asort( toISX.aList, -1, -1, 0, 1 )
    	Else
    		Dimension laValues[toISX.nCount,2]
    		For lnValue = 1 to toISX.nCount
    			laValues[m.lnValue,1] = Upper(toISX.aList[m.lnValue])
    			laValues[m.lnValue,2] = m.lnValue
    		EndFor
    		Asort( laValues, 1 )
    	EndIf 
    		
    	*--------------------------------------------------------------------------------------
    	* Fill the listbox with all possible values.
    	*--------------------------------------------------------------------------------------
    	Local lcValue, lnWidth, lnMaxWidth, lnValue, lcVarString, lnAvgCharWidth
    	lnMaxWidth = 0
    	lcVarString = ""
    	Dimension .aItems[toISX.nCount,2]
    	lnAvgCharWidth = Fontmetric(6,.isxList.FontName,.isxList.FontSize)
    	For lnValue = 1 to toISX.nCount
    		If Version(4) >= "07.00"
    			lcValue = toISX.aList[m.lnValue]
    		Else
    			lcValue = toISX.aList[laValues[m.lnValue,2]]
    		EndIf 
    		.aItems[m.lnValue,1] = Upper(m.lcValue)
    		.aItems[m.lnValue,2] = m.lcValue
    		lcVarString = m.lcVarString + ":" + Padr(Upper(m.lcValue),128)
    		lnWidth = Txtwidth(m.lcValue,.isxList.FontName,.isxList.FontSize) * m.lnAvgCharWidth
    		lnMaxWidth = Max( m.lnMaxWidth, m.lnWidth )
    	EndFor
    	.cVarString = m.lcVarString
    	lnMaxWidth = m.lnMaxWidth + 30
    	With .isxList
    		.ColumnWidths = "0," + Alltrim(Str(m.lnMaxWidth))
    		.RowSource = "Thisform.aItems"
    		.RowSourceType = 5
    		.Requery()
    		.Move( 0, 0, m.lnMaxWidth, 110 )
    		If .ListCount < 6
    			.Height = .ListCount*16 + 14
    		Endif
    	EndWith
    	.Width = m.lnMaxWidth
    	.Height = .isxList.Height
    	
    	*-----------------------------------------------------------------
    	* The original version of the following few code blocks has been 
    	* kindly provided by Louis D. Zelus. I've modified it to match the
    	* rest of the code here. The purpose is to simulate a behavior
    	* in VB. If the variable is inserted via ALT+I, everything already
    	* typed is used to position the list and if the already entered
    	* parts are sufficient to uniquely identify the variablem it's
    	* inserted without displaying the popup at all. All blocks based
    	* on his code start with LDZ.
    	*-----------------------------------------------------------------
    	
    	*-----------------------------------------------------------------
    	* LDZ: If a variable name has been entered, we highlight it in the
    	* edit window.
    	*-----------------------------------------------------------------
    	Local lnStartPos, lnEndPos, lcInput
    	lcInput = toISX.cName
    	If Len(m.lcInput) > 0
    		lnEndPos = GetFileCursorPos( .nWHandle )
    		lnStartPos = m.lnEndPos - Len(m.lcInput)
    		_EdSelect( .nWHandle, m.lnStartPos, m.lnEndPos )
    	Endif
    
    	*-----------------------------------------------------------------
    	* LDZ: Try to find this variable name in the list of variables we
    	* assembled above. If we find it, we select this entry and save
    	* what has been entered so far.
    	*-----------------------------------------------------------------
    	Local lnIndex
    	If Len(m.lcInput) > 0
    	 	lnIndex = At( ":"+Upper(m.lcInput), .cVarString )
    		If m.lnIndex == 0
    			.isxlist.ListIndex = 0
    		Else
    			.isxlist.ListIndex = (m.lnIndex/129) + 1
    		Endif
    		.cSearchString = m.lcInput
    	Endif
    
    	*-----------------------------------------------------------------
    	* LDZ: If there's no second instance of this start, accept it 
    	* immediately without displaying the popup. The full variable name
    	* is inserted with the proper case at the current position 
    	* replacing the selection.
    	*-----------------------------------------------------------------
    	If Len(m.lcInput) > 0
     		If At( ":"+Upper(m.lcInput), .cVarString, 2 ) == 0 ;
    	 	   and not m.lnIndex == 0
    			InsertText( .nWHandle, "", , "R" )
    			InsertText( .nWHandle, .isxList.List[.isxList.ListIndex,2] )
    			Return .F.
    		Endif
    	Endif
    
    	*-----------------------------------------------------------------
    	* Determine the cursor position in _SCREEN coordinates
    	*-----------------------------------------------------------------
    	Local lnLeft, lnTop
    	If not GetCaretPosition( .nWHandle, @lnTop, @lnLeft )
    		Return .F.
    	Endif
    	
    	*-----------------------------------------------------------------
    	* As we position the popup BELOW the current line, we need to 
    	* know the height of this line in pixels.
    	*-----------------------------------------------------------------
    	Local lnLineHeight, lnAvgCharWidth, lcFontName, lnFontSize
    	If not WGetFontInfo( .nWHAndle, @lcFontName, @lnFontSize )
    		Return .F.
    	Endif
    	lnLineHeight = FontMetric( 1, m.lcFontName, m.lnFontSize )
    	lnAvgCharWidth = FontMetric(6,m.lcFontName,m.lnFontSize)
    	
    	*-----------------------------------------------------------------
    	* We make sure that the popup doesn't move below the VFP window to
    	* keep it visible all the time. If it doesn't fit into the area 
    	* below the cursor, we move it upwards.
    	*-----------------------------------------------------------------
    	If m.lnTop + .Height + m.lnLineHeight > _Screen.Height
    		lnTop = m.lnTop - .Height
    	Else
    		lnTop = m.lnTop + m.lnLineHeight
    	Endif
    	.Top = m.lnTop
    	
    	*------------------------------------------------------------------
    	* As for the height of the VFP window, we do the same for the
    	* width. If the popup won't fit into the VFP _Screen, we flip
    	* it horizontally.
    	*------------------------------------------------------------------
    	If m.lnLeft + .Width + lnAvgCharWidth > _Screen.Width
    		lnLeft = m.lnLeft - .Width
    	Else
    		lnLeft = m.lnLeft + lnAvgCharWidth
    	EndIf
    	.Left = m.lnLeft
    Endwith
    EndProc
    
    
    *========================================================================================
    * If we don't hide the popup before releasing it, the focus might not go back to the 
    * edit window. This happens when we have a Data Session window docked on one side and 
    * a code editing window maximized. In this case the focus switches to the datasession
    * window and Aliases listbox disappears.
    *========================================================================================
    Procedure Release
    	This.Hide()
    EndProc
    
    	
    Procedure isxList.KeyPress
    LParameter tnKeyCode, tnSAC
    With This
    
    	*-----------------------------------------------------------------
    	* If the Up or Down Arrow has been pressed, we do nothing, but 
    	* remember that the user scrolled in the list, because this acti-
    	* vates the enter key.
    	*-----------------------------------------------------------------
    	Local llScrolled
    	If m.tnSAC == 0 and InList( m.tnKeyCode, 5, 24 )
    		.Parent.lScrolled = .T.
    		Return
    	Endif
    	llScrolled = .Parent.lScrolled
    	.Parent.lScrolled = .F.
    
    	*-----------------------------------------------------------------
    	* Determines whether a name qualifier has been entered.
    	*-----------------------------------------------------------------
    	Local llQualifier
    	llQualifier = .F.
    	If m.tnSAC == 0 and Between(m.tnKeyCode,Asc("a"),Asc("z"))
    		llQualifier = .T.
    	Endif	
    	If m.tnSAC == 1 and Between(m.tnKeyCode,Asc("A"),Asc("Z"))
    		llQualifier = .T.
    	Endif	
    	If m.tnSAC == 0 and Between(m.tnKeyCode,Asc("0"),Asc("9"))
    		llQualifier = .T.
    	Endif	
    	If m.tnSAC == 1 and m.tnKeyCode == Asc("_")
    		llQualifier = .T.
    	Endif	
    	
    	*-----------------------------------------------------------------
    	* If a qualifier has been entered, we insert the character into
    	* the current edit window. We also perform an incremental search
    	* on the Text being inserted.
    	*-----------------------------------------------------------------
    	Local lcSearch, lnIndex
    	If m.llQualifier
    		lcSearch = .Parent.cSearchString + Chr(m.tnKeyCode)
    	Endif
    	
    	*-----------------------------------------------------------------
    	* BACKSPACE deletes the last character.
    	*-----------------------------------------------------------------
    	If m.tnSAC == 0 and m.tnKeyCode == 127
    		If Len(.Parent.cSearchString) > 0
    			lcSearch = .Parent.cSearchString
    			lcSearch = Left( m.lcSearch, Len(m.lcSearch)-1 )
    			llQualifier = .T.
    		Endif
    	Endif
    
    	*-----------------------------------------------------------------
    	* Now that we handled BACKSPACE, we can update the variable name
    	* in the edit window.
    	*-----------------------------------------------------------------
    	If m.llQualifier
    		InsertText( .Parent.nWHandle, m.lcSearch, , "RH" )
    		lnIndex = At( ":"+Upper(m.lcSearch), .Parent.cVarString )
    		If m.lnIndex == 0
    			.ListIndex = 0
    		Else
    			.ListIndex = (m.lnIndex/129) + 1
    		Endif
    		.Parent.cSearchString = m.lcSearch
    		NoDefault
    		Return
    	Endif
    	
    	*-----------------------------------------------------------------
    	* The following flags determine how to procede. 
    	*-----------------------------------------------------------------
    	Local lcTextToInsert, llResendKey, llClearInput
    	lcTextToInsert = ""
    	llResendKey = .T.
    	llClearInput = .F.
    	Do Case
    	
    	*-----------------------------------------------------------------
    	* If TAB has been pressed, insert the current selection and 
    	* release the popup
    	*-----------------------------------------------------------------
    	Case m.tnSAC == 0 and m.tnKeyCode == 9 and .ListIndex > 0
    		lcTextToInsert = .List[.ListIndex,2]
    		llResendKey = .F.
    		llClearInput = .T.
    	
    	*-----------------------------------------------------------------
    	* If ENTER has been pressed after the user made a selection with
    	* the arrow keys, we insert the current selection and release the 
    	* popup, because after scrolling the user has the feeling of using
    	* a plain listbox where enter performs a selection.
    	*-----------------------------------------------------------------
    	Case     m.tnSAC == 0 ;
    	     and m.tnKeyCode == 13 ;
    	     and .ListIndex > 0 ;
    	     and m.llScrolled
    		lcTextToInsert = .List[.ListIndex,2]
    		llResendKey = .F.
    		llClearInput = .T.
    	
    	*-----------------------------------------------------------------
    	* Several keys insert the current selection plus the typed 
    	* character and release the popup. These are usually keys that 
    	* directly follow a variable name.
    	*-----------------------------------------------------------------
    	Case InList(m.tnKeyCode, ;
    	        Asc(" "), Asc(")"), Asc("["), Asc("."), Asc("="), ;
    	        Asc("+"), Asc("-"), Asc("*"), Asc("/"), Asc("%"), ;
    	        Asc(","), Asc("]") ;
    	     ) and .ListIndex > 0
    		lcTextToInsert = .List[.ListIndex,2]
    		llClearInput = .T.
    	
    	*-----------------------------------------------------------------
    	* If ESC has been pressed, the text is unselected.
    	*-----------------------------------------------------------------
    	Case m.tnSAC == 0 and m.tnKeyCode == 27
    		llResendKey = .F.
    	
    	*-----------------------------------------------------------------
    	* terminate the popup for any other key and leave the text.
    	*-----------------------------------------------------------------
    	Otherwise
    	Endcase
    	
    	*-----------------------------------------------------------------
    	* If the currently entered Text should be deleted, insert an empty
    	* string using the replace option. Insert text afterwards.
    	*-----------------------------------------------------------------
    	If m.llClearInput
    		InsertText( .Parent.nWHandle, "", , "R" )
    	Else
    		SetFileCursorPos( ;
    			.Parent.nWHandle, ;
    			.Parent.nCurrentPos + Len(.Parent.cSearchString) ;
    		)
    	Endif
    	If not Empty( m.lcTextToInsert )
    		InsertText( .Parent.nWHandle, m.lcTextToInsert )
    	Endif
    	
    	*-----------------------------------------------------------------
    	* Close the form.
    	*-----------------------------------------------------------------
    	NoDefault
    	Thisform.Release()
    	
    	*-----------------------------------------------------------------
    	* And repeat the keystroke if necessary
    	*-----------------------------------------------------------------
    	Local lcKey
    	If m.llResendKey
    		lcKey = GetKeyLabel( m.tnKeyCode, m.tnSAC )
    		If not Empty(m.lcKey)
    			Clear TypeAhead
    			If Len(m.lcKey) == 1
    				Keyboard m.lcKey
    			Else
    				Keyboard "{"+m.lcKey+"}"
    			Endif
    		Endif
    	Endif
    
    Endwith
    EndProc
    
    
    *====================================================================
    * Double-clicking is the same as TAB.
    *====================================================================
    Procedure isxList.DblClick
    
    	Clear TypeAhead
    	Keyboard "{Tab}" Plain
    
    EndProc
    
    
    EndDefine
    
    
    
    *========================================================================================
    * VFP 6: Returns a specific word in a string
    *========================================================================================
    Function X6_GetWordNum
    LParameter tcString, tnWord, tcDelimiter
    
    	Local lcString, lcDelimiter, lnWord, laWords[1], lnFound, lcWord
    	
    	If Vartype(m.tcDelimiter) == "C"
    		lcDelimiter = m.tcDelimiter
    	Else
    		lcDelimiter = Chr(9)+Chr(32)
    	EndIf 
    	lcString = Chrtran(m.tcString,m.lcDelimiter,Replicate(Chr(13),Len(m.lcDelimiter)))
    	lnFound = 0
    	lcWord = ""
    	For lnWord = 1 to ALines(laWords,m.lcString)
    		If not Empty(laWords[m.lnWord])
    			lnFound = lnFound + 1
    			If m.lnFound == m.tnWord
    				lcWord = laWords[m.lnWord]
    				Exit
    			EndIf 
    		EndIf 
    	EndFor 
    
    Return m.lcWord
    
    
    *========================================================================================
    * VFP 6: Returns a list of all defines
    *========================================================================================
    Procedure X6_AProcInfo
    LParameter taArray, tcFile
    
    	Local laLines[1], lnLine, lnFound
    	
    	lnFound = 0
    	For lnLine = 1 to ALines(laLines,FileToStr(m.tcFile))
    		If Upper(X6_GetWordNum(laLines[m.lnLine],1)) == "#DEFINE"
    			lnFound = lnFound + 1
    			Dimension taArray[m.lnFound,3]
    			taArray[m.lnFound,1] = X6_GetWordNum(laLines[m.lnLine],2)
    			taArray[m.lnFound,3] = "Define"
    		EndIf 
    	EndFor 
    
    Return m.lnFound
    
  • 相关阅读:
    分层图最短路(DP思想) BZOJ2662 [BeiJing wc2012]冻结
    动态规划 BZOJ1925 地精部落
    线性DP SPOJ Mobile Service
    线性DP codevs2185 最长公共上升子序列
    数位DP POJ3208 Apocalypse Someday
    线性DP POJ3666 Making the Grade
    杨氏矩阵 线性DP? POJ2279 Mr.Young's Picture Permutations
    tarjan强连通分量 洛谷P1262 间谍网络
    树链剖分 BZOJ3589 动态树
    二分图 BZOJ4554 [Tjoi2016&Heoi2016]游戏
  • 原文地址:https://www.cnblogs.com/allydd/p/6247835.html
Copyright © 2011-2022 走看看