11. Práce s grafikou I
  1. Nyní se budeme zabývat aplikací grafického editoru. Budeme zde používat palety nástrojů (bude zapotřebí mnoho obrázků) a abychom je nemuseli vytvářet sami opět si stáhneme již hotovou aplikaci. Následuje popis této aplikace. Na formulář jsou vloženy tři komponenty Panel. Mají nastaveny vlastnosti Align na alTop (tím zabírají celou šířku formuláře), mají zrušený titulek a obsahují tlačítka.

  2. Tlačítka na horním panelu se jmenují: LineButton, RectangleButton, EllipseButton, RoundRectButton, PenButton a BrushButton. Mají přiřazeny vhodné obrázky. U prvního tlačítka je nastavena jeho vlastnost Down na true a z prvních čtyř tlačítek je vytvořena skupina (GroupIndex je nastaveno na 1). Tlačítka Pera a Štětce (poslední dvě tlačítka) jsou jedno tlačítkové skupiny, které pracují jako přepínací. Mají nastaveny vlastnosti AllowAllUp na true a vlastnost GroupIndex pro PenButton na 2 a pro BrushButton na 3.
    Budeme se snažit vytvořenou paletu nástrojů zapojit do práce. Zapamatujeme si zvolený nástroj a při další práci jej budeme používat. K zapamatování zvoleného nástroje je nejvhodnější použít výčtový typ. Přidáme tedy před deklaraci typu formuláře deklaraci výčtového typu TDrawingTool a složku DrawingTool tohoto typu vložíme do veřejné části deklarace typu formuláře (jsou zde přidány i složky, o kterých jsme se zmínili na závěr předchozí kapitoly a složka pro uložení jména souboru):
    enum TDrawingTool {dtLine, dtRectangle, dtEllipse, dtRoundRect};

    bool Drawing;
    TPoint Origin, MovePt;
    TDrawingTool DrawingTool;
    AnsiString CurrentFile;
    Změnu nástroje provádíme v reakci na událost OnClick stisknutého tlačítka. Vytvoříme tedy následující obsluhy událostí:
    void __fastcall TForm1::LineButtonClick(TObject *Sender)
    {
      DrawingTool = dtLine;
    }
    void __fastcall TForm1::RectangleButtonClick(TObject *Sender)
    {
      DrawingTool = dtRectangle;
    }
    void __fastcall TForm1::EllipseButtonClick(TObject *Sender)
    {
      DrawingTool = dtEllipse;
    }
    void __fastcall TForm1::RoundRectButtonClick(TObject *Sender)
    {
      DrawingTool = dtRoundRect;
    }
    Nyní již můžeme říci formuláři, který typ nástroje má použít a je nutno ještě formulář naučit jak jej má použít. Kreslící nástroj se používá v obsluhách událostí OnMouseUp a OnMouseMove. Tyto obsluhy využívají zvolený nástroj (jeden ze čtyř možných) k provedení kreslení.
    void __fastcall TForm1::FormMouseUp(TObject *Sender,
        TMouseButton Button, TShiftState Shift, int X, int Y)
    {
      switch (DrawingTool){
        case dtLine:
           Canvas->MoveTo(Origin.X, Origin.Y);
           Canvas->LineTo(X, Y);
           break;
        case dtRectangle:
           Canvas->Rectangle(Origin.X, Origin.Y, X, Y);
           break;
        case dtEllipse:
           Canvas->Ellipse(Origin.X, Origin.Y, X, Y);
           break;
        case dtRoundRect:
           Canvas->RoundRect(Origin.X, Origin.Y, X, Y,
           (Origin.X - X) /2, (Origin.Y - Y) / 2);
           break;
      }
      Drawing = false;
    }
    void __fastcall TForm1::FormMouseMove(TObject *Sender,
         TShiftState Shift, int X, int Y)
    {
      if (Drawing) {
        Canvas->Pen->Mode = pmNotXor;
        switch (DrawingTool) {
          case dtLine:
                    Canvas->MoveTo(Origin.X, Origin.Y);
                    Canvas->LineTo(MovePt.X, MovePt.Y);
                    Canvas->MoveTo(Origin.X, Origin.Y);
                    Canvas->LineTo(X, Y);
                    break;
          case dtRectangle:
              Canvas->Rectangle(Origin.X, Origin.Y, MovePt.X, MovePt.Y);
              Canvas->Rectangle(Origin.X, Origin.Y, X, Y);
              break;
          case dtEllipse:
              Canvas->Ellipse(Origin.X, Origin.Y, MovePt.X, MovePt.Y);
              Canvas->Ellipse(Origin.X, Origin.Y, X, Y);
              break;
          case dtRoundRect:
              Canvas->RoundRect(Origin.X, Origin.Y, MovePt.X, MovePt.Y,
               (Origin.X - MovePt.X) / 2, (Origin.Y - MovePt.Y) / 2);
              Canvas->RoundRect(Origin.X, Origin.Y, X, Y,
               (Origin.X - X) / 2, (Origin.Y - Y) / 2);
              break;
        }
        MovePt = Point(X,Y);
      }
      Canvas->Pen->Mode = pmCopy;
    }
    V těchto metodách se několikrát opakuje stejný kód. Tento opakující se kód vložíme do samostatné metody, kterou v obou obsluhách použijeme. Deklarace metody DrawShape je vložena do veřejné části (na její konec) deklarace typu formuláře (mohli bychom ji vložit i do soukromé části):
    void __fastcall DrawShape(TPoint TopLeft, TPoint BottomRight,
                              TPenMode AMode);
    Definice této metody je zapsána do programové jednotky. Metoda má tento tvar:
    void __fastcall TForm1::DrawShape(TPoint TopLeft, TPoint BottomRight,
                                      TPenMode AMode){
      Canvas->Pen->Mode = AMode;
      switch (DrawingTool){
        case dtLine :
          Canvas->MoveTo(TopLeft.x, TopLeft.y);
          Canvas->LineTo(BottomRight.x, BottomRight.y);
          break;
        case dtRectangle :
          Canvas->Rectangle(TopLeft.x, TopLeft.y,
                            BottomRight.x, BottomRight.y);
          break;
        case dtEllipse :
          Canvas->Ellipse(TopLeft.x, TopLeft.y,
                          BottomRight.x, BottomRight.y);
          break;
        case dtRoundRect :
          Canvas->RoundRect(TopLeft.x, TopLeft.y, BottomRight.x,
                            BottomRight.y, (TopLeft.x - BottomRight.x)/2,
                            (TopLeft.y - BottomRight.y)/2);
          break;
        }
    }
    Tato metoda je použita k dále uvedenému zjednodušení našich obsluh událostí:
    void __fastcall TForm1::FormMouseUp(TObject *Sender,
                    TMouseButton Button, TShiftState Shift, int X, int Y)
    {
      if (Drawing){
        DrawShape(Origin, Point(X, Y), pmCopy);
        Drawing = false;
      }
    }
    void __fastcall TForm1::FormMouseMove(TObject *Sender,
                    TShiftState Shift, int X, int Y)
    {
      if (Drawing){
        DrawShape(Origin, MovePt, pmNotXor);
        MovePt = Point(X, Y);
        DrawShape(Origin, MovePt, pmNotXor);
      }
    }
    Tím máme volbu kreslícího nástroje vyřešenou.

  3. Dále se budeme zabývat dalšími paletami nástrojů (jedna se týká pera a druhá štětce). Paleta nástrojů nemusí být stále viditelná. Pokud aplikace obsahuje mnoho palet nástrojů, pak zobrazujeme pouze tu, kterou právě používáme. K vytvoření skryté palety nástrojů nastavíme vlastnost panelu Visible na false. V naši aplikaci máme dvě další komponenty panelu (budou reprezentovat skryté palety), jeden je nazvaný PenBar a druhý BrushBar (jsou zrušeny hodnoty jejich vlastností Caption). Vlastnosti komponent jsou nastaveny podle dalšího popisu (ovladače jsou uváděny zleva doprava). U prvních osmi tlačítek z BrushBar (vzory výplně) je nastavena vlastnost GroupIndex na 1 a u prvních šesti tlačítek z PenBar (typy čar) je nastavena GroupIndex také na 1.

  4. Tlačítka na BrushBar  jsou nazvána takto: SolidBrush, ClearBrush, VerticalBrush, HorizontalBrush, FDiagonalBrush, BDiagonalBrush, CrossBrush, DiagCrossBrush a BrushColor. U prvního tlačítka je nastavena vlastnost Down na true. Obdobně pro PenBar se tlačítka jmenují: SolidPen, DashPen, DotPen, DashDotPen, DashDotDotPen, ClearPen a PenColor. První tlačítko je opět stisknuté. Na této paletě je umístěn také editační ovladač nazvaný PenSize a vlastnost Text je u něj nastavena na 1. K tomuto editačnímu ovladači je připojena komponenta UpDown (je pojmenovaná PenWidth a její vlastnost Associate je nastavena na PenSize).
    K ukrytí nebo zobrazení palety nástrojů měníme její vlastnost Visible na false nebo true. Obvykle to provádíme v reakci na jistou událost nebo změnu režimu aplikace. V našem případě skrytí a zobrazování palety pera a štětce budeme ovládat tlačítky PenButton a BrushButton, tedy obsluhami jejich stisku.
    void __fastcall TForm1::PenButtonClick(TObject *Sender)
    {
      PenBar->Visible = PenButton->Down;
    }
    void __fastcall TForm1::BrushButtonClick(TObject *Sender)
    {
      BrushBar->Visible = BrushButton->Down;
    }
    Jestliže nyní spustíme naši aplikaci, můžeme zobrazovat a ukrývat paletu pera a paletu štětce (jsou umístěny v horní části formuláře pod paletou kreslících nástrojů).
    Styl pera určuje typ zobrazované čáry (plná, čárkovaná, tečkovaná atd.). Ke změně stylu pera, nastavíme vlastnost pera Style na hodnotu určující požadovaný styl. Je možno použít některou z následujících konstant: psSolid, psDash, psDot, psDashDot, psDashDotDot nebo psClear. Můžeme tedy pro každé tlačítko určující styl vytvořit obsluhu události OnClick a přiřadit v ní odpovídající konstantu vlastnosti Style. V našem programu budeme ale postupovat jiným způsobem. Pro všechna tato tlačítka vytvoříme sdílenou obsluhu události (nazveme ji SetPenStyle) a stisknuté tlačítko v ní určíme pomocí parametru Sender.
    void __fastcall TForm1::SetPenStyle(TObject *Sender)
    {
        if (Sender == SolidPen) Canvas->Pen->Style = psSolid;
        else if (Sender == DashPen) Canvas->Pen->Style = psDash;
        else if (Sender == DotPen) Canvas->Pen->Style = psDot;
        else if (Sender == DashDotPen) Canvas->Pen->Style = psDashDot;
        else if (Sender == DashDotDotPen) Canvas->Pen->Style = psDashDotDot;
        else if (Sender == ClearPen) Canvas->Pen->Style = psClear;
    }
    Barva pera určuje barvu kreslené čáry, včetně obrysů kreslených tvarů. Pro změnu barvy pera změníme vlastnost pera Color. Barvu zadáváme volbou v dialogovém okně barev Windows. Na formuláři je komponenta ColorDialog, a je vytvořena následující obsluha události OnClick pro tlačítko PenColor:
    ColorDialog1->Color = Canvas->Pen->Color;
    if (ColorDialog1->Execute())
      Canvas->Pen->Color = ColorDialog1->Color;
    Šířka pera určuje tloušťku čáry v bodech. Ke změně šířky pera přiřadíme číselnou hodnotu vlastnosti pera Width. Obsluha události OnChange editačního ovladače PenSize je tvořena příkazem:
    Canvas->Pen->Width = PenWidth->Position;
    Styl štětce určuje výplňový vzor při vyplňování tvarů. Předdefinované styly zahrnují plnou plochu, prázdný styl a různé styly šrafování. Pro změnu stylu štětce, nastavíme jeho vlastnost Style na jednu z předdefinovaných hodnot: bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal, bsCross nebo bsDiagCross. V naši aplikaci je sdílená obsluha události pro všech osm tlačítek stylu štětce (obdobně jako pro styl pera):
    void __fastcall TForm1::SetBrushStyle(TObject *Sender)
    {
      if (Sender == SolidBrush) Canvas->Brush->Style = bsSolid;
      else if (Sender == ClearBrush) Canvas->Brush->Style = bsClear;
      else if (Sender == HorizontalBrush) Canvas->Brush->Style = bsHorizontal;
      else if (Sender == VerticalBrush) Canvas->Brush->Style = bsVertical;
      else if (Sender == FDiagonalBrush) Canvas->Brush->Style = bsFDiagonal;
      else if (Sender == BDiagonalBrush) Canvas->Brush->Style = bsBDiagonal;
      else if (Sender == CrossBrush) Canvas->Brush->Style = bsCross;
      else if (Sender == DiagCrossBrush) Canvas->Brush->Style = bsDiagCross;
    }
    Barva štětce určuje barvu výplně. Při její změně měníme hodnotu vlastnosti štětce Color. V naši aplikaci změnu barvy štětce vyřešíme obdobně jako změnu barvy pera. Obsluha stisku tlačítka ColorBrush je tvořena příkazy:
    ColorDialog1->Color = Canvas->Brush->Color;
    if (ColorDialog1->Execute())Brush->Color = ColorDialog1->Color;
    Nyní již můžeme kreslit různé tvary, měnit jejich barvy, zadávat výplňové vzory apod. Vyzkoušejte.
  5. Dále se budeme zabývat stavovým řádkem. Přestože můžeme k vytvoření stavového řádku využít komponentu Panel, je jednodušší použít přímo komponentu StatusBar. Tato komponenta nám umožní rozdělit stavový řádek na několik textových oblastí. K vytvoření stavových panelů na stavovém řádku použijeme Editor stavového řádku a přidáme kód pro aktualizaci jednotlivých stavových panelů. Můžeme také určit, jak zarovnávat text na jednotlivých panelech. V naši aplikaci použijeme stavový řádek k zobrazování souřadnic počátku každého prvku a souřadnic aktuální pozice myši. V naši aplikaci je na formulář přidaná komponenta StatusBar, nazvaná StatusBar1 a je rozdělena na dva panely. Dále je přidán kód k obsluhám událostí, které aktualizují stavový řádek. Následuje text změněných obsluh událostí:

  6. void __fastcall TForm1::FormMouseDown(TObject *Sender,
         TMouseButton Button, TShiftState Shift, int X, int Y)
    {
      Drawing = true;
      Canvas->MoveTo(X, Y);
      Origin = Point(X, Y);
      MovePt = Origin;
      TVarRec tempvar[2] = {X, Y};
      StatusBar1->Panels->Items[0]->Text =
        Format("Origin: (%d, %d)", tempvar, 2);
    }
    void __fastcall TForm1::FormMouseMove(TObject *Sender,
         TShiftState Shift, int X, int Y)
    {
      if (Drawing){
        DrawShape(Origin, MovePt, pmNotXor);
        MovePt = Point(X, Y);
        DrawShape(Origin, MovePt, pmNotXor);
      }
      TVarRec tempvar[2] = {X, Y};
      StatusBar1->Panels->Items[1]->Text =
        Format("Current: (%d, %d)", tempvar, 2);
    }
  7. Čas, kdy kreslíme přímo na formulář již minul. Mnohem častěji aplikace kreslí na bitové mapy, neboť bitové mapy jsou velmi flexibilní pro operace (jako je kopírování, tisk nebo uložení). Komponenta Image je komponenta, která může obsahovat bitovou mapu a umožňuje snadné vložení jedné nebo více bitových map na formulář. Bitová mapa také nemusí mít stejnou velikost jako formulář; může být menší nebo větší.

  8. Často aplikace potřebuje zobrazovat více informací, než se vejde do jisté oblasti. Některé ovladače (např. okna seznamů) mohou automaticky rolovat svým obsahem. I jiné ovladače (včetně samotného formuláře) také poskytují schopnost rolování. Builder obsluhuje tyto posuvné oblasti ovladačem nazývaným posuvné okno. Posuvné okno se podobá panelu, který může obsahovat jiné ovladače, ale posuvné okno je normálně neviditelné. Jestliže ovladače obsažené v posuvném okně nejsou ve viditelné oblasti okna, pak je automaticky zobrazen jeden nebo dva posuvníky, umožňující rolovat oknem. K vytvoření posuvné oblasti, umístíme ovladač ScrollBox na formulář a nastavíme jeho meze na oblast, se kterou chceme rolovat (často to provedeme pomocí vlastnosti Align). V naši aplikaci vytvoříme posuvnou oblast zabírající celou plochu formuláře mezi paletou nástrojů a stavovým řádkem. Na formulář je umístěna komponenta ScrollBox a její vlastnost Align je nastavena na alClient.
    Pomocí komponenty Image specifikujeme oblast na formuláři, která může obsahovat objekt obrázku (např. bitovou mapu nebo metasoubor). Velikost obrázku lze nastavit manuálně nebo využít ovladač Image k udržování velikosti svého obrázku při běhu aplikace. Jestliže předpokládáme, že tento ovladač bude schopen měnit svou velikost, pak nastavíme umístění jeho levého horního rohu. V naši aplikaci je na formulář přidána komponenta Image a jsou u ní nastaveny tyto vlastnosti: Name na Image, AutoSize na true, Left na 0, Top na 0, Width na 200 a Height na 200. Když umístíme ovladač Image, pak je bez obrázku. Jestliže má obsahovat nějaký obrázek, můžeme při návrhu nastavit jeho vlastnost Picture. Ovladač také může zavést svůj obrázek ze souboru při běhu aplikace. Jestliže potřebujeme prázdnou bitovou mapu pro kreslení, můžeme ji vytvořit při běhu aplikace. K vytvoření prázdné bitové mapy při spuštění aplikace vytvoříme obsluhu události OnCreate pro formulář a vytvořený objekt bitové mapy přiřadíme vlastnosti Picture->Graphic ovladače Image.
    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
      TBitmap *Bitmap = new Graphics::TBitmap();
      Bitmap->Width = 200;
      Bitmap->Height = 200;
      Image->Picture->Graphic = Bitmap;
    }
    Přiřazení bitové mapy vlastnosti Graphic obrázku vytváří vzájemný vztah bitové mapy a objektu obrázku. Bitovou mapu tedy není nutno rušit, zruší ji objekt obrázku. Můžeme přiřadit objektu obrázku další bitovou mapu a obrázek uvolní starou bitovou mapu a převezme řízení nové. Jestliže nyní spustíme aplikaci, vidíme na klientské oblasti formuláře bílou oblast reprezentující bitovou mapu. Jestliže klientská oblast okna nemůže zobrazit celý obrázek, jsou automaticky zobrazeny posuvníky pomocí nichž lze zobrazit zbytek obrázku. Obrázek ale nelze kreslit, neboť kreslení stále probíhá na formuláři, který je nyní překryt posuvným oknem a komponentou Image. Pro kreslení je nutno nyní použít plátno ovladače Image namísto plátna formuláře. Nejsnadněji to provedeme změnou všech výskytů Canvas na Image->Canvas v naší programové jednotce přiřazené k formuláři. Dále musíme přiřadit obsluhy událostí myší odpovídajícím událostem ovladače Image. V Inspektoru objektu zobrazíme události objektu Image a odpovídajícím událostem přiřadíme již existující obsluhy událostí formuláře FormMouseDown, FormMouseMove a FormMouseUp. Původní obsluhy pro formulář můžeme zrušit, ale není to nutné. Nyní po spuštění aplikace již můžeme kreslit, ale pouze na ovladači Image. Můžeme také skrýt nebo zobrazit paletu pera nebo paletu štětce a rolovat obrázkem.
  9. Aplikace obsahuje také nabídku. Nabídka obsahuje obvyklé volby. Obsluha volby File | Exit je tvořena příkazem:

  10. Close();
    Aplikaci můžeme spustit a vyzkoušet.
    Tisk obrázku z aplikace Builderu je jednoduchý. Musíme přidat hlavičkový soubor Printers.hpp do programové jednotky formuláře, která bude tisknout. Tento hlavičkový soubor deklaruje objekt nazvaný Printer, který má plátno reprezentující tištěnou stranu. Obrázek vytiskneme kopírováním obrázku na plátno tiskárny. V naši aplikaci vytvoříme následující obsluhu události OnClick pro volbu File | Print:
    void __fastcall TForm1::Print1Click(TObject *Sender)
    {
        int BitmapInfoSize, BitmapImageSize;
        long DIBWidth, DIBHeight;
        PChar BitmapImage;
        Windows::PBitmapInfo BitmapInfo;
        Graphics::TBitmap *Bitmap;
        Printer()->BeginDoc();
        Bitmap = new Graphics::TBitmap();
        Bitmap->Assign(Image->Picture);
        GetDIBSizes(Bitmap->Handle, BitmapInfoSize, BitmapImageSize);
        BitmapInfo  = (PBitmapInfo) new char[BitmapInfoSize];
        BitmapImage = (PChar) new char [BitmapImageSize];
        GetDIB(Bitmap->Handle, 0, BitmapInfo, BitmapImage);
        DIBWidth  = BitmapInfo->bmiHeader.biWidth;
        DIBHeight = BitmapInfo->bmiHeader.biHeight;
        StretchDIBits(Printer()->Canvas->Handle,
                    0, 0, DIBWidth, DIBHeight,
                    0, 0, DIBWidth, DIBHeight,
                    BitmapImage, BitmapInfo,
                    DIB_RGB_COLORS, SRCCOPY);
        delete [] BitmapImage;
        delete [] BitmapInfo;
        delete Bitmap;
        Printer()->EndDoc();
    }
  11. Obrázky existují pouze při běhu aplikace. Často chceme použít stejný obrázek nebo chceme uložit vytvořený obrázek pro pozdější použití. Komponenta Image usnadňuje zavádění obrázku a jejich ukládání. Princip ukládání a zavádění grafických souborů je stejný jako u jiných souborů. Na formulář tedy přidáme komponenty OpenDialog a SaveDialog. K zavedení grafického souboru do ovladače Image volíme metodu LoadFromFile objektu Picture ovladače Image. Obsluha události OnClick volby File | Open vypadá takto:

  12. void __fastcall TForm1::Open1Click(TObject *Sender)
    {
      if (OpenDialog1->Execute()){
        CurrentFile = OpenDialog1->FileName;
        Image->Picture->LoadFromFile(CurrentFile);
      }
    }
    Když vytvoříme nebo modifikujeme obrázek, často jej chceme uložit do souboru pro pozdější použití. Objekt obrázku může ukládat grafiku v několika formátech a vývojář aplikace může vytvářet a registrovat vlastní formáty grafických souborů, které objekt obrázku pak může použít. K uložení obsahu ovladače Image do souboru voláme metodu SaveToFile objektu Picture ovladače Image. Následují obsluhy událostí OnClick voleb File | Save a File | Save As:
    void __fastcall TForm1::Save1Click(TObject *Sender)
    {
      if (CurrentFile != EmptyStr){
        Image->Picture->SaveToFile(CurrentFile);
      }
      else{
       SaveAs1Click(Sender);
      }
    }
    void __fastcall TForm1::SaveAs1Click(TObject *Sender)
    {
      if (SaveDialog1->Execute()){
        CurrentFile = SaveDialog1->FileName;
        Save1Click(Sender);
      }
    }
    Obrázek v ovladači můžeme kdykoli při běhu aplikace nahradit jiným obrázkem. Jestliže přiřadíme novou grafiku k obrázku, který již má grafiku, pak nová grafika nahradí existující. Vytváření nové grafiky je stejný proces jako jsme použili při vytváření inicializační grafiky (obsluha události OnCreate), ale musíme dát uživateli možnost zvolit i jinou než implicitní velikost obrázku. To snadno provedeme pomocí dialogového okna. Naše aplikace obsahuje dialogové okno NewBMPForm s editačními ovladači WidthEdit a HeightEdit. Obsluha volby File | New je tvořena příkazy:
    void __fastcall TForm1::New1Click(TObject *Sender)
    {
      Graphics::TBitmap *Bitmap;
      NewBMPForm->ActiveControl = NewBMPForm->WidthEdit;
      NewBMPForm->WidthEdit->Text =
        IntToStr(Image->Picture->Graphic->Width);
      NewBMPForm->HeightEdit->Text =
        IntToStr(Image->Picture->Graphic->Height);
      if (NewBMPForm->ShowModal() != IDCANCEL){
        Bitmap = new Graphics::TBitmap();
        Bitmap->Width = StrToInt(NewBMPForm->WidthEdit->Text);
        Bitmap->Height = StrToInt(NewBMPForm->HeightEdit->Text);
        Image->Picture->Graphic = Bitmap;
        CurrentFile = EmptyStr;
      }
    }
    Je důležité pochopit, že přiřazení nové bitové mapy k vlastnosti Graphic objektu obrázku, způsobí zrušení existující bitové mapy a vytvoření vzájemného vztahu s novou.
  13. Schránku Windows můžeme použít pro kopírování a vkládání grafiky ve své aplikaci nebo k výměně grafiky s jinou aplikací. Objekt schránky pracuje s různými typy informací, včetně grafických. Dříve než můžeme použít objekt schránky ve své aplikaci, musíme přidat hlavičkový soubor Clipbrd.hpp do jednotky, ve které chceme schránku používat. Můžeme kopírovat libovolný obrázek, včetně obsahu ovladače Image do schránky. Po vložení do schránky je obrázek přístupný pro všechny aplikace Windows. Pro kopírování obrázku do schránky přiřadíme obrázek k objektu schránky použitím metody Assign. V naší aplikaci je následující obsluha volby Edit | Copy.

  14. void __fastcall TForm1::Copy1Click(TObject *Sender)
    {
      Clipboard()->Assign(Image->Picture);
    }
    Vyjmutí grafiky do schránky se podobá kopírování, s tím, že je ještě nutno zrušit kopírovanou grafiku. Nejprve vytvoříme kopii ve schránce a potom zrušíme originál. Následuje obsluha volby Edit | Cut:
    void __fastcall TForm1::Cut1Click(TObject *Sender)
    {
     TRect ARect;
     Copy1Click(Sender);
     Image->Canvas->CopyMode = cmWhiteness;
     ARect = Rect(0, 0, Image->Width, Image->Height);
     Image->Canvas->CopyRect(ARect, Image->Canvas, ARect);
     Image->Canvas->CopyMode = cmSrcCopy;
    }
    Jestliže schránka obsahuje bitovou mapu, můžeme ji vložit do libovolného objektu obrázku, včetně ovladače Image nebo samotného formuláře. Pro vložení grafiky ze schránky, voláme metodu schránky HasFormat k zjištění, zda schránka obsahuje grafiku (pro test na grafiku předáme metodě parametr CF_BITMAP) a pokud schránka grafiku obsahuje, pak ji přiřadíme. V naši aplikaci je následující obsluha volby Edit | Paste:
    void __fastcall TForm1::Paste1Click(TObject *Sender)
    {
      Graphics::TBitmap *Bitmap;
      if (Clipboard()->HasFormat(CF_BITMAP)){
        Bitmap = new Graphics::TBitmap();
        try{
          Bitmap->Assign(Clipboard());
          Image->Canvas->Draw(0, 0, Bitmap);
          delete Bitmap;
        }
        catch(...){
          delete Bitmap;
        }
      }
    }
    Nyní již se schránkou můžeme pracovat běžným způsobem a lze ji tedy využít pro přenos obrázků mezi různými aplikacemi. Naše aplikace je hotova.
  15. Pokuste se aplikaci grafického editoru doplnit nějakou další činností. Vytvořte pro tuto aplikaci také nápovědu.
11. Práce s grafikou I