Reading time: 4 – 6 minutes
Years ago while working in happy Dos land, programming little game demos there, I hit a wall with my programming, and was forced to switch to Win32 programming if I wanted anyone to see my stuff. The transition had one bump in the road (Known as FreePascal), but then I started using Delphi. Took a while to get used to the whole WYSISYG, visual editor stuff, but after spending years of having to tweak menus and buttons at a per-pixel level, then having to compile and run the program just to see the changes, I can’t say I was upset.
There was one change though that I still can’t figure out, it’s with the case statement. C\C++ users know it as their switch staement (That uses case statement in each line). In Pascal, this is fine:
case Temp of 'bob' : begin end; 'Tom' : begin end; end;
Try that shit in Delphi, you get this error on the line “case Temp of”:
Ordinal type required
So no more using strings inside of case statements. Now some people may think “But I’ve seen them before!”. No. What you may have seen is something like this:
case Color of clRed : begin end; clBlue : begin end; end;
clRed and clBlue are predefined. One way around this that’s very sloppy is make a function called StrToOrd, like this:
function StrToOrd (AString : String) : int64; var Loop : Integer; begin Result := ''; for loop := 1 to length (AString) do Result := strtoint(inttostr(Result)+inttostr(ord(AString[Loop]))); end;
The returned value inside Result will be an int64 value of all the letters, so you’re very limited in char length. And while it works, you can not do this:
case StrToOrd(Edit1.Text) of StrToOrd('Bob') : Caption := 'You picked bob!'; StrToOrd('Tom') : Caption := 'You picked tom!'; else Caption := 'That''s not a choice!'; end;
This is because the items inside the case have to be static. So in order to use this function, you have to do this:
case StrToOrd(Edit1.Text) of 36611198 : Caption := 'You picked bob!'; 384111109 : Caption := 'You picked tom!'; else Caption := 'That''s not a choice!'; end;
At this point, you may as well use nested IFs unless there’s performance issues that require the case statement.
There’s another way to go about using strings in case statements, the way most Delphi guys do it. The command AnsiIndexText (Inside strutils) will return what item from an index of strings your text matches. Confused? Here’s how it works:
case AnsiIndexText(Edit1.text,['Bob','Tom']) of 0 : Caption := 'You picked bob!'; 1 : Caption := 'You picked tom!'; else begin Caption := 'That''s not a choice!'; end; end;
Crude, but it works. We can spice things up a bit by using a string list. Now the strings inside a string list can not be passed to AnsiIndexText as-is, they’re data structures, so one way to do it:
TStringArray = Record MyArray : Array of String; end;
TMyStringList = class(TStringList) Private function GetArray : TStringArray; Public Property StringArray : TStringArray read GetArray; end;
var MyStrings : TMyStringList;
function TMyStringList.GetArray : TStringArray; Var Loop : Integer; Begin SetLength(Result.MyArray,Self.Count+1); For Loop := 0 to Self.Count-1 do Result.MyArray[Loop] := Self.Strings[Loop]; end;
procedure TForm1.Button1Click(Sender: TObject); var Temp : String; Loop : Integer; begin case AnsiIndexText(Edit1.text,MyStrings.StringArray.MyArray) of 0 : Caption := 'You picked bob!'; 1 : Caption := 'You picked tom!'; else begin Caption := 'That''s not a choice!'; end; end; end;
procedure TForm1.FormCreate(Sender: TObject); begin MyStrings := TMyStringList.Create; MyStrings.Add('Bob'); MyStrings.Add('Tom'); end;
This is still a big pain, but better than nothing.
The way I’m using this case statement in my last program is via a static array, I also have variables declared for each item:
PanelNames : Array [0..11] of String = ('PanelAgeUp' , 'PanelAgeDown','PanelMoveUp','PanelMoveDown','PanelACUp','PanelACDown','PanelSaveUp','PanelSaveDown','PanelLevelUp','PanelLevelDown','PanelStatUp','PanelStatDown'); iPanelAgeUp = 0; iPanelAgeDown = 1; iPanelMoveUp = 2; iPanelMoveDown = 3; iPanelACUp = 4; iPanelACDown = 5; iPanelSaveUp = 6; iPanelSaveDown = 7; iPanelLevelUp = 8; iPanelLevelDown = 9; iPanelStatUp = 10; iPanelStatDown = 11;
Procedure TForm1.FindRepeat (Sender : TObject); var MyPanel : TPanel; Begin MyPanel := Sender as TPanel; Case AnsiIndexText(MyPanel.Name,PanelNames) of iPanelAgeUp : RepeatTimer.OnTimer := PanelAgeUpClick; iPanelAgeDown : RepeatTimer.OnTimer := PanelAgeDownClick; iPanelMoveUp : RepeatTimer.OnTimer := PanelMoveUpClick; iPanelMoveDown : RepeatTimer.OnTimer := PanelMoveDownClick; iPanelACUp : RepeatTimer.OnTimer := PanelACUpClick; iPanelACDown : RepeatTimer.OnTimer := PanelACDownClick; iPanelSaveUp : Begin RepeatTimer.OnTimer := PanelSaveClick; Modify := 1; end; iPanelSaveDown : Begin RepeatTimer.OnTimer := PanelSaveClick; Modify := -1; end; iPanelLevelUp : Begin RepeatTimer.OnTimer := PanelLevelClick; Modify := 1; end; iPanelLevelDown : Begin RepeatTimer.OnTimer := PanelLevelClick; Modify := -1; end; iPanelStatUp : Begin RepeatTimer.OnTimer := PanelStatClick; Modify := 1; end; iPanelStatDown : Begin RepeatTimer.OnTimer := PanelStatClick; Modify := -1; end; end; End;
The code above is so I can reuse one procedure for EVERY button since the program has about 45 buttons right now, it was getting pretty full of the same procedure over and over again. Simple trick to overcome a pretty silly restriction in my book.