Tuesday, April 13, 2021

Virtual Fox Fest 2021 (May): Procrastinators Unite and Register Today!

We’re glad so many have already registered for the May 6th Virtual Fox Fest. We still have room for many more.

If you’re planning to attend, please register sooner rather than later. Our hard working registration staff (Rick) is an organizer, and has a full-time job and then some working on customer projects. If too many of you wait until the week of the conference to register, some people may not get the credentials in time to see the first session on May 6th. It literally takes minutes for you to register, but it takes us longer to process your registration.

We don’t want you to miss all the details about the conference we’ll be sharing over the next couple of weeks. We want you to have a chance to read the white papers and download the examples once we release them before the conference starts. We’d hate to see you miss the opportunity to get a head start on all the goodies. Go get registered! Now, really, don’t waste another minute. Here is the link: http://geekgatherings.com/Registration

Virtual Fox Fest is May 6th, 2021! Conference website: https://virtualfoxfest.com/

Thursday, March 25, 2021

Getting Localized Day and Month Names

If you need to get a localized day or month name (such as "Thursday" or "March" in English, which are "Donnerstag" and "März" in German), there are Windows API calls that can help you but they can be complicated to use. This is especially true if you need the localized names in a language other than the one the user's machine is configured for. We have numerous customers that have their machine configured to use, for example, German, but want these names displayed in English.

wwDotNetBridge and .NET to the rescue. Pass this function a date or datetime value, the type of name wanted (see the comments in the code for the values; for example, use 2 for an abbreviated name such as "Thu" for "Thursday"), and a BCP-47 language code such as "en-US" for U.S. English or "de-DE" for Germany German. For example, to put a list of English month names into an array, use something like:

dimension laMonths[12]
for lnI = 1 to 12
laMonths[lnI] = GetDateName(date(2021, lnI, 1), 3, 'en-US')

Here's the code for the GetDateName function. It assumes the files for wwDotNetBridge are in place:

