Delphi v příkladech - 4. díl: Hledání min 2
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu
Reklama

Delphi v příkladech - 4. díl: Hledání min 2Delphi v příkladech - 4. díl: Hledání min 2

 

Delphi v příkladech - 4. díl: Hledání min 2

Google       Google       19. 2. 2008       20 767×

Dnes navážeme na náš poslední program Hledání min. Vytvoříme program s více formuláři a ukážeme si práci s *.ini soubory.

Reklama
Reklama

Minule jsme vytvářeli hledání min, které je ovšem velmi omezeno. Hráč nemůže měnit velikost hracího pole ani počet min. Tento program ještě vylepšíme, budete tedy potřebovat zdrojový kód.

Z programu jsem ale vynechal poznámky, budete-li tedy mít problém s nějakou částí, podívejte se do minulého dílu.

Formulář „Nastavení“

Začneme přidáním dalšího formuláře, na který umístíme prvky pro zadávání nových rozměrů. V nabídce File → New vyberte možnost Form. Do projektu se přidá další formulář pojmenovaný Form2. Když se nyní pokusíte projekt uložit (samozřejmě pomocí Save All), zjistíte, že v projektu přibyl další soubor. Implicitní pojmenování nabízené Delphi je Unit2.pas, my jej ale pojmenujeme Nastaveni_f.pas. Tento soubor představuje další jednotku (unitu), obsahuje vlastní formulář a také vlastní část zdrojového kódu (jednotlivé formuláře zobrazíte v nabídce View → Forms).

Tento formulář se sice při spuštění programu vytvoří, ale nezobrazí. Zobrazí se pouze hlavní formulář (u nás Form1). Nastavení, který formulář je hlavní, lze změnit v nabídce Project → Options na záložce Forms změnou položky Main form.

Tento formulář budeme během běhu programu potřebovat zobrazit. V Delphi jsou dva způsoby zobrazení formuláře – modální a nemodální. Rozdíl mezi nimi spočívá v tom, že modální zobrazení neumožňuje práci s ostatními formuláři (uživatel může pracovat pouze s tímto formulářem). U nemodálního zobrazení může uživatel mezi formuláři přepínat.

//kód pro zobrazení formuláře
JmenoFormulare.show; //nemodální zobrazení
JmenoFormulare.showmodal; //modální zobrazení

Zavření formuláře se programově uskuteční klasicky pomocí JmenoFormulare.Close. Při zavření hlavního formuláře se ukončí program.

My použijeme modální zobrazení, k otevírání budeme používat naše menu. Přidáme do něj položku „Nastavení“ a do procedury obsluhující jeho stisk (TForm1.Nasaven1Click) napíšeme

Form2.showmodal;

Při pokusu o kompilaci ale zjistíme, že se program nezkompiluje. V jednotce Miny_f (původně Unit1) totiž není žádná proměnná Form2. Delphi ale pozná, že chceme použít Form2 z jednotky Nastaveni_f a nabídne nám přidání reference do Miny_f. Pokud si chcete přidání zařídit sami, připište na začátek bloku implementation uses a jméno unity.

...
implementation

uses Nastaveni_f; //nová část kódu

{$R *.dfm}
...

Toto nám umožní používat v jednotce Miny_f i proměnné, procedury, funkce atd. unity Nastaveni_f (stále ale nemůžeme v jednotce Nastaveni_f používat věci z Miny_f, lze to vyřešit přidáním uses Miny_f; do Nastaveni_f). Když nyní spustíme program, můžeme si stisknutím položky v menu zobrazit druhý formulář. Po jeho zavření můžeme opět pracovat s Form1.

Nastavení vlastností Form2

Na Form2 rozmístíme 4 přepínací tlačítka (RadioButton) s Caption nastaveným na 'Začátečník', 'Pokročilý', 'Expert' a 'Vlastní', 2 tlačítka s Caption nastaveným na 'OK' a 'Cancel' a GroupBox. To je komponenta, která může obsahovat další komponenty a popisek (Caption), který nastavte na 'Vlastnosti'. Do GroupBoxu umístíme 3 Edity s hodnotou text '0' a 3 Labely s Caption 'Šířka', 'Výška' a 'Počet min'. Upravte také velikost a Caption formuláře a nastavte mu styl okraje (BorderStyle) na bsDialog – to způsobí, že u tohoto formuláře nemůže uživatel měnit velikost a také nebude mít všechny systémové ikony. Zůstane pouze křížek pro ukončení formuláře, chcete-li se zbavit i jej, nastavte u vlastnosti BorderIcons její vlastnost biSystemMenu na false. Vše bude nyní vypadat přibližně jako na obrázku.

