Friday, October 30, 2009

Get the last entry from a database table

Let's say you got a table(Users) with 3 fields(ID, FirstName and LastName), you need to get the last ID entry in that table, in this case you can use this nice SQL function
SELECT MAX("ID") FROM "Users";
you can do same for any other field, let's say FirstName
SELECT MAX("FirstName") FROM "Users";
MAX - is a SQL function which returns the last entry in the specified table.

How to pass a stream to a database query

In previous post I've shown you how to save a TRichEdit/TDBRichEdit or descendant to a TStream, now heres how to pass that stream to a INSERT query
// your procedure/function
var stream : TStream;
begin
     // the insert query
     myQuery.SQL.Text := 'INSERT INTO "Mytable"("MyStreamField") VALUES(:RichEdit)';
     // get TRichEdit as stream with helper function
     stream := RichEditToStream(myRichEdit);
     // create a new parameter with field type as ftBlob and parameter
     // type as ptInput
     myQuery.Params.CreateParam(ftBlob, 'RichEdit', ptInput);
     // now load the parameter from stream
     // we can also use myQuery.myQuery.Params[0].LoadFromStream();
     myQuery.ParamByName('RichEdit').LoadFromStream(stream, ftBlob);
     // we free the stream here
     FreeAndNil(stream);
     // execute the sql
     myQuery.ExecSQL;
     // clear the parameters
     myQuery.Params.Clear;
end;

How to save/load a TRichEdit to/from a string/stream

Recently I had to save a TRichEdit as a string so I can save it in a database field, after 1-2 hours I came up with this solutions
// this function extracts formatted string from a
// TRichEdit/TDBRichEdit or descendant and returns it as a TStream
function RichEditToStream(thisRichEdit: TRichEdit): TStream;
var ss : TStringStream;
begin
     ss := TStringStream.Create(EmptyStr);
     thisRichEdit.Lines.SaveToStream(ss);
     Result := ss;
     // you might need to set position to zero
     Result.Position := 0;
     // REMEMBER to free the stream variable
end;

// this functions returns the formatted of a
// TRichEdit/TDBRichEdit or descendant as plain string
function RichEditToString(thisRichEdit: TRichEdit): string;
var ss : TStringStream;
begin
     ss := TStringStream(RichEditToStream(thisRichEdit));
     Result := ss.DataString;
     FreeAndNil(ss);
end;

// this procedure loads formatted string from a
// string and loads it into a TRichEdit/TDBRichEdit or
// descendant
procedure RichEditFromStream(thisRichEdit: TRichEdit;
          const theString: string);
var ss : TStringStream;
begin
     ss := TStringStream.Create(theString);
     thisRichEdit.Lines.LoadFromStream(ss);
     FreeAndNil(ss);
end;

Friday, October 23, 2009

Boyer-Moore-Horspool algorithm

If you ever need to search a string very fast then you should use Boyer-Moore-Horspool algorithm, it's so fast that you can search 80Mb text files in under 300 miliseconds!
Example of implementation in Delphi
function search(pat: string; text: string): integer;
var
  i, j, k, m, n: integer;
  skip: array [0..MAXCHAR] of integer;
  found: boolean;
begin
  found := FALSE;
  search := 0;
  m := length(pat);
  if m=0 then
  begin
    search := 1;
    found := TRUE;
  end;
  for k:=0 to MAXCHAR do
    skip[k] := m;   {*** Preprocessing ***}
  for k:=1 to m-1 do
    skip[ord(pat[k])] := m-k;
  k := m;
  n := length(text);            {*** Search ***}
  while not found and (k <= n) do
  begin
    i := k; j := m;
    while (j >= 1) do
    begin
      if text[i] <> pat[j] then
        j := -1
      else
      begin
        j := j-1;
        i := i-1;
      end;
      if j = 0 then
      begin
        search := i+1;
        found := TRUE;
      end;
      k := k + skip[ord(text[k])];
    end;
  end;
end;
This snippet is taken from www.delphidabbler.com, it can be viewed fallowing this link.
In a few days I will post a trie implementation which is way faster than Boyer-Moore.

Wednesday, October 21, 2009

Terminating a process

If at any point you wish to terminate a/your process instantly then you should take a look at TerminateProcess function in Windows SDK(Software Developer Kit) help which comes with Delphi.
TerminateProcess function is defined as
BOOL TerminateProcess(
    HANDLE hProcess, // handle to the process 
    UINT uExitCode  // exit code for the process  
   );
