Thursday, March 05, 2009

Servoy Webinar for VFP Developers

Servoy is presenting a free Webinar discussing their product for VFP developers on March 26, 2009 at 10 am PST.

VFP DDEX Provider Updated

As Craig Berntson notes, Microsoft released an updated VFP DDEX Provider yesterday. The original, part of Sedna, had an expired license key so it wouldn't work if you installed it after March 2008. The new version is also a little easier to install, as there are fewer steps. I posted the new version of VFPX on the DDEX for VFP page.

Monday, March 02, 2009

Easier URL for VFPX

Sara Ford blogged Friday that all VFPX projects are now accessible using their name as a subdomain. This makes the URL for VFPX the nice and short http://vfpx.codeplex.com.

Replacement for SYS(2014)

SYS(2014) is a great little function that helps make your applications portable. Since it returns the relative path of a file to a specific folder, you can use it on an absolute file name (such as one returned from GETFILE()) and store the relative rather than absolute path.

However, one thing that's always bugged me about SYS(2014) is that it returns the path in upper-case. If you want to display the path to the user, they'll wonder why the path is upper-cased in your program but in a different case on disk.

Fortunately, there's a simple Windows API function you can call that does the same thing as SYS(2014) but respects the case. Here's an example of how to use that function:

(UPDATE: Walt Krzystek pointed out that GetRelativePath returned a blank string if the two paths are on different drives. Also, I neglected to trim trailing spaces from the return value.)

function GetRelativePath(tcTo, tcFrom)

#define FILE_ATTRIBUTE_DIRECTORY 0x10
#define FILE_ATTRIBUTE_NORMAL 0x80
#define MAX_PATH 260

declare integer PathRelativePathTo in shlwapi.dll ;
string @out, string @from, integer fromattrib, ;
string @to, integer toattrib
lcPath = space(MAX_PATH)
lcFrom = iif(vartype(tcFrom) = 'C', tcFrom, ;
sys(5) + curdir()) + chr(0)
lnFromAttrib = iif(directory(lcFrom), FILE_ATTRIBUTE_DIRECTORY, ;
FILE_ATTRIBUTE_NORMAL)
lcTo = iif(vartype(tcTo) = 'C', tcTo, ;
sys(5) + curdir()) + chr(0)
lnToAttrib = iif(directory(lcTo), FILE_ATTRIBUTE_DIRECTORY, ;
FILE_ATTRIBUTE_NORMAL)
PathRelativePathTo(@lcPath, @lcFrom, lnFromAttrib, @lcTo, lnToAttrib)
lcPath = alltrim(strtran(lcPath, chr(0), ' '))
if empty(lcPath)
lcPath = tcTo
endif empty(lcPath)
return lcPath

Sunday, March 01, 2009

Cleaning up _MemberData

The MemberData Editor, the replacement New Property/Method dialog, and the fantastic new PEM Editor all make it easy to get VFP to display the desired case for your custom properties and methods in IntelliSense and the Properties window rather than the lame lower-case it normally uses. The secret is _MemberData, a custom property added to a form or class that contains XML providing additional information about members, such as display case, whether they appear in the Favorites tab of the Properties window, and scripts to execute when you double-click properties in the Properties window.

One of the problems with _MemberData, though, is that it's limited to 8K. If you've ever worked with XML, you know it's very wordy and it doesn't take long to blow through the 8K limit. This is especially a problem with subclasses: not only do they contain the _MemberData XML for their own custom members, they also contain the XML for all parent class members. The deeper you go in a class hierarchy, the longer the XML gets and the closer you get to the 8K limit.

However, a little known fact about _MemberData is that it's cumulative: VFP internally combines the XML from the current class and all parent classes. Even if you blank _MemberData in a subclass, your members still display in the proper case. So, one way to keep that 8K limit at bay is to strip everything but the XML for the members added in the current class from _MemberData.

That would be a tedious chore to do manually. Fortunately, a couple of new features in version 4.0 of PEM Editor (currently in development) make this a breeze. The Cleanse MemberData function in the shortcut menu does this explicitly, but PEM Editor also does this automatically when you add a new member to a class.

If you can't wait for PEM Editor 4.0, I've uploaded a small PRG that does the same thing (in fact, it's the same code as in PEM Editor 4.0) to the Technical Papers page of the Stonefield Web site. To use it, open a class or form, then DO MDCleaner.PRG. Just to be safe, please back up your form or class first.

Tuesday, February 24, 2009

Southwest Fox Call for Speakers

Today we released the Call for Speakers for Southwest Fox 2009. Anyone interested in presenting should visit http://www.swfox.net/callforspeakers.aspx, read the Call for Speakers document, and download the proposal submission application. Session proposals are due by March 16.

We're adding a track this year called Technology for the VFP Developer. It'll cover things other than VFP that make life easier for FoxPro developers, like source control and virtual machines. Of course, we still plan to offer a good selection of topics in core VFP development, extending VFP, using VFP with other technologies, and of course, VFPX.

If you think you have something to say to the VFP community, please submit session proposals, even if you've never spoken at a conference before. Our community is strengthened when more people take an active role. Do be aware that speaking at a conference is a serious commitment. Even for experienced speakers, preparing a new session takes 40-80 hours. Doing it well at the conference calls for several rehearsals beforehand, too. So make sure you can commit the necessary time--the Call for Speakers lays out all the deadlines.

We're looking forward to seeing what you all come up with. Reading through the proposals we receive each year is really exciting, and choosing among them is always a challenge.

Tuesday, February 10, 2009

Using Tables Over 2GB in VFP

J.D. Mullin posted a video showing how to use Advantage Database Server to access DBF files that are over 2 GB in size, something you can't do if you access the table natively in VFP.

Friday, January 16, 2009

Playing with Windows 7

I've been playing with Windows 7 for about a week now. Like Andrew MacNeill, I installed it on a Virtual PC and also like Andrew, I got a BSOD when I tried to install Virtual Machine Additions (and in fact had to restore Windows 7 to a previous restore point to get it to work again). Fortunately, I remembered GIYF ("Google is your friend") and quickly found that Virtual PC SP1 was needed for Windows 7. After upgrading to SP1, I had no problems installing Virtual Machine Additions (which VPC can be a PITA to deal with if you don't).

I have a few other glitches right now, but they are all "Windows 7 on VPC" issues rather than Windows 7 issues:

  • No Aero Glass or related features (like the new Aero Peek). Come to think of it, I don't get Aero Glass when running Vista in a VPC either.
  • No sound.
  • Dragging a file from my main system to the VPC window gives an error. Copying a file from a shared folder on my main system to the VPC also has problems.

There are lots of cool things about Windows 7; Tim Sneath has blogged about some of them. The new taskbar and the concept of libraries will likely take some getting used to, but so far it feels like Vista with more fit-and-finish things taken care of.

Wednesday, January 14, 2009

PEM Editor is a Must-Have Tool

Jim Nelson has done a ton of work on the PEM Editor, the new name for the former Edit Property/Method replacement dialog project on VFPX that Marcia Akins originally created. He's made this into an incredible tool that I used about a hundred times a day. It completely replaces the native Edit Property/Method, New Property, and New Method dialogs, and in fact can even replace the Properties and Document View windows to some extent. Because it's modeless, resizable, and dockable, you never have to close it if you don't want to. Its customizable color coding and filtering abilities make working with forms and classes a lot easier. This is simply one of those must-have tools.

If you haven't already installed it, do yourself a favor and download it today. Installing it is as simple as DO PEMEditor.APP. From then on, you'll wonder what you ever did without it.

image

Changing the Color and Orientation of Images

Recently, I needed to change the color of an image. I have a container that includes an image and depending on the user's color choices, the container's background color might change. That means certain colors in the image must also change so the image blends in with the container properly, and rather than creating a number of images with pre-defined colors, I wanted to change the colors programmatically. One other wrinkle: because the orientation of the container might also change under some circumstances, I also wanted to rotate the image.

Fortunately, the VFPX GDIPlusX project makes this a snap and thanks to a recent blog posting by Cesar, it didn't take too long to figure out what to do. The following code reads the color of the upper left pixel in the image whose file name is in the lcImageFile variable and converts that color to the one specified in This.BackColor. It also rotates the image if it's wide and short but the container is narrow and tall. It then writes the new image directly to the PictureVal property of the imgImage object so there's no additional disk I/O.

* Create the GDIPlusX objects if necessary.

if type('_screen.System.Name') <> 'O'
do System.APP
endif type('_screen.System.Name') <> 'O'
with _screen.System.Drawing

* Load the image.

loBmp = .Bitmap.FromFile(lcImageFile)

* Get the color of the upper left pixel.

loColor = loBmp.GetPixel(0, 0)

* Create a color map and tell it to convert from the color of
* the pixel to our background color.

loColorMap = .Imaging.ColorMap.New()
loColorMap.OldColor = loColor
loColorMap.NewColor = .Color.FromRGB(This.BackColor)

* Create an attributes object and make it use the color map.

loAttr = .Imaging.ImageAttributes.New()
loAttr.SetRemapTable(loColorMap)

* Create a new image and draw the old image onto it using the new
* color mapping.

loDestBmp = .Bitmap.New(loBmp.Width, loBmp.Height, ;
.Imaging.PixelFormat.Format24bppRGB)
loGfx = .Graphics.FromImage(loDestBmp)
loRect = loBmp.GetBounds()
loGfx.DrawImage(loBmp, loRect, loRect, .GraphicsUnit.Pixel, loAttr)

* Flip the image if its orientation doesn't match ours.

if This.Width > This.Height and loBmp.Height > loBmp.Width
loDestBmp.RotateFlip(.RotateFlipType.Rotate90FlipNone)
endif This.Height > This.Width ...

* Write the image directly to the PictureVal property of our image
* object.

loDestBmp.Save(This.imgImage, .Imaging.ImageFormat.Bmp)
endwith