Přepínací tlačítka budou umožňovat 3 předdefinovaná nastavení a navíc bude mít uživatel možnost si zvolit vlastní, samozřejmě ale s určitými omezeními. Do Unit2 přidáme několik konstant s následujícími hodnotami.

const Hard_sirka = 30;
      Hard_vyska = 20;
      Hard_pocetmin = 99;
      Medium_sirka = 16;
      Medium_vyska = 16;
      Medium_pocetmin = 40;
      Easy_sirka = 9;
      Easy_vyska = 9;
      Easy_pocetmin = 10;
      Max_sirka = 30;
      Min_sirka = 9;
      Max_vyska = 25;
      Min_vyska = 9;
      Min_pocetmin = 10;
      //Max_pocetmin - podle rozmeru, polovina policek (sirka * vyska div 2)

Nejjednodušší metoda bude metoda onClick tlačítka Button2 (tlačítko Cancel).

procedure TForm2.Button2Click(Sender: TObject);
begin
  close;
end;

Nyní se postaráme o chování editů. Nejdříve si ale ujasníme, co od nich budeme očekávat. Poté vytvoříme proceduru, která bude obsluhovat metodu onKeyPress všech našich editů.

Po stisknutí Enteru se předá zaměření (focus). K tomu využijeme funkci SelectNext, která přebírá 3 parametry. Prvním je výchozí zaměřená komponenta typu TWinControl. Předávaným parametrem bude komponenta, jejíž metoda je obsluhována, tedy Sender, přetypovaná na TWinControl. Další dva parametry jsou typu boolean. První z nich určuje, zda-li se bude postupovat dopředu (true) nebo dozadu (false). Poslední parametr udává, zda-li se bude při předávání zaměření přihlížet k vlastnosti objektů TabStop. Předávání focusu se uskutečňuje ve stanoveném pořadí, které je určeno vlastností objektů TabOrder a které se vytváří při umísťování komponent na formulář. Pokud vám nevyhovuje, můžete jej samozřejmě upravit změnou hodnot této vlastnosti u jednotlivých komponent.

Při stisknutí Escapu se v editu obnoví hodnota, kterou obsahoval před poslední změnou, a zaměření se předá tlačítku Button2 (tlačítko Cancel). Tentokrát ovšem nepoužijeme funkci SelectNext, ale zavoláme metodu tlačítka SetFocus. Abychom docílili obnovení hodnoty v editu, musíme si ji nejdříve uložit. To učiníme se zaměřením editu. Při této příležitosti se vyvolá metoda onEnter. K uložení použijeme zatím nevyužitou vlastnost Tag. Vytvoříme tedy novou proceduru EditEnter, kterou poté asociujeme s událostí onEnter všech našich editů (v Object Inspectoru na záložce Events se po vybrání události a stisknutí rozbalovací šipky nabídnou všechny dostupné procedury s vyhovujícími parametry). Aby to bylo možné, musí se parametry naší procedury shodovat s parametry této události.

procedure TForm2.EditEnter(Sender: TObject);
begin
  (Sender as TEdit).Tag := strtoint((Sender as TEdit).Text);
end;

Aby uživatel nezadával nesmysly, zakážeme reakci na nechtěné klávesy. Pokud bude stisknutá klávesa jiná než číslice (0-9), Enter, Backspace nebo Escape, nastavíme parametr Key na #0 (prázdný znak). Reakci na Escape a Enter jsem popsal výše, obsloužení Backspacu nepotřebuje nijak upravovat. Toto „vynulování“ zamezí reakci na ostatní klávesy. To se ale nevztahuje na tzv. řídící klávesy (např. šipky, Delete), které mají speciální zacházení. Nyní následuje kód procedury EditKeyPress.

