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)
   IF m.loSizeReal.W == m.pnEmptyW AND m.loSizeReal.H == m.pnEmptyH
      LOOP
   ENDIF

   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

2021-03-23

Saving a VFP Report at a higher resolution with FoxyPreviewer

VFP9 brought the possibility to save our reports as images, using the ReportListener class, and the OutputPage method:

loListener.OutputPage(lnPage, "\MyReportImage.PNG", 104) && PNG file type


This brings us some useful, but lousy images regarding quality. For instance, the Sample "COLORS.FRX" brings me a 816 x 1056 pixels image - a really poor quality image, if we are thinking of printing or manipulating it further.


But the "OutputPage" method also allows us to draw the report page at any desired image size, by passing a GDI+ Graphics handle instead of the file name widely used.

Here is the working sample - notice that the report engine works only with 96 DPI, so to have better quality, you need to save in bigger dimensions.

Use the function GETREPORTPAGEEX to get your higher resolution reports, here is the parameter list

  • tcFile - The destination image file name
  • toListener - The ReportListener associated with the current report
  • tnPage - The report page number
  • tnEncoder - 100=EMF, 101=TIFF, 102=JPEG, 103=GIF, 104=PNG, 105=BMP
  • tnScale - the scale factor to be applied to the image. 1=Default (low quality), 10=Super high quality
  • tnWidth - The output image width (optional, if using the "tnScale")
  • tnHeight - The output image height (optional, if using "tnScale")


DO FoxyPreviewer.App

LOCAL loListener AS REPORTLISTENER
LOCAL lcFile, lnPage, lnFileType
m.loListener			  = CREATEOBJECT("FoxyListener")
m.loListener.LISTENERTYPE = 3

REPORT FORM (ADDBS(_Samples) + "Solution\Reports\Colors.FRX") OBJECT m.loListener

m.lnFileType = 104 && PNG
	&& 100 - EMF
	&& 101 - TIFF
	&& 102 - JPEG
	&& 103 - GIF
	&& 104 - PNG
	&& 105 - BMP

FOR m.lnPage = 1 TO m.loListener.PAGETOTAL
	m.lcFile = "c:\temp\Test__" + SYS(2015) + "__" + ALLTRIM(STR(m.lnPage)) + ".png"
	GetReportPageEx(m.lcFile, m.loListener, m.lnPage, m.lnFileType, 5) && 5 times bigger image than default
	* For the default lower quality image, use:
	*   loListener.OutputPage(lnPage, "c:\Test" + ALLTRIM(STR(lnPage)) + ".png", lnFileType)
ENDFOR
m.loListener = NULL
RETURN



PROCEDURE GetReportPageEx(tcFile, toListener AS REPORTLISTENER, tnPage, tnEncoder, tnScale, tnWidth, tnHeight)
	LOCAL lhGfx
	*!*	100 - image type EMF
	*!*	101 - image type TIFF
	*!*	102 - image type JPEG
	*!*	103 - image type GIF
	*!*	104 - image type PNG
	*!*	105 - image type BMP
	m.tnEncoder	= EVL(m.tnEncoder, 104) && Default = 104-PNG
	m.tnScale	= EVL(m.tnScale, 1)
	IF EMPTY(m.tnWidth)
		m.tnWidth  = m.toListener.GETPAGEWIDTH()  / 10 * m.tnScale
		m.tnHeight = m.toListener.GETPAGEHEIGHT() / 10 * m.tnScale
	ENDIF

	#DEFINE Gdiplus_PixelFormat_32BppArgb		0x0026200a
	#DEFINE OUTPUTDEVICETYPE_GDIPLUS 			1

	LOCAL loBMP AS GpBitmap OF ADDBS(HOME()) + "/FFC/_GDIPLUS.VCX"
	m.loBMP = NEWOBJECT("GpBitmap", ADDBS(HOME()) + "/FFC/_GDIPLUS.VCX")
	m.loBMP.CREATE(m.tnWidth, m.tnHeight, Gdiplus_PixelFormat_32BppArgb)

	LOCAL loGfx AS GpGraphics OF ADDBS(HOME()) + "/FFC/_GDIPLUS.VCX"
	m.loGfx = NEWOBJECT('GpGraphics', ADDBS(HOME()) + "/FFC/_GDIPLUS.VCX")
	m.loGfx.CreateFromImage(m.loBMP)
	m.lhGfx = m.loGfx.GetHandle()

	m.toListener.OUTPUTPAGE(m.tnPage, m.lhGfx, OUTPUTDEVICETYPE_GDIPLUS, 0, 0, m.tnWidth, m.tnHeight, 0, 0, m.tnWidth, m.tnHeight)
	m.loBMP.SaveToFile(m.tcFile, "image/png")
ENDPROC

2021-01-31

IMAP Configuration issues - "Sent items" not saving in Outlook and WLM - Windows Live Mail

