Showing posts with label foxpro. Show all posts
Showing posts with label foxpro. Show all posts

2022-05-08

Doug Hennig - Windows PowerShell: Batch Files on Steroids

On last May 05 at VFF - Virtual Fox Fest - the new online VFP conference, Doug Hennig presented the amazing session Windows PowerShell: Batch Files on SteroidsThe organizers of VFF were very kind offering a recorded version of all sessions for free for the community in YouTube.



This specific session dropped me completely, opening a whole new world of possibilities. As we can see in the video description: "Windows PowerShell has been included with the operating system since Windows 7 and is available for download for older systems. What is PowerShell? It's Microsoft's task automation scripting framework. PowerShell isn't just a replacement for batch files; it can do a lot more than batch files ever could. This session looks at PowerShell, including why you should start using it and how to create PowerShell scripts."

I just loved to know we can access almost the whole "Net Framework" using some batch files! I just started with some simple tests, and this looks really very promising, and will simplify several tasks.

Thanks very much for sharing!

XFRX vs FoxyPreviewer

I felt very honored to know that the VFP guru Rick Schummer presented a session - Visual FoxPro Reporting: XFRX vs. FoxyPreviewer in the 2022 version of VFF - Virtual Fox Fest. The organizers were very generous offering a recorded version of all sessions for free for the community in YouTube.



Rick very quickly and impartially shown some of the main features of both tools. 

Regarding FoxyPreviewer, I have some few considerations or remarks. Of course it would be impossible or Rick to know all the possibilities and features available. I just want to make clear that I feel very thankful for the review. And I am totally aware that the documentation needs some updates, and some points could be better explained, so here I take the opportunity to try to clarify some points.


  • Exporting report to an image file - There are 3 available options:
    - OBJECT TYPE 16 - This is the object type related to exporting to image files. Works exactly the same as the others.
    - OBJECT TYPE 20 - This OBJECT TYPE was not discussed in the presentation. It will choose automatically the ReportListener needed to generate the desired output, according to the file extension passed in the TO FILE clause. The available types are: PDF, RTF, HTML, MHTML, GIF, TIFF, EMF, JPG or PNG.
    REPORT FORM YourReport TO FILE MyImageReport.JPG OBJECT TYPE 20
    - Using the FoxyListener.OutputPage method &&Like regular VFP9
  • The truncated numeric fields in reports - He was very precise in his explanation, but I still would like to add some few information from a FAQ related to this:
When I run my reports with FoxyPreviewer sometimes asterisks symbols ********************* appear instead of the field. This was originally working, before using FoxyPreviewer

That happens because FoxyPreviewer uses the SET REPORTBEHAVIOR 90 mode, that uses GDI+ to render the texts. Unfortunately there is a slight difference of the size of the strings between these modes. To fix it, just edit your report and enlarge that field!

You can set the property - lExpandFields to make the report engine show the field numeric value ignoring the field size.
Using "lExpandFields", FoxyPreviewer retrieves the value that overflowed and resends it to the report engine with an enlarged field width.

This is a known issue, and Lisa Slater Nicholls wrote a short blog post regarding it:
Why do report layouts in VFP9 need wider field/expression controls than in VFP8 and earlier?