procedure TForm2.EditKeyPress(Sender: TObject; var Key: Char);
  //tuto proceduru asociujeme s událostí onKeyPress všech našich editů
const backspace = #8;
      enter = #13;
      esc = #27;
      //pro názornost jsem přidal tyto konstanty
begin
  if not (key in ['0'..'9',backspace,enter,esc]) then key := #0
    //ostatní tlačítka se "vynulují" - nezapíší se
  else
    if key = enter then SelectNext((Sender as TWinControl),true,true);
      //při enteru se předá zaměření
    if key = esc then
      begin
        (Sender as TEdit).Text := inttostr((Sender as TEdit).Tag);
          //obnovení hodnoty uložené při OnEnter
        Button2.SetFocus;
          //předání zaměření buttonu2
      end
    else
      RadioButton4.Checked := true;
        //zatrhnutí RadioButtonu4
end;

Ještě přidáme kontrolu hodnot při ztrátě focusu (v metodě editů onExit). Tentokrát to ale uděláme pro každý edit zvlášť, protože hodnoty v každém z nich musí splňovat jiné parametry.

procedure TForm2.Edit1Exit(Sender: TObject);
begin
  if Edit1.Text = '' then Edit1.Text := inttostr(Edit1.Tag) else //prázdný edit -> obnovení hodnoty z Tagu
    if strtoint(Edit1.Text) > Max_sirka then Edit1.Text := inttostr(Max_sirka) else //příliš velké
      if strtoint(Edit1.Text) < Min_sirka then Edit1.Text := inttostr(Min_sirka); //příliš malé
  if strtoint(Edit3.Text) > strtoint(Edit1.Text)*strtoint(Edit2.Text) div 2 then Edit3.Text := inttostr(strtoint(Edit1.Text)*strtoint(Edit2.Text) div 2);
    //se změnou rozměrů se změní kritérium počtu min
end;

//podobně u následujících editů
procedure TForm2.Edit2Exit(Sender: TObject);
begin
  if Edit2.Text = '' then Edit2.Text := inttostr(Edit2.Tag) else
    if strtoint(Edit2.Text) > Max_vyska then Edit2.Text := inttostr(Max_vyska) else
      if strtoint(Edit2.Text) < Min_vyska then Edit2.Text := inttostr(Min_vyska);
  if strtoint(Edit3.Text) > strtoint(Edit1.Text)*strtoint(Edit2.Text) div 2 then Edit3.Text := inttostr(strtoint(Edit1.Text)*strtoint(Edit2.Text) div 2);
end;

procedure TForm2.Edit3Exit(Sender: TObject);
begin
  if Edit3.Text = '' then Edit3.Text := inttostr(Edit3.Tag) else
    if strtoint(Edit3.Text) > strtoint(Edit1.Text)*strtoint(Edit2.Text) div 2 then Edit3.Text := inttostr(strtoint(Edit1.Text)*strtoint(Edit2.Text) div 2) else
      if strtoint(Edit3.Text) < Min_pocetmin then Edit3.Text := inttostr(Min_pocetmin);
end;

Abychom zabránili problémům s přetečením (tj. zadání větší hodnoty než je rozsah proměnné), omezíme maximální počet znaků v editech nastavením jejich vlastnosti MaxLength. Mně se jako potenciálnímu uživateli pracovalo nejlépe s maximem tří znaků, vy si však můžete (v rozumném rozmezí) vybrat vlastní nastavení.

Nyní jsou na řadě RadioButtony. Myslím, že následující část kódu nepotřebuje žádný komentář.

procedure TForm2.RadioButton1Click(Sender: TObject);
begin
  Edit1.Text := inttostr(Easy_sirka);
  Edit2.Text := inttostr(Easy_vyska);
  Edit3.Text := inttostr(Easy_pocetmin);
end;

procedure TForm2.RadioButton2Click(Sender: TObject);
begin
  Edit1.Text := inttostr(Medium_sirka);
  Edit2.Text := inttostr(Medium_vyska);
  Edit3.Text := inttostr(Medium_pocetmin);
end;

procedure TForm2.RadioButton3Click(Sender: TObject);
begin
  Edit1.Text := inttostr(Hard_sirka);
  Edit2.Text := inttostr(Hard_vyska);
  Edit3.Text := inttostr(Hard_pocetmin);