I've just passed through a weird issue, when after reconfiguring Outlook Mail, the "Sent items" folder was not showing the sent e-mails. Of course, I made sure to tick the checkbox that tells to store the sent items.

After a long search, I found the most ridiculous solution from "Hawk", at:

https://answers.microsoft.com/en-us/windowslive/forum/livemail-files/windows-live-mail-my-sent-items-are-no-longer/b9d6f6eb-0072-4d5f-875c-55ce01d81e87

This problem is very easy to fix if this is an IMAP mail folder.


You may notice that your drafts aren't saving either, in fact nothing is going into the folders from your computer when you produce a mail. You may find that mail produced from other devices appears, but not the mail from your windows 7 computer. You may also notice that your folders, Drafts, Junk E-mail, etc are not positioned at the left hand side of the list, but are in fact tabbed across a bit, right of the inbox. Is this so?


The Solution is simple:


1) Right Click on the account and open Properties

2) Go to the IMAP tab.

3) Enter "Inbox" into the Root folder path *AND HERE's THE TRICK: IT MUST BE "Inbox", not "inbox" the entry is case sensitive.

4) Press apply and let the folders reorganise themselves. They should now reload and all be on the left hand side.


Done. New sent items, drafts etc will now move to the correct folders.

2021-01-23

PUTFILE function as originally typed (not in uppercase)

As already discussed in Fox.Wikis - "VFP has always been a bit funny about filename cases. More specifically, how it works with filename case is not documented. It will translate filenames to lower case in some cases, and to upper in others, and leave it alone in yet others." PUTFILE is in that list of strange functions, always returning UPPERCASE file names. So here's a short function that I've been using that returns the chosen filename the way the user typed.

The parameters and usage is exactly the same as the VFP original PUTFILE function:

FUNCTION XPUTFILE(tcCustomText, tcFileName, tcFileExt)

	* Usage:
	* ? PUTFILE("Save file as...", "MyFile.PDF", "PDF;TXT;*")

	#DEFINE COMMDLOG_DEFAULT_FLAG 	0x00080000
	#DEFINE COMMDLOG_RO 			4
	#DEFINE COMMDLOG_MULTFILES 		512

	LOCAL lcSetDefa
	m.lcSetDefa = SET("Default") + CURDIR()

	LOCAL loDlgForm AS "Form"
	m.loDlgForm = CREATEOBJECT("Form")
	m.loDlgForm.ADDOBJECT("oleObject1", "oleComDialObject")

	LOCAL loDlg
	m.loDlg = m.loDlgForm.OleObject1

	LOCAL lcFilter, lcFileExt, lnExtCount, n
	IF NOT EMPTY(tcFileExt)
		lnExtCount = GETWORDCOUNT(m.tcFileExt, ";")
		lcFilter = ""
		FOR n = 1 TO lnExtCount
			lcFileExt = GETWORDNUM(m.tcFileExt, n, ";")
			IF lcFileExt = "*"
				lcFilter = lcFilter + "All files|*.*"
			ELSE 
				lcFilter = lcFilter + lcFileExt + " files|*." + lcFileExt && EVL(tcFileExt, "All files|*.*")
			ENDIF			
			IF n < lnExtCount
				lcFilter = lcFilter + "|"
			ENDIF 
		ENDFOR
	ELSE
		lcFilter = "*.*|*.*" && EVL(tcFileExt, "All files|*.*")
	ENDIF 

	m.loDlg.FILTER		= lcFilter
	m.loDlg.FileName	= EVL(m.tcFileName, "")
	m.loDlg.DialogTitle	= EVL(m.tcCustomText, "Save file as...")
	m.loDlg.FLAGS		= COMMDLOG_RO + COMMDLOG_DEFAULT_FLAG
	m.loDlg.MaxFileSize	= 256

	LOCAL lnResult AS INTEGER, lcFileName
	* lnResult = loDlg.ShowOpen()
	m.lnResult = m.loDlg.ShowSave()

	* Restore the original directory
	SET DEFAULT TO (m.lcSetDefa)

	IF EMPTY(m.loDlg.FileTitle) && Clicked 'Cancel'
		m.lcFileName = ""
	ELSE
		m.lcFileName = m.loDlg.FileName
	ENDIF
	m.loDlgForm = NULL
	RETURN m.lcFileName


DEFINE CLASS oleComDialObject AS OLECONTROL
	OLECLASS ="MSComDlg.CommonDialog.1"
ENDDEFINE

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+



2020-07-03

FoxyYearCalendar - Adding Win32 controls to VFP forms

Here's a sample that uses the Win32 MonthCalendar control in a VFP form.
This can be used as a base to insert other Windows controls, uch as the trackbar, the date picker and scrollbars.

Allows you to select between a range of dates, and to mark some dates in bold.



Download sample
https://www.foxite.com/uploads/db1656e4-4819-4634-bda7-eb0c3d2460da.zip