And here's another interesting text from Lisa, that explains the reason for that:
With REPORTBEHAVIOR=90, the new report engine uses GDI+ to render output, and text string rendering requires more space than plain old GDI.
The Report Designer uses GDI - not GDI+ - to render the report layout components, including all the text strings that you see. So if you visually right-align a label report element, the report designer records the leftmost co-ordinates of the element (the text start position) in the layout.
The length of the string under GDI+ rendering will most likely be greater than what you would think, based on what you see in the Designer.

  • SET COMPATIBLE ON - Oh, this is a known issue, and I had it fixed before. I just don't know why the fix was not included in the last version. I hope to post an update very soon.
  • ReportPreview.App and ReportOutput.App - As shown by Rick, these files are no longer needed if FoxyPreviewer was elected to be your single ReportEngine. I just feel important to add that if you are used to turn it off, you still need those files to restore the Reporting engine to the default VFP9.
  • Excel bug / Enhancements - Please send me your files and I will be very happy and honored to aply your updates to the main distributable version!
  • lOpenViewer property - In one of your samples Rick was using the property ".lOpenViewer = .T." - That means the generated output file would be opened automatically after generated. But the next REPORT FORM command contained the "TO FILE" and "PREVIEW" clauses. The "PREVIEW" clause means exactly the same - to open the file automatically!
    That's a very interesting usage of FoxyPreviewer. If you want your report to be shown in the default PDF viewer instead of a VFP FORM, just use the command:
    REPORT FORM YourReport OBJECT TYPE 20 TO FILE TempFile.pdf PREVIEW
  • The "PrintingPreferences" - Rick told us he feels some kind of frustration with the fact that the report is closed after you change your report settings in that dialog. But that's exactly what the "PRINTER PROMPT" clause does! From that dialog you have the opportunity to change the printer, copies and other settings and call the printer directly.
  • Rick was asked about embedding the toolbar in the Preview window. As he said, not in v2.99, but docking the toolbar is possible.
  • David Acuña asked about determining the SMTP server values previously. Yes, that's possible. Just set the "cSMTPServer", "nSMTPPort" and other related properties previously, just after FoxyPreviewer was initialized.
  • Maybe I misunderstood the real meaning of Rick's statement when he said that "The configurations are Global, not Per User". In fact, when you start FoxyPreviewer.App it will load the settings from the file "FoxyPreviewerSettings.dbf". This file is automatically generated after the 1st run of Foxy in that folder. Each of your users can have their own Settings Dbf file! You can save a copy of FoxyPreviewerSettings.DBF when your user logs off. Next time, after logging in and before calling FoxyPreviewer.App replace this file with the desired one. So, you can save a settins file for each of your users! When FoxyPreviewer does not find that DBF in its folder it will create a new one.

    Yes, everyone using that APP on that same machine will use those settings, as he mentioned.

    Here are some new thoughts regarding this, using a not very well documented possibility... There is an option to start FoxyPreviewer in a separate folder:

    Immediately after the user logs in your App, and the user is stored as "m.lcUser", people can try the following:

    lcFolder = ADDBS(JUSTPATH(SYS(16)))
    lcUserFolder = lcFolder + "Users\" + m.lcUser && Make sure the folder exists
    DO FOXYPREVIEWER.APP WITH (lcUserFolder)

    This way, every user will easily have his own Settings table. What do you think?


  • Distributing FoxyPreviewer - People just need ONE SINGLE FILE - FoxyPreviewer.App. And there is no need to install it. Just a simple"DO FOXYPREVIEWER.APP" at the beginning of your EXE code and it's done! The recommended is to store it at the same folder of your EXE, and make sure that folder will allow Foxy to create a subfolder and save some custom configuration files.

2021-04-25

Getting the correct Windows version - OS()

VFP9's OS() function can't return reliable information to help us to detect wether running WIN10.

Under Win10, the OS() returns to us "Windows 6.02", the same information for Windows 8.

A Simple workaround is to use the RtlGetVersion Api call to get the OSVERSIONINFO structure that brings us the relevant information.

Save the code below as GETWINVERSION.PRG, and call it the same way you call the OS() function.


Parameters

nValue

Specifies the item to return, according to the following table.

nValues

ValueDescription

     1     

Specifies that the name and version number of the operating system is returned.

     3     

Identifies the major version number of the operating system. For example, for Windows 2000, the major number is 5.
For Windows 10, the major number is 10.

     4     

Identifies the minor version number of the operating system. For example, for Windows 2000, the minor version number is 0.

     5     

Identifies the build number of the operating system.





FUNCTION GetWinVersion(tnType)
* Similar to VFP9 OS() function, returning the correct Win version for Windows 10

* OSVERSIONINFOA structure (winnt.h)
* https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoa
*!*	typedef struct _OSVERSIONINFOA {
*!*	  DWORD dwOSVersionInfoSize;
*!*	  DWORD dwMajorVersion;
*!*	  DWORD dwMinorVersion;
*!*	  DWORD dwBuildNumber;
*!*	  DWORD dwPlatformId;
*!*	  CHAR  szCSDVersion[128];
*!*	} OSVERSIONINFOA, *POSVERSIONINFOA, *LPOSVERSIONINFOA

*!*	The following table summarizes the values returned by supported versions of Windows. 
*!*	Use the information in the column labeled "Other" to distinguish between operating systems with identical version numbers.

*!*	Operating system	 Version dwMajorVersion	dwMinorVersion	Other
*!*	Windows 10	           10.0*		    10		0	
*!*	Windows Server 2016	   10.0*		    10		0	
*!*	Windows 8.1	            6.3*		     6		3	
*!*	Windows Server 2012 R2	6.3*		     6		3	
*!*	Windows 8	              6.2		       6		2	
*!*	Windows Server 2012	    6.2	    	   6		2	
*!*	Windows 7	              6.1	    	   6		1	
*!*	Windows Server 2008 R2	6.1	    	   6		1	
*!*	Windows Server 2008	    6.0	    	   6		0	
*!*	Windows Vista	          6.0	    	   6		0	
*!*	Windows Server 2003 R2	5.2	    	   5		2	
*!*	Windows Server 2003	    5.2	    	   5		2	
*!*	Windows XP	            5.1	    	   5		1	
*!*	Windows 2000	          5.0	    	   5		0	
*!*	* For applications that have been manifested for Windows 8.1 or Windows 10. Applications not manifested for Windows 8.1 or Windows 10 will return the Windows 8 OS version value (6.2). To manifest your applications for Windows 8.1 or Windows 10, refer to Targeting your application for Windows.