end;

Další procedura, kterou vytvoříme, se bude jmenovat prepnuti a bude mít parametry sirka, vyska a pocetmin, všechny typu integer. Nebude obsluhovat žádnou událost žádné komponenty, ale, jak název napovídá, bude zajišťovat přepnutí. Před zobrazením Formu2 totiž provede ještě několik jednoduchých příkazů a nakonec sama zobrazí Form2. Po vytvoření této procedury nahradíme Form2.showmodal za Form2.prepnuti (sirka, vyska, pocetmin). Nyní se již nemusíme starat o výchozí nastavení komponent při zobrazení Formu2.

procedure TForm2.prepnuti (sirka,vyska,pocetmin:integer);
begin
  Edit1.Text := inttostr(sirka);
  Edit2.Text := inttostr(vyska);
  Edit3.Text := inttostr(pocetmin);

  if (sirka = Easy_sirka) and (vyska = Easy_vyska) and (pocetmin = Easy_pocetmin) then Form2.RadioButton1.Checked := true
    else //  parametry jsou easy
  if (sirka = Medium_sirka) and (vyska = Medium_vyska) and (pocetmin = Medium_pocetmin) then Form2.RadioButton2.Checked := true
    else //  parametry jsou medium
  if (sirka = Hard_sirka) and (vyska = Hard_vyska) and (pocetmin = Hard_pocetmin) then Form2.RadioButton3.Checked := true
    else //  parametry jsou hard
  Form2.RadioButton4.Checked := true;

  Form2.ShowModal; //zobrazení Form2
end;

Poslední procedurou z této jednotky bude procedura obsluhující metodu onClick tlačítka OK. K tomu ovšem zatím nemáme připravený zbytek programu, proto se nyní vrátíme k jednotce Miny_f a tuto proceduru doplníme později.

Měnitelné rozměry hracího pole

Naše práce bude usnadněna tím, že jsme s touto možností počítali již od začátku a použili jsme konstanty sirka, vyska a pocetmin. Proto nám stačí pouze nahradit je proměnnými a zajistit patřičné změny rozměrů našeho hracího pole v návaznosti na změně těchto proměnných. V takovém případě bude samozřejmě potřeba začít novou hru, proto příslušnou část kódu přidáme do procedury NovaHra.

Nejdříve ale odstraníme konstanty sirka, vyska a pocetmin a nahradíme je proměnnými. Tyto proměnné nemohou být součástí třídy Form1, protože tak bychom vytvořili proměnné Form1.sirka, Forma1.vyska a Form1.pocetmin. To by způsobilo problémy u částí kódu, které nejsou součástí třídy Form1 (třeba metody třídy TPolicko). Zbývá nám tedy možnost použít globální proměnné a přidat tyto tři k proměnným Form1 a Pole.

var
  Form1: TForm1;
  Pole:TPole;
  vyska,sirka,pocetmin:integer;

Vlastní změna rozměrů bude spočívat ve zrušení starého pole a vytvoření nového podle zadaných rozměrů. To svěříme nové proceduře, která bude přebírat parametry nových rozměrů pole. Také nesmíme zapomenout upravit velikost formuláře podle nové velikosti pole.

procedure TForm1.ZmenaRozmeru(novasirka,novavyska,novypocetmin:integer);
  var x,y:integer;
begin
  for x := 0 to sirka - 1 do
    for y := 0 to vyska - 1 do
      Pole[x,y].Free;              //uvolnění jednotlivých prvků
  Pole := nil;                     //a samotného pole
                                   //z paměti
  sirka := novasirka;
  vyska := novavyska;
  pocetmin := novypocetmin;        //nové parametry

  SetLength(Pole,sirka,vyska);     //vytvoření nového pole
  for x := 0 to sirka - 1 do
    for y := 0 to vyska - 1 do
      Pole[x,y] :=  TPolicko.Init(x,y); //inicializace prvků

  // úprava velikosti formuláře:
  Form1.ClientWidth := Image2.Left*2 + sirka*velikost;
  Form1.ClientHeight := Image2.Top + Image2.Left + vyska*velikost;
    //tuto část přidáme později i na začátek programu do procedury FormCreate
