De ouderwetse ini-file in een nieuw jasje.

In deze aflevering gaan we alle instellingen en de positie van het klokje bewaren, zodat deze weer actief worden bij een volgende start.

We gaan twee procedures maken: LeesIni en SchrijfIni.
Met LeesIni lezen we de opgeslagen instellingen en passen deze toe voordat de klok gaat werken.
Met SchrijfIni slaan we alle instellingen op, op het moment dat de klok wordt afgesloten.

De procedure SchrijfIni is niet zo heel ingewikkeld om te maken.
De procedure LeesIni daarentegen is veel lastiger, omdat er dan allerlei controles moeten worden uitgevoerd.

Plaats van de cfg-file

Daar waar Windows programma’s vroeger een .ini-file aanmaakte is dat tegenwoordig een .cfg-file; cfg is de extensie die duidt op een configuratie bestand.
Verder is de plaats waar de cfg-file moet komen ook belangrijk, daar Windows tegenwoordig heel veel mappen beveiligt tegen schrijven.

Kortweg zijn er twee plaatsen waar een cfg-file kan staan.
De map C:\ProgramData\[naam applicatie] of een persoonlijke map C:\Users\[naam gebruiker]\AppData\Local\[naam applicatie].

Wanneer een applicatie een algemeen karakter heeft, dus door alle gebruikers kan worden gebruikt is de eerste map van toepassing, is de applicatie persoonsgebonden dan ligt de tweede map voor de hand.

Gelukkig heeft Lazarus hier een mooie functie voor: GetAppConfigFile(Global: Boolean). Als de parameter Global True is dan wordt de algemene map gekozen, is Global False dan wordt de persoonlijke map gekozen.

Om gebruik te maken van speciale ini-methodes moet u de unit IniFiles aanroepen.

Daar LeesIni en SchrijfIni onderdeel moeten gaan uitmaken van de class TfrmKlokje moeten ze daar ook in worden opgenomen. Dit doen we in de private-sectie van de class-definitie.

Tijd om aan de slag te gaan.

– Zet in de implementation-sectie onder de regel {$R *.lfm} de volgende regels:
  Uses
    IniFiles;
– Ga naar de class-definitie van TfrmKlokje in de interface-sectie en voeg de volgende regels toe na de regel met {private declarations}:
  procedure LeesIni;
  procedure SchrijfIni;
– Ga naar de implementation-sectie en voeg na de variabele-declaraties, dus onder { TfrmKlokje } de volgende code toe:
  procedure TfrmKlokje.LeesIni;
  begin
    // hier komt de code
  end;

–   procedure TfrmKlokje.SchrijfIni;
  begin
    // hier komt de code
  end;

SchrijfIni

Het raam-werk staat. We gaan beginnen met de implementatie van SchrijfIni.
We geven onze klok een globaal karakter, dus de cgf-file moet in de map ProgramData komen te staan.
Verder willen we de volgende zaken opslaan:

  • De positie van de klok (Left en Top van de form)
  • OnTop van de klok
  • Het Font van de klok
  • De voorgrondkleur van de klok
  • De achtergrondkleur van de klok
  • De doorzichtbaarheid

De cfg-file moet er (ongeveer) als volgt uit gaan zien:

[CLOCK]
OnTop=-1
Position-X=966
Position-Y=76

[FONT]
Name=Microsoft Sans Serif
Size=16
ForeColor=0
BackColor=536870912
Style=Standard

[OPACITY]
Percentage=75

U ziet dat dit nog erg lijkt op een ouderwetse ini-file.

We gaan de eerste code “kloppen”:

– Ga naar de implementation-sectie, procedure SchrijIni.
– Haal het commentaar weg.
– Zet tussen de regels procedure … en begin de volgende regels code:
  var
    ini: TINIFile;
    opacperc, fstyle: String;
– Ga nu tussen begin en end; staan.
– Tik de volgende regels code in:
  ini := TINIFile.Create(GetAppConfigFile(True));
  try
    ini.WriteString(‘CLOCK’,’OnTop’,BoolToStr(frmKlokje.FormStyle = fsSystemStayOnTop));
    ini.WriteString(‘CLOCK’,’Position-X’,IntToStr(frmKlokje.Left));
    ini.WriteString(‘CLOCK’,’Position-Y’,IntToStr(frmKlokje.Top));

–     ini.WriteString(‘FONT’,’Name’,lblTijd.Font.Name);
    ini.WriteString(‘FONT’,’Size’,IntToStr(lblTijd.Font.Size));
    //Hier komt code voor de Font-Style
    ini.WriteString(‘FONT’,’ForeColor’,IntToStr(lblTijd.Font.Color));
    ini.WriteString(‘FONT’,’BackColor’,IntToStr(frmKlokje.Color));

–     if popOpacity100.Checked then
      opacperc := ‘100’;
    if popOpacity75.Checked then
      opacperc := ’75’;
    if popOpacity50.Checked then
      opacperc := ’50’;
    if popOpacity25.Checked then
      opacperc := ’25’;
    ini.WriteString(‘OPACITY’,’Percentage’,opacperc);
  finally
    ini.Free;
  end;

Een hoop om even te bespreken. De class TINIFile zorgt ervoor dat er een ini-file (cfg-file dus) kan worden aangemaakt, er naar toe kan worden geschreven en eruit gelezen kan worden.
Deze class is echter, bij declaratie, nog geen instantie. Hij bestaat dus nog niet. Met de methode Create maken we de instantie aan. In dit geval verwacht Create de map en de naam van het ini-bestand. En daarvoor gebruiken we dus de eerder genoemde functie GetAppConfigFile.

Vervolgens maken we gebruik van de methode WriteString. Deze methode verwacht drie parameters die allen van het type String zijn. De eerste parameter geeft de sectie aan, de tweede de zogenaamde indent en de derde de waarde van deze indent. Omdat ook de waarde van het type String is moeten dus de andere grootheden die geen String zijn worden omgezet in een string.
Voor een Boolean gebruiken we de functie BoolToStr en voor een geheel getal (integer) de functie IntToStr.

Omdat er een instantie van de class TINIFile wordt gemaakt moet deze ook weer netjes worden afgesloten (in dit geval moet ook de text-file worden gesloten). Dit gebeurt met de methode Free. Omdat deze ALTIJD moet worden uitgevoerd zetten we de schrijfopdrachten e.d. tussen een try-finally-blok. Mocht er tussen try en finally iets mis gaan, dan wordt toch de code tussen finally en end; uitgevoerd (deze end; hoort dus bij try-finally!).

Set

Om de Font-style netjes te kunnen opslaan moeten we weten hoe de Style is opgebouwd. In Lazarus (Free-Pascal) is de Style-property van een Font een zogenaamde Set (een verzameling). We moeten deze Set dus gaan omzetten naar een String. Verder moet u weten dat de Style altijd minimaal Standard is.