LOCAL lcOS, lcOsVersionInfo, lcReturn, lcVersion, lnBuild, lnMajor, lnMinor, lnPlatformId, lnRet, lnSize

* https://docs.microsoft.com/en-us/windows/win32/devnotes/rtlgetversion
DECLARE INTEGER RtlGetVersion IN NTDLL.DLL STRING @lcOsVersionInfo

m.lcOsVersionInfo = REPLICATE(CHR(0), 148) && Initialize osVersionInfo structure
m.lnRet = RtlGetVersion( @m.lcOsVersionInfo )

m.lnSize = CTOBIN(SUBSTR(m.lcOsVersionInfo, 1, 2), "2RS") && DWORD dwlcOsVersionInfoSize
m.lnMajor = CTOBIN(SUBSTR(m.lcOsVersionInfo, 5, 4), "4RS") && DWORD dwMajorVersion
m.lnMinor = CTOBIN(SUBSTR(m.lcOsVersionInfo, 9, 4), "4RS") && DWORD dwMinorVersion
m.lnBuild = CTOBIN(SUBSTR(m.lcOsVersionInfo, 13, 4), "4RS") && DWORD dwBuildNumber
m.lnPlatformId = CTOBIN(SUBSTR(m.lcOsVersionInfo, 17, 4), "4RS") && DWORD dwPlatformId
m.lcVersion = SUBSTR(m.lcOsVersionInfo, 21) && CHAR  szCSDVersion[128]

DO CASE

CASE EMPTY(m.tnType)
	DO CASE
	CASE m.lnMajor = 10 AND m.lnMinor = 0
		m.lcOS = "Windows 10"
	CASE m.lnMajor = 6 AND m.lnMinor = 3
		m.lcOS = "Windows 8.1 / Server 2012 R2"
	CASE m.lnMajor = 6 AND m.lnMinor = 2
		m.lcOS = "Windows 8 / Server 2012"
	CASE m.lnMajor = 6 AND m.lnMinor = 1
		m.lcOS = "Windows 7 / Server 2008 R2"
	CASE m.lnMajor = 6 AND m.lnMinor = 0
		m.lcOS = "Windows Vista / Server 2008"
	CASE m.lnMajor = 5 AND m.lnMinor = 2
		m.lcOS = "Windows Server 2003"
	CASE m.lnMajor = 5 AND m.lnMinor = 1
		m.lcOS = "Windows XP"
	CASE m.lnMajor = 5 AND m.lnMinor = 0
		m.lcOS = "Windows 2000"
	OTHERWISE
	ENDCASE
	m.lcReturn = m.lcOS + " " + TRANSFORM(m.lnMajor) + "." + TRANSFORM(m.lnMinor) + " build " + TRANSFORM(m.lnBuild)

CASE m.tnType = 1
	m.lcReturn = "Windows " + TRANSFORM(m.lnMajor) + "." + TRANSFORM(m.lnMinor)

CASE INLIST(m.tnType, 2, 6, 7, 8, 9, 10, 11)
	m.lcReturn = ""

CASE m.tnType = 3
	m.lcReturn = TRANSFORM(m.lnMajor)

CASE m.tnType = 4
	m.lcReturn = TRANSFORM(m.lnMinor)

CASE m.tnType = 5
	m.lcReturn = TRANSFORM(m.lnBuild)

OTHERWISE
	m.lcReturn = ""

ENDCASE
RETURN m.lcReturn


2021-04-08

Segoe MDL2 Assets Icons in VFP9 with Gdi+

As discussed before in this blog, VFP can't display natively any character having its CHR() higher than 0xFF (decimal 255). 

There are several very interesting fonts that bring very cool and up to date icons that we could use in our apps, such as the SEGOE MDL2 ASSETS, used by Windows 10 all over.

The Unicodes can be obtained directly by the CharMap.EXE or all over the web. Here's an excellent starting point: https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font


The samples below will use GDI+ to save any desired character as an image, allowing us to use those cool images in our apps. They use the _GDIPLUS.VCX FFC classes, but is really very easy to adapt to GdiPlusX as well, if needed.

Adapt it to your needs!

Basically a function that retrieves a single unicode character and saves it as an image file. 

Usage:
To get the "Print" icon: 


EXTRACT A SINGLE ICON


lcFile = "Print.bmp"
lcUnicode = "e749"
lcFont = "SEGOE MDL2 ASSETS"
lnSize = 32 && Pixels
lnForeColor = RGB(0, 0, 255) && Black
lnBackColor = RGB(255, 255, 255) && White
=MakeImageFromUnicode(m.lcFile, lcUnicode, lcFont, lnSize, lnForeColor, lnBackColor)


Save the code Below as "MakeImageFromUnicode.prg":


