Useful code snippets

I program occasionally. The occasions are often quite far apart, so when I start a new project I often find that I've forgotten how to do things. So I've started documenting some of the not-especially-challenging, but not-completely-trivial-either things I've done. And when someone else has produced a document that describes how to do something useful, but forgettable, then I include a link to that person's site. Feel free to use these techniques in your own programs.

 

 

Delphi

Put

positions interface items relative to each other

Interpolater

changes the value of an arbitrary number of identifiers (usually tControl properties such as panel.top, panel.left) gradually over a specified time period. Useful for animating appearance or disappearance of controls, and movements. The intermediate value for the identifier is determined by a callback function progressFunction(relativeProgress). Three progress functions are supplied (linear, sine (soft-finish) and bulge (overshoots)). The default is linear; you can override it globally or on a per-identifier basis, and you can write your own progress functions.

function
getNodeCalled

based on
function GetNodeByText

Searches a tTreeView for a node with a specified name. Makes it visible if it's hidden, either because the the part of the tree that contains the node is collapsed, or because the node is off the screen.

Command undo/redo

The units in this snippet allow you to add infinite undo/redo behaviour to your application, and to display a command trace that the user can interact with to control the opeation of the undo/redo.

 

Javascript

URLExists

function UrlExists(url) {
 
var http = new XMLHttpRequest();
  http
.open('HEAD', url, false);
  http
.send();
 
return http.status!=404;
 

Interpolater class declaration
(includes nameAndValues)

 

Interpolater constructors and destructors

 

Interpolater main methods

 

Some progress functions, including linear, the (essential) default

 

(All Delphi)

 

The interpolater generates a series of values for a variable that are intermediate between some start value and some endvalue. The values at any instant are a function of the time since the start of the interpolation, as a proportion of the specified total duration.

It is intended to allow a programmer to set up size and location animations with minimal fuss. You tell it the names of the variables whose values to be interpolated (typically properties of controls such as memo1.top, memo1.width and so on), the startvalue and endvalue for each variable, and optionally, the name of a function that defines an type of  interpolation.

It can handle any number of properties. For example, it can manage an animation in which a control's (top, left, bottom, right) properties are continuously varied from , say (0, 0, 100, 500) to, say, (300, 400, 20, 800), so that a control that is five times wider than it is high and is located in the top, left corner of the window moves so that its top left corner is at (300, 400) at the same time as its height reduces to 20 and its width increase to 800. If desired, other controls, which may or may not be children of the first control, can be moved and resized independently during the same interpolatoin. Any other integer-based property could also be varied continuously during the animation. 

The heart of the interpolater is really just a single statement:  

currentVal := trunc( startVal + progressFunction(relativeProgress ) * (endVal - startVal) );

which produces a current value for some variable. During the course of the interpolation, the value changes from startVal to endVal. relativeProgress varies from 0 to 1 during the course of the interpolation. The default  easures the progress of the interpolation, and the default progressFunction (called linear) simply returns relativeProgress, so that the value changes steadily throughout the duration of the interpolation.

The generality of the approach stems from two things

  • the interpolater has a run method  that is invoked repeatedly, and generates the intermediate values that you can use however you like
  • the run method takes a list of quintuples (last item optional)    (name,  startVal,  endVal, currentVal,  progressFunction)  

where
name
is a string that identifies the quantity being varied (that's the inelegant part).
Startval
and endVal speak for themselves,
currentVal
is the intermediate value that is appropriate for the proportion of the total allowed duration that the interpolation has been running.
ProgressFunction
is a function of relativeProgress. Its output typically varies between 0 and 1. If you omit that parameter, the default function, which simply returns relativeProgress, and this produces a linear interpolation, is used   You could supply an alternative function that varied between 0 and 0, for example, if you wanted to move things around in some cyclic animation, or 0 -> 1.25 -> 1 if you want the interpolation to overshoot a little.

USAGE example

interpolater := tInterpolater.create;
interpolater.duration := 0.5; // seconds
  // The following statement is optional: it specifies a function that will override
  // the default progress function linear. Result should be real
interpolater.genericProgressFunction := myProgressFunction;

  // Now we tell the interpolater the names of all the variables we want to interpolate
interpolater.add('myPanel.width' , myPanel.minWidth , myPanel.maxWidth            );
interpolater.add('myPanel.height', myPanel.minHeight, myPanel.maxHeight           );
interpolater.add('myPanel.top'   , myPanel.minTop   , myPanel.maxTop    , sigmoid );
  // where sigmoid is a soft-start-and-finish progress function that you are free to write
  // Here, it overrides the default linear progress function, and also myProgressFunction,
  // but for one value (myPanel.top) only. The other values will use myProgressFunction
  // The output of sigmoid should rise from 0 to 1 for input values in the range 0 to 1,
  // starting and ending slowly.        
  // You can also assign a function other than linear to interpolater.genericProgressFunction,
repeat // calculate the interpolated values for the current time
   interpolater.run;   
      // apply the results to the affected varaiables; get invocations do not have to occur
      // in the same order as the add invocations
   myPanel.height := interpolater.get('myPanel.height');
   myPanel.top    := interpolater.get('myPanel.top'   );
   myPanel.width  := interpolater.get('myPanel.width' );
   application.processMessages; // if interpolater is being used for animation
until interpolater.finished;
interpolater.destroy;

The application.processMessages command, executed once per iteration, ensures that the progress of the animation is displayed.

Put Procedure
(Delphi)
 The put procedure can be used to position one control with respect to another.

USAGE examples

put (C1, 5 (* pixels *), ioOutside,  rtTopEdge,           C2)
put (C1, 0 (* pixels *), ioOnCentre, rtVerticalCentre,    C2)
put (C1, 0 (* pixels *), ioInside,   rtBottomRightCorner, C2)

                                                                 etc.

The identifiers with the funny prefixes (ioOutside, rtTopEdge etc.) use the following two types that need to be declared.

TYPE

 tInOrOut =

 ( ioOutside , ioInside , ioOnCentre );

 tRelativeTo =

 ( rtTopEdge       , rtBottomEdge     , rtLeftEdge         , rtRightEdge
 , rtTopLeftCorner , rtTopRightCorner , rtBottomLeftCorner , rtBottomRightCorner
 , rtCentreX       , rtCentreY        , rtCentreXY
 );

There is more explanation in the code.

undo/redo

CommandAdminUnit

CommandsUnit

(Delphi)

This is a set of units that allow a programmer to create commands with (infinite) undo/redo capabilities. Each user command may incorporate an arbitrary number of system-level commands. The undo/redo facility acts at the user command level. For example, the user may generate a Clear command that deletes a number of items. each deletion effected by a separate delete system command. The system commands are derived from a superclass of type tCommand, and are stored in a  list. For each command there are three methods; enact, unact, and react. React is often just a call to enact, but sometimes there are initialisation actions that should not be repeated, and in that case, react is different. 

CommandAdminUnit handles creating the command list, populating it and running the commands. You should basically be able to paste it straight into your program.

CommandsUnit unit contains the class definitions of all the command classes and the definitions of the enact, unact, and react methods. These you will have to write. .Each command class will have a set of properties that contain the information used by its enact, unact, and react methods. When the program creates the command, it loads these properties.  

There is a special syntax for naming the command classes (at least if you want to take advantage of the command list display that helps the user see where in the undo/redo sequence they have reached). The trace procedure matches words (string with an upper case first letter) in the classname for the command and replaces underscores with values for those strings. For example if you had a command class called tAddPhoto_ToPanel_ and your enact method for that command included the code

     trace('photo', 17);
  trace('panel', 'reject Images Panel')

then the following line of text would be added to the command list display

    Add photo 17 to panel REJECT IMAGES PANEL

Unfortunately, if an object is declared as type tCommand and assigned an object of type tAddPhoto_ToPanel_ (as happens when the command is passed as a parameter to the enact method) Delphi needs to be told that its type is tAddPhoto_ToPanel_  before it can execute the enact method unique to the tAddPhoto_ToPanel_ command. So the enact method of tCommand (the parent class of all the commands) contains a big nested if statement that decides which child command to execute. Here's the first three lines of such a nested if statement

     if self is tAddTag_ToParent_InTagtree_            then (self as tAddTag_ToParent_InTagtree_)            .enact
else if self is tAddTag_ToSibling_InTagtree            then (self as tAddTag_ToSibling_InTagtree)            .enact
else if self is tAddTagbag_FromTagtreeToTagbagTreeView then (self as tAddTagbag_FromTagtreeToTagbagTreeView) .enact

where self is the command.

That sorta breaks the usual convention about inheritance, because the enact methods of those commands can't have an inherited statement, as that would cause an infinite loop. I don't like doing it, but I can't see a neat alternative. It's really annoying that you can't access the subclass method directly, without going through the is/as rigmarole.

USAGE example

Create a user command that copies one or more tags to a photo. Each copy operation involves a separate system level command, so if there's only one tag to copy, the commandType of the single command that's created is firstAndLastCommand. If there are multiple tags, then the first command has commandType firstcommand, and the last command has the commandType lastCommand.

var command : tCopyTag_FromTaghierarchytreeToPhoto_;

...

for i := 0 to tagTally - 1 do
  begin

  // set flags on the system commands at the start and end of the the user command
  if {there's only one tag to transfer} (i = 0) and (i = tagTally-1) then
    commandType := firstAndLastCommand
  else if {this is the first tag} i = 0 then
    commandType := firstCommand
  else if {this is the last tag} i = tagTally-1 then
    commandType := lastCommand
  else // the tag is somewhere in the middle
    commandType := ordinaryCommand;

  // create the command
  command := tCopyTag_FromTaghierarchytreeToPhoto_.create(commandType);

  // add it to the command list
  commandList.addCommand(command) as tCopyTag_FromTaghierarchytreeToPhoto_;

  // set any special values the command needs; these are different for each command type
  command.tag        := tag;
  command.photoPanel := photoPanel;
  end;

// once all the system commands that make up the user command have been set up, run them
commandList.enactUserCommand;
 

That code appears in the program somewhere other than the commandAdmin unit of the Commands unit (in a unit called eventHandler, in my program). The commandAdmin unit obeys the enactUserCommand method call by running through the command  list from firstCommand to lastCommand. Thus the user sees all the tag copies as a single action. The command trace tells a more detailed story. The code above has  generated system commands  0-5 in the trace listing below

\_ _ _ _ _ _ _ _ _ _ _ _ _ _
/
0: Copy tag ANIMAL from taghierarchytree to photo 1
1: Copy tag SEAGULL from taghierarchytree to photo 1
2: Copy tag SEABIRD from taghierarchytree to photo 1
3: Copy tag BIRD from taghierarchytree to photo 1
4: Copy tag OCELOT from taghierarchytree to photo 1
\_ _ _ _ _ _ _ _ _ _ _ _ _ _
/
5: Copy tagbag STREAM from taglibrary to tagbag workingArea
\_ _ _ _ _ _ _ _ _ _ _ _ _ _
/
6: Clear selected photos
7: Select photo 0
\__________________________

The horizontal lines are divisions between user commands. Clicking on a dashed horizontal line undoes or redoes the command list to that point (and the solid line moves to that point to indicate the current state of the system). When some of the commands have been undone - so that the solid line has moved to the point between system command 4 and system command 5, for example - generating a new command wipes out the commands after the solid line. I could have built the commands into a tree instead of a list, and then it would have been possible to explore alternative paths without losing the original command sequence. One day, maybe...