Voeg op de plek van het commentaar (//Hier komt code voor de Font-Style) de volgende code toe:
  fstyle := ‘Standard, ‘;
  if fsBold in lblTijd.Font.Style then
    fstyle := fstyle + ‘Bold, ‘;
  if fsItalic in lblTijd.Font.Style then
    fstyle := fstyle + ‘Italic, ‘;
  if fsStrikeOut in lblTijd.Font.Style then
    fstyle := fstyle + ‘StrikeOut, ‘;
  if fsUnderline in lblTijd.Font.Style then
    fstyle := fstyle + ‘Underline, ‘;
  fstyle := Copy(fstyle,1,Length(fstyle)-2);
  ini.WriteString(‘FONT’,’Style’,fstyle);

In bovenstaande code wordt de String fstyle netjes opgebouwd. De verschillende stijlen zijn netjes met een komma+spatie van elkaar gescheiden. Omdat deze komma+spatie ook aan het eind van fstyle staat worden deze d.m.v. een Copy weer weggehaald.

Waar we nu nog even voor moeten zorgen is dat bij het sluiten van de klok de procedure SchrijfIni wordt aangeroepen.
Omdat het klokje op meerdere manieren gesloten kan worden (via het snelmenu, evt. dubbelklikken, maar ook via de taakbalk) is het handig om het OnClose-event van de Form te gaan gebruiken.

– Maak het OnClose-event van frmKlokje aan.
– Voeg de volgende regel code toe:
  SchrijfIni;
– Run de klok en sluit deze daarna weer af.
– Bekijk de cfg-file in C:\ProgramData\klokje.

Dit was al heel wat werk, zei het dat er enige repetitie in zit (ofwel copy-paste).
En dit was nog wel het gemakkelijke gedeelte.

LeesIni

Nu moeten we LeesIni nog gaan implementeren.

Laten we maar beginnen.

LeesIni gaat gebruik maken van de method ReadString dat een functie is en een String als waarde teruggeeft. ReadString heeft ook drie parameters: sectie, indent en een default waarde. De default waarde wordt toegekend als de indent in de cfg-file leeg is. Ook zullen we de String in voorkomende gevallen moet omzetten naar een Boolean of een getal. Dit doen we met de functies StrToBool en TryStrToInt die een String probeert om te zetten naar een Integer en als dan niet lukt de waarde False terug geeft. Ook moeten we na iedere ReadString een controle uitvoeren, zodat ons klokje niet gaat “hangen” vanwege een verkeerde waarde.
Overigens wordt door de functie BoolToStr de waarde ‘-1’ omgezet naar True en de waarde ‘0’ omgezet naar False.

Met deze kennis kunnen we aan de slag:

– Voeg aan de procedure LeesIni de volgende variabelen toe:
  var
    ini: TINIFile;
    s, h: String;
    i: integer;
– Haal het commentaar weg en voeg na begin de volgende regels code toe:
  ini := TINIFile.Create(GetAppConfigFile(True));
  try
    s := ini.ReadString(‘CLOCK’,’OnTop’,”);
    if s = ‘-1’ then
      begin
        frmKlokje.FormStyle := fsSystemStayOnTop;
        popOnTop.Checked := True;
      end
    else
      begin
        frmKlokje.FormStyle := fsNormal;
        popOnTop.Checked := False;
      end;
    s := ini.ReadString(‘CLOCK’,’Position-X’,”);
    if TryStrToInt(s,i) then
      frmKlokje.Left := i
    else
      frmKlokje.Left := 256;
    s := ini.ReadString(‘CLOCK’,’Position-Y’,”);
    if TryStrToInt(s,i) then
      frmKlokje.Top := i
    else
      frmKlokje.Top := 130;

–     s := ini.ReadString(‘FONT’,’Name’,’Arial’);
    lblTijd.Font.Name := s;
    s := ini.ReadString(‘FONT’,’Size’,”);
    if TryStrToInt(s,i) then
      lblTijd.Font.Size := i
    else
      lblTijd.Font.Size := 14;
    lblTijd.Font.Style := [];
    s := ini.ReadString(‘FONT’,’Style’,”);
    s := s + ‘,’;
    i := Pos(‘,’, s);
    while i > 0 do
      begin
        h := Trim(Copy(s,1,i-1));
        if UpperCase(h) = ‘BOLD’ then
          lblTijd.Font.Style := lblTijd.Font.Style + [fsBold];
        if UpperCase(h) = ‘ITALIC’ then
          lblTijd.Font.Style := lblTijd.Font.Style + [fsItalic];
        if UpperCase(h) = ‘STRIKEOUT’ then
          lblTijd.Font.Style := lblTijd.Font.Style + [fsStrikeOut];
        if UpperCase(h) = ‘UNDERLINE’ then
          lblTijd.Font.Style := lblTijd.Font.Style + [fsUnderline];
        s := Copy(s,i+1,Length(s));
        i := Pos(‘,’, s);
      end;
    s := ini.ReadString(‘FONT’,’ForeColor’,”);
    if TryStrToInt(s,i) then
      lblTijd.Font.Color := i
    else
      lblTijd.Font.Color := RGBToColor(0, 0, 0); //zwart
    s := ini.ReadString(‘FONT’,’BackColor’,”);
    if TryStrToInt(s,i) then
      frmKlokje.Color := i
    else
      frmKlokje.Color := RGBToColor(127, 127, 127); //grijs

–     popOpacity100.Checked := True;
    popOpacity75.Checked := False;
    popOpacity50.Checked := False;
    popOpacity25.Checked := False;
    frmKlokje.AlphaBlendValue := 255;
    s := ini.ReadString(‘OPACITY’,’Percentage’,”);
    if s = ’25’ then
      begin
        popOpacity100.Checked := False;
        popOpacity25.Checked := True;
        frmKlokje.AlphaBlendValue := 64;
      end;
    if s = ’50’ then
      begin
        popOpacity100.Checked := False;
        popOpacity50.Checked := True;
        frmKlokje.AlphaBlendValue := 128;
      end;
    if s = ’75’ then
      begin
        popOpacity100.Checked := False;
        popOpacity75.Checked := True;
        frmKlokje.AlphaBlendValue := 191;
      end;
  finally
    ini.Free;
  end;

Rest ons niets anders dan LeesIni op de juiste plaats op te nemen in de code.
Deze plaats is het OnActivate event van de Form.

– Activeer het OnActivate event van het form.
– Neem de volgende regel code op:
  LeesIni;
– Run de klok, pas wat instellingen aan en verplaats de klok.
– Sluit de klok af en start weer opnieuw op.

Als het goed is zijn alle instellingen van de eerste keer de tweede keer actief.

In de volgende aflevering gaan we iets geheel nieuw doen. We gaan tekenen. Jawel, de analoge klok staat op het programma.

Naar de volgende aflevering…