FUNCTION MakeImageFromUnicode(tcFileName, tcUnicode, tcFontName, tnImgSize, tnForeColor, tnBackColor)
*!* tcUnicode allows up to 2 characters, that will be drawn one over the other
*!* Par1: Main Unicode
*!* Par2: Socondary Unicode
*!* Par3: Mode, where 0=Center, 1=TopLeft, 2=TopRight, 3=BottLeft, 4=BottRight
*!* Par4: Size of the 2nd character

	LOCAL lnChars, lnFactor, lnFontHeight, lnFontSize, lnHeight, lnLines, lnNewFontSize, lnWidth
	LOCAL lqUnicode
	LOCAL lcUnicode1, lcUnicode2, lnMode, lnSize2
	IF EMPTY(m.tcFileName) OR EMPTY(m.tcUnicode) OR EMPTY(m.tcFontName) OR EMPTY(m.tnImgSize)
		RETURN
	ENDIF

	m.lnFontSize = 48
	m.lnWidth	 = m.tnImgSize
	m.lnHeight	 = m.tnImgSize

	* Create a font object using the text object's settings.
	LOCAL loFont0 AS GpFont OF HOME() + "FFC/_GdiPlus.vcx"
	m.loFont0 = NEWOBJECT('gpFont', HOME() + 'FFC/_GdiPlus.vcx')
	m.loFont0.Create(m.tcFontName, m.lnFontSize, 0, 3) && 0 = Font Style

	LOCAL loGfx0 AS GpGraphics OF HOME() + "FFC/_GdiPlus.vcx"
	m.loGfx0 = NEWOBJECT('gpGraphics', home() + 'FFC\_GdiPlus.vcx')
	m.loGfx0.CreateFromHWnd(_screen.HWnd)
	m.lnChars = 0
	m.lnLines = 0

	LOCAL loSize AS gpSize OF HOME() + "FFC/_GdiPlus.vcx"
	m.loSize  = m.loGfx0.MeasureStringA("A", m.loFont0, , , @m.lnChars, @m.lnLines)
	* lnFontWidth = loSize.W
	m.lnFontHeight	= m.loSize.H
	m.lnFactor		= m.lnFontHeight / m.tnImgSize
	m.lnNewFontSize	= INT(m.lnFontSize / m.lnFactor)

	* Create a font object using the text object's settings.
	LOCAL loFont AS GpFont OF HOME() + "FFC/_GdiPlus.vcx"
	m.loFont = NEWOBJECT('gpFont', HOME() + 'FFC/_GdiPlus.vcx')
	m.loFont.Create(m.tcFontName, m.lnNewFontSize, 0, 3) && 0 = Font Style

	LOCAL loBMP AS GpBitmap OF HOME() + "FFC/_GdiPlus.vcx"
	m.loBMP = NEWOBJECT("gpBitmap", HOME() + "FFC/_GdiPlus.vcx")
	#DEFINE GdiPlus_PixelFormat_32BPPARGB        0x0026200a
	m.loBMP.Create(m.lnHeight, m.lnHeight, GdiPlus_PixelFormat_32BPPARGB)

	LOCAL loGfx AS GpGraphics OF HOME() + "FFC/_GdiPlus.vcx"
	m.loGfx = NEWOBJECT('gpGraphics', HOME() + 'FFC/_GdiPlus.vcx')
	m.loGfx.CreateFromImage(m.loBMP)

	* Setting the Backcolor
	LOCAL loBackColor AS GpColor OF HOME() + "FFC/_GdiPlus.vcx"
	IF EMPTY(m.tnBackColor)
		m.loBackColor = 0xFFFFFFFF && White background
	ELSE
		m.loBackColor		 = NEWOBJECT("gpColor", HOME() + 'FFC/_GdiPlus.vcx')
		m.loBackColor.FoxRGB = m.tnBackColor
	ENDIF
	m.loGfx.CLEAR(m.loBackColor) && Background

	* Create a rectangle
	LOCAL loRect AS GpRectangle OF HOME() + "FFC/_GdiPlus.vcx"
	m.loRect = NEWOBJECT("GPRectangle", HOME() + 'FFC/_GdiPlus.vcx', "", 0, 0, m.lnWidth, m.lnHeight)
	m.loRect.y = m.loRect.y + 1

	* Setting the Forecolor
	LOCAL loColor AS GpColor OF HOME() + "FFC/_GdiPlus.vcx"
	IF EMPTY(m.tnForeColor)
		m.tnForeColor = 0 && Black
	ENDIF
	m.loColor		 = NEWOBJECT("gpColor", HOME() + 'FFC/_GdiPlus.vcx')
	m.loColor.FoxRGB = m.tnForeColor

	LOCAL loBrush AS GpSolidBrush OF HOME() + "FFC/_GdiPlus.vcx"
	m.loBrush = NEWOBJECT("gpSolidBrush", HOME() + 'FFC/_GdiPlus.vcx', "", m.loColor)

	* The character need to be drawn at the center of the image object
	* Get a basic string format object
	* StringAlignment enumeration
	* Applies to GpStringFormat::Alignment, GpStringFormat::LineAlignment
	#DEFINE GDIPLUS_STRINGALIGNMENT_Near	0	&& in Left-To-Right locale, this is Left
	#DEFINE GDIPLUS_STRINGALIGNMENT_Center	1
	#DEFINE GDIPLUS_STRINGALIGNMENT_Far		2	&& in Left-To-Right locale, this is Right
	LOCAL loStringFormat AS gpStringFormat OF HOME() + "FFC/_GdiPlus.vcx"
	m.loStringFormat = NEWOBJECT("GpStringFormat", HOME() + "FFC/_GdiPlus.vcx")
	m.loStringFormat.Create()
	m.loStringFormat.Alignment	   = GDIPLUS_STRINGALIGNMENT_Center
	m.loStringFormat.LineAlignment = GDIPLUS_STRINGALIGNMENT_Center

	* Prepare the Unicode
	m.lcUnicode1 = GETWORDNUM(m.tcUnicode, 1, ",")
	m.lqUnicode	 = LEFT(BINTOC(EVALUATE("0x" + m.lcUnicode1), "4RS"), 2)

	* Draw the string
	m.loGfx.DrawStringW(m.lqUnicode, m.loFont, m.loRect, m.loStringFormat, m.loBrush)
	m.lcUnicode2	= GETWORDNUM(m.tcUnicode, 2, ",")

	IF NOT EMPTY(m.lcUnicode2)
		m.lqUnicode	= LEFT(BINTOC(EVALUATE("0x" + m.lcUnicode2), "4RS"), 2)
		m.lnMode	= VAL(GETWORDNUM(m.tcUnicode, 3, ","))
		m.lnSize2	= VAL(GETWORDNUM(m.tcUnicode, 4, ","))
		m.lnSize2	= EVL(m.lnSize2, 100)

		lnNewFontSize = CEILING(m.lnNewFontSize * (lnSize2/100))
		m.loFont.Create(m.tcFontName, m.lnNewFontSize, 0, 3) && 0 = Font Style
		m.loStringFormat.Alignment	   = GDIPLUS_STRINGALIGNMENT_Center
		m.loStringFormat.LineAlignment = GDIPLUS_STRINGALIGNMENT_Center

		m.loRect.w = INT(m.lnWidth  * (m.lnSize2 / 100))
		m.loRect.H = INT(m.lnHeight * (m.lnSize2 / 100))

		DO CASE
			CASE m.lnMode = 0 && No transformation, the 2nd image will be drawn over the original
				m.loRect.x = INT((m.lnWidth  - m.loRect.w) / 2)
				m.loRect.Y = INT((m.lnHeight - m.loRect.H) / 2)

			CASE m.lnMode = 1 && Top-Left
				m.loRect.x = 0
				m.loRect.Y = 0

			CASE m.lnMode = 2 && Top-Right
				m.loRect.x = m.lnWidth - m.loRect.w
				m.loRect.Y = 0

			CASE m.lnMode = 3 && Bottom-Left
				m.loRect.x = 0
				m.loRect.Y = m.lnHeight - m.loRect.H

			CASE m.lnMode = 4 && Bottom-Right
				m.loRect.x = m.lnWidth - m.loRect.w
				m.loRect.Y = m.lnHeight - m.loRect.H

			OTHERWISE
		ENDCASE
		m.loRect.y = m.loRect.y + 1
		m.loGfx.DrawStringW(m.lqUnicode, m.loFont, m.loRect, m.loStringFormat, m.loBrush)
	ENDIF

	* Save as image
	m.loBMP.SaveToFile(m.tcFileName, "image/bmp")

	RETURN
ENDFUNC 


The function also allows you to create new icons by merging two, in this case, the Printer and the Settings icon at the bottom right:

CUSTOMIZE YOUR ICONS


lcFile = "Print.bmp"
lcUnicode = "e749,e713,4,25"
lcFont = "SEGOE MDL2 ASSETS"
lnSize = 32 && Pixels
lnForeColor = RGB(0, 0, 128) && Dark Blue
lnBackColor = RGB(255, 255, 128) && Yellow
=MakeImageFromUnicode(m.lcFile, lcUnicode, lcFont, lnSize, lnForeColor, lnBackColor)


EXTRACTING ALL ICONS FROM A FONT

The above function can be adapted in order to extract all characters of a given font, using a loop.
Fonts usually have some codes that are not being used, so in the code below I used a simple trick to detect the empty font dimensions, and every time the same conditions are met in the loop, the unicode will be discarded.
Just run the code below to extract all icons from any given font, at the desired image size and colors. Adjust the initial variables to fit your needs!

* Setup the initial 5 variables
LOCAL lcFontName, lnImgSize, lnForeColor, lnBackColor, lcImageType
m.lcFontName  = "SEGOE MDL2 ASSETS"
m.lnImgSize	  = 64  && The desired bmp size in pixels
m.lnForeColor = RGB(0, 0, 0) && the ForeColor
m.lnBackColor = RGB(255, 255, 255) && the BackColor
m.lcImageType = "bmp" && available: bmp, jpg, gif, tif, png



