Textový editor na 350 řádků v Gtkmm

Tomáš Velecký 12. 4. 2011

Pokračování seriálu o gtkmm – dnes spolu začneme v C++ tvořit minimalistický, ale funkční textový editor. Tomu bude pro zajímavost předcházet malý průzkum kódu ostatních „konkurenčních“ editorů. Také si představíme další dva widgety a část XML kódu pocházejícího z knihovny Glade ve formě menu.

Jak to bude vypadat aneb menší statistika

Textový editor, to je velmi široký pojem. Samozřejmě, Microsoft Wordu nebo Open/LibreOffice Writeru se pravděpodobně moc podobat nebude. Zaprvé bych je označil spíše jako textové procesory a zadruhé, pokud bychom se zabývali tvorbou něčeho takového, neměl by se tento seriál jmenovat „Knihovna Gtkmm pro tvorbu GTK+ v C++“, ale raději ( ⇒ určitě) „Tvoříme textový procesor v gtkmm a C++“. To, co se musí vysvětlit na tomto ukázkovém programu, se totiž nevejde do jednoho článku, ale do dvou. Samotný kód našeho editoru by vydal na články tři. Jen tak pro zajímavost, OpenOffice.org je také v C++ a jako celek měl v roce 2003 asi 9 miliónů řádků kódu (zdroj).

Náš textový editor se tedy více podobá geditu nebo ne. I oproti těm je však velmi osekaný. Takový zdrojový kód ne „měří“ 42 tisíc řádků a geditu dokonce 5,4×104 řádků! (No dobrá, nebudu to tak zveličovat – 5,4×104 je jen 54 tisíc.) Pak tu máme ještě základnější editory – KWrite, jehož kód má asi 1 300 řádků a Leafpad – 8 000. Náš editor se asi nejvíce podobá Leafpadu, který ani nepodporuje zvýrazňování částí kódu a přesto má asi 8 000 řádků, což je asi 23krát více, než kolik má ten náš.

Takže co vlastně bude umět? Měl by alespoň základně podporovat klávesové zkratky Ctrl+C, Ctrl+V apod. Také by neměl mlčet (stavový řádek) a měl by umět říct něco o sobě (dialog „O programu“). Takovou slušností textového editoru je ochota upravovat text (nějaké pole pro práci s textem), a ten následně ukládat (položky v menu, propojené s klávesovými zkratkami; dialogy pro výběr cesty), popř. otevírat (práce s taby v „notebooku“). Některé texťáky, zvláště textové procesory, mají ještě další, obrázkové, menu. My budeme mít nějaký základ také.

Screenshot ukázkového programu

Novinky

Změna ikonky okna

Začneme něčím lehčím. Až doposud měly všechny programy ikonu jakéhosi prázdného okna. To může změnit metoda okenní třídy set_icon_from_file(), které jako argument předáme cestu k souboru s ikonou.

Ikona programu

Je v podstatě jedno, jaké bude mít ikona rozměry. Také je možné použít set_icon(). Tato metoda však vyžaduje ikonu v podobě třídy Gdk::Pixbuf, kterou pravděpodobně podrobněji rozebereme v jednom z příštích dílů.

Menu

Zatím všem oknům něco chybělo. Také vás to trochu štvalo? Už nebude, textový editor to menu mít bude. Tedy obligátní lištu nahoře, kterou najdete snad v každém programu s položkami Soubor, Úpravy, Nástroje, Nápověda a podobně, ale které se už různé aplikace, zvláště webové prohlížeče, zbavují, aby zobrazily co nejvíce z webové stránky. K tomu přidáme ještě „ikonkové menu“, nástrojovou lištu.

Ukázka jednoduchého menu

Jelikož se jedná o widget s velkým množstvím položek a kód by byl velice dlouhý, pomůžeme si s XML, které by vygenerovalo např. Glade. Právě proto zatím nebudu moc vysvětlovat, jak to funguje, ale zaměřím se na to, jak menu vytvořit. Více do hloubky to bude vysvětleno v díle o Glade, prozatím přikládám okomentovaný kód. Ke změně je třeba pozměnit pouze XML kód a přiřadit položku do ActionGroup.

#include <gtkmm.h>
#include <iostream>

class Okno : public Gtk::Window {
    public:
        Okno();
        void Handler();

    protected:
        Gtk::VBox ram;
        Glib::RefPtr<Gtk::UIManager> UIManager;
        Glib::RefPtr<Gtk::ActionGroup> ActionGroup;
        Glib::RefPtr<Gtk::TextBuffer> Buffer;
};

Okno::Okno() {
    set_default_size(200, 100);

    ActionGroup = Gtk::ActionGroup::create();

    // První argument metod create() musí korespondovat s označením prvku v XML
    // Druhý argument určuje, jaký název má daná položka nést, případně (pokud jej zadáe pomocí Gtk::Stock::NĚCO), jakou klávesovou zkratkou se dá vyvolat

    // Položka "Nabídka"
    ActionGroup->add(Gtk::Action::create("Nabidka", "Nabídka")); // Místo "Nabídka" může dát také Gtk::Stock::NĚCO
    // Submenu položky "Nabídka"
    ActionGroup->add(Gtk::Action::create("Podnabidka", "Soubory"));
    ActionGroup->add(Gtk::Action::create("Ukoncit", Gtk::Stock::QUIT), sigc::mem_fun(*this, &Okno::Handler));
    // Submenu položky "Podnabídka"
    ActionGroup->add(Gtk::Action::create("Ulozit", Gtk::Stock::SAVE), sigc::mem_fun(*this, &Okno::Handler));
    ActionGroup->add(Gtk::Action::create("Novy", Gtk::Stock::NEW, "Noový soouboor"), sigc::mem_fun(*this, &Okno::Handler)); // Ač je zadáno, že se jedná o Gtk::Stock::NEW, položka nese název Noový soouboor
    ActionGroup->add(Gtk::Action::create("Neco", Gtk::Stock::OPEN), Gtk::AccelKey("E"), sigc::mem_fun(*this, &Okno::Handler)); // Ač se má jednat o položku Gtk::Stock::OPEN, vyvolává se klávesovou zkratkou Ctrl+E, ne Ctrl+O

    UIManager = Gtk::UIManager::create();
    UIManager->insert_action_group(ActionGroup);

    add_accel_group(UIManager->get_accel_group());

    // Zde je rozvržení menu v XML
    Glib::ustring ui_info =
    "<ui>"
    "  <menubar name='Menu'>"
    "    <menu action='Nabidka'>"
    "      <menu action='Podnabidka'>"
    "        <menuitem action='Ulozit'/>"
    "        <menuitem action='Novy'/>"
    "        <menuitem action='Neco'/>"
    "      </menu>"
    "      <separator/>"
    "      <menuitem action='Ukoncit'/>"
    "    </menu>"
    "  </menubar>"
    "  <toolbar  name='NastrojovaLista'>"
    "    <toolitem action='Novy'/>"
    "    <toolitem action='Ukoncit'/>"
    "  </toolbar>"
    "</ui>";

    // Toto vygeneruje uživatelské rozhraní z výše napsaného XML kódu
    try {
        UIManager->add_ui_from_string(ui_info);
    } catch(const Glib::Error& ex) {
        std::cerr << "Selhalo vytvoření menu: " <<  ex.what();
    }

    add(ram);

    // Toto přiřazuje jednotlivé prvky UI příslušným kontejnerům (pokud daný prvek existuje)
    Gtk::Widget* Menubar = UIManager->get_widget("/Menu");
    if (Menubar) {ram.pack_start(*Menubar, Gtk::PACK_SHRINK);}

    Gtk::Widget* Toolbar = UIManager->get_widget("/NastrojovaLista");
    if (Toolbar) {ram.pack_start(*Toolbar, Gtk::PACK_SHRINK);}

    show_all_children();
}

void Okno::Handler() {
    std::cout << "Byla stisknuta nějaká položka v menu." << std::endl;
}

int main(int argc, char *argv[]) {
    Gtk::Main kit(argc, argv);
    Okno okno;
    Gtk::Main::run(okno);
    return 0;
}

Dialog „O programu“

Asi každý autor se chce nějak pod svým programem podepsat, případně zveřejnit e-mail, na který mohou uživatelé posílat bug reporty. Ani jednoduše zjistit, jakou verzi programu vlastně používám, nemusí být k zahození. Od toho je v gtkmm hned několik tříd standardizovaných dialogových oken. Jeden z nich řeší problém, který jsem právě popsal a jmenuje se jednoduše AboutDialog. Práce s dalšími dvěma je popsána níže.

widgety

Dialog O programu

Pro zadání toho, co se má v okně zobrazit, jsou určeny metody set_comments(), set_copyright(), set_license(), set_program_name(), set_version() a set_website(), jenž požadují pouze jeden řetězec a set_authors(), které předáme vector ze standardní knihovny. Rozdíl mezi set_copyright() a set_license() je, že do set_copyright() se píše, kdo program napsal, a to se poté zobrazí hned v hlavním okně dialogu; zatímco set_license() zobrazí licenci, kterou si může uživatel přečíst až po kliknutí na příslušné tlačítko (viz screenshot). V dokumentaci je jich samozřejmě ještě více.

#include <gtkmm.h>

class HlavniOkno : public Gtk::Window {
    public:
        HlavniOkno();

    protected:
        void OProgramu();
        Gtk::Button otevri_dialog;
        Gtk::AboutDialog o_programu;
};

HlavniOkno::HlavniOkno() {
    otevri_dialog.set_label("Chcu něco vědět o tomto programu!");
    otevri_dialog.signal_clicked().connect(sigc::mem_fun(*this, &HlavniOkno::OProgramu));
    add(otevri_dialog);
    show_all();
}

void HlavniOkno::OProgramu() {
    std::vector<Glib::ustring> autori(2);
    autori[0] = "První Autor";
    autori[1] = "Druhý Autor";

    o_programu.set_authors(autori);
    o_programu.set_comments("Super program, který o sobě umí něco říct!");
    o_programu.set_copyright("Copyright (C), První Autor a Druhý Autor, 2011");
    o_programu.set_license("Zde je licence.");
    o_programu.set_program_name("Název programu");
    o_programu.set_version("číslo verze");
    o_programu.set_website("domovskastranka.cz");

    gint result = o_programu.run();
    switch (result) {
        case GTK_RESPONSE_DELETE_EVENT:
        break;
    }

    o_programu.hide();
}

int main(int argc, char *argv[]) {
    Gtk::Main kit(argc, argv);
    HlavniOkno hlavni_okno;
    Gtk::Main::run(hlavni_okno);
    return 0;
}

Pokračování

Příště editor dokončíme. K dispozici samozřejmě bude ke stažení jeho zdrojový kód, který – jak již bylo naznačeno v úvodních odstavcích – nemá ani 350 řádků.

Našli jste v článku chybu?
DigiZone.cz: Ultra HD v praxi a v Portugalsku

Ultra HD v praxi a v Portugalsku

120na80.cz: Co je padesátkrát sladší než cukr?

Co je padesátkrát sladší než cukr?

Vitalia.cz: Test dětských svačinek: Tyhle ne!

Test dětských svačinek: Tyhle ne!

Vitalia.cz: Voda z Vltavy před a po úpravě na pitnou

Voda z Vltavy před a po úpravě na pitnou

120na80.cz: Galerie: Čínští policisté testují českou minerálku

Galerie: Čínští policisté testují českou minerálku

DigiZone.cz: Technisat připravuje trojici DAB

Technisat připravuje trojici DAB

Vitalia.cz: Tohle jsou nejlepší česká piva podle odborníků

Tohle jsou nejlepší česká piva podle odborníků

Lupa.cz: Další Češi si nechali vložit do těla čip

Další Češi si nechali vložit do těla čip

Lupa.cz: Adblock Plus začal prodávat reklamy

Adblock Plus začal prodávat reklamy

Podnikatel.cz: Tyto pojmy k #EET byste měli znát

Tyto pojmy k #EET byste měli znát

Lupa.cz: Jak levné procesory změnily svět?

Jak levné procesory změnily svět?

Podnikatel.cz: Letáky? Lidi zuří, ale ony stále fungují

Letáky? Lidi zuří, ale ony stále fungují

DigiZone.cz: Nova opět stahuje „milionáře“

Nova opět stahuje „milionáře“

Lupa.cz: Jak se prodává firma za miliardu?

Jak se prodává firma za miliardu?

DigiZone.cz: Světový pohár v přímém přenosu na ČT

Světový pohár v přímém přenosu na ČT

DigiZone.cz: DVB-T2 ověřeno: seznam TV zveřejněn

DVB-T2 ověřeno: seznam TV zveřejněn

DigiZone.cz: Parlamentní listy: kde končí PR...

Parlamentní listy: kde končí PR...

Vitalia.cz: Jak Ondra o astma přišel

Jak Ondra o astma přišel

Podnikatel.cz: ČSSZ posílá přehled o důchodovém kontě

ČSSZ posílá přehled o důchodovém kontě

DigiZone.cz: Wimbledon na Nova Sport až do 2019

Wimbledon na Nova Sport až do 2019