Parameters information according to Delphi Help
hProcess
  Identifies the process to terminate.
  Windows NT: The handle must have PROCESS_TERMINATE access. For more information, see Process Objects(in Windows SDK Help).

uExitCode
  Specifies the exit code for the process and for all threads terminated as a result of this call. Use the GetExitCodeProcess function to retrieve the process's exit value. Use the GetExitCodeThread function to retrieve a thread's exit value.
Create a new VCL application, add a button, double-click that button and copy-paste
procedure TForm1.Button1Click(Sender: TObject);
begin
  TerminateProcess(GetCurrentProcess, 0);
end;
Your application should terminate instantly.

Tuesday, October 20, 2009

Delphigeist has a new interface

Like the title says, delphigeist got a "face-lift".
I really hope you guys like the new template, I've worked a lot to make it fluid(stretch across the screen), why? because after spending some time at this blog's traffic I came with the conclusion that over 80% of visitors have resolution 1280x1024 and over(some have even 1920x1200), those visitors deserve a good view.
Anyways I'm working on a project which(from what I was told) it will take at least over a year to complete, so I'm looking for people who wishes to write on this blog, if anyone out there has some knowledge of HTML and is a Delphi programmer, mail me with subject "delphigeist author" and body should be your first blog post, it must be heavily commented(it doesn't matter if that tip/tutorial already exists somewhere on the Internet, as long as you write a link to the source and your tip/tutorial is easier to understand).

Tuesday, October 13, 2009

Make the mouse wheel work properly in TListBox

Here is how to a make a descentant of a TListBox to behave properly when user tries to scroll using the mouse wheel.Instead of scrolling down to the bottom of the list
this code will make TListBox to scroll by one item at a time,regardless of the mouse wheel direction up or down.

unit sodListBox;
interface
uses
Classes
,StdCtrls
,Windows
,Messages
;
type
TsodListBox = class(TListBox)
private
{ Private declarations }
protected
{ Protected declarations }
procedure WndProc(var Message : TMessage);override;
public
{ Public declarations }
constructor Create(AOwner:TComponent);override;
destructor  Destroy;override;
published
{ Published declarations}
end;

implementation
{------------------------------------------------------------------------------}
constructor TsodListBox .Create(AOwner: TComponent);
begin
inherited Create(AOwner);
end;
{------------------------------------------------------------------------------}
destructor TsodListBox .Destroy;
begin
inherited;
end;
{------------------------------------------------------------------------------}
procedure TsodListBox .WndProc(var Message: TMessage);
var
i : SmallInt;
begin
case Message.Msg of
WM_MOUSEWHEEL :
begin
//Changing the message code from mousewheel to keydown
Message.Msg    := WM_KEYDOWN;
Message.LParam := 0;
//Finding the direction of mousewheel up or down.
i              := HiWord(Message.WParam);
//Simulating a keystroke according to mousewheel direction.
if i > 0 then
Message.WParam := VK_UP
else
Message.WParam := VK_DOWN;
end;
end;
inherited WndProc(Message);
end;
{------------------------------------------------------------------------------}

There you go you now have a TListBox behaving properly to mousewheel.

Sunday, October 11, 2009

Get dynamic array limits

Knowing the start and end index of an array is important, specially if you have a dynamic array.
A static array is a array of type with defined number of elements, that means that you know it's start and end index.
Note The number of elements in a static array cannot be changed!
Example of a static array
type TMyStaticArray = array[0..5] of integer;
A dynamic array is a array of type with dynamic number of elements, this means that at any time you can change it size using the SetLength procedure.
Note The number of elements in a static array can be changed using the SetLength procedure, example
type TMyDynamicArray = array of integer;

procedure ChangeMyDynamicArraySize;
var myDynamicArray : TMyDynamicArray;
    currentLength : Integer;
begin
     (* get the current number of elements from array *)
     currentLength := Length(myDynamicArray);
     (* increase it's number of elements by 1 *)
     SetLength(myDynamicArray, currentLength + 1);
     (* now our dynamic array has 1 more element *)
end;
Now back to the subject of this post, as you can see above, the number of elements of a dynamic array is unknown at compile time but it can drastically change at runtime, getting the start of a dynamic array is easy you just have to use the Low function and to get the end of it you use High or Length functions, Length function can be used on strings also, because a string is a dynamic array of char.
type TMyDynamicArray = array of integer;

procedure getArrayLimits;
var myDynamicArray : TMyDynamicArray;
    startIndex,
    endIndex : Integer;
begin
     startIndex := Low(myDynamicArray);
     endIndex := High(myDynamicArray);
     (* as I said above you can also use the Length function *)
     (* to get the end index so... *)
     endIndex := Length(myDynamicArray) - 1;
    (* Warning, when using the Length function you must *)
    (* decrement it's return value by 1 otherwise you *)
    (* will get an AV(Access Violation) *)
end;

Saturday, October 10, 2009

Using InputBox function

If you don't feel like creating complex forms just so you can ask the user for hes name or age, etc... then you can use the InputBox function which takes 3 parameters
function InputBox(const ACaption, APrompt, ADefault: string): string;
it's defined in Dialogs.pas unit.
Example of usage
var sValue: String;
begin
     sValue := InputBox('User input','Your nickname', 'nick');
     if sValue <> '' then
        ShowMessageFmt('Your nickname is "%s".', [sValue]);
end;

How to simplify CreateProcess

Simplification of CreateProcess function
procedure dgCreateProcess(const FileName: string);
var ProcInfo: TProcessInformation;
    StartInfo: TStartupInfo;
begin
     FillMemory(@StartInfo, sizeof(StartInfo), 0);
     StartInfo.cb := sizeof(StartInfo);
     CreateProcess(
                   nil,
                   PChar(FileName),
                   nil, Nil, False,
                   NORMAL_PRIORITY_CLASS,
                   nil, nil,
                   StartInfo,
                   ProcInfo);
     CloseHandle(ProcInfo.hProcess);
     CloseHandle(ProcInfo.hThread);
end;
Example of usage
CreateProcess('C:\Windows\Notepad.exe');
CreateProcess('notepad');
CreateProcess('mspaint');

Bookmark your code

Tired of scrolling your source code from interface section to implementation and vice-versa?
If so, then you don't know about code bookmarking, for example your at line number 2561 and you have to declare some constants or whatever at line 4163(you must be out of your mind to scroll that much every time you make adjustments) then if you wish to bookmark a line then put the cursor at that line and press CTR+K and then a number from 1 to 0(or 0 to 9) then when you wish to go to a bookmark just press CTRL+ the number of the bookmark that you wish to jump to.

Friday, October 9, 2009

TListView and CSV

If you ever need to save/load the content of a CSV file from/to a TListView, then this post will give you a start, but first what is a CSV file anyways? A CSV file is a format in which you can save table values as text. CSV stands for Comma Separated Values, Microsoft Excel can save/load sheets as/from CSV.
More info about CSV format can be found here and here.
here are the procedures which let's you save/load the content of a TListView to/from a CSV file.
(* this procedure saves the content of a TListView *)
(*   to a CSV file *)
procedure ListViewToCSV(
          theListView: TListView;
          const FileName: String);
var item: TListItem;
    index,
    subIndex: Integer;
    theFile: TStringList;
    Line: String;
begin
     (* initialize a TStringList *)
     theFile := TStringList.Create;
     (* loop to get all items of theListView *)
     for index := 0 to theListView.Items.Count -1 do begin
         (* store the current item in a local variable *)
         item := theListView.Items[index];
         (* format the current line of the CSV *)
         Line := Format('%s,%s',
              [item.Caption, item.SubItems.CommaText]);
         (* add the line to CSV *)
         theFile.Add(Line);
     end;
     (* save the CSV file *)
     theFile.SaveToFile(FileName);
     (* free theFile variable *)
     FreeAndNil(theFile);
end;

(* this procedure loads the content of a CSV file *)
(* to a TListView *)
procedure ListViewFromCSV(
          theListView: TListView;
          const FileName: String);
var item: TListItem;
    index,
    comPos,
    subIndex: Integer;
    theFile: TStringList;
    Line: String;
begin
     (* initialize a TStringList *)
     theFile := TStringList.Create;
     (* then load it from FileName *)
     theFile.LoadFromFile(FileName);
     (* loop to get all lines *)
     for index := 0 to theFile.Count -1 do begin
         (* store each line in a string variable *)
         Line := theFile[index];
         (* for every line add a item to the list *)
         item := theListView.Items.Add;
         (* get the first comma from the line *)
         comPos := Pos(',', Line);
         (* copy the value from start of string to *)
         (* the position of comma -1 *)
         item.Caption := Copy(Line, 1, comPos -1);
         (* delete from Line starting from 1 to *)
         (* position of comma *)
         Delete(Line, 1, comPos);
         (* get the next comma *)
         comPos := Pos(',', Line);
         (* loop to get all values from the string *)
         (* 1 comma means 1 column *)
         while comPos > 0 do begin
               item.SubItems.Add(Copy(Line, 1, comPos -1));
               Delete(Line, 1, comPos);
               comPos := Pos(',', Line);
         end;
         (* add last value as SubItem *)
         item.SubItems.Add(Line);
     end;
     (* free the TStringList *)
     FreeAndNil(theFile);
end;
Screen shot of the demo application

Download source code or binary form of the demo application.

Thursday, October 8, 2009

Delete/Clear rows in TStringGrid

If you ever needed to delete rows from a TStringGrid component you should have notice that it does not have a method for deleting/Clearing rows, here is nice class helper which does that, declare a class helper type
type
    TStringGridRowDeletion = class helper for TStringGrid
    public
          procedure RemoveRows(RowIndex, RCount: Integer);
          procedure Clear;
    end;
and the implementation
{ TStringGridRowDeletion }

procedure TStringGridRowDeletion.Clear;
var i: integer;
begin
     for i := 0 to RowCount -1 do
         RemoveRows(0, RowCount);
end;

procedure TStringGridRowDeletion.RemoveRows(RowIndex, RCount: Integer);
var i: Integer;
begin
     for i := RowIndex to RowCount - 1 do
         Rows[i] := Rows[i + RCount];
     RowCount := RowCount -RCount;
end;
now all you have to do is to add this helper in a unit where you need to call RemoveRows or create a utility unit(like most projects have) and add it to uses clause, from now on you can delete rows the easy way
...
var myStringGrid: TStringGrid;
begin
     myStringGrid.RemoveRows(0, 4); 
end;
Note: RemoveRows procedure can be invoked from all TStringGrid descendants.

Total Commander clone

I belive theres no need to tell you about the infamous Total Commander, this post is to let you know that theres a new sheriff in town and it's called Double Commander or DoubleCmd, it's a cross-platform open source file manager application written in Lazarus/Freepascal.
Here are some key feauters of Double Commander
- Unicode support
- All operations working in background
- Multi-rename tool
- Tabbed interface
- Custom columns
- Internal text editor (F4) with syntax hightlighting
- Built in file viewer (F3) to view files of in hex, binary or text format
- Archives are handled like subdirectories. You can easily copy files to and from archives. Supported archive types: ZIP, TAR GZ, TGZ and also BZ2, RPM, CPIO, DEB, RAR.
- Extended search function with full text search in any files
- Configurable button bar to start external programs or internal menu commands
- Total Commander WCX, WDX and WLX plug-ins support
- File operations logging
- And many more...
Double Commander screen shots

Double Commander Linux GTK 2


Compare files dialog



You can download Double Commander from this link.

Pointer to Byte array

Did you ever needed to get a variable as an array of byte? I mean you should know that the smallest value with which you can work is a byte(you can play with bits also but normally you don't) so a Word is represented on 2 Bytes, a Integer/Cardinal on 4 bytes, Double 7 Bytes, Int64 8 Bytes(se Delphi help for more info)...
First we need to declare 2 types
type
    (* declare a dynamic array of Byte type *)
    TypeByteArray = array of Byte;
    (* declare a dynamic array of PByte type *)
    TypePtrByteArray = array of PByte;
and now we need the utility functions which translates a pointer to an array of bytes, these utility functions take 2 parameters, the first parameter is the pointer to a variable(the @ operator can be used) and the second parameter takes the size in bytes of the variable, for instance if we pass a Integer value then the parameters should look like so: (@myInteger, 4), if the size of the passed parameter is 4 then you do not need to pass the second parameter, because it is declared as a constant of 4 bytes
function PointerToByteArray(
         (* the pointer to variable,
            @ operator can be used *)
         Value: Pointer;
         (* the size in bytes of the
            variable:
            Byte = 1
            Word = 2
            Integer/Cardinal = 4
            Double = 7
            Int64 = 8 *)
         const SizeInBytes: Cardinal = 4
         ): TypeByteArray;
var Address, (* store the address locally *)
    index: integer; (* for loop *)
begin
     (* get the pointer address *)
     Address := Integer(Value);
     (* set the length of the array *)
     SetLength(Result, SizeInBytes);
     (* loop to get all bytes *)
     for index := 0 to SizeInBytes do
         (* convert the address + index to a PByte pointer,
            use the ^ operator so compiler knows that
            we refer to the pointer's value *)
         Result[index] := PByte(Ptr(Address + Index))^;
end;

function PointerToPtrByteArray
         (* the pointer to variable,
            @ operator can be used *)
         (Value: Pointer;
         (* the size in bytes of the
            variable:
            Byte = 1
            Word = 2
            Integer/Cardinal = 4
            Double = 7
            Int64 = 8 *)
         const SizeinBytes: Cardinal = 4): TypePtrByteArray;
var Address, (* store the address locally *)
    index: integer; (* for loop *)
begin
     (* get the pointer address *)
     Address := Integer(Value);
     (* set the length of the array *)
     SetLength(Result, SizeInBytes);
     (* loop to get all bytes *)
     for index := 0 to SizeInBytes do
         (* convert the address + index to a PByte pointer *)
         Result[index] := Ptr(Address + Index);
end;
An example of getting an integer value as an array of bytes and display each byte value
procedure TForm1.Button1Click(Sender: TObject);
const
     CH_CR = #13; (* da' return char *)
var
   value: Integer;
   s: String;
   arr: TypeByteArray;
   index: integer;
begin
     (* assign a value to "Value" variable *)
     value := 2009;
     (* store the bytes on our dynamic array *)
     arr := PointerToByteArray(@Value);
     (* loop to get each byte from array *)
     for index := 0 to 3 do
         s := s + CH_CR + IntToStr(arr[index]);
     (* display array values in a message *)
     ShowMessage(s);
     (* resize the array to 0 elements *)
     SetLength(arr, 0);
     (* nil it *)
     arr := nil;
end;

Tuesday, October 6, 2009

Cannot find resource file

Sometimes Delphi IDE gives you an error like Cannot find resource file: '%s'. Recreated.
In most cases this means that the ProjectName.res file does not exist, to be more precise if you have a Delphi project called Project1 and you get this error a file called Project1.res does not exist. This error is caused intentionally by programmers who give you a application(tutorial) source code but they delete the *.res file because it only contains the default application icon which is automatically recreated by the IDE with the up-to-date icon, for instance the Delphi 7 default application icon is different from Delphi 2010 or other Delphi version.
In other cases programmers forget to pack the *.res files in the source they upload.
When you create a new VCL Froms Application you should see something like {$R *.dfm} in a form's unit source, well if you go to main menu -> Project -> View source then you should see something like this
program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res} (* this is the way resource files are included and the default application icon in this case *)

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Super duper Python IDE

Now this is an interesting post, why is so super? um... oh wait! it's created with... you guessed it! Delphi.
And the name of the IDE(Integrated Development Environment) i'm talking about is PyScripter, it can be downloaded by accessing this link from code.google.com(on the right side you can see a section called Featured downloads.
But this is just the IDE, you also need to download Python from this link.
Here is a list of PyScripter feauters just a copy-paste(nothing fancy or commented) from http://code.google.com/p/pyscripter/wiki/Features
Syntax Highlighting Editor
  Unicode based
  Full support for encoded Python source files
  Brace Highlighting
  Python source code utilities ((un)tabify, (un)comment, (un)indent, etc.)
  Code completion and call tips
  Code and debugger hints
  Syntax checking as you type
  Context sensitive help on Python keywords
  Parameterized Code Templates
  Accept files dropped from Explorer
  File change notification
  Converting line breaks (Windows, Unix, Mac)
  Print preview and print syntax highlighted Python code
  Syntax highlighting of HTML, XML and CSS files
  Split view file editing
Integrated Python Interpreter
  Code Completion
  Call Tips
  Command History
  Execute scripts without saving them
Integrated Python Debugging
  Remote Python Debugger
  Call Stack
  Variables Window
  Watches Window
  Conditional breakpoints
  Debugger hints
  Post-mortem analysis
  Can run or debug files without first saving them
Editor Views
  Disassembly
  HTML Documentation (pydoc)
File Explorer
  Easy configuration and browsing of the Python Path
  Integrated Version Control using Tortoise CVS or Tortoise SVN
Integrated Unit testing
  Automatic generation of tests
  Unit testing GUI
External Tools (External run and capture output)
  Integration with Python tools such as PyLint, TabNanny, Profile etc.
  Powerful parameter functionality for customized external tool integration
Other Features
  Code Explorer
  Access to Python manuals through the Help menu
  To Do List
  Find and Replace in Files
  Integrated regular expression testing
  Choice of Python version to run via command line parameters
  Run Python Script externally (highly configurable)
  Find Definition/Find references
  Find definition by clicking and browsing history
  Modern GUI with docked forms and configurable look&feel (themes)
  Persistent configurable IDE options
I bulive this is the best Python IDE you can find with or without paying for it, and if you got to this blog I belive that you know a thing or two about programming in Delphi, so if at any time you belive that you can modify something in PyScripter's source code to make it more productive then you know from where to get it.
Here are some PyScripter screen shots
Main PyScripter window

Dissassembly View

Function List

If you need more information go to PyScripter at code.google.com or just click here.

How to wipe a file

Here is a nice method to wipe a file as many times as you want(can wait)
procedure WipeFile(
  const FileName: String; (* file to be wiped *)
  const Times: Integer = 1 (* how many times ? *)
  );
type
  (* wipe 4kb at a time *)
  TBuffer = array[0..4095] of Byte;
  (* size of TBuffer *)
const
  szBuffer = sizeof(TBuffer);
var
  (* we need to know how many bytes we have left *)
  BytesLeft,
  (* this is to know how bytes we write *)
  BufferSize: Int64;
  (* a buffer variable *)
  buffer: TBuffer;
  index: Integer;
  (* we use a file stream to overwrite the file *)
  theFile: TFileStream;
begin
  (* open the file *)
  theFile := TFilestream.Create(FileName, fmOpenReadWrite or fmShareExclusive);
  try
    (* fill buffer with 128 values *)
    FillChar(buffer, szBuffer, 128);
    (* how many times should we wipe it ? *)
    for index := 1 to Times do begin
      (* remaining bytes *)
      BytesLeft := theFile.Size;
      (* start/restart from the beginning of the file *)
      theFile.Position := 0;
      (* while not end-of-file... *)
      while BytesLeft > 0 do begin
        (* check how many bytes we have left *)
        if BytesLeft > szBuffer then
          (* if we have more than 4096 bytes left
            then we only wipe 4096 bytes this time *)
          BufferSize := szBuffer else
          (* we have 4096 or less bytes left *)
          BufferSize := BytesLeft;
        (* overwrite the file(wipe) *)
        theFile.WriteBuffer(Buffer, BufferSize);
        (* update BytesLeft variable *)
        BytesLeft := BytesLeft -BufferSize;
      end;
    end;
  finally
    (* free the file stream *)
    FreeAndNil(theFile);
  end;
  (* ...finally delete the file, it should not be
    possible to *undelete* it *)
  Deletefile(FileName);
end;

MDI Application Tutorial

How about creating a MDI(Multiple Document Interface) application?
For this you need to
- start Delphi
- create a new VCL forms application
- change main form's FormStyle property to fsMDIForm
- create a new form via main menu File -> New -> Form
- go to main menu Project -> View Source
begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TForm2, Form2); (* remove this line! or comment *)
  Application.Run;
end.
it should look like this after modification
begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
we don't want form2 to be created at startup.
Now switch to form2 and change FormStyle to fmMDIChild, go to Events in object inspector double click the OnClose property and write Action := caFree; so that the form will be freed from memory when it's closed
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;
now add a TMainMenu component from Standard tab, add a File menu item and a New Form(or whatever you want to call it) menu item to File and on New Form's event add this code
procedure TForm1.NewForm1Click(Sender: TObject);
var
  NewMDIChild: TForm2;
begin
  NewMDIChild := TForm2.Create(Application);
end;
all you have to do now is to hit F9 to run the application and have fun with it, you can download source code of this tutorial fallowing this link.

Monday, October 5, 2009

Compiler complains

Here's a short list of frequent compiler errors and examples of how to produce and solve them

E1038 Unit identifier '%s' does not match file name
- create a new project press F12 to switch to code editor, press CTRL+Home and change unit1 to anything else i.e. unit_E1038;
solve
- the unit identifier and the unit file name must perfectly match(case sensitive), i.e. if the identifier after unit keyword is myFavoritUnit then the file name must be myFavoritUnit.pas.

E2003 Undeclared identifier '%s'
problem You must declare the identifier and it's type before you can use it
procedure produce_E2003;
begin
  ShowMessage(myUndeclaredIdentifier);
end;
solve
procedure solve_E2003;
var
  myDeclaredIdentifier: String;
begin
  myDeclaredIdentifier := 'I love Delphi';
  ShowMessage(myDeclaredIdentifier);
end;

E2004 Identifier redeclared '%s'
procedure produce_E2004;
var
  myRedeclaredIdentifier: Integer;
  myRedeclaredIdentifier: Integer;
begin
...
end;
solve
procedure solve_E2004;
var
  mySingleTimeDeclaredIdentifier1: Integer;
  mySingleTimeDeclaredIdentifier2: Integer;
begin
...
end;

E2005 '%s' is not a type identifier
This error is shown when you try to declare a variable of an unknown type
procedure produce_E2005;
var
  aVariable: MySuperClass; (* <-- MySuperClass is not defined *)
begin
end;
solve
type
  MySuperClass = class (* <-- MySuperClass is now defined *)
  (* ... *)
  end;
procedure solve_E2005;
var
  aVariable: MySuperClass;
begin
end;
E2009/E2010 Incompatible type(s) '%s' (and '%s')
procedure produce_E2009_E2010;
var
  myIntegerVariable: Integer;
begin
  myIntegerVariable := 'a string cannot be assigned to an integer variable';
end;
solve
procedure solve_E2009_E2010;
var
  myIntegerVariable: Integer;
begin
  myIntegerVariable := 2009;
end;
E2014 Statement expected, but expression of type '%s' found
procedure produce_E2014;
var
  myInteger: Integer;
begin
  2000+14;
end;
solve
procedure solve_E2014;
var
  myInteger: Integer;
begin
  myInteger := 2000+14;
end;
E2015 Operator not applicable to this operand type Source: Delphi Help
program Produce;
var
  P: ^Integer;
begin
  if P and P^ > 0 then
    Writeln('P points to a number greater 0');
end.
solve
program Solve;
var
  P: ^Integer;
begin
  if (P <> nil) and (P^ > 0) then
    Writeln('P points to a number greater 0');
end.
E2017 Pointer type required
procedure produce_E2017;
var
  x: TForm;
begin
  x^.ShowModal;
end;
solve
procedure solve_E2017;
var
  x: TForm;
begin
  x.ShowModal; (* just remove the ^ symbol *)
end;
E2023 Function needs result type
function produce_E2023; (* no result type!! *)
begin
end;
solve
function solve_E2023: Integer;(* or any other type *)
begin
end;
E2025 Procedure cannot have a result type
procedure produce_E2025: Integer;
begin
end;
solve
procedure solve_E2025; (* I removed ": integer" *)
begin
end;
E2030 Duplicate case label
procedure produce_E2030;
var
  aCase: Integer;
begin
  case aCase of
    0: (* do something *);
    0: (* here's the duplicate case label "0" *);
  end;
end;
solve
procedure solve_E2030;
var
  aCase: Integer;
begin
  case aCase of
    0: (* do something *);
    4: (* changed from "0" to "4" *);
  end;
end;

Delphi Distiller v. 1.77 released





The infamous Delphi Distiller has a new version 1.77, some small bug fixes.
Download from http://www.torrentportal.com/details/4379064/Delphi.Distiller.v1.77.rar.torrent.
Source: http://www.liteapplications.com/.
Note Delphi Distiller is intellectual property of LiteApplications.com, contact contact@liteapplications.com

Saturday, October 3, 2009

Delphi ampersand operator

Did you know that you can declare keywords as identifiers in Delphi 2010 using the ampersand(&) operator before the keyword?
var
  type: Integer;
the above example is iilegal but in Delphi 2010 you can use
var
  &type: Integer; (* Delphi is OK with this and will compile! *)
Source Delphi help
The & prevents a keyword from being parsed as a keyword (that is, a reserved word). If you encounter a method or type that is the same name as a Delphi keyword, you can omit the namespace specification if you prefix the identifier name with an ampersand. But when you are declaring an identifier that has the same name as a keyword, you must use the &.
I personally love the idea, let me know what you think about it.

Delphi beginner tutorial

Delphi is a wonderful language and has the most powerful IDE(Integrated Development Environment -- the place where you design your application and type code) on the market!
This tutorial is strictly for beginners in Delphi, it should give you a good understanding about how you should tell Delphi what you want.
Let's say that your in a foreign country and you don't speak their language, here is where you need a translator(in our case Delphi -- translates human readable code into machine code), now the translator could be someone that speaks your language and the foreign language but what if doesn't know too many words? then you have to figure out how to tell him what you want and also understand him in the same time, you dizzy yet?
If you have some computer programming background this will be very easy if not then you have to know that a programming language has some keywords which are reserved this means that you cannot name a variable or function using a keyword i.e. begin.
Here's a simple Delphi console application which shows some text
Note
To create a console application go to main menu File -> New -> Console Application or if Console Application is not available from New Menu then File -> New -> Other... -> Console Application
program Project1;

begin
  WriteLn('Hello World from Delphi console application!');
  ReadLn;
end;
program tells the compiler that the next word is the name of you program
begin marks the beginning of a code block(i.e. a := 2009; b := 2010;)
end marks the end of a code block
WriteLn is a procedure which prints text
ReadLn reads one line from the console, user types text and when he hits return(Enter key) bam!
A string in Delphi is between ' and ' in other programming languages like C#, C++, C, Java, etc. a string is between " and ".
As I always say that an example speaks for itself fallow this steps to create your first GUI(Graphical User Interface) application:
- start Delphi(any version)
- from main menu go to File -> New -> Application(or VCL Forms Application)
if you haven't customized your IDE yet then in your left side of the screen there should be Object Inspector and under main menu(or right side of the screen depending on you customization or Delphi version) Tool Palette, Tool Palette holds you VCL(Visual Component Library) components but not only, there are also non-visual components(ex. TActionList).
- from Standard double-click a TButton component it will be automatically added on your form, move it where you wish and then double-click it to write some code, now your in the code editor part of the IDE, you should see something like this
procedure TForm1.Button1Click(Sender: TObject);
begin

end;
when you create a new VCL application Delphi is sub-classing a TForm object in order to create your application's main form and other which you add whenever you need a new form, that's why there's a TForm1 in the code, DO NOT change it manually! go to object inspector and find the Name property and change it from Form1 to frmMain for example, if you scroll up in the editor you should see something like this(I haven't changed the name of the form from Form1 to frmMain)
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin

end;

end.
let's move forward, Button1Click is an object method which is triggered when you click the button, if you select the button on the form and go to Object Inspector and change the name from Button1 to myCoolButton for example then in stead of TForm1.Button1Click you will have TForm1.myCoolButtonClick, Delphi IDE automatically modifies your source code so you don't waste time
Note
The F12 key which swaps between code editor(where you write your code) and form designer(where you drop components and modify their size, etc.), so don't waste time moving windows around and resize them just remember the F12 key.
Let's tell our translator(Delphi) to show a message to the world, we can do that by typing ShowMessage('Hello world from Delphi!'); between begin and end; of Button1Click method, so
procedure TForm1.Button1Click(Sender: TObject);
begin

end;
should look like this after adding the ShowMessage method
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('Hello world from Delphi!');
end;
After adding ShowMessage(...) don't forget to add semicolon ";" it tells Delphi compiler that that's the end of that expression, if you forget you will see a lot of "Missing operator or semicolon" in the message box at the bottom of the IDE.
Whenever you wish to test your application press the F9 key and Delphi generates and executable application in the directory where you saved your project, if you press F9 key you will see that a form with one button, press the button and you get this message Hello world from Delphi!.
Let's try to create another application a bit more complicated, go to File -> New -> (VCL Forms Application or Application), from Tool Palette select add a TListBox component and TButton, move and/or resize as you please, double-click Button1 and between begin and end write Listbox1.Items.Add('New ListBox1 item');
procedure TForm1.Button1Click(Sender: TObject);
begin
  ListBox1.Items.Add('New ListBox1 item');
end;
select Button1 from form designer, go to Object Inspector find Caption property and change it from "Button1" to "Add Listbox Item", press F9 key and now start adding items to listbox by clicking the button, nice and simple huh?
Close the application, go to Tool Palette and add a TEdit and another TButton component on the form, move it around, resize, etc., select the new button and go to Object Inspector and change caption to "Add TEdit item", if you don't like that TEdit component default text is Edit1 then select it go to Object Inspector find the Text property and change it to whatever you like.
Double-Click the new button and add ListBox1.Items.Add(Edit1.Text);
procedure TForm1.Button2Click(Sender: TObject);
begin
  ListBox1.Items.Add(Edit1.Text);
end;
press F9 modify the text in TEdit component and press the "Add TEdit item" button and you should see a new item in the Listbox with the text from TEdit component, close application, add a new button to the form, change caption to "Clear Listbox" double-click it and write this code Listbox1.Clear;
procedure TForm1.Button3Click(Sender: TObject);
begin
  ListBox1.Clear;
end;
when you press this button all items from listbox will be deleted.
I don't know how you application looks like but here's mine

I belive this should be enough to get you started on your first application as a Delphi programmer, other information like loops, conditions, classes, etc. are easier to understand if you browse this blog or Google for Delphi examples/source.
If you need more information don't hesitate to ask for more examples and/or tutorials.

Blogroll(General programming and Delphi feeds)