* Let's start
LOCAL lcEmptyUnicode, lcFileName, lcHex, lcUnicode, lnChars, lnEmptyH, lnEmptyW, lnFactor
LOCAL lnFontHeight, lnFontSize, lnFontWidth, lnHeight, lnLines, lnNewFontSize, lnWidth, loSizeReal, n

m.lnFontSize  = 48
m.lnWidth	  = m.lnImgSize
m.lnHeight	  = m.lnImgSize
m.lcImageType = LOWER(EVL(m.lcImageType, "bmp"))

* Create a rectangle
LOCAL loRect AS GpRectangle OF HOME() + "FFC/_GdiPlus.vcx"
m.loRect   = NEWOBJECT("GPRectangle", HOME() + 'FFC/_GdiPlus.vcx', "", 0, 0, m.lnWidth, m.lnHeight)
m.loRect.y = m.loRect.y + 1

* The character need to be drawn at the center of the image object
* Get a basic string format object
* StringAlignment enumeration
* Applies to GpStringFormat::Alignment, GpStringFormat::LineAlignment
#DEFINE GDIPLUS_STRINGALIGNMENT_Near	0	&& in Left-To-Right locale, this is Left
#DEFINE GDIPLUS_STRINGALIGNMENT_Center	1
#DEFINE GDIPLUS_STRINGALIGNMENT_Far		2	&& in Left-To-Right locale, this is Right
LOCAL loStringFormat AS gpStringFormat OF HOME() + "FFC/_GdiPlus.vcx"
m.loStringFormat = NEWOBJECT("GpStringFormat", HOME() + "FFC/_GdiPlus.vcx")
m.loStringFormat.Create()
m.loStringFormat.Alignment	   = GDIPLUS_STRINGALIGNMENT_Center
m.loStringFormat.LineAlignment = GDIPLUS_STRINGALIGNMENT_Center



* Create a font object using the text object's settings.
LOCAL loFont0 AS GpFont OF HOME() + "FFC/_GdiPlus.vcx"
m.loFont0 = NEWOBJECT('gpFont', HOME() + 'FFC/_GdiPlus.vcx')
m.loFont0.Create(m.lcFontName, m.lnFontSize, 0, 3) && 0 = Font Style

LOCAL loGfx0 AS GpGraphics OF HOME() + "FFC/_GdiPlus.vcx"
m.loGfx0 = NEWOBJECT('gpGraphics', home() + 'FFC\_GdiPlus.vcx')
m.loGfx0.CreateFromHWnd(_screen.HWnd)

LOCAL loSize AS gpSize OF HOME() + "FFC/_GdiPlus.vcx"
m.lnChars	   = 0
m.lnLines	   = 0
m.loSize	   = m.loGfx0.MeasureStringA("A", m.loFont0, , , @m.lnChars, @m.lnLines)
m.lnFontWidth  = m.loSize.W
m.lnFontHeight = m.loSize.H

m.lnFactor		= m.lnFontHeight / m.lnImgSize
m.lnNewFontSize	= INT(m.lnFontSize / m.lnFactor)

* Create a font object using the text object's settings.
LOCAL loFont AS GpFont OF HOME() + "FFC/_GdiPlus.vcx"
m.loFont = NEWOBJECT('gpFont', HOME() + 'FFC/_GdiPlus.vcx')
m.loFont.Create(m.lcFontName, m.lnNewFontSize, 0, 3) && 0 = Font Style

* Get the measure of the empty character, that will be used to avoid saving it several times
m.lcEmptyUnicode = CHR(0) + CHR(0)
LOCAL loSizeEmpty AS gpSize OF HOME() + "FFC/_GdiPlus.vcx"
m.loSizeEmpty = m.loGfx0.MeasureStringW(m.lcEmptyUnicode, m.loFont, m.loRect, m.loStringFormat, @m.lnChars, @m.lnLines)
m.lnEmptyW	  = m.loSizeEmpty.W
m.lnEmptyH	  = m.loSizeEmpty.H

LOCAL loBMP AS GpBitmap OF HOME() + "FFC/_GdiPlus.vcx"
m.loBMP = NEWOBJECT("gpBitmap", HOME() + "FFC/_GdiPlus.vcx")
#DEFINE GdiPlus_PixelFormat_32BPPARGB        0x0026200a
m.loBMP.Create(m.lnHeight, m.lnHeight, GdiPlus_PixelFormat_32BPPARGB)

LOCAL loGfx AS GpGraphics OF HOME() + "FFC/_GdiPlus.vcx"
m.loGfx = NEWOBJECT('gpGraphics', HOME() + 'FFC/_GdiPlus.vcx')
m.loGfx.CreateFromImage(m.loBMP)

* Setting the Backcolor
LOCAL loBackColor AS GpColor OF HOME() + "FFC/_GdiPlus.vcx"
IF EMPTY(m.lnBackColor)
   m.loBackColor = 0xFFFFFFFF && White background