end;

Toto bude potřeba vykonat při změně možností, přesněji při potvrzení nových hodnot. Vrátíme se tedy do unity Nastaveni_f a naprogramujeme proceduru Button1Click, kterou jsme vynechali. Nebude ani nijak složitá, stačí pouze zavolat proceduru ZmenaRozmeru, vytvořit novou hru a zavřít formulář s nastavením.

procedure TForm2.Button1Click(Sender: TObject);
begin
  Form1.ZmenaRozmeru(strtoint(Edit1.Text),strtoint(Edit2.Text),strtoint(Edit3.Text));
  Form1.NovaHra;
  close;
end;

Soubory INI

Dále se zaměříme na uložení nastavení a jeho opětovné načtení při dalším spuštění aplikace. Použijeme k tomu inicializační (INI) soubor. Pro práci s nimi je v Delphi speciální třída TIniFile. Pro jejich použití je nezbytné přidat do části uses na začátku unity knihovnu IniFiles.

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

Do procedury FormCreate přidáme lokální proměnnou ini typu TIniFile. Jelikož se jedná o třídu, je potřeba ji před použitím inicializovat. Jako parametr předáme konstruktoru jméno INI souboru, se kterým budeme pracovat. V našem případě to bude soubor miny.ini v místě umístění programu.

ini := TIniFile.Create(ExtractFileDir(Application.ExeName)+'\miny.ini');
  //Application.ExeName - jméno programu včetně adresy
  //ExtractFileDir - funkce, která vrací adresu z úplného jména souboru

Nejdříve se ale podíváme na strukturu dat v tomto souboru.

[Sekce1]
udaj1=hodnota
udaj2=hodnota
[Sekce2]
x=10
y=50
povoleni=false
text=Nejaky text

Data jsou uspořádaná do jednotlivých sekcí i s názvem údaje, ke kterému se vztahují. Pro vlastní získání nebo zapsání dat slouží několik funkcí podle typu dat. My budeme pracovat jen s čísly, tak si vystačíme s WriteInteger a ReadInteger (práce s jiným typem dat je ovšem velmi podobná – např. s řetězci se pracuje pomocí ReadString a WriteString atp.). Obě přejímají 3 parametry. První je řetězec určující název sekce, druhý je řetězec určující název údaje. Třetí parametr je zapisovaná hodnota, případně výchozí hodnota, kterou funkce vrátí, selže-li načtení hodnoty. Po skončení práce opět uvolníme instanci z paměti.

procedure TForm1.FormCreate(Sender: TObject);
  var x,y:byte;
      ini:TIniFile;
begin
  randomize; //mělo by být vždy na začátku programu

  ini := TIniFile.Create(extractFileDir(application.ExeName)+'\miny.ini');
  sirka := ini.ReadInteger('Pole','sirka',30);
  vyska := ini.ReadInteger('Pole','vyska',20);
  pocetmin := ini.ReadInteger('Pole','min',99);
  
  if (sirka > 30) or (sirka < 9) then sirka := 30;
  if (vyska > 25) or (vyska < 9) then vyska := 20;
  if (vyska*sirka div 2 < pocetmin) or (pocetmin < 10) then pocetmin := vyska*sirka div 8;
  
  ini.Free;

  //úprava rozměrů formuláře a obrázku
  Form1.ClientWidth := Image2.Left*2 + sirka*velikost;
  Form1.ClientHeight := Image2.Left + Image2.Top + vyska*velikost;

  Image2.Width := sirka*velikost-1;
  Image2.Height := vyska*velikost-1;
  ...

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var ini:TIniFile;
    x,y:byte;
begin
  ini := TIniFile.Create(extractFileDir(application.ExeName)+'\miny.ini');
  ini.WriteInteger('Pole', 'sirka', sirka);
  ini.WriteInteger('Pole', 'vyska', vyska);
  ini.WriteInteger('Pole', 'min', pocetmin);
  ini.Free;

  for x := 0 to sirka - 1 do
    for y := 0 to vyska - 1 do
      begin
        Pole[x,y].Free;
      end;
  Pole := nil;
end;