function GetDateName(tuDate, tnType, tcCulture)
local loBridge, lcFormat, loCulture, lcReturn
do wwDotNetBridge
loBridge = GetwwDotNetBridge()
do case
case tnType = 1
&& day
lcFormat = '{0:dddd}'
case tnType = 2
&& abbreviated day
lcFormat = '{0:ddd}'
case tnType = 3
&& month
lcFormat = '{0:MMMM}'
case tnType = 4
&& abbreviated month
lcFormat = '{0:MMM}'
loCulture = loBridge.CreateInstance('System.Globalization.CultureInfo', ;
lcReturn = loBridge.InvokeStaticMethod('System.String', 'Format', loCulture, ;
lcFormat, tuDate)
return lcReturn

Tuesday, March 23, 2021

Time to Register for Virtual Fox Fest

Thanks to all who have already registered for Virtual FoxFest (May 6, 2021); we appreciate your support and our speakers are excited to share their sessions with you soon.

That said, we know there are those who have not registered and have good intentions to do so before the conference starts. Please do so sooner than later. It literally takes minutes to register. Here is the link: http://geekgatherings.com/Registration

Our Virtual Fox Fest shirt store has been updated for 2021! Check it out at https://geekgatherings.logosoftwear.com/. You can choose your favorite color or put the VFF logo on any item LogoSoftwear sells. You’ll find instructions for customizing your purchase at https://virtualfoxfest.com/geekwear.aspx.

After much consideration, we’ve decided to stick with a virtual event this fall. It will be structured similarly to Virtual Fox Fest 2020. Dates will be announced later. We’re already looking forward to a live Southwest Fox in 2022.

Saturday, February 20, 2021

Announcing Virtual Fox Fest May 2021

We had so much fun at Virtual Fox Fest 2020 that we’ve decided to do more online conferences. They won’t all be three-day events; in fact, the next one (Thursday, May 6, 2021) is a one-day event. However, it’ll still have the same great features everyone loves about VFF: great speakers delivering great sessions, live chatting during presentations, and getting to hang out virtually with new and old friends. This event will feature classic sessions from Southwest Fox, updated for 2021. Registration is available now so sign up today!

We’re still discussing whether to hold Southwest Fox in Arizona this fall. We expect to announce our plans for a fall conference by the end of March.

Wednesday, January 20, 2021

Changing Execution Priority on Demand

Recently, a friend asked me if it was possible to change the execution priority for some tasks on the fly. The issue was that when a user on a terminal server runs a report in Stonefield Query, which can be computing intensive as it retrieves and massages the data required for the report, other users may experience slower response. I did some research and it turned out to be easy to do, thanks to Rick Strahl's wwDotNetBridge.

The following code changes the priority for the running application to BelowNormal (see the documentation for the System.Diagnostics.ProcessPriorityClass enum for the different priority levels):

do wwDotNetBridge
loBridge = GetwwDotNetBridge()
loProcess = loBridge.InvokeStaticMethod('System.Diagnostics.Process', 'GetCurrentProcess')
loProcess.PriorityClass = loBridge.GetEnumValue('System.Diagnostics.ProcessPriorityClass.BelowNormal')

(This assumes you have wwDotNetBridge.prg included in your project and wwDotNetBridge.dll and ClrHost.dll in the current folder or VFP path.)

Use this code just before some process you want to run at a lower priority. Use similar code but a different value, such as Normal, to set the priority back again after the process is done.

As I have said many times, there's almost nothing we can't do in VFP with wwDotNetBridge.

Monday, December 28, 2020

Microsoft Office 365-like Ribbon Control

Merry Christmas! Every year between Christmas and New Year's, I like to work on a fun project, such as learning a new technique or writing some cool utility. Last year, I worked on the open source version of Hacker's Guide to Visual FoxPro. This year, I created an open source Microsoft Office 365-like ribbon control for VFP forms.

Here's a sample form containing the ribbon control that resembles Microsoft Outlook. It has two tabs: Home and Send / Receive. Clicking a tab displays the toolbar for that tab.

Here's the Send / Receive tab:

So far, I've included two themes: Colorful (above) and Dark Grey (below):

Themes are defined as RGB colors in an XML file so it's a simple matter to add another theme.

The ribbon control is 100% VFP code. Please try it out and let me know of any enhancements you'd like by creating issues in the GitHub repository for the project.

Wednesday, December 23, 2020

Help us plan the next Virtual Fox Fest

We have 11 quick questions to help us plan future Virtual Fox Fest conferences. We would really appreciate your input:


Happy Holidays and wishes for a healthy and prosperous 2021 to all.

Friday, November 06, 2020

Virtual Fox Fest 2020 Follow-up

Here are some follow-up notes about Virtual Fox Fest 2020, the first but likely not the last online event hosted by Geek Gatherings:

  • Videos for all presentations are now available on YouTube for free to everyone, as our contribution to the VFP community. Be sure to subscribe to our channel.
  • If you didn't attend Virtual Fox Fest and want materials (white papers and sample code) for the presentations, go to  https://geekgatherings.com/Registration and "register" for the conference. You're not really registering since it's over, but in the Registration Fees section of the registration page, you'll see "Virtual Fox Fest 2020 Session Materials (download)". The cost of the materials is $49. There's also an opportunity to sponsor Virtual Fox Fest, which means your name will be listed on our Sponsors page.
  • We're planning on doing a survey soon. It'll be sent to all attendees but if you want to participate so you can help shape future events, email info@geekgatherings.com and ask to be included.
  • We have tentative dates for an in-person Southwest Fox conference next year: October 14-17, 2021, at the same location as previous years: the SanTan Conference Center in Gilbert, Arizona. At this point, we don't know whether we'll be able to have an in-person event, but hope springs eternal!
  • If you didn't attend Virtual Fox Fest, check out our Facebook page and see what you missed, then plan to attend a future one so you can join in the fun.

Friday, October 30, 2020

Default Git Message as a Pre-Commit Reminder

If you want a reminder when you commit in Git that you need to do something before committing, such as generating the text equivalents of binary files (something I occasionally forget to do if I working on something that doesn't use Project Explorer, which automatically does that for me), do the following:

  • Create a text file anywhere you wish (named, for example, commit.txt) with the default message you want used in Git commits, such as “Remember to DO WHATEVER YOU NEED TO DO".
  • Open a command window in the project folder (the one that has .git as a subdirectory).
  • Type in a command window: git config commit.template “path\commit.txt”, where path is the full path for the text file you created.

Now, whenever you commit, the commit message defaults to “Remember to DO WHATEVER YOU NEED TO DO” (or whatever your message was), which of course you will then overwrite with the real commit message after you do what the reminder tells you to do.

Application Configuration using JSON

After watching Andrew MacNeill's Quasar and JSON: A Full Stack Experience for the DB Developer presentation at Virtual Fox Fest 2020, I was inspired to look at nfJSON, a VFX project by Marco Plaza. This cool project adds JSON support to VFP applications. The thing that tweaked my interest was the ability to convert a JSON string to a VFP object and vice versa with one line of code.

My first thought was using this for configuration settings. Just about every application needs configuration settings: it's better to read settings such as database connection information, file locations, email settings, etc. from a configuration source rather than hard-coding them into the app. I've used the Windows Registry, DBF files, INI, and XML files at various times but all of those require manually coding the reading and writing between the source and the VFP object(s) containing the settings. With nfJSON, it's one line of code.

I created a wrapper class called SFConfiguration. It only has three methods:

  • Load returns an object with properties matching the name/value pairs in the JSON contained in the specified settings file. If the file doesn't exist or is empty (such as the first time the application is run), it calls GetDefaultSettings (described below) to get default settings.
  • Save saves the properties of the specified settings object to the file specified in either the passed file name or the cSettingsFile property if a file name isn't passed.
  • GetDefaultSettings returns JSON for default settings. You can use this one of two ways: subclass SFConfiguration and override GetDefaultSettings to return the desired JSON, or set the oSettings property to a settings object containing the default settings.

Here's an example of using this class to get email settings:

loConfig = createobject('SFConfiguration')
loConfig.cSettingsFile = 'email.json'
loConfig.oSettings     = createobject('EmailSettings')
loSettings = loConfig.Load()
* loSettings contains the email settings for the user;
* if email.json doesn't exist, the settings in the
* EmailSettings class below are used as defaults.
* After the user enters the desired settings in some dialog,
* save them using:

define class EmailSettings as Custom
    Email      = 'dhennig@stonefield.com'
    MailServer = 'mail.stonefield.com'
    Port       = 25
    UseSSL     = .F.
    UserName   = 'dhennig'
    Password   = 'mypw'

Here's what the settings object looks like:

Here's what the saved JSON looks like:

Here's another example, this time using a subclass of SFConfiguration for the same thing:

loConfig = createobject('SFEmailConfiguration')
loConfig.cSettingsFile = 'email.json'
loSettings = loConfig.Load()
* loSettings contains the email settings for the user;
* if email.json doesn't exist, the settings in the
SFEmailConfiguration class below are used as defaults.

define class SFEmailConfiguration as SFConfiguration
    function GetDefaultSettings
        text to lcSettings noshow
            return lcSettings

Here's the code for SFConfiguration. It requires nfJSONRead.prg and nfJSONCreate.prg, which you can get from the nfJSON GitHub repository:

define class SFConfiguration as Custom
  cSettingsFile = ''
        && the name and path for the settings file
    cErrorMessage = ''
        && the text of any error that occurs
    oSettings     = ''
        && a settings object

* Load the settings from the file specified in the parameter
* or in This.cSettingsFile and return a settings object. If
* the file doesn't exist
 (such as the first time we're called),
* a settings object containing default
 settings is returned.

    function Load(tcSettingsFile)
        local lcSettingsFile, ;
            lcSettings, ;
            loSettings, ;
            loException as Exception
            lcSettingsFile = evl(tcSettingsFile, This.cSettingsFile)
            if not empty(lcSettingsFile) and file(lcSettingsFile)
                lcSettings = filetostr(lcSettingsFile)
            endif not empty(lcSettingsFile) ...
            if empty(lcSettings)
                lcSettings = This.GetDefaultSettings()
            endif empty(lcSettings)
            loSettings = nfJSONRead(lcSettings)
            This.cErrorMessage = ''
        catch to loException
            This.cErrorMessage = loException.Message
            loSettings = NULL
        This.oSettings = loSettings
        return loSettings

* Save the settings in the specified object to the file
* specified in
 the parameter or This.cSettingsFile.

    function Save(toSettings, tcSettingsFile)
        local lcSettingsFile, ;
            lcSettings, ;
            loException as Exception
        lcSettingsFile = evl(tcSettingsFile, This.cSettingsFile)
        if not empty(lcSettingsFile)
                lcSettings = nfJSONCreate(toSettings, .T.)
                strtofile(lcSettings, lcSettingsFile)
                This.cErrorMessage = ''
            catch to loException
                This.cErrorMessage = loException.Message
            This.cErrorMessage = 'Settings file not specified.'
        endif not empty(lcSettingsFile)
        return empty(This.cErrorMessage)

* Gets the default set of settings as a JSON string; override
* this in a
 subclass if necessary.

    function GetDefaultSettings
        local lcSettings
        if vartype(This.oSettings) = 'O'
            lcSettings = nfJSONCreate(This.oSettings, .T.)
            lcSettings = '{"text":"some text"}'
        endif vartype(This.oSettings) = 'O'
        return lcSettings