ELSE
   m.loBackColor		 = NEWOBJECT("gpColor", HOME() + 'FFC/_GdiPlus.vcx')
   m.loBackColor.FoxRGB = m.lnBackColor
ENDIF

* Setting the Forecolor
LOCAL loColor AS GpColor OF HOME() + "FFC/_GdiPlus.vcx"
IF EMPTY(m.lnForeColor)
   m.lnForeColor = 0 && Black
ENDIF
m.loColor		 = NEWOBJECT("gpColor", HOME() + 'FFC/_GdiPlus.vcx')
m.loColor.FoxRGB = m.lnForeColor

LOCAL loBrush AS GpSolidBrush OF HOME() + "FFC/_GdiPlus.vcx"
m.loBrush = NEWOBJECT("gpSolidBrush", HOME() + 'FFC/_GdiPlus.vcx', "", m.loColor)


FOR m.n = 0xe001 TO 0xf8b3 && the last available found in charmap
   m.lcHex		 = TRANSFORM(m.n, "@0")
   m.lcHex		 = STRTRAN(m.lcHex, "0x0000", "")
   m.lcFileName = FORCEEXT(m.lcHex, m.lcImageType)

   * Prepare the Unicode
   m.lcUnicode	 = LEFT(BINTOC(EVALUATE("0x" + m.lcHex), "4RS"), 2)

   m.loSizeReal = m.loGfx0.MeasureStringW(m.lcUnicode, m.loFont, m.loRect, m.loStringFormat, @m.lnChars, @m.lnLines)

   m.loGfx.CLEAR(m.loBackColor) && Background

   * Draw the string
   m.loGfx.DrawStringW(m.lcUnicode, m.loFont, m.loRect, m.loStringFormat, m.loBrush)

   * Save as image
   m.loBMP.SaveToFile(m.lcFileName, "image/" + m.lcImageType)
ENDFOR

* Clear GDI+ objects
m.loRect         = NULL
m.loStringFormat = NULL
m.loColor        = NULL
m.loBackColor    = NULL
m.loBrush        = NULL
m.loSize         = NULL
m.loSizeEmpty    = NULL
m.loGfx0         = NULL
m.loGfx          = NULL
m.loBMP          = NULL
m.loFont0        = NULL
m.loFont         = NULL

RETURN

 

IMPORTANT

Don't forget that all fonts have a license. That means that you need to check first if you are allowed to distribute the images generated. Make sure to read the EULA, and see what you can or cannot do with them, ok?


SEE ALSO

Unicode button icons in Visual FoxPro
Unicode Controls in Visual FoxPro - A new faster and efficient aproach
Unicode Button Icons in Visual FoxPro

2020-11-23

Unicode Controls in Visual FoxPro - A new faster and efficient aproach

As a continuation of my last post - Unicode Button Icons in Visual FoxPro, I decided to keep trying different solutions to bring Unicodes to our controls - specially as button icons.

The most natural option would be to use real Windows Buttons like the sample we have in VFPX - https://vfpimaging.blogspot.com/2020/11/unicode-button-icons-in-visual-foxpro.html

But this brings the con that we would have to make several changes in our legacy forms in order to adapt all codes related to the Click event, and others.

So, I decided to try a hybrid solution - "Draw" a "Static" Win32 control - similar to our VFP Label control over regular CommandButtons and Labels. These "Static" Win controls allow Unicodes, and would allow us to keep all our legacy codes as they are. 

So here's FOXYOBJECTS - a custom VFP class that can be tossed in our forms, and will "Convert" all buttons and labels that have contents between the <UC> </UC> tags to unicodes, just like the previous post. This time about 4-5 times faster than GradObjects, with the same result.

If you didn't read the previous post, my goal is to bring some cool icons, the same we see all over in WIN10 UI, specially those from SEGOE MDL2 ASSETS, like shown in the CharMap below:



The usage of this new class is really very simple:

  • Open the FoxyObjects project
  • Toss an instance of FoxyObjects to your form
  • Set your command button FontName property to "SEGOE MDL2 ASSETS" or any other that you desire.
  • Set the caption property of the button to accept unicodes, by introducing the unicodes between the <UC> </UC> tags, for instance:
          greek <UC>03b5 03b9 03c1 03ae 03bd 03b7</UC> - This will show the word "Peace" in greek characters, in any regular font, like Arial, Tahoma, Segoe UI, etc
          To get the Printer icon from the SEGOE MDL2 ASSETS, set the commandbutton font to it, and add the following to its caption property: "<UC>E749</UC>"


FoxyObjects brings some few properties, that will be applied to all CommandButton and Label controls that are on the same object level of the FoxyObjects level. For instance, if you want FoxyObjects to apply changes to some selected objects, you can insert them into a container, and add an instance of the class to it. This way, the rest of the objects will not be affected.

By default, it will apply changes only to objects that have the <UC> tag in their captions.



  • BackColor - Numeric, specifies the background color used to display text and graphics in an object.
  • DisabledForeColor - Numeric, specifies the color used to display text when the object is disabled.
  • ForeColor - Numeric, specifies the color used to display text.
  • MouseOverForeColor - Numeric, specifies the color the object text (and icon) will turn into when the mouse will be over the specified object. If you don't want this effect, store the value -1
  • lBindAll - Logical, determines that all objects will be affected, even if they don't have the <UC> in the caption. That means that you can change the caption at runtime, and the unicodes will be respected 
  • lBindLabels - Logical, determines that both CommandButtons and Labels will be affected
  • lBindResize - Logical, determines that whenever any control is resized or moved the label mask will be updated as well.
  • lBindVisible - Logical, determines that whenever any control is hidden or visible label mask will be updated as well.









The Unicodes can be obtained directly by the CharMap.EXE or all over the web. Here's an excellent starting point: https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font


The SEGOE MDL2 ASSETS font comes with Win10, but it is not allowed to be distributed to other OS'. This is not a big deal, because Tahoma also brings several options to us, and we can always work with some of the free fonts available on the web, such as "Material.io". They bring tons of modern and beautiful icons for free. It's really worth a visit - https://material.io/resources/icons/?style=outline


SPECIAL THANKS to Mustapha Bihmuten from Morocco and Leandro Walfrans from Brazil for testing the Pre-Alpha version of the class and for bringing valuable suggestions



 FoxyObjects v0.4 Download 


See Also:
Drawing Unicode Texts with GDI+
Unicode Button Icons in Visual FoxPro
Unicode character search by compart.com
Material icons

2020-11-15

Unicode button icons in Visual FoxPro

A big difficulty foxers have is to update their user interfaces. Since we lost MS support, we need to do almost all UI changes by our own. The Win10 UI today is based in monochrome icons - the ones from the SEGOE UI family - SEGOE UI SYMBOL and SEGOE MDL2 ASSETS. These are true type fonts, that bring tons of icons - the ones that we see all over in Win10.



Unfortunately we can't access those icons directly in VFP, because they use a range higher than the CHR(255) supported by VFP. We still have some options:

1 - Use an ACTIVE-X that supports unicodes

2 - Use "Real Window buttons", that support unicodes - https://github.com/VFPX/Win32API/blob/master/samples/sample_274.md

3 - Get some help from GDI+ - gdiplus.dll and do the drawings for us.


The 3rd option is really nice, but demands a lot, really a lot of work. Fortunately, I did almost all the hard work before, back in 2005, in the GradObjects class, that originally was created to bring gradient backgrounds and buttons to our forms. It recreated every button from the form, and redrawn it to an image file, allowing cool gradient, mouse over and disabled effects.

Having this, I just needed to adapt it, leaving the almost abandoned gradients behind, adding support to unicodes and some adaptations for the mouse over effects.


Here's an updated version of the good and old GRADOBJECTS class, that was intended to generate gradient buttons and backgrounds to our forms back in 2005, in the WinXP times. It's still the same GradObjects, but with some new properties and features. 


The usage is really very simple:

  • Open the GradObjects project
  • Create a form, add some buttons
  • Toss an instance of Gradobjects to your form
  • Set your command button FontName property to "SEGOE MDL2 ASSETS" or any other that you desire.
  • Set the caption property of the button to accept unicodes, by introducing the unicodes between the <UC> </UC> tags, for instance:
          greek <UC>03b5 03b9 03c1 03ae 03bd 03b7</UC> - This will show the word "Peace" in greek characters, in any regular font, like Arial, Tahoma, Segoe UI, etc
          To get the Printer icon from the SEGOE MDL2 ASSETS, set the commandbutton font to it, and add the following to its caption property: "<UC>E749</UC>"


The default "GradObjects" properties will bring you a gradient look, but all you need is to change just 5:

  • BackColor1: Numeric, the RGB of the backcolor
  • BackColor2: Set it to .F. (false), because we don't need gradients here, do we?
  • CaptionForeColor: Numeric, the RGB of the forecolor
  • GradientMode: 0 - We don't need gradients!
  • SelBackColor: Numeric, the RGB of the backcolor when a button is focused or mouse over it
  • SelForeColor: Numeric, the RGB of the forecolor when a button is focused or mouse over it

That's it!

The Unicodes can be obtained directly by the CharMap.EXE or all over the web. Here's an excellent starting point: https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font


For a more detailed information, please refer to the original post for GradObjects: http://vfpimaging.blogspot.com/2006/07/gradient-objects-with-gdi-revisited_48.html



The "GradObjects" object will transform all CommandButtons, Graphical OptionButtons from the same parent object. Use containers, if you need different effects (or none) to some individual controls in your forms.

Start playing with the sample form "TESTUNICODEBTNS.SCX"

 Unicode Buttons Download 


See Also:
Drawing Unicode Texts with GDI+