Protože přepsat data v uloženém INI souboru nevyžaduje žádné odborné znalosti, přidal jsem kontrolu načtených dat podle dříve zmiňovaných kritérií. Také bychom neměli zapomenout přizpůsobit nově získaným hodnotám velikost formuláře a Image2.

Aby bylo možné po sobě uklidit, přidáme ještě do menu položku „Vymazat uložená nastavení“, která se postará o odstranění našeho INI souboru, a položku „Uložit nastavení po ukončení“, která bude mít charakter zaškrtávacího tlačítka. Toho docílíme nastavením vlastnosti AutoCheck této položky na true. Pro funkčnost je pak potřeba zařadit vytvoření INI souboru do podmíněného příkazu.

procedure TForm1.Vymazatuloennastaven1Click(Sender: TObject);
begin
  if FileExists(extractFileDir(application.ExeName)+'\miny.ini') then
    DeleteFile(extractFileDir(application.ExeName)+'\miny.ini');
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
  var ini:TIniFile; x,y:byte;
begin
  if Uloitnastavenpoukonen1.Checked then
    begin //pouze zařadíme vytvoření ini souboru do podmíněného příkazu
      ini := TIniFile.Create(extractFileDir(application.ExeName)+'\miny.ini');
      ini.WriteInteger('Pole', 'sirka', sirka);
      ini.WriteInteger('Pole', 'vyska', vyska);
      ini.WriteInteger('Pole', 'min', pocetmin);
      ini.Free;
    end;
  ...

Poslední problém

A jsme skoro u konce. Na závěr jsem si však ještě nechal poslední problém. Jak jste si možná všimli, nastne problém, pokud zvětšíme pole oproti stavu na začátku programu. Velikost pole se nezmění. Ve skutečnosti to není pravda. Pole se vytvoří a vše funguje, výsledek se pouze nevykreslí na Image2. Je to dáno tím, že Canvas obrázku se inicializuje s těmito rozměry a při kreslení mimo něj se nic nezobrazí. Jelikož Canvas je read-only vlastností (pouze pro čtení), nemůžeme jej uvolnit z paměti a znovu inicializovat. Můžeme to ovšem udělat s celým obrázkem. Do procedury ZmenaRozmeru přidáme následující část kódu.

  Image2.Free; //uvolnění z paměti
  
  //vytvoření nové instance
  Image2 := TImage.Create(Form1);
  Image2.Parent := form1;
  Image2.Top := 20;
  Image2.Left := 1;
  Image2.OnMouseDown := Form1.Image2MouseDown;
  Image2.Width := sirka*velikost-1;
  Image2.Height := vyska*velikost-1;

Tím jsou naše miny hotové, příště se k nim již vracet nebudeme.

×Odeslání článku na tvůj Kindle

Zadej svůj Kindle e-mail a my ti pošleme článek na tvůj Kindle.
Musíš mít povolený příjem obsahu do svého Kindle z naší e-mailové adresy kindle@programujte.com.

E-mailová adresa (např. novak@kindle.com):

TIP: Pokud chceš dostávat naše články každé ráno do svého Kindle, koukni do sekce Články do Kindle.

Hlasování bylo ukončeno    
0 hlasů
Google
Autor programuje v Delphi, baví ho 3D animace a zajímá se o chemii a fyziku.

Nové články

Obrázek ke článku Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres HCPP16 pořádá od 30. září do 2. října nezisková organizace Paralelní Polis již potřetí, a to ve stejnojmenném bitcoinovém prostoru v pražských Holešovicích. Letos přiveze na třídenní konferenci přes 40 většinou zahraničních speakerů – lídrů z oblastí technologií, decentralizované ekonomiky, politických umění a aktivismu. Náměty jejich přednášek budou také hacking, kryptoměny, věda, svoboda nebo kryptoanarchie.

Reklama
Reklama
Obrázek ke článku ICT PRO školení zaměřené nejenom na ICT

ICT PRO školení zaměřené nejenom na ICT

Dovolte, abychom se představili. Jsme zaměstnanci společnosti ICT Pro, profesionálové v oblasti poskytování komplexních ICT služeb. Neboli služeb spojených s informačními a komunikačními technologiemi, které dnes - ve 21. století - tvoří  nedílnou součást běžného provozu všech moderních firem.

loadingtransparent (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })();
Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032016 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý