# Програмирање уз gtkmm 4

Мари Каминг
Бернхард Ридер Поглавље о „Временским роковима“.
Џонатон Јонгсма Поглавље о „Цртању уз Каиро“. Поглавље о „Раду са изворним кодом за gtkmm“. Поглавље о „Недавним датотекама“.
Оле Лаурсен Делови поглавља о „Интернационализацији“.
Марко Анастасов Поглавље о „Штампању“. Делови поглавља о „Интернационализацији“.
Данијел Елстнер Одељак „Структура изградње“ у поглављу о „Омотавању Ц библиотека помоћу gmmproc-а“.
Крис Вајн Поглавље о „Вишенативним програмима“.
Дејвид Кинг Одељак о Gtk::Grid-у.
Педро Фереира Поглавље о „Догађајима тастатуре“.
Кјел Алстет Ажурирање са gtkmm 3 на gtkmm 4. Поглавље о „Изградњи програма“. Поглавље о „Елементу падајућег списка“. Поглавље о „ListView, GridView, ColumnView“.
Данијел Боулс Напомене о потреби за уклањањем елемената у неуправљаним омотачима из родитеља ради ослобађања, и друга лицкања.

Ова књига објашњава кључна начела gtkmm C++ API-ја за стварање корисничких прочеља. Такође представља главне елементе корисничког прочеља („елементе“).

2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Мари Каминг

Дозвољено је умножавање, дељење и/или мењање овог документа под условима ГНУ-ове Слободне документационе лиценце, верзије 1.2 или било које новије верзије коју објави Задужбина слободног софтвера; без непроменљивих одељака, без текста на предњој страни и без текста на задњој страни. Примерак лиценце је укључен у одељку под насловом „GNU Free Documentation License“.

## Увод

### Ова књига

Ова књига објашњава кључна начела gtkmm C++ API-ја за стварање корисничких прочеља. Такође представља главне елементе корисничког прочеља („елементе“). Иако помиње класе, конструкторе и методе, не улази у велике детаље. Због тога, за потпуне податке о API-ју треба да пратите везе у референтној документацији.

Ова књига претпоставља добро разумевање језика C++ и начина прављења C++ програма.

Веома бисмо волели да чујемо за било какве проблеме које имате приликом учења gtkmm-а уз овај документ, и ценили бисмо доприносе у погледу побољшања. Погледајте одељак Допринос за даље податке.

### gtkmm

gtkmm је Ц++ омотач за ГТК, библиотеку која се користи за прављење графичких корисничких прочеља. Лиценциран је под ЛГПЛ лиценцом, тако да можете развијати отворени софтвер, слободни софтвер или чак комерцијални несофтвер користећи gtkmm без куповине лиценци.

gtkmm се првобитно звао gtk-- јер се ГТК првобитно звао ГТК+ и имао је + у називу. Међутим, пошто претраживачи не могу лако да индексирају --, пакет је углавном познат под именом gtkmm, и то је оно што смо задржали.

#### Зашто користити gtkmm уместо ГТК-а?

gtkmm вам омогућава да пишете код користећи уобичајене Ц++ технике као што су енкапсулација, извођење и полиморфизам. Као Ц++ програмер вероватно већ схватате да ово води ка јаснијем и боље организованом коду.

gtkmm је безбеднији у погледу типова, тако да преводилац може да открије грешке које би се откриле тек у време извршавања када се користи Ц. Ова употреба одређених типова такође чини АПИ јаснијим јер можете видети који типови треба да се користе само гледањем у декларацију метода.

Наслеђивање се може користити за извођење нових виџета. Извођење нових виџета у ГТК Ц коду је толико компликовано и подложно грешкама да то скоро ниједан Ц програмер не ради. Као Ц++ програмер знате да је извођење суштинска објектно оријентисана техника.

Могу се користити примерци чланова, што поједностављује управљање меморијом. Сви ГТК Ц виџети се обрађују коришћењем показивача. Као Ц++ програмер знате да показиваче треба избегавати где год је то могуће.

gtkmm укључује мање кода у поређењу са ГТК-ом, који користи префиксна имена функција и много макроа за претварање типова.

#### gtkmm у поређењу са Qt-ом

Trolltech-ов Qt је најближа конкуренција gtkmm-у, па заслужује расправу.

Програмери gtkmm-а обично више воле gtkmm од Qt-а јер gtkmm ради ствари на више Ц++ начин. Qt потиче из времена када Ц++ и стандардна библиотека нису били стандардизовани или добро подржани од стране преводилаца. Стога он дуплира много ствари које су сада у стандардној библиотеци, као што су контејнери и подаци о типовима. Најзначајније је то што је Trolltech изменио језик Ц++ да би обезбедио сигнале, тако да се Qt класе не могу лако користити са класама које нису из Qt-а. gtkmm је могао да користи стандардни Ц++ да обезбеди сигнале без промене језика Ц++. Погледајте ЧПП за детаљније разлике.

#### gtkmm је омотач

gtkmm није изворни Ц++ скуп алата, већ Ц++ омотач Ц скупа алата. Ово раздвајање прочеља и изведбе има предности. Програмери gtkmm-а проводе већину свог времена разговарајући о томе како gtkmm може да представи најјаснији АПИ, без незгодних компромиса због нејасних техничких детаља. Ми мало доприносимо основној ГТК бази кода, али то раде и Ц програмери, и Перл програмери и Пајтон програмери, итд. Стога ГТК има користи од шире базе корисника него скупови алата специфични за језик — има више реализатора, више програмера, више тестера и више корисника.

### Ц++ ресурси

Корисне везе:

- Википедија Ц++: Увод у Ц++ и многе везе.

- Ц++ референца: Погодна верзија Ц и Ц++ стандарда.

- Мрежа Ц++ ресурса

- Почетна страница Бјарнеа Струструпа

## Инсталација

### Зависности

Пре него што покушате да инсталирате gtkmm-4.0, можда ћете прво морати да инсталирате ове друге пакете.

- sigc++-3.0

- gtk4

- glibmm-2.68

- cairomm-1.16

- pangomm-2.48

Ове зависности имају своје сопствене зависности, укључујући следеће програме и библиотеке:

- pkg-config

- glib-2.0

- pango

- cairo

- gdk-pixbuf-2.0

- graphene-1.0

### Јуникс и Линукс

#### Претходно изграђени пакети

Новија издања gtkmm-а су упакована у скоро свакој већој Линукс дистрибуцији данас. Дакле, ако користите Линукс, вероватно можете почети са gtkmm-ом тако што ћете инсталирати пакет из званичне ризнице за вашу дистрибуцију. Дистрибуције које укључују gtkmm у своје ризнице укључују Дебијан, Убунту, Ред Хет, Федору, Мандриву, Сузе и многе друге.

Називи gtkmm пакета се разликују од дистрибуције до дистрибуције (нпр. libgtkmm-4.0-dev на Дебијану и Убунту или gtkmm4.0-devel на Ред Хету и Федори), па проверите са програмом за управљање пакетима ваше дистрибуције тачан назив пакета и инсталирајте га као што бисте инсталирали било који други пакет.

Називи пакета се неће мењати када буду објављена нова АПИ/АБИ-сагласна издања gtkmm-а. У супротном не би била АПИ/АБИ-сагласна. Зато се немојте изненадити, на пример, ако нађете gtkmm 4.8 који обезбеђује Дебијанов пакет libgtkmm-4.0-dev.

#### Инсталирање из изворног кода

Ако ваша дистрибуција не обезбеђује претходно изграђен gtkmm пакет, или ако желите да инсталирате другачије издање од оног које обезбеђује ваша дистрибуција, можете такође инсталирати gtkmm из изворног кода. Изворни код за gtkmm се може преузети са .

Након што инсталирате све зависности, преузмите изворни код за gtkmm, распакујте га и пређите у новостворени директоријум. gtkmm се може изградити помоћу Мезона. Погледајте датотеку `README` у издању gtkmm-а које сте преузели.

Запамтите да ће вам на Јуникс или Линукс оперативном систему вероватно бити потребна `root` овлашћења за инсталирање софтвера. Наредба `su` или `sudo` ће вам омогућити да унесете `root` лозинку и привремено стекнете `root` статус.

Скрипта `configure` или `meson` ће проверити да ли су све неопходне зависности већ инсталиране. Ако вам недостаје било која зависност, изаћи ће и приказати грешку.

Подразумевано, ако се гради помоћу Мезона или Аутотулса, gtkmm ће бити инсталиран у директоријум `/usr/local`. На неким системима ћете можда морати да га инсталирате на другу локацију. На пример, на Red Hat Linux системима можете користити опцију `--prefix` уз configure, као што је:

    # meson setup --prefix=/usr <builddir> <srcdir>
    # meson configure --prefix=/usr
    # ./configure --prefix=/usr

Требало би да будете веома опрезни када инсталирате у стандардне системске префиксе као што је `/usr`. Линукс дистрибуције инсталирају софтверске пакете у `/usr`, па би инсталирање пакета из изворног кода у овај префикс могло да оштети или доведе до сукоба са софтвером који је инсталиран помоћу система за управљање пакетима ваше дистрибуције. Идеално би било да користите посебан префикс за сав софтвер који инсталирате из изворног кода.

Ако желите да помогнете у развоју gtkmm-а или да експериментишете са новим функцијама, можете такође инсталирати gtkmm из гита. Већини корисника ово никада неће бити потребно, али ако сте заинтересовани да помогнете у развоју gtkmm-а, погледајте додатак Рад са изворним кодом gtkmm-а.

### Microsoft Windows

GTK и gtkmm су дизајнирани да добро раде са Microsoft Windows-ом, а програмери подстичу његово коришћење на win32 платформи. Међутим, Windows нема стандардни систем инсталације за развојне библиотеке. Погледајте страницу Windows инсталација (донекле застарела) или додатак gtkmm и Win32 за упутства и напомене о инсталацији специфичне за Windows.

## Основно

Ово поглавље ће представити неке од најважнијих аспеката програмирања помоћу gtkmm-а. Они ће бити демонстрирани једноставним примером кода који ради. Међутим, ово је само увод, па треба да погледате остала поглавља за опширније информације.

Ваше постојеће знање језика C++ ће вам помоћи код gtkmm-а као и код било које друге библиотеке. Осим ако не наведемо другачије, можете очекивати да се класе gtkmm-а понашају као и било која друга C++ класа, и можете очекивати да користите своје постојеће C++ технике са класама gtkmm-а.

### Једноставан пример

Да бисмо започели наш увод у gtkmm, почећемо са најједноставнијим могућим програмом. Овај програм ће направити празан прозор величине 200 x 200 пиксела.

Изворни код

File: `base.cc` (For use with gtkmm 4)

    #include <gtkmm.h>

    class MyWindow : public Gtk::Window
    {
    public:
      MyWindow();
    };

    MyWindow::MyWindow()
    {
      set_title("Basic application");
      set_default_size(200, 200);
    }

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.examples.base");

      return app->make_window_and_run<MyWindow>(argc, argv);
    }

Сада ћемо објаснити сваки део примера

    #include <gtkmm.h>

Сви gtkmm програми морају да укључе одређена gtkmm заглавља; `gtkmm.h` укључује цео gtkmm комплет. Ово обично није добра идеја, јер укључује око мегабајт заглавља, али за једноставне програме је довољно.

Следећи део програма:

    class MyWindow : public Gtk::Window
    {
    public:
      MyWindow();
    };

    MyWindow::MyWindow()
    {
      set_title("Basic application");
      set_default_size(200, 200);
    }

дефинише класу MyWindow. Њен подразумевани конструктор поставља наслов прозора и подразумевану (почетну) величину.

Прва наредба функције `main()`:

    auto app = Gtk::Application::create("org.gtkmm.examples.base");

ствара објекат Gtk::Application, смештен у паметном показивачу Glib::RefPtr. Ово је потребно у свим gtkmm програмима. Метод create() за овај објекат покреће gtkmm.

Последњи ред ствара и приказује прозор и улази у gtkmm главну петљу обраде, која ће се завршити када се прозор затвори. Ваша функција `main()` ће се тада вратити са одговарајућим кодом успеха или грешке. Аргументи argc и argv, који се прослеђују вашем програму из линије наредби, могу се проверити када се позове make_window_and_run(), али овај једноставан програм не користи те аргументе.

    return app->make_window_and_run<MyWindow>(argc, argv);

Након стављања изворног кода у `base.cc`, можете компајлирати горњи програм помоћу gcc-а користећи:

    g++ base.cc -o base `pkg-config --cflags --libs gtkmm-4.0` -std=c++17

Имајте на уму да морате окружити позив pkg-config-а обрнутим апострофима. Обрнути апострофи узрокују да шкољка изврши наредбу унутар њих и да користи излаз наредбе као део линије наредби. Такође имајте на уму да `base.cc` мора доћи пре позива pkg-config-а у линији наредби. `-std=c++17` је неопходно само ако ваш преводилац није подразумевано усклађен са C++17 стандардом.

### Заглавља и повезивање

Иако смо приказали наредбу за компајлирање за једноставан пример, заиста би требало да користите Мезон систем за изградњу. Примери коришћени у овој књизи су укључени у пакет gtkmm-documentation, са одговарајућим датотекама за изградњу, тако да убудуће нећемо приказивати наредбе за изградњу. Датотека `README` у gtkmm-documentation описује како се граде примери.

Да бисмо поједноставили компајлирање, користимо `pkg-config`, који је присутан у свим (правилно инсталираним) gtkmm инсталацијама. Овај програм „зна“ који су прекидачи преводиоца потребни за компајлирање програма који користе gtkmm. Опција `--cflags` узрокује да `pkg-config` испише списак директоријума за укључивање које преводилац треба да претражи; опција `--libs` захтева списак библиотека са којима преводилац треба да се повеже и директоријуме у којима се оне налазе. Пробајте да га покренете из вашег упита шкољке да бисте видели резултате на вашем системе.

Међутим, ово је још једноставније када се користи функција `dependency()` у `meson.build` датотеци са Мезоном. На пример:

    gtkmm_dep = dependency('gtkmm-4.0', version: '>= 4.6.0')

Ово проверава присуство gtkmm-а и дефинише `gtkmm_dep` за употребу у вашим `meson.build` датотекама. На пример:

    exe_file = executable('my_program', 'my_source1.cc', 'my_source2.cc',
      dependencies: gtkmm_dep,
      win_subsystem: 'windows',
    )

gtkmm-4.0 је назив тренутног стабилног АПИ-ја. Постоје старији АПИ-ји под називом gtkmm-2.4 и gtkmm-3.0 који се инсталирају паралелно када су доступни. Постоји неколико верзија gtkmm-2.4, као што је gtkmm 2.10, а постоји и неколико верзија gtkmm-3.0 АПИ-ја. Имајте на уму да се назив АПИ-ја не мења за сваку верзију јер би то био некомпатибилан прекид АПИ-ја и АБИ-ја. Можда ће постојати будући gtkmm-5.0 АПИ који би се инсталирао паралелно са gtkmm-4.0 без утицаја на постојеће програме.

Ако почнете експериментисањем са малим програмом који планирате да користите само за себе, лакше је почети са `meson.build` датотеком сличном `meson.build` датотекама у поглављу Изградња програма.

Ако користите старији Autotools систем за изградњу, погледајте и ГНУ веб сајт. Тамо има више информација о autoconf-у и automake-у. Постоје и неке књиге које описују Autotools: „GNU Autoconf, Automake, and Libtool“ аутора Gary Vaughan-а и осталих и „Autotools, A Practitioner's Guide to GNU Autoconf, Automake, and Libtool“ аутора John Calcote-а.

### Елементи

gtkmm програми се састоје од прозора који садрже елементе, као што су дугмад и поља за текст. У неким другим системима, елементи се називају „контроле“. За сваки елемент у прозорима вашег програма, постоји C++ објекат у коду вашег програма. Дакле, само треба да позовете метод класе елемента да бисте утицали на видљиви елемент.

Елементи су распоређени унутар елемената контејнера као што су оквири и бележнице, у хијерархији елемената унутар елемената. Неки од ових елемената контејнера, као што је Gtk::Grid, нису видљиви — они постоје само да би распоредили друге елементе. Ево неког примера кода који додаје 2 Gtk::Button елемента у Gtk::Box елемент контејнера:

    m_box.append(m_Button1);
    m_box.append(m_Button2);

а ево како да додате Gtk::Box, који садржи ту дугмад, у Gtk::Frame, који има видљив оквир и наслов:

    m_frame.set_child(m_box);

Већина поглавља у овој књизи се бави одређеним елементима. Погледајте одељак Елементи садржаоца за више детаља о додавању елемената у елементе садржаоце.

Иако можете одредити распоред и изглед прозора и елемената помоћу C++ кода, вероватно ће вам бити згодније да дизајнирате своја корисничка прочеља помоћу `.ui` XML датотека и да их учитате у време извршавања помоћу Gtk::Builder-а. Погледајте поглавље Gtk::Builder.

Иако примерци gtkmm елемената имају животни век и опсеге баш као и они код других C++ класа, gtkmm има необавезну функцију за уштеду времена коју ћете видети у неким од примера. `Gtk::make_managed()` вам омогућава да направите нови елемент и наведете да ће постати власништво садржаоца у који га поставите. Ово вам омогућава да направите елемент, додате га у садржаоца и да не бринете о његовом брисању, јер ће се то догодити када се матични садржалац (који и сам може бити управљан) обрише. Можете сазнати више о gtkmm техникама управљања меморијом у поглављу о управљању меморијом.

### Сигнали

gtkmm, као и већина ГКС алата, заснован је на догађајима. Када се догоди неки догађај, као што је притисак на дугме миша, одговарајући сигнал ће бити емитован од стране елемента који је притиснут. Сваки елемент има различит скуп сигнала које може емитовати. Да би клик на дугме резултирао радњом, постављамо руковаоца сигналом да ухвати сигнал „clicked“ (кликнуто) дугмета.

gtkmm користи библиотеку libsigc++ за имплементацију сигнала. Ево примера линије кода која повезује сигнал „clicked“ (кликнуто) Gtk::Button-а са руковаоцем сигнала који се зове „on_button_clicked“:

    m_button1.signal_clicked().connect( sigc::mem_fun(*this,
      &HelloWorld::on_button_clicked) );

За детаљније информације о сигналима, погледајте додатак.

За информације о имплементацији сопствених сигнала уместо самог повезивања на постојеће gtkmm сигнале, погледајте додатак.

### Glib::ustring

Можда ћете се изненадити када сазнате да gtkmm не користи std::string у својим прочељима. Уместо тога користи Glib::ustring, који је толико сличан и ненаметљив да бисте заправо могли да се претварате да је сваки Glib::ustring заправо std::string и да занемарите остатак овог одељка. Али наставите са читањем ако желите да користите језике који нису енглески у свом програму.

std::string користи 8 битова по знаку, али 8 битова није довољно за кодирање језика као што су арапски, кинески и јапански. Иако је кодирања за ове језике одредио Unicode конзорцијум, језици C и C++ још увек не пружају никакву стандардизовану подршку за Уникод за UTF-8 кодирање. ГТК и Гном су изабрали да имплементирају Уникод користећи UTF-8, и то је оно што је омотано помоћу Glib::ustring. Он пружа скоро потпуно исто прочеље као std::string, заједно са аутоматским претварањем у и из std::string.

Једна од предности UTF-8 је та што не морате да га користите осим ако то не желите, тако да не морате одједном да преправљате сав свој код. std::string ће и даље радити за 7-битне ASCII ниске. Али када покушате да локализујете свој програм за језике као што је кинески, на пример, почећете да виђате чудне грешке и могућа рушења. Тада је све што треба да урадите да почнете да користите Glib::ustring уместо њега.

Имајте на уму да UTF-8 није сагласан са 8-битним кодирањима као што је ISO-8859-1. На пример, немачки умлаути нису у ASCII опсегу и захтевају више од 1 бајта у UTF-8 кодирању. Ако ваш код садржи 8-битне литерале ниски, морате их претворити у UTF-8 (нпр. баварски поздрав „Grüß Gott“ би био „Gr\xC3\xBC\xC3\x9F Gott“).

Требало би да избегавате аритметику показивача у C-стилу и функције као што је strlen(). У UTF-8, сваки знак може захтевати од 1 до 6 бајтова, тако да није могуће претпоставити да је следећи бајт други знак. Glib::ustring брине о детаљима овога уместо вас, тако да можете користити методе као што је Glib::ustring::substr() док и даље размишљате у смислу знакова уместо бајтова.

За разлику од Windows UCS-2 Уникод решења, ово не захтева никакве посебне опције преводиоца за обраду литерала ниски, и не резултира Уникод извршним датотекама и библиотекама које су несагласне са ASCII датотекама.

Референца

Погледајте одељак Интернационализација за информације о обезбеђивању UTF-8 литерала ниски.

### Мешање Ц и Ц++ API-ја

Можете користити Ц API-је који још увек немају згодна Ц++ прочеља. Обично није проблем користити Ц API-је из Ц++-а, а gtkmm помаже пружањем приступа основном Ц објекту и пружањем лаког начина за стварање Ц++ објекта омотача из Ц објекта, под условом да је Ц API такође заснован на систему GObject.

Да бисте користили gtkmm инстанцу са Ц функцијом која захтева Ц GObject инстанцу, користите функцију `gobj()` Ц++ инстанце да бисте добили показивач на основну Ц инстанцу. На пример:

    Gtk::Button button("example");
    gtk_button_do_something_that_gtkmm_cannot(button.gobj());

Да бисте добили gtkmm инстанцу из Ц GObject инстанце, користите једну од многих преоптерећених `Glib::wrap()` функција. Број референци Ц инстанце се не увећава, осим ако не поставите опциони аргумент take_copy на `true`. На пример:

    GtkButton* cbutton = get_a_button();
    Gtk::Button* button = Glib::wrap(cbutton);
    button->set_label("Now I speak C++ too!");

Ц++ омотач ће бити експлицитно обрисан ако

- то је елемент или друга класа која наслеђује из Gtk::Object, и

- Ц инстанца има плутајућу референцу када се омотач створи, и

- `Gtk::manage()` није позвано над њом (што укључује и ако је створена помоћу `Gtk::make_managed()`), или

- `Gtk::manage()` је позвано над њом, али никада није додато родитељу.

`Glib::wrap()` везује Ц и Ц++ инстанце једну за другу. Немојте брисати Ц++ инстанцу пре него што желите да Ц инстанца нестане.

У свим осталим случајевима Ц++ инстанца се аутоматски брише када се одбаци последња референца на Ц инстанцу. Ово укључује сва преоптерећења `Glib::wrap()` која враћају Glib::RefPtr.

### „Здраво свете“ у gtkmm-у

Сада смо научили довољно да погледамо прави пример. У складу са древном традицијом рачунарства, сада представљамо „Здраво свете“, на gtkmm начин:

Изворни ко̂д

File: `helloworld.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_HELLOWORLD_H
    #define GTKMM_EXAMPLE_HELLOWORLD_H

    #include <gtkmm/button.h>
    #include <gtkmm/window.h>

    class HelloWorld : public Gtk::Window
    {

    public:
      HelloWorld();
      ~HelloWorld() override;

    protected:
      //Signal handlers:
      void on_button_clicked();

      //Member widgets:
      Gtk::Button m_button;
    };

    #endif // GTKMM_EXAMPLE_HELLOWORLD_H

File: `helloworld.cc` (For use with gtkmm 4)

    #include "helloworld.h"
    #include <iostream>

    HelloWorld::HelloWorld()
    : m_button("Hello World")   // creates a new button with label "Hello World".
    {
      // Sets the margin around the button.
      m_button.set_margin(10);

      // When the button receives the "clicked" signal, it will call the
      // on_button_clicked() method defined below.
      m_button.signal_clicked().connect(sigc::mem_fun(*this,
                  &HelloWorld::on_button_clicked));

      // This packs the button into the Window (a container).
      set_child(m_button);
    }

    HelloWorld::~HelloWorld()
    {
    }

    void HelloWorld::on_button_clicked()
    {
      std::cout << "Hello World" << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "helloworld.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<HelloWorld>(argc, argv);
    }

Пробајте да га компајлирате и покренете пре него што наставите. Требало би да видите нешто овако:

Здраво свете

Прилично узбудљиво, зар не? Хајде да испитамо ко̂д. Прво, класа HelloWorld:

    class HelloWorld : public Gtk::Window
    {
    public:
      HelloWorld();
      ~HelloWorld() override;

    protected:
      //Signal handlers:
      void on_button_clicked();

      //Member widgets:
      Gtk::Button m_button;
    };

Овај разред имплементира прозор „Здраво свете“. Изведен је из Gtk::Window и има једно Gtk::Button као члан. Одабрали смо да користимо конструктор за обављање свих послова иницијализације прозора, укључујући подешавање сигнала. Ево га, са изостављеним коментарима:

    HelloWorld::HelloWorld()
    : m_button("Hello World")
    {
      m_button.set_margin(10);
      m_button.signal_clicked().connect(sigc::mem_fun(*this,
        &HelloWorld::on_button_clicked));
      set_child(m_button);
    }

Приметите да смо користили наредбу за иницијализацију да бисмо објекту `m_button` дали натпис „Hello World“.

Затим позивамо метод дугмета set_margin(). Ово поставља количину простора око дугмета.

Потом повезујемо руковалац сигнала са сигналом `clicked` објекта `m_button`. Ово исписује наш пријатељски поздрав на `stdout`.

Затим користимо метод прозора set_child() да поставимо `m_button` у прозор. Метод set_child() смешта елемент у прозор.

Сада погледајмо функцију `main()` нашег програма. Ево је, без коментарима:

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");
      return app->make_window_and_run<HelloWorld>(argc, argv);
    }

Прво стварамо примерак објекта сачуваног у паметном показивачу Glib::RefPtr под називом `app`. Он је типа Gtk::Application. Сваки gtkmm програм мора имати један овакав.

Затим позивамо make_window_and_run() који ствара објекат нашег разреда HelloWorld, приказује тај прозор и покреће gtkmm петљу догађаја. Током петље догађаја gtkmm мирује, чекајући акције корисника и одговарајући на одговарајући начин. Када корисник затвори прозор, make_window_and_run() ће се вратити, узрокујући да се наша функција `main()` заврши. Програм ће се тада завршити.

Попут једноставног примера који смо раније показали, овај програм „Здраво свете“ не користи параметре командне линије.

## Промене у gtkmm 3

gtkmm-3.0 је стара верзија gtkmm АПИ-ја која се инсталира паралелно са још старијим gtkmm-2.4 АПИ-јем и новим gtkmm-4.0 АПИ-јем. Последња верзија gtkmm-2.4 АПИ-ја била је gtkmm 2.24. gtkmm 3 нема великих суштинских разлика у односу на gtkmm 2, али ипак доноси неколико малих измена које нису биле могуће уз одржавање бинарне компатибилности. Ако никада нисте користили gtkmm-2.4 АПИ, можете слободно занемарити ово поглавље.

Библиотека gtkmm 3 се зове `libgtkmm-3.0` уместо `libgtkmm-2.4` и инсталира своја заглавља у директоријум са сличном верзијом, тако да ваша pkg-config провера треба да тражи `gtkmm-3.0` уместо `gtkmm-2.4`.

gtkmm 3 је додао неке нове разреде:

1. Gtk::AppChooser, Gtk::AppChooserButton, Gtk::AppChooserDialog омогућавају кориснику да изабере инсталирани програм за отварање одређене врсте садржаја.

2. Gtk::Grid је нови елемент контејнера који ће на крају заменити Gtk::Box и Gtk::Table. Он распоређује своје садржане елементе према особинама тих елемената, а не према сопственим детаљима распореда.

3. Gtk::Switch приказује стања Укључено/Искључено јасније од Gtk::CheckButton. Може бити користан, на пример, када се корисницима омогућава активирање хардвера.

gtkmm 3 је такође направио неколико малих измена у АПИ-ју, на које ћете вероватно наићи приликом преношења кода који је користио gtkmm-2.4. Ево кратког списка:

1. Gtk::CellLayout, који користе Gtk::IconView, Gtk::TreeView::Column и Gtk::ComboBox, сада има Gtk::CellArea који се може користити за навођење више детаља о томе како су CellRenderer-и распоређени и поравнати.

2. Gtk::ComboBox сада изводи из CellLayout-а, омогућавајући лакши распоред и поравнање његових Gtk::CellRenderer-а.

3. Gtk::Adjustment, IconSet и Gdk::Cursor се сада користе путем Glib::RefPtr-а.

4. Gtk::Box, Gtk::ButtonBox, Gtk::IconView, Gtk::Paned, Gtk::ProgressBar, Gtk::ScaleButton, Gtk::Scrollbar и Gtk::Separator сада изводе из Gtk::Orientable-а, омогућавајући да се њихово усмерење (усправно или водоравно) наведе без захтевања коришћења изведене класе као што је Gtk::HBox.

5. Gtk::IconView, Gtk::TextView, Gtk::TreeView и други елементи изводе из Scrollable-а уместо да имају сопствене методе као што је get_vadjustment() и уместо да имају сопствени сигнал set_scroll_adjustments.

6. Gtk::Style и Gtk::Rc су уклоњени, замењени са Gtk::StyleContext и Gtk::StyleProvider-има, као што је Gtk::CssProvider.

7. Widget::on_expose_event() је замењен са Widget::on_draw(), који подразумева да се cairomm користи за цртање, путем достављеног Cairo::Context-а и не захтева од вас да позовете Cairo::Context::clip().

8. Gdk::RGBA замењује Color, додајући алфа компоненту за непрозирност. Colormap је уклоњен, заједно са његовим незгодним коришћењем за додељивање боја.

9. Gdk::Pixmap и Gdk::Bitmap су уклоњени у корист Gdk::Pixbuf-а.

10. Gdk::Drawable је уклоњен, а његове методе су премештене у Gdk::Window.

11. Сада користимо std::vector у неколико метода уместо посредних *Handle типова како бисмо API учинили јаснијим.

Сав застарели API је уклоњен у gtkmm-у 3.0, иако је било нових застарелости у каснијим gtkmm 3.x издањима.

Као први корак ка пребацивању вашег изворног кода на gtkmm-3.0 вероватно би требало да се уверите да се ваш програм изграђује са онемогућеним застарелим gtkmm-2.4 API-јем, дефинисањем макроа као што је GTKMM_DISABLE_DEPRECATED. Постоје неки autotools макрои који могу помоћи у томе њиховим опционим дефинисањем у време изградње. Погледајте вики страницу о пребацивању на gtkmm 3 за више детаља.

## Измене у gtkmm-у 4.0 и glibmm-2.68-у

gtkmm-4.0 је ново издање gtkmm API-ја које се инсталира паралелно са старијим gtkmm-2.4 и gtkmm-3.0 API-јима. Последње издање gtkmm-3.0 API-ја је gtkmm 3.24. gtkmm 4 нема великих суштинских разлика у односу на gtkmm 3, али ипак доноси неколико измена (малих и великих) које нису биле могуће уз одржавање бинарне усклађености. Ако никада нисте користили gtkmm-3.0 API, онда можете безбедно занемарити ово поглавље.

Библиотека gtkmm-а 4 се зове `libgtkmm-4.0` уместо `libgtkmm-3.0` и инсталира своја заглавља у директоријум са сличном верзијом, тако да би ваша pkg-config провера требало да тражи `gtkmm-4.0` уместо `gtkmm-3.0`.

gtkmm-4.0 се користи у комбинацији са glibmm-2.68-ом, који поставља општу локализацију за ваш програм. Старији glibmm-2.4 то не ради, а gtkmm-3.0 то ради само у одређеној мери. Ово укратко значи да ако ваш gtkmm-3.0 програм садржи позив `std::locale::global(std::locale(""))`, вероватно га можете уклонити. Ако не желите да glibmm или gtkmm постављају општу локализацију уместо вас, требало би да додате позив `Glib::set_init_to_users_preferred_locale(false)` пре било ког позива `Glib::init()` или Gtk::Application::create(). Погледајте glibmm референцу.

Постоји веома много разлика између gtkmm-а 3.0 и gtkmm-а 4.0. Следећи спискови нису потпуни.

Постоје неке важне промене у понашању, којима се морате прилагодити приликом преласка:

- Док је у gtkmm-у 3.0 уништавање неуправљаног C++ омотача елемента узроковало уништавање обавијеног GtkWidget-а, измене у GTK4 значе да у gtkmm-у 4.0 то не може увек бити случај. GTK4 нема једнообразан начин за уклањање елемента из родитеља, као што је GTK3 радио са Gtk::Container::remove(), тако да gtkmm-4.0 не може да изврши уклањање уместо вас као што је то радио gtkmm-3.0. Стога, ако се примерак неуправљаног C++ елемента уништи док је елемент дете другог елемента, gtkmm-4.0 га неће уклонити из родитеља, родитељ задржава референцу која спречава одбацивање детета, а GtkWidget остаје жив. Крајњи резултат је да бисте уништили неуправљани елемент са његовим C++ омотачем, морате га прво уклонити из његовог родитеља (како год тај родитељ дозвољава) како би деструктор C++ омотача могао да отпусти последњу референцу.

Неке нове класе су додате у gtkmm 4 и glibmm 2.68:

1. Glib::ExtraClassInit и Gtk::Snapshot: Ове класе су потребне само за писање прилагођених виџета. Погледајте одељак Прилагођени виџети.

2. Gtk::EventControllerKey, Gtk::EventControllerMotion, Gtk::EventControllerScroll и Gtk::GestureStylus

3. Gdk::Paintable, Gdk::Texture, Gtk::Picture и Gtk::WidgetPaintable

4. Gdk::Window је преименован у Gdk::Surface. (Gtk::Window задржава своје име.)

5. Gdk::DrawContext и Gdk::CairoContext су нови. Gdk::DrawingContext је уклоњен.

6. Gtk::Clipboard је замењен новим Gdk::Clipboard.

7. Gdk::DragContext је подељен на Gdk::Drag и Gdk::Drop.

Такође је дошло до неколико измена у API-ју, на које ћете вероватно наићи приликом преношења кода који је користио gtkmm-3.0 и glibmm-2.4. Ево кратког списка:

1. Потребан је C++17 компајлер.

2. Gtk::Button, Gtk::ToolButton, Gtk::MenuItem и Gtk::Switch имплементирају интерфејс Gtk::Actionable уместо уклоњеног интерфејса Gtk::Activatable.

3. Gtk::FontButton имплементира интерфејс Gtk::FontChooser.

4. Gtk::Widget: get_preferred_*_vfunc() су замењени са measure_vfunc(). Ова измена утиче само на прилагођене виџете.

5. sigc::slot користи синтаксу sigc::slot<R(Args...)>. Пример: sigc::slot<void(int, int)> уместо sigc::slot<void, int, int>.

6. Gtk::DrawingArea користи функцију цртања уместо сигнала цртања.

7. Glib::ArrayHandle, Glib::StringArrayHandle, Glib::ListHandle и Glib::SListHandle су уклоњени. Коришћени су у glibmm-2.4, али не и у gtkmm-3.0. Ако сте икада користили ове класе, замените их стандардним C++ контејнером, као што је std::vector.

8. Gtk::Container је уклоњен.

9. Gtk::Widget::show_all() је уклоњен. Подразумевана вредност Gtk::Widget::property_visible() је промењена из `false` у `true`.

10. Сви сигнали догађаја су уклоњени из Gtk::Widget. У већини случајева можете користити једну од подкласа Gtk::EventController као замену. На пример, користите Gtk::GestureMultiPress уместо signal_button_press_event() и signal_button_release_event(), и Gtk::EventControllerKey уместо signal_key_press_event() и signal_key_release_event().

11. Glib::RefPtr је алијас за std::shared_ptr. Ако правите сопствене класе изведене из Glib::ObjectBase са create() методама које враћају Glib::RefPtr, морате користити Glib::make_refptr_for_instance() у својим create() методама.

12. Gtk::Box::pack_start() и Gtk::Box::pack_end() су уклоњени. Користите нове Gtk::Box методе append(), prepend(), insert_child_after() и insert_child_at_start().

13. Gtk::ButtonBox је уклоњен.

14. Gtk::RadioButton и Gtk::RadioButtonGroup су уклоњени. Користите Gtk::CheckButton или Gtk::ToggleButton са set_group().

Сав застарели АПИ је уклоњен у gtkmm 4.0 и glibmm 2.68, иако ће бити нових застарелости у будућим верзијама.

Као први корак у пребацивању вашег изворног кода на gtkmm-4.0 вероватно би требало да се уверите да се ваша апликација изграђује са онемогућеним застарелим gtkmm-3.0 и glibmm-2.4 АПИ-јем, дефинисањем макроа GTKMM_DISABLE_DEPRECATED, GDKMM_DISABLE_DEPRECATED, GLIBMM_DISABLE_DEPRECATED и GIOMM_DISABLE_DEPRECATED. Постоје неки autotools макрои који могу помоћи у томе њиховим опционалним дефинисањем у време изградње. Погледајте вики страницу Porting from gtkmm-2.4 to gtkmm-3.0 за више детаља.

Погледајте такође Migrating from GTK 3.x to GTK 4.

### Застарелости у gtkmm 4.10

Многе класе су застареле од верзије gtkmm 4.10. Оне се и даље могу користити у gtkmm4 апликацијама, под условом да GTKMM_DISABLE_DEPRECATED и GDKMM_DISABLE_DEPRECATED нису дефинисани. Такође постоје многе нове класе у gtkmm 4.10, које замењују неке од застарелих класа. Неки примери програма у овом водичу користе класе застареле од верзије gtkmm 4.10. Неки други програми користе класе доступне од верзије gtkmm 4.10.

Застареле класе: AppChooser, AppChooserButton, AppChooserDialog, AppChooserWidget, CellArea, CellAreaBox, CellAreaContext, CellLayout, CellRenderer, CellRendererAccel, CellRendererCombo, CellRendererPixbuf, CellRendererProgress, CellRendererSpin, CellRendererSpinner, CellRendererText, CellRendererToggle, CellView, ComboBox, ComboBoxText, EntryCompletion, IconView, ListStore, ListViewText, StyleContext, TreeDragDest, TreeDragSource, TreeIter и друге класе у treeiter.h, TreeModel, TreeModelFilter, TreeModelSort, TreePath, TreeRowReference, TreeSelection, TreeSortable, TreeStore, TreeView, TreeViewColumn, именски простор CellRenderer_Generation, именски простор TreeView_Private, ColorButton, ColorChooser, ColorChooserDialog, FileChooser, FileChooserDialog, FileChooserNative, FileChooserWidget, FontButton, FontChooser, FontChooserDialog, FontChooserWidget, MessageDialog, TreeModelColumn, TreeModelColumnRecord, InfoBar, Assistant, AssistantPage, LockButton, Statusbar, VolumeButton.

Нове класе и набрајања: AlertDialog, ColorDialog, ColorDialogButton, ColumnViewSorter, FileDialog, FontDialog, FontDialogButton, FileLauncher, UriLauncher, ATContext, набрајања DialogError, FontLevel, Collation.

У већини случајева постоје замене за застареле класе. Погледајте референтну документацију.

## Дугмад

gtkmm обезбеђује четири основне врсте дугмади:

Обична дугмад

  Gtk::Button. Стандардна дугмад, обично означена натписом или сликом. Притисак на једно покреће радњу. Погледајте одељак Дугме.

Прекидачка дугмад

  Gtk::ToggleButton. За разлику од обичног дугмета, које се враћа назад, прекидачко дугме остаје притиснуто док га поново не притиснете. Може бити корисно као прекидач за укључивање/искључивање. Погледајте одељак Прекидачко дугме.

Дугмад за потврду

  Gtk::CheckButton. Она се понашају као прекидачка дугмад, али приказују своје стање у малим квадратима, са натписом са стране. Требало би их користити у већини ситуација које захтевају поставку укључено/искључено. Погледајте одељак Дугме за потврду.

Радио дугмад

  Названа по бирачима станица на старим ауто-радио апаратима, ова дугмад се користе у групама за опције које се међусобно искључују. Притисак на једно доводи до тога да се сва остала у његовој групи искључе. Слична су прекидачким дугмадима или дугмадима за потврду (мали елемент са натписом са стране), али обично изгледају другачије. Не постоји посебна класа радио дугмета. Дугмад за потврду и прекидачка дугмад могу деловати као радио дугмад. Погледајте одељак Радио дугме.

Имајте на уму да ће, због ГТК-овог система тема, изглед ових елемената варирати. У случају дугмади за потврду и радио дугмади, они могу значајно варирати.

### Дугме

Постоје два начина за прављење дугмета. Можете навести ниску натписа у Gtk::Button конструктору, или је поставити касније помоћу set_label().

Да бисте дефинисали тастер пречице за навигацију тастатуром, поставите доњу црту испред једног од знакова натписа и наведите `true` за опциони `mnemonic` параметар. На пример:

    Gtk::Button* pButton = new Gtk::Button("_Something", true);

Gtk::Button је такође и контејнер, тако да у њега можете ставити било који други елемент, као што је Gtk::Image.

Елемент Gtk::Button има сигнал `clicked` који се емитује када се дугме притисне и отпусти.

Референца

#### Пример

Овај пример прави дугме са сликом и натписом.

пример дугмића

Изворни код

File: `buttons.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_BUTTONS_H
    #define GTKMM_EXAMPLE_BUTTONS_H

    #include <gtkmm/window.h>
    #include <gtkmm/button.h>

    class Buttons : public Gtk::Window
    {
    public:
      Buttons();
      virtual ~Buttons();

    protected:
      //Signal handlers:
      void on_button_clicked();

      //Child widgets:
      Gtk::Button m_button;
    };

    #endif //GTKMM_EXAMPLE_BUTTONS_H

File: `buttons.cc` (For use with gtkmm 4)

    #include "buttons.h"
    #include <gtkmm/box.h>
    #include <gtkmm/image.h>
    #include <gtkmm/label.h>
    #include <iostream>

    Buttons::Buttons()
    {
      // This corresponds to Gtk::Bin::add_pixlabel("info.xpm", "cool button") in gtkmm3.
      //Create Image and Label widgets:
      auto pmap = Gtk::make_managed<Gtk::Image>("info.xpm");
      auto label = Gtk::make_managed<Gtk::Label>("cool button");
      label->set_expand(true);

      //Put them in a Box:
      auto hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 5);
      hbox->append(*pmap);
      hbox->append(*label);

      //And put that Box in the button:
      m_button.set_child(*hbox);

      set_title("Pixmap'd buttons!");

      m_button.signal_clicked().connect( sigc::mem_fun(*this,
                  &Buttons::on_button_clicked) );

      m_button.set_margin(10);
      set_child(m_button);
    }

    Buttons::~Buttons()
    {
    }

    void Buttons::on_button_clicked()
    {
      std::cout << "The Button was clicked." << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "buttons.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<Buttons>(argc, argv);
    }

### Дугме за пребацивање

ToggleButton дугмад су као обична Button дугмад, али када се кликне на њих, она остају активирана, или притиснута, док се поново не кликне.

Да бисте добили стање ToggleButton дугмета, можете користити метод get_active(). Он враћа `true` ако је дугме „доле“. Такође можете поставити стање дугмета за пребацивање помоћу set_active(). Имајте на уму да, ако то урадите и стање се заиста промени, то узрокује емитовање сигнала „clicked“. Ово је обично оно што желите.

Можете користити метод toggled() да пребаците стање дугмета, уместо да га присиљавате да буде горе или доле: ово мења стање дугмета и узрокује емитовање сигнала `toggled`.

Референца

### Дугме за штиклирање

Gtk::CheckButton наслеђује директно из Gtk::Widget. Слично је Gtk::ToggleButton дугмету. Једина стварна разлика између њих је изглед Gtk::CheckButton дугмета. Можете штиклирати и поставити дугме за штиклирање користећи исте методе чланове као и за Gtk::ToggleButton.

Референца

#### Пример

Дугме за штиклирање

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_BUTTONS_H
    #define GTKMM_EXAMPLE_BUTTONS_H

    #include <gtkmm/window.h>
    #include <gtkmm/checkbutton.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_toggled();

      //Child widgets:
      Gtk::CheckButton m_button;
    };

    #endif //GTKMM_EXAMPLE_BUTTONS_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_button("something")
    {
      set_title("checkbutton example");

      m_button.signal_toggled().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_toggled) );

      m_button.set_margin(10);
      set_child(m_button);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_toggled()
    {
      std::cout << "The Button was toggled: state="
          << (m_button.get_active() ? "true" : "false")
          << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Радио дугме

Не постоји посебна класа за радио дугмад. Дугмад за штиклирање и дугмад за пребацивање се понашају као радио дугмад када формирају групу. Само једно дугме у групи може бити изабрано у било ком тренутку.

#### Групе

Прво направите дугмад, а затим подесите њихову групу. У следећем примеру, ставићемо 3 радио дугмета у групу:

    auto rb1 = Gtk::make_managed<Gtk::CheckButton>("button1");
    auto rb2 = Gtk::make_managed<Gtk::CheckButton>("button2");
    auto rb3 = Gtk::make_managed<Gtk::CheckButton>("button3");
    rb2->set_group(*rb1);
    rb3->set_group(*rb1);

Рекли смо gtkmm-у да стави сва три CheckButton-а у исту групу коришћењем методе set_group() како бисмо рекли осталим CheckButton-има да деле групу са првим CheckButton-ом.

#### Методе

CheckButton и ToggleButton дугмад су „искључена“ када се направе; то значи да ће сви бити искључени када први пут направите групу од њих. Не заборавите да укључите једно од њих користећи методу set_active().

Референца

#### Пример

Следећи пример демонстрира употребу груписаних CheckButton дугмади:

Радио дугме

Изворни ко̂д

File: `radiobuttons.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_RADIOBUTTONS_H
    #define GTKMM_EXAMPLE_RADIOBUTTONS_H

    #include <gtkmm/box.h>
    #include <gtkmm/window.h>
    #include <gtkmm/button.h>
    #include <gtkmm/checkbutton.h>
    #include <gtkmm/separator.h>

    class RadioButtons : public Gtk::Window
    {
    public:
      RadioButtons();
      virtual ~RadioButtons();

    protected:
      //Signal handlers:
      void on_button_clicked();

      //Child widgets:
      Gtk::Box m_Box_Top, m_Box1, m_Box2;
      Gtk::CheckButton m_RadioButton1, m_RadioButton2, m_RadioButton3;
      Gtk::Separator m_Separator;
      Gtk::Button m_Button_Close;
    };

    #endif //GTKMM_EXAMPLE_RADIOBUTTONS_H

File: `main.cc` (For use with gtkmm 4)

    #include "radiobuttons.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<RadioButtons>(argc, argv);
    }

File: `radiobuttons.cc` (For use with gtkmm 4)

    #include "radiobuttons.h"

    RadioButtons::RadioButtons() :
      m_Box_Top(Gtk::Orientation::VERTICAL),
      m_Box1(Gtk::Orientation::VERTICAL, 10),
      m_Box2(Gtk::Orientation::VERTICAL, 10),
      m_RadioButton1("button1"),
      m_RadioButton2("button2"),
      m_RadioButton3("button3"),
      m_Button_Close("close")
    {
      // Set title and border of the window
      set_title("radio buttons");

      // Gtk::CheckButton and Gtk::ToggleButton have set_group() methods.
      // They act as radio buttons, if they are included in a group.

      // Put radio buttons 2 and 3 in the same group as 1:
      m_RadioButton2.set_group(m_RadioButton1);
      m_RadioButton3.set_group(m_RadioButton1);

      // Add outer box to the window (because the window
      // can only contain a single widget)
      set_child(m_Box_Top);

      //Put the inner boxes and the separator in the outer box:
      m_Box_Top.append(m_Box1);
      m_Box_Top.append(m_Separator);
      m_Box_Top.append(m_Box2);
      m_Separator.set_expand();

      // Set the inner boxes' margins
      m_Box1.set_margin(10);
      m_Box2.set_margin(10);

      // Put the radio buttons in Box1:
      m_Box1.append(m_RadioButton1);
      m_Box1.append(m_RadioButton2);
      m_Box1.append(m_RadioButton3);
      m_RadioButton1.set_expand();
      m_RadioButton2.set_expand();
      m_RadioButton3.set_expand();

      // Set the second button active
      m_RadioButton2.set_active(true);

      // Put Close button in Box2:
      m_Box2.append(m_Button_Close);
      m_Button_Close.set_expand();

      // Make the button the default widget
      set_default_widget(m_Button_Close);

      // Connect the toggled signal of the button to
      // RadioButtons::on_button_toggled()
      m_Button_Close.signal_clicked().connect(sigc::mem_fun(*this,
                  &RadioButtons::on_button_clicked) );
    }

    RadioButtons::~RadioButtons()
    {
    }

    void RadioButtons::on_button_clicked()
    {
      close();
    }

## Елементи опсега

Gtk::Scale наслеђује из Gtk::Range. Gtk::Scrollbar не наслеђује из Gtk::Range, али дели многе функционалности са Gtk::Scale. Оба садрже „удубљење“ и „клизач“ (који се понекад назива „точкић“ у другим ГУИ окружењима). Превлачење клизача показивачем помера га унутар удубљења, док кликање у удубљењу помера клизач према месту клика, било потпуно или за одређени износ, у зависности од тога који тастер миша се користи. Ово би требало да буде познато понашање траке клизача.

Као што ће бити објашњено у одељку Поправке, сви елементи опсега су повезани са објектом Adjustment. Да бисте променили доњу, горњу и тренутну вредност коју користи елемент, морате користити методе његовог Adjustment-а, који можете добити методом get_adjustment(). Подразумевани конструктори елемената опсега аутоматски праве Adjustment, или можете навести постојећи Adjustment, можда да бисте га поделили са другим елементом. Погледајте одељку Поправке за више детаља.

Референца

### Елементи траке клизача

Ово су стандардне траке клизача. Требало би их користити само за померање другог елемента, као што су Gtk::Entry или Gtk::Viewport, иако је обично лакше користити елемент Gtk::ScrolledWindow у већини случајева.

Усмерење Gtk::Scrollbar-а може бити или водоравно или усправно.

Референца

### Елементи скале

Gtk::Scale елементи (или „клизачи“) омогућавају кориснику да визуелно изабере и обради вредност унутар одређеног опсега. Можете користити један, на време, за подешавање нивоа увећања на умањеном прегледу слике, или за контролу осветљености боје, или за навођење броја минута неактивности пре него што чувар екрана преузме екран.

Као и код Scrollbar-а, усмерење може бити или водоравно или усправно. Подразумевани конструктор прави Adjustment са свим његовим вредностима постављеним на `0.0`. Ово није корисно, па ћете морати да подесите неке детаље Adjustment-а да бисте добили смислено понашање.

#### Корисне методе

Елементи Scale могу приказати своју тренутну вредност као број поред увале. Подразумевано приказују вредност, али то можете променити методом set_draw_value().

Вредност коју приказује елемент размере је подразумевано заокружена на једну децималу, као и поље `value` у његовом Gtk::Adjustment-у. Ово можете променити методом set_digits().

Такође, вредност може бити исцртана на различитим положајима у односу на увалу, што се наводи методом set_value_pos().

Референце

### Пример

Овај пример приказује прозор са три елемента опсега који су сви повезани на исту поправку, заједно са пар контрола за подешавање неких од горе поменутих параметара и у одељку о поправкама, тако да можете видети како они утичу на начин на који ови елементи раде за корисника.

Елементи опсега

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_RANGEWIDGETS_H
    #define GTKMM_EXAMPLE_RANGEWIDGETS_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_checkbutton_toggled();
      void on_dropdown_position();
      void on_adjustment1_value_changed();
      void on_adjustment2_value_changed();
      void on_button_quit();

      //Child widgets:
      Gtk::Box m_VBox_Top, m_VBox2, m_VBox_HScale;
      Gtk::Box m_HBox_Scales, m_HBox_DropDown, m_HBox_Digits, m_HBox_PageSize;

      Glib::RefPtr<Gtk::Adjustment> m_adjustment, m_adjustment_digits, m_adjustment_pagesize;

      Gtk::Scale m_VScale;
      Gtk::Scale m_HScale, m_Scale_Digits, m_Scale_PageSize;

      Gtk::Separator m_Separator;

      Gtk::CheckButton m_CheckButton;

      Gtk::Scrollbar m_Scrollbar;

      Gtk::DropDown m_DropDown_Position;
      Gtk::Button m_Button_Quit;
    };

    #endif //GTKMM_EXAMPLE_RANGEWIDGETS_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <array>
    #include <iostream>

    namespace
    {
    struct PositionTypeStruct
    {
      Gtk::PositionType position;
      Glib::ustring text;
    };

    const std::array<PositionTypeStruct, 4> positionTypes =
    {
      PositionTypeStruct{Gtk::PositionType::TOP,    "Top"},
      PositionTypeStruct{Gtk::PositionType::BOTTOM, "Bottom"},
      PositionTypeStruct{Gtk::PositionType::LEFT,   "Left"},
      PositionTypeStruct{Gtk::PositionType::RIGHT,  "Right"}
    };

    } // anonymous namespace

    ExampleWindow::ExampleWindow()
    :
      m_VBox_Top(Gtk::Orientation::VERTICAL, 0),
      m_VBox2(Gtk::Orientation::VERTICAL, 20),
      m_VBox_HScale(Gtk::Orientation::VERTICAL, 10),
      m_HBox_Scales(Gtk::Orientation::HORIZONTAL, 10),
      m_HBox_DropDown(Gtk::Orientation::HORIZONTAL, 10),
      m_HBox_Digits(Gtk::Orientation::HORIZONTAL, 10),
      m_HBox_PageSize(Gtk::Orientation::HORIZONTAL, 10),

      // Value, lower, upper, step_increment, page_increment, page_size:
      // Note that the page_size value only makes a difference for
      // scrollbar widgets, and the highest value you'll get is actually
      // (upper - page_size).
      m_adjustment( Gtk::Adjustment::create(0.0, 0.0, 101.0, 0.1, 1.0, 1.0) ),
      m_adjustment_digits( Gtk::Adjustment::create(1.0, 0.0, 5.0, 1.0, 2.0) ),
      m_adjustment_pagesize( Gtk::Adjustment::create(1.0, 1.0, 101.0) ),

      m_VScale(m_adjustment, Gtk::Orientation::VERTICAL),
      m_HScale(m_adjustment, Gtk::Orientation::HORIZONTAL),
      m_Scale_Digits(m_adjustment_digits),
      m_Scale_PageSize(m_adjustment_pagesize),

      // A checkbutton to control whether the value is displayed or not:
      m_CheckButton("Display value on scale widgets", 0),

      // Reuse the same adjustment again.
      // Notice how this causes the scales to always be updated
      // continuously when the scrollbar is moved.
      m_Scrollbar(m_adjustment),

      m_Button_Quit("Quit")
    {
      set_title("range controls");
      set_default_size(300, 350);

      //VScale:
      m_VScale.set_digits(1);
      m_VScale.set_value_pos(Gtk::PositionType::TOP);
      m_VScale.set_draw_value();
      m_VScale.set_inverted(); // highest value at top

      //HScale:
      m_HScale.set_digits(1);
      m_HScale.set_value_pos(Gtk::PositionType::TOP);
      m_HScale.set_draw_value();

      set_child(m_VBox_Top);
      m_VBox_Top.append(m_VBox2);
      m_VBox2.set_expand(true);
      m_VBox2.set_margin(10);
      m_VBox2.append(m_HBox_Scales);
      m_HBox_Scales.set_expand(true);

      //Put VScale and HScale (above scrollbar) side-by-side.
      m_HBox_Scales.append(m_VScale);
      m_VScale.set_expand(true);
      m_HBox_Scales.append(m_VBox_HScale);
      m_VBox_HScale.set_expand(true);

      m_VBox_HScale.append(m_HScale);
      m_HScale.set_expand(true);

      //Scrollbar:
      m_VBox_HScale.append(m_Scrollbar);
      m_Scrollbar.set_expand(true);

      //CheckButton:
      m_CheckButton.set_active();
      m_CheckButton.signal_toggled().connect( sigc::mem_fun(*this,
        &ExampleWindow::on_checkbutton_toggled) );
      m_VBox2.append(m_CheckButton);

      //Position DropDown:
      //Create the StringList:
      auto string_list = Gtk::StringList::create({});
      m_DropDown_Position.set_model(string_list);

      // Fill the DropDown's list model:
      for (const auto& positionType : positionTypes)
        string_list->append(positionType.text);

      m_VBox2.append(m_HBox_DropDown);
      m_HBox_DropDown.append(*Gtk::make_managed<Gtk::Label>("Scale Value Position:", 0));
      m_HBox_DropDown.append(m_DropDown_Position);
      m_DropDown_Position.property_selected().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_dropdown_position));
      m_DropDown_Position.set_selected(0); // Top
      m_DropDown_Position.set_expand(true);

      //Digits:
      m_HBox_Digits.append(*Gtk::make_managed<Gtk::Label>("Scale Digits:", 0));
      m_Scale_Digits.set_digits(0);
      m_Scale_Digits.set_expand(true);
      m_adjustment_digits->signal_value_changed().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_adjustment1_value_changed));
      m_HBox_Digits.append(m_Scale_Digits);

      //Page Size:
      m_HBox_PageSize.append(*Gtk::make_managed<Gtk::Label>("Scrollbar Page Size:", 0));
      m_Scale_PageSize.set_digits(0);
      m_Scale_PageSize.set_expand(true);
      m_adjustment_pagesize->signal_value_changed().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_adjustment2_value_changed));
      m_HBox_PageSize.append(m_Scale_PageSize);

      m_VBox2.append(m_HBox_Digits);
      m_VBox2.append(m_HBox_PageSize);
      m_VBox_Top.append(m_Separator);
      m_VBox_Top.append(m_Button_Quit);

      set_default_widget(m_Button_Quit);
      m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_button_quit));
      m_Button_Quit.set_margin(10);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_checkbutton_toggled()
    {
      m_VScale.set_draw_value(m_CheckButton.get_active());
      m_HScale.set_draw_value(m_CheckButton.get_active());
    }

    void ExampleWindow::on_dropdown_position()
    {
      const auto selected = m_DropDown_Position.get_selected();
      if (selected == GTK_INVALID_LIST_POSITION)
        return;

      const auto postype = positionTypes[selected].position;
      m_VScale.set_value_pos(postype);
      m_HScale.set_value_pos(postype);
    }

    void ExampleWindow::on_adjustment1_value_changed()
    {
      const double val = m_adjustment_digits->get_value();
      m_VScale.set_digits((int)val);
      m_HScale.set_digits((int)val);
    }

    void ExampleWindow::on_adjustment2_value_changed()
    {
      const double val = m_adjustment_pagesize->get_value();
      m_adjustment->set_page_size(val);
      m_adjustment->set_page_increment(val);

      // Note that we don't have to emit the "changed" signal
      // because gtkmm does this for us.
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

## Разни елементи

### Ознака

Натписи су главни начин постављања неизмењивог текста у прозоре, на пример за постављање наслова поред елемента Entry. Текст можете навести у конструктору, или касније методама set_text() или set_markup().

Ширина натписа ће бити аутоматски прилагођена. Вишелинијске натписе можете направити стављањем прелома линија („\ “) у ниску натписа.

Текст натписа се може поравнати методом set_justify(). Елемент је такође способан за преламање речи, што се може активирати са set_wrap().

Gtk::Label подржава неко једноставно форматирање, на пример омогућавајући вам да неки текст учините подебљаним, обојеним или већим. То можете урадити достављањем ниске за set_markup(), користећи синтаксу Панго означавања. На пример, `<b>подебљан текст</b> и <s>прецртан текст</s>` .

Референце

#### Пример

Испод је кратак пример који илуструје ове функције. Овај пример користи елемент Frame како би боље демонстрирао стилове натписа. (Елемент Frame је објашњен у одељку Frame.) Могуће је да је први знак у `m_Label_Normal` приказан подвучено само када притиснете тастер Alt.

Ознака

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:

      //Child widgets:
      Gtk::Box m_HBox;
      Gtk::Box m_VBox, m_VBox2;
      Gtk::Frame m_Frame_Normal, m_Frame_Multi, m_Frame_Left, m_Frame_Right,
        m_Frame_LineWrapped, m_Frame_FilledWrapped, m_Frame_Underlined;
      Gtk::Label m_Label_Normal, m_Label_Multi, m_Label_Left, m_Label_Right,
        m_Label_LineWrapped, m_Label_FilledWrapped, m_Label_Underlined;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    :
      m_HBox(Gtk::Orientation::HORIZONTAL, 5),
      m_VBox(Gtk::Orientation::VERTICAL, 5),
      m_VBox2(Gtk::Orientation::VERTICAL, 5),
      m_Frame_Normal("Normal Label"),
      m_Frame_Multi("Multi-line Label"),
      m_Frame_Left("Left Justified Label"),
      m_Frame_Right("Right Justified Label"),
      m_Frame_LineWrapped("Line wrapped label"),
      m_Frame_FilledWrapped("Filled, wrapped label"),
      m_Frame_Underlined("Underlined label"),
      m_Label_Normal("_This is a Normal label", true),
      m_Label_Multi("This is a Multi-line label.\nSecond line\nThird line"),
      m_Label_Left("This is a Left-Justified\nMulti-line label.\nThird line"),
      m_Label_Right("This is a Right-Justified\nMulti-line label.\nThird line"),
      m_Label_Underlined("<u>This label is underlined!</u>\n"
              "<u>T</u>h<u>is one is</u> <u>u</u>n<u>derlin</u>ed "
              "in<u> q</u>u<u>ite a f</u>u<u>nky</u> fashion")
    {
      set_title("Label");

      m_HBox.set_margin(5);
      set_child(m_HBox);

      m_HBox.append(m_VBox);

      m_Frame_Normal.set_child(m_Label_Normal);
      m_VBox.append(m_Frame_Normal);

      m_Frame_Multi.set_child(m_Label_Multi);
      m_VBox.append(m_Frame_Multi);

      m_Label_Left.set_justify(Gtk::Justification::LEFT);
      m_Frame_Left.set_child(m_Label_Left);
      m_VBox.append(m_Frame_Left);

      m_Label_Right.set_justify(Gtk::Justification::RIGHT);
      m_Frame_Right.set_child(m_Label_Right);
      m_VBox.append(m_Frame_Right);

      m_HBox.append(m_VBox2);

      m_Label_LineWrapped.set_text(
              "This is an example of a line-wrapped label.  It "
              /* add a big space to the next line to test spacing */
              "should not be taking up the entire             "
              "width allocated to it, but automatically "
              "wraps the words to fit.  "
              "The time has come, for all good men, to come to "
              "the aid of their party.  "
              "The sixth sheik's six sheep's sick.\n"
              "     It supports multiple paragraphs correctly, "
              "and  correctly   adds "
              "many          extra  spaces. ");
      m_Label_LineWrapped.set_wrap();
      m_Frame_LineWrapped.set_child(m_Label_LineWrapped);
      m_VBox2.append(m_Frame_LineWrapped);

      m_Label_FilledWrapped.set_text(
              "This is an example of a line-wrapped, filled label.  "
              "It should be taking "
              "up the entire              width allocated to it.  "
              "Here is a sentence to prove "
              "my point.  Here is another sentence. "
              "Here comes the sun, do de do de do.\n"
              "    This is a new paragraph.\n"
              "    This is another newer, longer, better "
              "paragraph.  It is coming to an end, "
              "unfortunately.");
      m_Label_FilledWrapped.set_justify(Gtk::Justification::FILL);
      m_Label_FilledWrapped.set_wrap();
      m_Frame_FilledWrapped.set_child(m_Label_FilledWrapped);
      m_VBox2.append(m_Frame_FilledWrapped);

      m_Label_Underlined.set_justify(Gtk::Justification::LEFT);
      m_Label_Underlined.set_use_markup(true);
      m_Frame_Underlined.set_child(m_Label_Underlined);
      m_VBox2.append(m_Frame_Underlined);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Унос

#### Једноставна употреба

Елементи уноса (Entry) омогућавају кориснику да унесе текст. Садржај можете променити методом set_text(), а прочитати тренутни садржај методом get_text().

Повремено ћете можда желети да елемент Entry учините само за читање. Ово се може постићи прослеђивањем вредности `false` методу set_editable().

За унос лозинки, фраза и других података које не желите да буду приказани на екрану, позивање метода set_visibility() са вредношћу `false` ће довести до скривања текста.

Можда ћете желети да будете обавештени кад год корисник куца у елементу за унос текста. Gtk::Entry пружа два сигнала, `activate` (од верзије gtkmm 4.8) и `changed`, за ову сврху. `activate` се емитује када корисник притисне тастер Enter у елементу за унос текста; `changed` се емитује када се текст у елементу промени. Ово можете користити, на пример, за проверу или филтрирање текста који корисник уноси. Померање фокуса тастатуре на други елемент такође може сигнализирати да је корисник завршио унос текста. Сигнал `leave` у Gtk::EventControllerFocus вас може обавестити када се то догоди. Одељак ComboBox са Entry-јем садржи примере програма који користе ове сигнале.

Ако проследите `true` методу set_activates_default(), притисак на Enter у Gtk::Entry ће активирати подразумевани елемент прозора који садржи Gtk::Entry. Ово је посебно корисно у прозорчићима. Подразумевани елемент је обично једно од дугмади прозорчића, које ће нпр. затворити прозорчић. Да бисте поставили елемент као подразумевани, користите Gtk::Window::set_default_widget().

Референце

##### Пример једноставног уноса

Овај пример користи Gtk::Entry. Такође има два CheckButton-а, помоћу којих можете мењати заставице за измењивост и видљивост.

Унос

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_checkbox_editable_toggled();
      void on_checkbox_visibility_toggled();
      void on_button_close();

      //Child widgets:
      Gtk::Box m_HBox;
      Gtk::Box m_VBox;
      Gtk::Entry m_Entry;
      Gtk::Button m_Button_Close;
      Gtk::CheckButton m_CheckButton_Editable, m_CheckButton_Visible;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Close("Close"),
      m_CheckButton_Editable("Editable"),
      m_CheckButton_Visible("Visible")
    {
      set_size_request(200, 100);
      set_title("Gtk::Entry");

      set_child(m_VBox);

      m_Entry.set_max_length(50);
      m_Entry.set_text("hello");
      m_Entry.set_text(m_Entry.get_text() + " world");
      m_Entry.select_region(0, m_Entry.get_text_length());
      m_Entry.set_expand(true);
      m_VBox.append(m_Entry);

      m_VBox.append(m_HBox);

      m_HBox.append(m_CheckButton_Editable);
      m_CheckButton_Editable.set_expand(true);
      m_CheckButton_Editable.signal_toggled().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_checkbox_editable_toggled) );
      m_CheckButton_Editable.set_active(true);

      m_HBox.append(m_CheckButton_Visible);
      m_CheckButton_Visible.set_expand(true);
      m_CheckButton_Visible.signal_toggled().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_checkbox_visibility_toggled) );
      m_CheckButton_Visible.set_active(true);

      m_Button_Close.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_close) );
      m_VBox.append(m_Button_Close);
      m_Button_Close.set_expand();
      set_default_widget(m_Button_Close);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_checkbox_editable_toggled()
    {
      m_Entry.set_editable(m_CheckButton_Editable.get_active());
    }

    void ExampleWindow::on_checkbox_visibility_toggled()
    {
      m_Entry.set_visibility(m_CheckButton_Visible.get_active());
    }

    void ExampleWindow::on_button_close()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Довршавање уноса

Gtk::EntryCompletion је превазиђен од верзије gtkmm 4.10. Нема замене у gtkmm.

Елемент Entry може понудити падајућу листу постојећих избора на основу првих неколико знакова које је корисник укуцао. На пример, прозорче за претрагу би могло да предложи текст из претходних претрага.

Да бисте омогућили ову функционалност, морате креирати објекат EntryCompletion и обезбедити га елементу Entry путем метода set_completion().

EntryCompletion може користити TreeModel који садржи могуће уносе, одређене помоћу set_model(). Затим треба да позовете set_text_column() да бисте навели која од колона вашег модела треба да се користи за подударање могућих текстуалних уноса.

Алтернативно, ако би потпуна листа могућих уноса била превелика или незгодна за генерисање, уместо тога се може навести слот повратног позива помоћу set_match_func(). Ово је такође корисно ако желите да извршите подударање на делу ниске који није почетак.

Референце

##### Пример довршавања уноса

Овај пример прави Gtk::EntryCompletion и придружује га елементу Gtk::Entry. Довршавање користи Gtk::TreeModel могућих уноса, и неке додатне радње.

Довршавање уноса

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_close();

      //See the comment in the implementation:
      //bool on_completion_match(const Glib::ustring& key, const Gtk::TreeModel::const_iterator& iter);

      //Tree model columns, for the EntryCompletion's filter model:
      class ModelColumns : public Gtk::TreeModel::ColumnRecord
      {
      public:
        ModelColumns()
        { add(m_col_id); add(m_col_name); }

        Gtk::TreeModelColumn<unsigned int> m_col_id;
        Gtk::TreeModelColumn<Glib::ustring> m_col_name;
      };

      ModelColumns m_Columns;

      //Child widgets:
      Gtk::Box m_HBox;
      Gtk::Box m_VBox;
      Gtk::Entry m_Entry;
      Gtk::Label m_Label;
      Gtk::Button m_Button_Close;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Label("Press a or b to see a list of possible completions."),
      m_Button_Close("Close")
    {
      //set_size_request(200, 100);
      set_title("Gtk::EntryCompletion");

      set_child(m_VBox);
      m_VBox.append(m_Entry);

      m_VBox.append(m_Label);
      m_Label.set_expand(true);

      m_Button_Close.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_close) );
      m_VBox.append(m_Button_Close);
      set_default_widget(m_Button_Close);

      //Add an EntryCompletion:
      auto completion =
          Gtk::EntryCompletion::create();
      m_Entry.set_completion(completion);

      //Create and fill the completion's filter model
      auto refCompletionModel =
          Gtk::ListStore::create(m_Columns);
      completion->set_model(refCompletionModel);

      // For more complex comparisons, use a filter match callback, like this.
      // See the comment below for more details:
      //completion->set_match_func( sigc::mem_fun(*this,
                  //&ExampleWindow::on_completion_match) );

      //Fill the TreeView's model
      auto row = *(refCompletionModel->append());
      row[m_Columns.m_col_id] = 1;
      row[m_Columns.m_col_name] = "Alan Zebedee";

      row = *(refCompletionModel->append());
      row[m_Columns.m_col_id] = 2;
      row[m_Columns.m_col_name] = "Adrian Boo";

      row = *(refCompletionModel->append());
      row[m_Columns.m_col_id] = 3;
      row[m_Columns.m_col_name] = "Bob McRoberts";

      row = *(refCompletionModel->append());
      row[m_Columns.m_col_id] = 4;
      row[m_Columns.m_col_name] = "Bob McBob";

      //Tell the completion what model column to use to
      //- look for a match (when we use the default matching, instead of
      //  set_match_func().
      //- display text in the entry when a match is found.
      completion->set_text_column(m_Columns.m_col_name);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_close()
    {
      close();
    }

    /* You can do more complex matching with a handler like this.
     * For instance, you could check for substrings inside the string instead of the start,
     * or you could look for the key in extra model columns as well as the model column that will be displayed.
     * The code here is not actually more complex - it's a reimplementation of the default behaviour.
     *
    bool ExampleWindow::on_completion_match(const Glib::ustring& key, const
            Gtk::TreeModel::const_iterator& iter)
    {
      if(iter)
      {
        const auto row = *iter;

        const auto key_length = key.size();
        auto filter_string = row[m_Columns.m_col_name];

        auto filter_string_start = filter_string.substr(0, key_length);
        //The key is lower-case, even if the user input is not.
        filter_string_start = filter_string_start.lowercase();

        if(key == filter_string_start)
          return true; //A match was found.
      }

      return false; //No match.
    }
    */

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Иконице уноса

Елемент Entry може приказати иконицу на почетку или на крају текстуалне области. Иконица се може навести методама као што су set_icon_from_paintable() или set_icon_from_icon_name(). Програм може одговорити на корисниково притискање иконице руковањем сигналом signal_icon_press.

##### Пример иконице уноса

Овај пример приказује елемент Gtk::Entry са именованом иконицом претраге, и исписује текст у терминалу када се притисне иконица.

Унос са иконицом

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_icon_pressed(Gtk::Entry::IconPosition icon_pos);
      void on_button_close();

      //Child widgets:
      Gtk::Box m_VBox;
      Gtk::Entry m_Entry;
      Gtk::Button m_Button_Close;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Close("Close")
    {
      set_title("Gtk::Entry");

      set_child(m_VBox);

      m_Entry.set_max_length(50);
      m_Entry.set_text("Hello world");
      m_VBox.append(m_Entry);

      m_Entry.set_icon_from_icon_name("edit-find");
      m_Entry.signal_icon_press().connect( sigc::mem_fun(*this, &ExampleWindow::on_icon_pressed) );

      m_Button_Close.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_close) );
      m_VBox.append(m_Button_Close);
      set_default_widget(m_Button_Close);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_icon_pressed(Gtk::Entry::IconPosition /* icon_pos */)
    {
      std::cout << "Icon pressed." << std::endl;
    }

    void ExampleWindow::on_button_close()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Напредак уноса

Елемент Entry може приказати траку напретка унутар текстуалне области, испод унетог текста. Трака напретка ће бити приказана ако се позову методе set_progress_fraction() или set_progress_pulse_step().

##### Пример напретка уноса

Овај пример приказује елемент Gtk::Entry са траком напретка.

Унос са траком напретка

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      bool on_timeout();
      void on_button_close();

      //Child widgets:
      Gtk::Box m_VBox;
      Gtk::Entry m_Entry;
      Gtk::Button m_Button_Close;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Close("Close")
    {
      set_title("Gtk::Entry");

      set_child(m_VBox);

      m_Entry.set_max_length(50);
      m_Entry.set_text("Hello world");
      m_VBox.append(m_Entry);

      //Change the progress fraction every 0.1 second:
      Glib::signal_timeout().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_timeout),
        100
      );

      m_Button_Close.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_close) );
      m_VBox.append(m_Button_Close);
      set_default_widget(m_Button_Close);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    bool ExampleWindow::on_timeout()
    {
      static double fraction = 0;
      m_Entry.set_progress_fraction(fraction);

      fraction += 0.01;
      if(fraction > 1)
        fraction = 0;

      return true;
    }

    void ExampleWindow::on_button_close()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Вртеће дугме

SpinButton омогућава кориснику да изабере вредност из опсега бројчаних вредности. Садржи елемент Entry са дугмићима за повећање и смањење са стране. Кликање на дугмиће доводи до тога да вредност „вррти“ горе-доле кроз опсег могућих вредности. Елемент Entry се такође може користити за директан унос вредности.

Вредност може имати подесив број децималних места, а величина корака се може подесити. SpinButton дугмићи имају и функцију „самопонављања“: држање притиснутог дугмета за повећање или смањење може опционално довести до тога да се вредност мења брже што се дугме дуже држи.

SpinButton дугмад користе објекат Adjustment за чување података о опсегу вредности. Ове особине подешавања користи вртеће дугме на следећи начин:

- `value`: вредност за вртеће дугме

- `lower`: доња вредност опсега

- `upper`: горња вредност опсега

- `step_increment`: вредност за повећање/смањење при притиску на 1. тастер миша

- `page_increment`: вредност за повећање/смањење при притиску на 2. тастер миша

- `page_size`: некоришћено

Додатно, 3. тастер миша се може користити за директан скок на `upper` или `lower` вредности.

SpinButton може да направи подразумевани Adjustment, коме можете приступити путем методе get_adjustment(), или можете навести постојећи Adjustment у конструктору.

#### Методе

Број децималних места се може изменити коришћењем методе set_digits().

Можете поставити вредност вртећег дугмета коришћењем методе set_value(), и добавити је помоћу get_value().

Метода spin() „врти“ SpinButton, као да је кликнуто на његово дугме за повећање или смањење. Треба да наведете Gtk::SpinType да бисте одредили смер или нови положај.

Да бисте спречили корисника да уноси небројевне знакове у поље за унос, проследите `true` методи set_numeric().

Да би се SpinButton „премотавао“ између својих горњих и доњих граница, користите методу set_wrap().

Да бисте га приморали да се прилепи на најближи `step_increment`, користите set_snap_to_ticks().

Референце

#### Пример

Ево примера SpinButton дугмета на делу:

Вртеће дугме

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_checkbutton_snap();
      void on_checkbutton_numeric();
      void on_spinbutton_digits_changed();
      void on_button_close();

      enum enumValueFormats
      {
        VALUE_FORMAT_INT,
        VALUE_FORMAT_FLOAT
      };
      void on_button_getvalue(enumValueFormats display);

      //Child widgets:
      Gtk::Frame m_Frame_NotAccelerated, m_Frame_Accelerated;
      Gtk::Box m_HBox_NotAccelerated, m_HBox_Accelerated,
        m_HBox_Buttons;
      Gtk::Box m_VBox_Main, m_VBox, m_VBox_Day, m_VBox_Month, m_VBox_Year,
        m_VBox_Accelerated, m_VBox_Value, m_VBox_Digits;
      Gtk::Label m_Label_Day, m_Label_Month, m_Label_Year,
        m_Label_Value, m_Label_Digits,
        m_Label_ShowValue;
      Glib::RefPtr<Gtk::Adjustment> m_adjustment_day, m_adjustment_month, m_adjustment_year,
        m_adjustment_value, m_adjustment_digits;
      Gtk::SpinButton m_SpinButton_Day, m_SpinButton_Month, m_SpinButton_Year,
        m_SpinButton_Value, m_SpinButton_Digits;
      Gtk::CheckButton m_CheckButton_Snap, m_CheckButton_Numeric;
      Gtk::Button m_Button_Int, m_Button_Float, m_Button_Close;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>
    #include <cstdio>

    ExampleWindow::ExampleWindow()
    :
      m_Frame_NotAccelerated("Not accelerated"),
      m_Frame_Accelerated("Accelerated"),
      m_VBox_Main(Gtk::Orientation::VERTICAL, 5),
      m_VBox(Gtk::Orientation::VERTICAL),
      m_VBox_Day(Gtk::Orientation::VERTICAL),
      m_VBox_Month(Gtk::Orientation::VERTICAL),
      m_VBox_Year(Gtk::Orientation::VERTICAL),
      m_VBox_Accelerated(Gtk::Orientation::VERTICAL),
      m_VBox_Value(Gtk::Orientation::VERTICAL),
      m_VBox_Digits(Gtk::Orientation::VERTICAL),
      m_Label_Day("Day: ", Gtk::Align::START),
      m_Label_Month("Month: ", Gtk::Align::START),
      m_Label_Year("Year: ", Gtk::Align::START),
      m_Label_Value("Value: ", Gtk::Align::START),
      m_Label_Digits("Digits: ", Gtk::Align::START),
      m_adjustment_day( Gtk::Adjustment::create(1.0, 1.0, 31.0, 1.0, 5.0, 0.0) ),
      m_adjustment_month( Gtk::Adjustment::create(1.0, 1.0, 12.0, 1.0, 5.0, 0.0) ),
      m_adjustment_year( Gtk::Adjustment::create(2012.0, 1.0, 2200.0, 1.0, 100.0, 0.0) ),
      m_adjustment_value( Gtk::Adjustment::create(0.0, -10000.0, 10000.0, 0.5, 100.0, 0.0) ),
      m_adjustment_digits( Gtk::Adjustment::create(2.0, 1.0, 5.0, 1.0, 1.0, 0.0) ),
      m_SpinButton_Day(m_adjustment_day),
      m_SpinButton_Month(m_adjustment_month),
      m_SpinButton_Year(m_adjustment_year),
      m_SpinButton_Value(m_adjustment_value, 1.0, 2),
      m_SpinButton_Digits(m_adjustment_digits),
      m_CheckButton_Snap("Snap to 0.5-ticks"),
      m_CheckButton_Numeric("Numeric only input mode"),
      m_Button_Int("Value as Int"),
      m_Button_Float("Value as Float"),
      m_Button_Close("Close")
    {
      set_title("SpinButton");

      m_VBox_Main.set_margin(10);
      set_child(m_VBox_Main);

      m_VBox_Main.append(m_Frame_NotAccelerated);

      m_VBox.set_margin(5);
      m_Frame_NotAccelerated.set_child(m_VBox);

      /* Day, month, year spinners */

      m_VBox.set_spacing(5);
      m_VBox.append(m_HBox_NotAccelerated);

      m_Label_Day.set_expand();
      m_VBox_Day.append(m_Label_Day);

      m_SpinButton_Day.set_wrap();
      m_SpinButton_Day.set_expand();
      m_VBox_Day.append(m_SpinButton_Day);

      m_HBox_NotAccelerated.set_spacing(5);
      m_HBox_NotAccelerated.append(m_VBox_Day);

      m_Label_Month.set_expand();
      m_VBox_Month.append(m_Label_Month);

      m_SpinButton_Month.set_wrap();
      m_SpinButton_Month.set_expand();
      m_VBox_Month.append(m_SpinButton_Month);

      m_HBox_NotAccelerated.append(m_VBox_Month);

      m_Label_Year.set_expand();
      m_VBox_Year.append(m_Label_Year);

      m_SpinButton_Year.set_wrap();
      m_SpinButton_Year.set_expand();
      m_SpinButton_Year.set_size_request(55, -1);
      m_VBox_Year.append(m_SpinButton_Year);

      m_HBox_NotAccelerated.append(m_VBox_Year);

      //Accelerated:
      m_VBox_Main.append(m_Frame_Accelerated);

      m_VBox_Accelerated.set_margin(5);
      m_Frame_Accelerated.set_child(m_VBox_Accelerated);

      m_VBox_Accelerated.set_spacing(5);
      m_VBox_Accelerated.append(m_HBox_Accelerated);

      m_HBox_Accelerated.append(m_VBox_Value);

      m_Label_Value.set_expand();
      m_VBox_Value.append(m_Label_Value);

      m_SpinButton_Value.set_wrap();
      m_SpinButton_Value.set_expand();
      m_SpinButton_Value.set_size_request(100, -1);
      m_VBox_Value.append(m_SpinButton_Value);

      m_HBox_Accelerated.append(m_VBox_Digits);

      m_VBox_Digits.append(m_Label_Digits);
      m_Label_Digits.set_expand();

      m_SpinButton_Digits.set_wrap();
      m_adjustment_digits->signal_value_changed().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_spinbutton_digits_changed) );

      m_VBox_Digits.append(m_SpinButton_Digits);
      m_SpinButton_Digits.set_expand();

      //CheckButtons:
      m_VBox_Accelerated.append(m_CheckButton_Snap);
      m_CheckButton_Snap.set_expand();
      m_CheckButton_Snap.set_active();
      m_CheckButton_Snap.signal_toggled().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_checkbutton_snap) );

      m_VBox_Accelerated.append(m_CheckButton_Numeric);
      m_CheckButton_Numeric.set_expand();
      m_CheckButton_Numeric.set_active();
      m_CheckButton_Numeric.signal_toggled().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_checkbutton_numeric) );

      //Buttons:
      m_VBox_Accelerated.append(m_HBox_Buttons);

      m_Button_Int.signal_clicked().connect( sigc::bind( sigc::mem_fun(*this,
                      &ExampleWindow::on_button_getvalue), VALUE_FORMAT_INT) );
      m_HBox_Buttons.set_spacing(5);
      m_HBox_Buttons.append(m_Button_Int);
      m_Button_Int.set_expand();

      m_Button_Float.signal_clicked().connect( sigc::bind( sigc::mem_fun(*this,
                      &ExampleWindow::on_button_getvalue), VALUE_FORMAT_FLOAT) );
      m_HBox_Buttons.append(m_Button_Float);
      m_Button_Float.set_expand();

      m_VBox_Accelerated.append(m_Label_ShowValue);
      m_Label_ShowValue.set_expand();
      m_Label_ShowValue.set_text("0");

      //Close button:
      m_Button_Close.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_close) );
      m_VBox_Main.append(m_Button_Close);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_close()
    {
      close();
    }

    void ExampleWindow::on_checkbutton_snap()
    {
      m_SpinButton_Value.set_snap_to_ticks( m_CheckButton_Snap.get_active() );
    }

    void ExampleWindow::on_checkbutton_numeric()
    {
      m_SpinButton_Value.set_numeric( m_CheckButton_Numeric.get_active() );
    }

    void ExampleWindow::on_spinbutton_digits_changed()
    {
      m_SpinButton_Value.set_digits( m_SpinButton_Digits.get_value_as_int() );
    }

    void ExampleWindow::on_button_getvalue(enumValueFormats display)
    {
      gchar buf[32];

      if (display == VALUE_FORMAT_INT)
        sprintf (buf, "%d", m_SpinButton_Value.get_value_as_int());
      else
        sprintf (buf, "%0.*f", m_SpinButton_Value.get_digits(),
                m_SpinButton_Value.get_value());

      m_Label_ShowValue.set_text(buf);
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Трака напретка

Траке напретка се користе за приказ стања текуће радње. На пример, ProgressBar може показати колики део задатка је завршен.

Да бисте променили приказану вредност, користите метод set_fraction(), прослеђујући double између 0.0 и 1.0 да бисте поставили нови део.

ProgressBar је водораван и слева надесно по подразумеваним поставкама, али га можете променити у усправну траку напретка коришћењем метода set_orientation().

Референца

#### Режим активности

Осим што указује на количину напретка који се догодио, трака напретка се такође може користити да укаже на то да постоји нека активност; то се ради постављањем траке напретка у режим активности. У овом режиму, трака напретка приказује мали правоугаоник који се креће напред-назад. Режим активности је користан у ситуацијама када се напредак радње не може израчунати као опсег вредности (нпр. примање датотеке непознате дужине).

Да бисте то урадили, потребно је да позовете метод pulse() у редовним интервалима. Такође можете изабрати величину корака помоћу метода set_pulse_step().

Трака напретка такође може приказати подесиву ниску текста поред траке, коришћењем метода set_text().

#### Пример

Трака напретка

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_checkbutton_text();
      void on_checkbutton_activity();
      void on_checkbutton_inverted();

      bool on_timeout();
      void on_button_close();

      //Child widgets:
      Gtk::Box m_VBox;
      Gtk::Grid m_Grid;
      Gtk::ProgressBar m_ProgressBar;
      Gtk::Separator m_Separator;
      Gtk::CheckButton m_CheckButton_Text, m_CheckButton_Activity, m_CheckButton_Inverted;
      Gtk::Button m_Button_Close;

      sigc::connection m_connection_timeout;
      bool m_bActivityMode;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL, 5),
      m_CheckButton_Text("Show text"),
      m_CheckButton_Activity("Activity mode"),
      m_CheckButton_Inverted("Right to Left"),
      m_Button_Close("Close"),
      m_bActivityMode(false)
    {
      set_resizable();
      set_title("Gtk::ProgressBar");

      m_VBox.set_margin(10);
      set_child(m_VBox);

      m_VBox.append(m_ProgressBar);
      m_ProgressBar.set_margin_end(5);
      m_ProgressBar.set_halign(Gtk::Align::CENTER);
      m_ProgressBar.set_valign(Gtk::Align::CENTER);
      m_ProgressBar.set_size_request(100, -1);
      m_ProgressBar.set_text("some text");
      m_ProgressBar.set_show_text(false);

      //Add a timer callback to update the value of the progress bar:
      m_connection_timeout = Glib::signal_timeout().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_timeout), 50 );

      m_VBox.append(m_Separator);
      m_VBox.append(m_Grid);
      m_Grid.set_expand(true);
      m_Grid.set_row_homogeneous(true);

      //Add a check button to select displaying of the trough text:
      m_Grid.attach(m_CheckButton_Text, 0, 0);
      m_CheckButton_Text.set_margin(5);
      m_CheckButton_Text.signal_toggled().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_checkbutton_text) );

      //Add a check button to toggle activity mode:
      m_Grid.attach(m_CheckButton_Activity, 0, 1);
      m_CheckButton_Activity.set_margin(5);
      m_CheckButton_Activity.signal_toggled().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_checkbutton_activity) );

      //Add a check button to select growth from left to right or from right to left:
      m_Grid.attach(m_CheckButton_Inverted, 0, 2);
      m_CheckButton_Inverted.set_margin(5);
      m_CheckButton_Inverted.signal_toggled().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_checkbutton_inverted) );

      //Add a button to exit the program.
      m_VBox.append(m_Button_Close);
      m_Button_Close.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_close) );
      set_default_widget(m_Button_Close);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_checkbutton_text()
    {
      const bool show_text = m_CheckButton_Text.get_active();
      m_ProgressBar.set_show_text(show_text);
    }

    void ExampleWindow::on_checkbutton_activity()
    {
      m_bActivityMode = m_CheckButton_Activity.get_active();

      if(m_bActivityMode)
        m_ProgressBar.pulse();
      else
        m_ProgressBar.set_fraction(0.0);
    }

    void ExampleWindow::on_checkbutton_inverted()
    {
      const bool inverted = m_CheckButton_Inverted.get_active();
      m_ProgressBar.set_inverted(inverted);
    }

    void ExampleWindow::on_button_close()
    {
      close();
    }

    /* Update the value of the progress bar so that we get
     * some movement */
    bool ExampleWindow::on_timeout()
    {
      if(m_bActivityMode)
        m_ProgressBar.pulse();
      else
      {
        double new_val = m_ProgressBar.get_fraction() + 0.01;

        if(new_val > 1.0)
          new_val = 0.0;

        //Set the new value:
        m_ProgressBar.set_fraction(new_val);
      }

      //As this is a timeout function, return true so that it
      //continues to get called
      return true;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Трака обавештења

InfoBar може приказати мале ставке информација или поставити кратка питања. За разлику од Dialog-а, појављује се на врху тренутног прозора уместо да отвара нови прозор. Његов АПИ је врло сличан Gtk::Dialog АПИ-ју.

Референца

#### Пример

Елемент InfoBar је превазиђен почев од gtkmm 4.10. Пример приказује траку обавештења која се састоји од Box-а са Label-ом и Button-ом.

Трака обавештења

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_infobar_ok();
      void on_button_quit();
      void on_button_clear();
      void on_textbuffer_changed();
      void on_parsing_error(const Glib::RefPtr<const Gtk::CssSection>& section,
        const Glib::Error& error);

      void set_infobar_background();

      //Child widgets:
      Gtk::Box m_VBox;

      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::TextView m_TextView;

      Glib::RefPtr<Gtk::TextBuffer> m_refTextBuffer;

      Gtk::Box m_InfoBar;
      Gtk::Label m_Message_Label;
      Gtk::Button m_Button_OK;

      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;
      Gtk::Button m_Button_Clear;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    // The Gtk::InfoBar widget is deprecated since gtk/gtkmm 4.10.
    // A Box with a Label and Button is often a good replacement.
    // If you don't care about its background color, it's even easier
    // than the InfoBar replacement in this example program.

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL, 6),
      m_InfoBar(Gtk::Orientation::HORIZONTAL, 6),
      m_Button_OK("_OK", true),
      m_ButtonBox(Gtk::Orientation::HORIZONTAL, 6),
      m_Button_Quit("_Quit", true),
      m_Button_Clear("_Clear", true)
    {
      set_title("Infobar example");
      set_default_size(400, 200);

      m_VBox.set_margin(6);
      set_child(m_VBox);

      // Add the message label to the InfoBar:
      m_InfoBar.append(m_Message_Label);
      m_Message_Label.set_margin_start(5);
      m_Message_Label.set_hexpand(true);
      m_Message_Label.set_halign(Gtk::Align::START);

      // Add an ok button to the InfoBar:
      m_InfoBar.append(m_Button_OK);
      m_Button_OK.set_margin(5);

      // Add the InfoBar to the vbox:
      m_VBox.append(m_InfoBar);

      // Create the buffer and set it for the TextView:
      m_refTextBuffer = Gtk::TextBuffer::create();
      m_TextView.set_buffer(m_refTextBuffer);

      // Add the TextView, inside a ScrolledWindow:
      m_ScrolledWindow.set_child(m_TextView);

      // Show the scrollbars only when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);

      // Add button box:
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Clear);
      m_ButtonBox.append(m_Button_Quit);
      m_Button_Clear.set_hexpand(true);
      m_Button_Clear.set_halign(Gtk::Align::END);

      // Connect signals:
      m_Button_OK.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_infobar_ok) );
      m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit) );
      m_Button_Clear.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_clear) );
      m_refTextBuffer->signal_changed().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_textbuffer_changed) );

      // Keep the InfoBar hidden until a message needs to be shown:
      m_InfoBar.set_visible(false);
      set_infobar_background();

      // Make the clear button insensitive until text is typed in the buffer.  When
      // the button is sensitive and it is pressed, the InfoBar is displayed with a
      // message.
      m_Button_Clear.set_sensitive(false);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_infobar_ok()
    {
      // Clear the message and hide the info bar:
      m_Message_Label.set_text("");
      m_InfoBar.set_visible(false);
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_button_clear()
    {
      m_refTextBuffer->set_text("");
      m_Message_Label.set_text("Cleared the text.");
      m_InfoBar.set_visible(true);
    }

    void ExampleWindow::on_textbuffer_changed()
    {
      m_Button_Clear.set_sensitive(m_refTextBuffer->size() > 0);
    }

    void ExampleWindow::set_infobar_background()
    {
      // Concerning color in CSS files, see https://www.w3.org/TR/css-color-3.
      const std::string css = "#infobarbox { background-color: beige; }";

      m_InfoBar.set_name("infobarbox");
      auto css_provider = Gtk::CssProvider::create();
      Gtk::StyleProvider::add_provider_for_display(
        m_InfoBar.get_display(), css_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);

      css_provider->load_from_data(css);
      css_provider->signal_parsing_error().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_parsing_error));
    }

    void ExampleWindow::on_parsing_error(const Glib::RefPtr<const Gtk::CssSection>& section,
      const Glib::Error& error)
    {
      std::cerr << "on_parsing_error(): " << error.what() << std::endl;
      if (section)
      {
        const auto file = section->get_file();
        if (file)
        {
          std::cerr << "  URI = " << file->get_uri() << std::endl;
        }

        auto start_location = section->get_start_location();
        auto end_location = section->get_end_location();
        std::cerr << "  start_line = " << start_location.get_lines()+1
                  << ", end_line = " << end_location.get_lines()+1 << std::endl;
        std::cerr << "  start_position = " << start_location.get_line_chars()
                  << ", end_position = " << end_location.get_line_chars() << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Облачићи

Облачићи су мали прозори са информацијама који искачу када оставите показивач изнад елемента на неколико секунди. Користите set_tooltip_text() да поставите ниску текста као облачић на било ком Widget-у. Gtk::Tooltip се користи за напредније коришћење облачића, као што је приказивање слике поред текста.

Референца елемента

Референца облачића

#### Пример

Облачић

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:

      ExampleWindow();
      virtual ~ExampleWindow();

    protected:

      //Methods:
      void prepare_textview();
      void connect_signals();

      //Signal handlers:
      void on_markup_checkbutton_toggled();
      bool on_textview_query_tooltip(int x, int y, bool keyboard_tooltip, const Glib::RefPtr<Gtk::Tooltip>& tooltip);
      bool on_button_query_tooltip(int x, int y, bool keyboard_tooltip, const Glib::RefPtr<Gtk::Tooltip>& tooltip);

      //Child widgets:
      Gtk::Box m_vbox;

      Gtk::CheckButton m_checkbutton;
      Gtk::Label m_label;

      Gtk::ScrolledWindow m_scrolled_window;
      Gtk::TextView m_text_view;
      Glib::RefPtr<Gtk::TextBuffer> m_ref_text_buffer;
      Glib::RefPtr<Gtk::TextTag> m_ref_bold_tag;

      Gtk::Button m_button;
      Gtk::Box m_button_tooltip_widget;
    };

    #endif // GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <vector>

    const Glib::ustring app_title = "gtkmm tooltips example";
    const Glib::ustring non_markedup_tip = "A tooltip without markup.";
    const Glib::ustring markedup_tip = "<i>Markup</i> in a tooltip.";

    ExampleWindow::ExampleWindow()
      :
      m_vbox(Gtk::Orientation::VERTICAL, 3),
      m_checkbutton("Click to alternate markup in tooltip"),
      m_label("A label"),
      m_button("Button with a custom tooltip widget")
    {
      //Set up window and the top-level container:
      set_title(app_title);

      m_vbox.set_margin(10);
      set_child(m_vbox);

      //Check button with markup in tooltip:
      m_checkbutton.set_tooltip_text(non_markedup_tip);
      m_vbox.append(m_checkbutton);

      //Label:
      m_label.set_tooltip_text("Another tooltip");
      m_vbox.append(m_label);

      //Textview:
      prepare_textview();

      //Button:
      //When only connecting to the query-tooltip signal, and not using any
      //of set_tooltip_text() or set_tooltip_markup(), we need to explicitly
      //tell GTK that the widget has a tooltip which we'll show.
      m_button.set_has_tooltip();
      m_vbox.append(m_button);

      //Button's custom tooltip widget:
      auto label = Gtk::make_managed<Gtk::Label>("A label in a custom tooltip widget");
      m_button_tooltip_widget.append(*label);

      connect_signals();
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::prepare_textview()
    {
      Gtk::TextIter iter;
      std::vector<Glib::RefPtr<Gtk::TextTag>> tags;

      //Set up a scrolled window:
      m_scrolled_window.set_child(m_text_view);
      m_scrolled_window.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_scrolled_window.set_expand();
      m_vbox.append(m_scrolled_window);

      //Create a text buffer with some text:
      m_ref_text_buffer = Gtk::TextBuffer::create();

      iter = m_ref_text_buffer->end();
      m_ref_text_buffer->insert(iter, "Hover over the text ");

      //Insert some text with a tag.
      //In the tooltip signal handler below, we will show a tooltip
      //when mouse pointer is above this tagged text.
      m_ref_bold_tag = m_ref_text_buffer->create_tag("bold");
      m_ref_bold_tag->set_property("weight", Pango::Weight::BOLD);

      tags.push_back(m_ref_bold_tag);

      iter = m_ref_text_buffer->end();
      m_ref_text_buffer->insert_with_tags(iter, "in bold", tags);

      iter = m_ref_text_buffer->end();
      m_ref_text_buffer->insert(iter, " to see its tooltip");

      m_text_view.set_buffer(m_ref_text_buffer);

      m_text_view.set_size_request(320, 50);

      //When only connecting to the query-tooltip signal, and not using any
      //of set_tooltip_text() or set_tooltip_markup(), we need to explicitly
      //tell GTK that the widget has a tooltip which we'll show.
      m_text_view.set_has_tooltip();
    }

    void ExampleWindow::connect_signals()
    {
      m_checkbutton.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_markup_checkbutton_toggled));

      m_text_view.signal_query_tooltip().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_textview_query_tooltip), true);

      m_button.signal_query_tooltip().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_query_tooltip), true);
    }

    void ExampleWindow::on_markup_checkbutton_toggled()
    {
      if (m_checkbutton.get_active())
      {
        m_checkbutton.set_tooltip_markup(markedup_tip);
      }
      else
      {
        m_checkbutton.set_tooltip_markup(non_markedup_tip);
      }
    }

    bool ExampleWindow::on_textview_query_tooltip(int x, int y, bool keyboard_tooltip, const Glib::RefPtr<Gtk::Tooltip>& tooltip)
    {
      Gtk::TextIter iter;

      if (keyboard_tooltip)
      {
        int offset = m_ref_text_buffer->property_cursor_position().get_value();
        iter = m_ref_text_buffer->get_iter_at_offset(offset);
      }
      else
      {
        int mouse_x, mouse_y, trailing;
        m_text_view.window_to_buffer_coords(Gtk::TextWindowType::TEXT,
                                            x, y, mouse_x, mouse_y);
        m_text_view.get_iter_at_position(iter, trailing, mouse_x, mouse_y);
      }

      if (!iter.has_tag(m_ref_bold_tag))
        return false;

      //Show a tooltip if the cursor or mouse pointer is over the text
      //with the specific tag:
      tooltip->set_markup("<b>Information</b> attached to a text tag");
      tooltip->set_icon("dialog-information");
      return true;
    }

    bool ExampleWindow::on_button_query_tooltip(int, int, bool, const Glib::RefPtr<Gtk::Tooltip>& tooltip)
    {
      tooltip->set_custom(m_button_tooltip_widget);
      return true;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

## Елементи контејнери

Елементи контејнери, као и други елементи, потичу из Gtk::Widget-а. Неки елементи контејнери, као што је Gtk::Grid, могу да садрже много садржаних елемената, па они обично имају сложенија прочеља. Други, као што је Gtk::Frame, садрже само један садржани елемент.

### Контејнери са једном ставком

Већина елемената контејнера са једном ставком имају методе set_child() и unset_child() за садржани елемент. Gtk::Button и Gtk::Window су технички контејнери са једном ставком, али смо о њима већ расправљали на другим местима.

Такође ћемо расправљати о елементу Gtk::Paned, који вам омогућава да поделите прозор на две засебне „површи“. Овај елемент заправо садржи два садржана елемента, али је број фиксан па се чини прикладним.

#### Оквир

Оквири могу да окруже један или групу елемената унутар кутије, опционално са насловом. На пример, можете поставити групу ToggleButton-а или CheckButton-а у Frame.

Референца

##### Пример

Оквир

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:

      //Child widgets:
      Gtk::Frame m_Frame;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    {
     /* Set some window properties */
      set_title("Frame Example");
      set_size_request(300, 300);

      /* Sets the margin around the frame. */
      m_Frame.set_margin(10);

      set_child(m_Frame);

      /* Set the frames label */
      m_Frame.set_label("Gtk::Frame Widget");

      /* Align the label at the right of the frame */
      m_Frame.set_label_align(Gtk::Align::END);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Површи

Површи деле елемент на две половине, раздвојене покретним разделником. Две половине (површи) могу бити усмерене или водоравно (једна поред друге) или усправно (једна изнад друге).

За разлику од других елемената у овом одељку, елементи са површима не садрже један већ два садржана елемента, по један у свакој површи. Због тога треба да користите set_start_child() и set_end_child() уместо методе set_child().

Можете подесити положај разделника користећи методу set_position(), и вероватно ће вам то бити потребно.

Референца

##### Пример

Површи

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include "messageslist.h"
    #include "messagetext.h"
    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:

      //Child widgets:
      Gtk::Paned m_VPaned;
      MessagesList m_MessagesList;
      MessageText m_MessageText;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `messageslist.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_MESSAGESLIST_H
    #define GTKMM_EXAMPLE_MESSAGESLIST_H

    #include <gtkmm.h>

    class MessagesList: public Gtk::ScrolledWindow
    {
    public:
      MessagesList();
      ~MessagesList() override;

    protected:
      // Signal handlers:
      void on_setup_message(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_message(const Glib::RefPtr<Gtk::ListItem>& list_item);

      Glib::RefPtr<Gtk::StringList> m_refStringList; // The List Model.
      Gtk::ListView m_ListView; // The List View.
    };
    #endif //GTKMM_EXAMPLE_MESSAGESLIST_H

File: `messagetext.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_MESSAGETEXT_H
    #define GTKMM_EXAMPLE_MESSAGETEXT_H

    #include <gtkmm.h>

    class MessageText : public Gtk::ScrolledWindow
    {
    public:
      MessageText();
      virtual ~MessageText();

      void insert_text();

    protected:
      Gtk::TextView m_TextView;
    };

    #endif //GTKMM_EXAMPLE_MESSAGETEXT_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VPaned(Gtk::Orientation::VERTICAL)
    {
      set_title ("Paned Windows");
      set_default_size(450, 400);
      m_VPaned.set_margin(10);

      /* Add a vpaned widget to our toplevel window */
      set_child(m_VPaned);

      /* Now add the contents of the two halves of the window */
      m_VPaned.set_start_child(m_MessagesList);
      m_VPaned.set_end_child(m_MessageText);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `messageslist.cc` (For use with gtkmm 4)

    #include "messageslist.h"

    MessagesList::MessagesList()
    {
      /* Create a new scrolled window, with scrollbars only if needed */
      set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);

      set_child(m_ListView);

      /* create list store */
      m_refStringList = Gtk::StringList::create({});
      auto selection_model = Gtk::SingleSelection::create(m_refStringList);
      m_ListView.set_model(selection_model);

      /* Add some messages to the window */
      for (int i = 1; i <= 10; ++i)
        m_refStringList->append(Glib::ustring::format("message #", i));

      // Create a ListItemFactory to use for populating list items.
      auto factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(sigc::mem_fun(*this, &MessagesList::on_setup_message));
      factory->signal_bind().connect(sigc::mem_fun(*this, &MessagesList::on_bind_message));
      m_ListView.set_factory(factory);
    }

    MessagesList::~MessagesList()
    {
    }

    void MessagesList::on_setup_message(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto label = Gtk::make_managed<Gtk::Label>();
      label->set_halign(Gtk::Align::START);
      list_item->set_child(*label);
    }

    void MessagesList::on_bind_message(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto pos = list_item->get_position();
      if (pos == GTK_INVALID_LIST_POSITION)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(m_refStringList->get_string(pos));
    }

File: `messagetext.cc` (For use with gtkmm 4)

    #include "messagetext.h"

    MessageText::MessageText()
    {
      set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);

      set_child(m_TextView);
      insert_text();
    }

    MessageText::~MessageText()
    {
    }

    void MessageText::insert_text()
    {
      auto refTextBuffer = m_TextView.get_buffer();

      auto iter = refTextBuffer->get_iter_at_offset(0);
      refTextBuffer->insert(iter,
        "From: pathfinder@nasa.gov\n"
        "To: mom@nasa.gov\n"
        "Subject: Made it!\n"
        "\n"
        "We just got in this morning. The weather has been\n"
        "great - clear but cold, and there are lots of fun sights.\n"
        "Sojourner says hi. See you soon.\n"
        " -Path\n");
    }

#### Прозор са клизачима

ScrolledWindow елементи стварају област са клизачима. Можете уметнути било коју врсту елемента у ScrolledWindow, и он ће бити приступачан без обзира на његову величину коришћењем трака клизача. Имајте на уму да ScrolledWindow није Gtk::Window упркос мало обмањујућем називу.

Прозори са клизачима имају смернице траке клизача које одређују да ли ће Scrollbar траке бити приказане. Смернице се могу поставити методом set_policy(). Смерница може бити на пример `Gtk::PolicyType::AUTOMATIC` или `Gtk::PolicyType::ALWAYS`. `Gtk::PolicyType::AUTOMATIC` ће довести до тога да прозор са клизачима прикаже траку клизача само ако је садржани елемент већи од видљиве области. `Gtk::PolicyType::ALWAYS` ће довести до тога да трака клизача увек буде приказана.

Референца

##### Пример

Ево једноставног примера који пакује 100 прекидачких дугмади у ScrolledWindow. Пробајте да промените величину прозора да видите како траке клизача реагују.

Прозор са клизачима

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_button_close();

      //Child widgets:
      Gtk::Box m_VBox;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::Grid m_Grid;
      Gtk::Button m_ButtonClose;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL, 10),
      m_ButtonClose("_Close", true)
    {
      set_title("Gtk::ScrolledWindow example");
      set_size_request(300, 300);

      set_child(m_VBox);
      m_VBox.set_margin(10);

      /* The policy is one of Gtk::PolicyType::AUTOMATIC, or Gtk::PolicyType::ALWAYS.
       * Gtk::PolicyType::AUTOMATIC will automatically decide whether you need
       * scrollbars, whereas Gtk::PolicyType::ALWAYS will always leave the scrollbars
       * there. The first one is the horizontal scrollbar, the second, the vertical. */
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::ALWAYS);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);

      /* set the spacing to 10 on x and 10 on y */
      m_Grid.set_row_spacing(10);
      m_Grid.set_column_spacing(10);

      /* pack the grid into the scrolled window */
      m_ScrolledWindow.set_child(m_Grid);

      /* this simply creates a grid of toggle buttons
       * to demonstrate the scrolled window. */
      for (int i = 0; i < 10; i++)
      {
        for (int j = 0; j < 10; j++)
        {
          auto s = Glib::ustring::compose("button (%1,%2)", i, j);
          auto pButton = Gtk::make_managed<Gtk::ToggleButton>(s);
          m_Grid.attach(*pButton, i, j);
        }
      }

      /* Add a "close" button to the bottom of the dialog */
      m_VBox.append(m_ButtonClose);
      m_ButtonClose.set_halign(Gtk::Align::END);
      m_ButtonClose.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_close));

      /* Hitting the "Enter" key will cause this button to activate. */
      m_ButtonClose.grab_focus();
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_close()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Оквир размере

Елемент AspectFrame изгледа као Frame елемент, али такође намеће однос размере (однос ширине и висине) садржаног елемента, додајући додатни простор ако је потребно. На пример, ово би вам омогућило да прикажете фотографију без дозвољавања кориснику да је изобличи водоравно или усправно приликом промене величине.

Референца

##### Пример

Следећи програм користи Gtk::AspectFrame да представи област за цртање чији ће однос размере увек бити 2:1, без обзира на то како корисник мења величину прозора највишег нивоа.

Оквир размере

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:

      //Child widgets:
      Gtk::AspectFrame m_AspectFrame;
      Gtk::Frame m_Frame;
      Gtk::DrawingArea m_DrawingArea;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_AspectFrame(
        Gtk::Align::CENTER, /* center x */
        Gtk::Align::CENTER, /* center y */
        2.0, /* xsize/ysize = 2 */
        false /* ignore child's aspect */),
      m_Frame("2x1" /* label */)
    {
      set_title("Aspect Frame");

      // Set a child widget to the aspect frame */
      // Ask for a 200x200 window, but the AspectFrame will give us a 200x100
      // window since we are forcing a 2x1 aspect ratio */
      m_DrawingArea.set_content_width(200);
      m_DrawingArea.set_content_height(200);
      m_Frame.set_child(m_DrawingArea);
      m_AspectFrame.set_child(m_Frame);
      m_AspectFrame.set_margin(10);

      // Add the aspect frame to our toplevel window:
      set_child(m_AspectFrame);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Остали контејнери са једном ставком

Постоје и други контејнери са једном ставком. Погледајте референтну документацију за потпуни списак. Ево веза до неких примера програма који приказују контејнере који нису поменути на другим местима у овом упутству.

Изворни код, разграник

Изворни код, искок

### Контејнери са више ставки

Вишеструки контејнерски елементи имају и друге методе осим set_child() и unset_child(). Различити контејнери могу имати различите методе за додавање и уклањање садржаних елемената. На пример, Gtk::Box има append() и remove() као и друге методе. Метода remove() за вишеструке контејнере прима аргумент који одређује који елемент треба уклонити.

#### Паковање

Вероватно сте приметили да gtkmm прозори изгледају „еластично“ – обично се могу развући на много различитих начина. То је захваљујући систему за паковање елемената.

Многи ГУИ скупови алата захтевају да прецизно поставите елементе у прозор користећи апсолутно позиционирање, често уз помоћ визуелног уређивача. То доводи до неколико проблема:

- Елементи се не преуређују сами када се промени величина прозора. Неки елементи остају скривени када се прозор смањи, а појављује се много бескорисног простора када се прозор повећа.

- Немогуће је предвидети количину простора неопходну за текст након што се преведе на друге језике или прикаже другачијим фонтом. На Јуниксу је такође немогуће предвидети ефекте сваке теме и управника прозора.

- Промена распореда прозора „у ходу“, како би се на пример приказали неки додатни елементи, сложена је. Захтева заморно поновно израчунавање положаја сваког елемента.

gtkmm користи систем паковања како би решио ове проблеме. Уместо одређивања положаја и величине сваког елемента у прозору, своје елементе можете поређати у редове, колоне и/или мреже. gtkmm може аутоматски одредити величину вашег прозора на основу величина елемената које садржи. А величине елемената су, заузврат, одређене количином текста који садрже, или минималним и максималним величинама које одредите, и/или тиме како сте захтевали да се доступни простор подели између скупова елемената. Свој распоред можете усавршити одређивањем маргина и вредности центрирања за сваки од ваших елемената. gtkmm затим користи све ове информације како би разумно и глатко променио величину и положај свега када корисник манипулише прозором.

gtkmm распоређује елементе хијерархијски, користећи контејнере. Контејнерски елемент садржи друге елементе. Већина gtkmm елемената су контејнери. Прозори, језичци бележнице и дугмад су сви контејнерски елементи. Постоје две врсте контејнера: контејнери са једним садржаним елементом и контејнери са више садржаних елемената. Већина контејнерских елемената у gtkmm-у су контејнери са једним садржаним елементом, укључујући Gtk::Window.

Да, то је тачно: прозор може да садржи највише један елемент. Како онда можемо користити прозор за било шта корисно? Постављањем контејнера са више садржаних елемената у прозор. Најкориснији контејнерски елементи су Gtk::Grid и Gtk::Box.

- Gtk::Grid распоређује своје садржане елементе у редове и колоне. Користите attach() и attach_next_to() за уметање садржаних елемената.

- Gtk::Box распоређује своје садржане елементе усправно или водоравно. Користите append() за уметање садржаних елемената.

Постоји још неколико контејнера о којима ћемо такође расправљати.

Ако никада раније нисте користили скуп алата са паковањем, може бити потребно неко време да се навикнете. Вероватно ћете ипак открити да не морате толико да се ослањате на визуелне уређиваче образаца као што бисте то чинили са другим скуповима алата.

#### Побољшани „Здраво свете“

Погледајмо мало побољшани `helloworld`, који показује шта смо научили.

Здраво свете 2

Изворни ко̂д

File: `helloworld.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_HELLOWORLD_H
    #define GTKMM_EXAMPLE_HELLOWORLD_H

    #include <gtkmm/box.h>
    #include <gtkmm/button.h>
    #include <gtkmm/window.h>

    class HelloWorld : public Gtk::Window
    {
    public:
      HelloWorld();
      ~HelloWorld() override;

    protected:

      // Signal handlers:
      // Our new improved on_button_clicked().
      void on_button_clicked(const Glib::ustring& data);

      // Child widgets:
      Gtk::Box m_box1;
      Gtk::Button m_button1, m_button2;
    };

    #endif // GTKMM_EXAMPLE_HELLOWORLD_H

File: `helloworld.cc` (For use with gtkmm 4)

    #include "helloworld.h"
    #include <iostream>

    HelloWorld::HelloWorld()
    : m_button1("Button 1"),
      m_button2("Button 2")
    {
      // This just sets the title of our new window.
      set_title("Hello Buttons!");

      // Sets the margin around the box.
      m_box1.set_margin(10);

      // put the box into the main window.
      set_child(m_box1);

      // Now when the button is clicked, we call the on_button_clicked() function
      // with a pointer to "button 1" as its argument.
      m_button1.signal_clicked().connect(sigc::bind(
                  sigc::mem_fun(*this, &HelloWorld::on_button_clicked), "button 1"));

      // We use Gtk::Box::append() to pack this button into the box,
      // which has been packed into the window.
      // A widget's default behaviour is not to expand if extra space is available.
      // A container widget by default expands if any of the contained widgets
      // wants to expand.
      m_box1.append(m_button1);
      m_button1.set_expand();

      // call the same signal handler with a different argument,
      // passing a pointer to "button 2" instead.
      m_button2.signal_clicked().connect(sigc::bind(
                  sigc::mem_fun(*this, &HelloWorld::on_button_clicked), "button 2"));

      m_box1.append(m_button2);
      m_button2.set_expand();

      // Gtk::Widget::set_visible(true) is seldom needed. All widgets are visible by default.
    }

    HelloWorld::~HelloWorld()
    {
    }

    // Our new improved signal handler.  The data passed to this method is
    // printed to stdout.
    void HelloWorld::on_button_clicked(const Glib::ustring& data)
    {
      std::cout << "Hello World - " << data << " was pressed" << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "helloworld.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<HelloWorld>(argc, argv);
    }

Након изградње и покретања овог програма, покушајте да промените величину прозора како бисте видели понашање. Такође, покушајте да се играте са set_expand(), set_hexpand(), set_vexpand(), set_halign() и set_valign() док читате одељак Кутије.

#### Кутије

Већина паковања користи кутије као у горњем примеру. То су невидљиви контејнери у које можемо паковати наше елементе. Приликом паковања елемената у водоравну кутију, објекти се умећу водоравно слева надесно. У усправној кутији, елементи се пакују одозго надоле. Можете користити било коју комбинацију кутија унутар или поред других кутија како бисте постигли жељени ефекат.

##### Додавање елемената

###### Опције паковања по елементу

Метод append() смешта елементе унутар ових контејнера. Почеће од врха и ићи надоле у Box објекту са усправним усмерењем, или паковати слева надесно у Box објекту са водоравним усмерењем. Ако је незгодно додавати елементе овим редом, користите insert_child_after() или insert_child_at_start(). Ми ћемо користити append() у нашим примерима.

Постоји неколико опција које одређују како ће елементи бити спаковани, што у почетку може бити збуњујуће. Можете изменити паковање користећи set_expand(), set_hexpand(), set_vexpand(), set_halign(), set_valign() и/или set_margin() на садржаним елементима. Ако имате потешкоћа, понекад је добра идеја поиграти се са Cambalache дизајнером графичког прочеља како бисте видели шта је све могуће. Потом можете користити Gtk::Builder АПИ за учитавање вашег графичког прочеља у време извршавања.

У основи постоји пет различитих стилова, као што је приказано на овој слици:

Паковање кутија 1

Сваки ред садржи једну водоравну Box кутију са неколико дугмића. Свако дугме у реду је спаковано у Box кутију са истим аргументима за методе set_hexpand(), set_halign(), set_margin_start() и set_margin_end().

Референца

###### Опције паковања по контејнеру

Ево конструктора за Box елемент, као и метода за постављање опција паковања по контејнеру:

    Gtk::Box(Gtk::Orientation orientation = Gtk::Orientation::HORIZONTAL, int spacing = 0);
    void set_orientation(Gtk::Orientation orientation);
    void set_spacing(int spacing);
    void set_homogeneous(bool homogeneous = true);

Прослеђивање вредности `true` методу set_homogeneous() ће проузроковати да сви садржани елементи буду исте величине. spacing је (најмањи) број пиксела који се оставља између сваког елемента.

Која је разлика између размака (поставља се при стварању кутије) и маргина (постављају се посебно за сваки садржани елемент)? Размак се додаје између објеката, а маргине се додају на једну или више страна елемента. Следећа фигура би то требала разјаснити. Приказане маргине су лева и десна маргина сваког дугмета у реду.

Паковање кутија 2

##### Gtk::Application и опције командне линије

Следећи пример програма захтева опцију командне линије. Изворни ко̂д приказује два начина руковања опцијама командне линије у комбинацији са Gtk::Application.

- Обрадите опције у `main()` и сакријте их од Gtk::Application постављањем `argc = 1` у позиву Gtk::Application::run().

- Проследите све опције командне линије методу Gtk::Application::run() и додајте заставицу `Gio::Application::Flags::HANDLES_COMMAND_LINE` у Gtk::Application::create(). Повежите руковалац сигнала на сигнал `command_line` и обрадите опције командне линије у руковаоцу сигнала.

  Морате поставити опциони параметар `after = false` у позиву `signal_command_line().connect()`, јер ваш руковалац сигнала мора бити позван пре подразумеваног руковаоца сигнала. Такође морате позвати Gio::Application::activate() у руковаоцу сигнала, осим ако не желите да ваша апликација изађе без приказивања свог главног прозора. (Gio::Application је базни разред за Gtk::Application.)

##### Пример

Ево изворног кода за пример који је произвео снимке екрана изнад. Када покренете овај пример, проследите број између 1 и 3 као опцију командне линије, да бисте видели различите опције паковања у употреби.

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow(int which);
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_quit_clicked();

      //Child widgets:
      Gtk::Button m_button;
      Gtk::Box m_box1;
      Gtk::Box m_boxQuit;
      Gtk::Button m_buttonQuit;

      Gtk::Label m_Label1, m_Label2;

      Gtk::Separator m_separator1, m_separator2;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `packbox.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_PACKBOX_H
    #define GTKMM_EXAMPLE_PACKBOX_H

    #include <gtkmm.h>

    class PackBox : public Gtk::Box
    {
    public:
      PackBox(bool homogeneous = false, int spacing = 0, bool expand = false,
        Gtk::Align align = Gtk::Align::FILL, int margin = 0);

    protected:
      Gtk::Button m_buttons[4];
    };

    #endif //GTKMM_EXAMPLE_PACKBOX_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include <iostream>
    #include "examplewindow.h"
    #include "packbox.h"

    ExampleWindow::ExampleWindow(int which)
    : m_box1(Gtk::Orientation::VERTICAL),
      m_buttonQuit("Quit")
    {
      set_title("Gtk::Box example");

      m_separator1.set_margin_top(5);
      m_separator1.set_margin_bottom(5);
      m_separator2.set_margin_top(5);
      m_separator2.set_margin_bottom(5);

      switch(which)
      {
        case 1:
        {
          m_Label1.set_text("Gtk::Box(Gtk::Orientation::HORIZONTAL, 0); set_homogeneous(false);");

          // Align the label to the left side.
          m_Label1.set_halign(Gtk::Align::START);
          m_Label1.set_valign(Gtk::Align::START);

          // Pack the label into the vertical box (vbox box1).  Remember that
          // widgets added to a vbox will be packed one on top of the other in
          // order.
          m_box1.append(m_Label1);

          // Create a PackBox - homogeneous = false, spacing = 0,
          // expand = false, Gtk::Align::FILL, margin = 0
          // These are the default values.
          auto pPackBox = Gtk::make_managed<PackBox>();
          m_box1.append(*pPackBox);

          // Create a PackBox - homogeneous = false, spacing = 0,
          // expand = true, Gtk::Align::CENTER, margin = 0
          pPackBox = Gtk::make_managed<PackBox>(false, 0, true, Gtk::Align::CENTER);
          m_box1.append(*pPackBox);

          // Create a PackBox - homogeneous = false, spacing = 0,
          // expand = true, Gtk::Align::FILL, margin = 0
          pPackBox = Gtk::make_managed<PackBox>(false, 0, true);
          m_box1.append(*pPackBox);

          // pack the separator into the vbox.  Remember each of these
          // widgets are being packed into a vbox, so they'll be stacked
          // vertically.
          m_box1.append(m_separator1);

          // create another new label, and show it.
          m_Label2.set_text("Gtk::Box(Gtk::Orientation::HORIZONTAL, 0); set_homogeneous(true);");
          m_Label2.set_halign(Gtk::Align::START);
          m_Label2.set_valign(Gtk::Align::START);
          m_box1.append(m_Label2);

          // Args are: homogeneous, spacing, expand, align, margin
          pPackBox = Gtk::make_managed<PackBox>(true, 0, true, Gtk::Align::CENTER);
          m_box1.append(*pPackBox);

          // Args are: homogeneous, spacing, expand, align, margin
          pPackBox = Gtk::make_managed<PackBox>(true, 0, true);
          m_box1.append(*pPackBox);

          m_box1.append(m_separator2);

          break;
        }

        case 2:
        {
          m_Label1.set_text("Gtk::Box(Gtk::Orientation::HORIZONTAL, 10); set_homogeneous(false);");
          m_Label1.set_halign(Gtk::Align::START);
          m_Label1.set_valign(Gtk::Align::START);
          m_box1.append(m_Label1);

          auto pPackBox = Gtk::make_managed<PackBox>(false, 10, true, Gtk::Align::CENTER);
          m_box1.append(*pPackBox);

          pPackBox = Gtk::make_managed<PackBox>(false, 10, true);
          m_box1.append(*pPackBox);

          m_box1.append(m_separator1);

          m_Label2.set_text("Gtk::Box(Gtk::Orientation::HORIZONTAL, 0); set_homogeneous(false);");
          m_Label2.set_halign(Gtk::Align::START);
          m_Label2.set_valign(Gtk::Align::START);
          m_box1.append(m_Label2);

          pPackBox = Gtk::make_managed<PackBox>(false, 0, false, Gtk::Align::FILL, 10);
          m_box1.append(*pPackBox);

          pPackBox = Gtk::make_managed<PackBox>(false, 0, true, Gtk::Align::FILL, 10);
          m_box1.append(*pPackBox);

          m_box1.append(m_separator2);

          break;
        }

        case 3:
        {
          // This demonstrates the ability to use Gtk::Align::END to
          // right justify widgets.  First, we create a new box as before.
          auto pPackBox = Gtk::make_managed<PackBox>();

          // create the label that will be put at the end.
          m_Label1.set_text("end");

          // pack it using Gtk::Align::END, so it is put on the right side
          // of the PackBox.
          m_Label1.set_halign(Gtk::Align::END);
          m_Label1.set_hexpand(true);
          pPackBox->append(m_Label1);

          m_box1.append(*pPackBox);

          // This explicitly sets the separator to 700 pixels wide by 5 pixels
          // high.  This is so the hbox we created will also be 700 pixels wide,
          // and the "end" label will be separated from the other labels in the
          // hbox.  Otherwise, all the widgets in the hbox would be packed as
          // close together as possible.
          m_separator1.set_size_request(700, 5);

          // pack the separator into the vbox.
          m_box1.append(m_separator1);

          break;
        }

        default:
        {
          std::cerr << "Unexpected command-line option." << std::endl;
          break;
        }
      }

      // Connect the signal to hide the window:
      m_buttonQuit.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit_clicked) );

      // pack the button into the quitbox.
      m_boxQuit.append(m_buttonQuit);
      m_buttonQuit.set_hexpand(true);
      m_buttonQuit.set_halign(Gtk::Align::CENTER);
      m_box1.append(m_boxQuit);

      // pack the vbox (box1) which now contains all our widgets, into the
      // main window.
      set_child(m_box1);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit_clicked()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>
    #include <iostream>
    #include <cstdlib>

    #define GTK_APPLICATION_RECEIVES_COMMAND_LINE_ARGUMENTS 0

    #if GTK_APPLICATION_RECEIVES_COMMAND_LINE_ARGUMENTS
    namespace
    {
    int on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine>& command_line,
                        Glib::RefPtr<Gtk::Application>& app)
    {
      int argc = 0;
      char** argv = command_line->get_arguments(argc);

      for (int i = 0; i < argc; ++i)
        std::cout << "argv[" << i << "] = " << argv[i] << std::endl;

      app->activate(); // Without activate() the window won't be shown.
      return EXIT_SUCCESS;
    }
    } // anonymous namespace
    #endif

    int main(int argc, char *argv[])
    {
      if (argc != 2)
      {
        std::cerr << "Usage: example <num>, where <num> is 1, 2, or 3." << std::endl;
        return EXIT_FAILURE;
      }

      int argc1 = argc;

    #if GTK_APPLICATION_RECEIVES_COMMAND_LINE_ARGUMENTS
      // The Gio::Application::Flags::HANDLES_COMMAND_LINE flag and the
      // on_command_line() signal handler are not necessary. This program is simpler
      // without them, and with argc = 1 in the call to Gtk::Application::make_window_and_run().
      // They are included to show a program with Gio::Application::Flags::HANDLES_COMMAND_LINE.
      // Gio::Application::Flags::NON_UNIQUE makes it possible to run several instances of
      // this application simultaneously.
      auto app = Gtk::Application::create(
        "org.gtkmm.example", Gio::Application::Flags::HANDLES_COMMAND_LINE | Gio::Application::Flags::NON_UNIQUE);

      // Note after = false.
      // Only one signal handler is invoked. This signal handler must run before
      // the default signal handler, or else it won't run at all.
      app->signal_command_line().connect(
        [&app](const auto& command_line)
        { return on_command_line(command_line, app); }, false);
    #else
      // Gio::Application::Flags::NON_UNIQUE makes it possible to run several instances of
      // this application simultaneously.
      argc1 = 1; // Don't give the command line arguments to Gtk::Application.
      auto app = Gtk::Application::create("org.gtkmm.example", Gio::Application::Flags::NON_UNIQUE);
    #endif

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc1, argv, std::atoi(argv[1]));
    }

File: `packbox.cc` (For use with gtkmm 4)

    #include "packbox.h"
    #include <map>

    namespace
    {
      const std::map<Gtk::Align, Glib::ustring> align_string = {
        {Gtk::Align::FILL, "Gtk::Align::FILL"},
        {Gtk::Align::START, "Gtk::Align::START"},
        {Gtk::Align::END, "Gtk::Align::END"},
        {Gtk::Align::CENTER, "Gtk::Align::CENTER"},
        {Gtk::Align::BASELINE, "Gtk::Align::BASELINE"},
      };
    }

    PackBox::PackBox(bool homogeneous, int spacing, bool expand, Gtk::Align align, int margin)
    : Gtk::Box(Gtk::Orientation::HORIZONTAL, spacing)
    {
      set_homogeneous(homogeneous);

      m_buttons[0].set_label("box.append(button);");
      m_buttons[1].set_label("expand=" + Glib::ustring(expand ? "true" : "false"));
      m_buttons[2].set_label(align_string.at(align));
      m_buttons[3].set_label("margin=" + Glib::ustring::format(margin));

      for (auto& button : m_buttons)
      {
        append(button);
        button.set_hexpand(expand);
        button.set_halign(align);
        button.set_margin_start(margin);
        button.set_margin_end(margin);
      }
    }

#### Мрежа

Grid динамички распоређује садржане елементе у редове и колоне. Димензије мреже не морају бити наведене у конструктору.

Садржани елементи се могу простирати кроз више редова или колона, коришћењем attach(), или се могу додати поред постојећег елемента унутар мреже помоћу attach_next_to(). Појединачни редови и колоне мреже се могу поставити да имају уједначену висину или ширину помоћу set_row_homogeneous() и set_column_homogeneous().

Можете поставити својства margin и expand садржаних Widget елемената да бисте контролисали њихов размак и понашање приликом промене величине мреже.

Референце

##### Пример

Овај пример прави прозор са три дугмета у мрежи. Прва два дугмета су у горњем реду, слева надесно. Треће дугме је прикачено испод првог дугмета, у новом доњем реду, простирући се кроз две колоне.

Мрежа

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    private:
      // Signal handlers:
      void on_button_quit();
      void on_button_numbered(const Glib::ustring& data);

      // Child widgets:
      Gtk::Grid m_grid;
      Gtk::Button m_button_1, m_button_2, m_button_quit;
    };

    #endif /* GTKMM_EXAMPLEWINDOW_H */

File: `examplewindow.cc` (For use with gtkmm 4)

    #include <iostream>
    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_button_1("button 1"),
      m_button_2("button 2"),
      m_button_quit("Quit")
    {
      set_title("Gtk::Grid");

      m_grid.set_margin(12);
      set_child(m_grid);

      m_grid.attach(m_button_1, 0, 0);
      m_grid.attach(m_button_2, 1, 0);
      m_grid.attach_next_to(m_button_quit, m_button_1, Gtk::PositionType::BOTTOM, 2, 1);

      m_button_1.signal_clicked().connect(
        sigc::bind( sigc::mem_fun(*this, &ExampleWindow::on_button_numbered), "button 1") );
      m_button_2.signal_clicked().connect(
        sigc::bind( sigc::mem_fun(*this, &ExampleWindow::on_button_numbered), "button 2") );

      m_button_quit.signal_clicked().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_button_quit) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void
    ExampleWindow::on_button_numbered(const Glib::ustring& data)
    {
      std::cout << data << " was pressed" << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Бележница

Notebook има скуп наслаганих `страница`, од којих свака садржи елементе. Означени `језичци` омогућавају кориснику да изабере странице. Notebook елементи омогућавају да се неколико скупова елемената постави у мали простор, приказујући само једну страницу у исто време. На пример, често се користе у прозорчићима са поставкама.

Користите методе append_page(), prepend_page() и insert_page() за додавање страница са језичцима у `Notebook`, прослеђујући садржани елемент и назив за језичак.

Да бисте открили тренутно видљиву страницу, користите метод get_current_page(). Ово враћа број странице, а затим ће вам позивање get_nth_page() са тим бројем дати показивач на стварни садржани елемент.

Да бисте програмски променили изабрану страницу, користите метод set_current_page().

Референце

##### Пример

Бележница

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_quit();
      void on_notebook_switch_page(Gtk::Widget* page, guint page_num);

      //Child widgets:
      Gtk::Box m_VBox;
      Gtk::Notebook m_Notebook;
      Gtk::Label m_Label1, m_Label2;

      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include <iostream>
    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Label1("Contents of tab 1"),
      m_Label2("Contents of tab 2"),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::Notebook example");
      set_default_size(400, 200);

      m_VBox.set_margin(10);
      set_child(m_VBox);

      //Add the Notebook, with the button underneath:
      m_Notebook.set_margin(10);
      m_Notebook.set_expand();
      m_VBox.append(m_Notebook);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::CENTER);
      m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit) );

      //Add the Notebook pages:
      m_Notebook.append_page(m_Label1, "First");
      m_Notebook.append_page(m_Label2, "Second");

      m_Notebook.signal_switch_page().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_notebook_switch_page) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_notebook_switch_page(Gtk::Widget* /* page */, guint page_num)
    {
      std::cout << "Switched to tab with index " << page_num << std::endl;

      //You can also use m_Notebook.get_current_page() to get this index.
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Помоћник

Gtk::Assistant је застарео од gtkmm 4.10. Не постоји замена у gtkmm-у. libadwaita (C библиотека) има заменске делове (као што је AdwCarousel). У неким случајевима, Gtk::Notebook може бити прихватљива замена.

Помоћник дели сложену операцију на кораке. Сваки корак је страница која садржи заглавље, садржани елемент и област радње. Област радње помоћника има навигациону дугмад која се аутоматски ажурирају у зависности од врсте странице, постављене помоћу set_page_type().

Користите методе append_page(), prepend_page и insert_page() за додавање страница у Помоћника, достављајући садржани елемент за сваку страницу.

Да бисте одредили тренутно видљиву страницу, користите метод get_current_page() и проследите резултат у get_nth_page(), који враћа показивач на стварни елемент. Да бисте програмски променили тренутну страницу, користите метод set_current_page().

Да бисте поставили наслов странице, користите метод set_page_title().

Да бисте додали елементе у област радње, користите метод add_action_widget(). Они ће бити упаковани заједно са подразумеваном дугмадима. Користите метод remove_action_widget() за уклањање елемената.

Референца

##### Пример

Помоћник

Изворни код

File: `exampleassistant.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEASSISTANT_H
    #define GTKMM_EXAMPLEASSISTANT_H

    #include <gtkmm.h>

    class ExampleAssistant : public Gtk::Assistant
    {
    public:
      ExampleAssistant();
      virtual ~ExampleAssistant();

      void get_result(bool& check_state, Glib::ustring& entry_text);

    private:
      // Signal handlers:
      void on_assistant_apply();
      void on_assistant_cancel();
      void on_assistant_close();
      void on_assistant_prepare(Gtk::Widget* widget);
      void on_entry_changed();

      // Member functions:
      void print_status();

      // Child widgets:
      Gtk::Box m_box;
      Gtk::Label m_label1, m_label2;
      Gtk::CheckButton m_check;
      Gtk::Entry m_entry;
    };

    #endif /* GTKMM_EXAMPLEASSISTANT_H */

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include "exampleassistant.h"
    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    private:
      // Signal handlers:
      void on_button_clicked();
      void on_assistant_apply();

      // Child widgets:
      Gtk::Grid m_grid;
      Gtk::Button m_button;
      Gtk::Label m_label1, m_label2;
      Gtk::CheckButton m_check;
      Gtk::Entry m_entry;
      ExampleAssistant m_assistant;
    };

    #endif /* GTKMM_EXAMPLEWINDOW_H */

File: `exampleassistant.cc` (For use with gtkmm 4)

    #include <iostream>
    #include "exampleassistant.h"

    ExampleAssistant::ExampleAssistant()
    : m_box(Gtk::Orientation::HORIZONTAL, 12),
      m_label1("Type text to allow the assistant to continue:"),
      m_label2("Confirmation page"),
      m_check("Optional extra information")
    {
      set_title("Gtk::Assistant example");
      set_default_size(400, 200);

      m_box.append(m_label1);
      m_box.append(m_entry);
      m_label1.set_wrap();
      m_label1.set_valign(Gtk::Align::CENTER);
      m_entry.set_valign(Gtk::Align::CENTER);

      append_page(m_box);
      append_page(m_check);
      append_page(m_label2);

      set_page_title(*get_nth_page(0), "Page 1");
      set_page_title(*get_nth_page(1), "Page 2");
      set_page_title(*get_nth_page(2), "Confirmation");

      set_page_complete(m_check, true);
      set_page_complete(m_label2, true);

      set_page_type(m_box, Gtk::AssistantPage::Type::INTRO);
      set_page_type(m_label2, Gtk::AssistantPage::Type::CONFIRM);

      signal_apply().connect(sigc::mem_fun(*this,
        &ExampleAssistant::on_assistant_apply));
      signal_cancel().connect(sigc::mem_fun(*this,
        &ExampleAssistant::on_assistant_cancel));
      signal_close().connect(sigc::mem_fun(*this,
        &ExampleAssistant::on_assistant_close));
      signal_prepare().connect(sigc::mem_fun(*this,
        &ExampleAssistant::on_assistant_prepare));

      m_entry.signal_changed().connect(sigc::mem_fun(*this,
        &ExampleAssistant::on_entry_changed));
    }

    ExampleAssistant::~ExampleAssistant()
    {
    }

    void ExampleAssistant::get_result(bool& check_state, Glib::ustring& entry_text)
    {
      check_state = m_check.get_active();
      entry_text = m_entry.get_text();
    }

    void ExampleAssistant::on_assistant_apply()
    {
      std::cout << "Apply was clicked";
      print_status();
    }

    void ExampleAssistant::on_assistant_cancel()
    {
      std::cout << "Cancel was clicked";
      print_status();
      set_visible(false);
    }

    void ExampleAssistant::on_assistant_close()
    {
      std::cout << "Assistant was closed";
      print_status();
      set_visible(false);
    }

    void ExampleAssistant::on_assistant_prepare(Gtk::Widget* /* widget */)
    {
      set_title(Glib::ustring::compose("Gtk::Assistant example (Page %1 of %2)",
        get_current_page() + 1, get_n_pages()));
    }

    void ExampleAssistant::on_entry_changed()
    {
      // The page is only complete if the entry contains text.
      if(m_entry.get_text_length())
        set_page_complete(m_box, true);
      else
        set_page_complete(m_box, false);
    }

    void ExampleAssistant::print_status()
    {
      std::cout << ", entry contents: \"" << m_entry.get_text()
        << "\", checkbutton status: " << m_check.get_active() << std::endl;
    }

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include "exampleassistant.h"

    ExampleWindow::ExampleWindow()
    : m_button("Show the assistant"),
      m_label1("State of assistant checkbutton:", Gtk::Align::START, Gtk::Align::CENTER),
      m_label2("Contents of assistant entry:", Gtk::Align::START, Gtk::Align::CENTER)
    {
      set_title("Gtk::Assistant example");

      m_grid.set_row_homogeneous(true);
      m_grid.set_column_spacing(5);
      m_grid.set_margin(12);

      m_grid.attach(m_button, 0, 0, 2, 1);
      m_button.set_hexpand(true);
      m_button.set_valign(Gtk::Align::CENTER);

      m_grid.attach(m_label1, 0, 1, 1, 1);

      m_grid.attach(m_label2, 0, 2, 1, 1);

      m_grid.attach(m_check, 1, 1, 1, 1);
      m_check.set_halign(Gtk::Align::START);

      m_grid.attach(m_entry, 1, 2, 1, 1);
      m_entry.set_hexpand(true);

      set_child(m_grid);

      m_button.signal_clicked().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_button_clicked));
      m_assistant.signal_apply().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_assistant_apply));

      m_check.set_sensitive(false);
      m_entry.set_sensitive(false);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_assistant_apply()
    {
      bool check_state;
      Glib::ustring entry_text;

      m_assistant.get_result(check_state, entry_text);
      m_check.set_active(check_state);
      m_entry.set_text(entry_text);
    }

    void ExampleWindow::on_button_clicked()
    {
      m_assistant.set_visible(true);
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Остали контејнери са више ставки

Постоје и други контејнери са више ставки. Погледајте референтну документацију за потпуни списак. Овде су везе до неких примерних програма који приказују контејнере, а који нису поменути на другим местима у овом упутству.

Изворни код, ActionBar

Изворни код, FlowBox

## ListView, GridView, ColumnView

Спискови су намењени за коришћење кад год програмери желе да прикажу много објеката на приближно исти начин. Сасвим су прикладни за веома кратке спискове од само 2 или 3 ставке, али се углавном добро прилагођавају и хиљадама ставки.

Спискови су намењени за коришћење са променљивим подацима, како са самим ставкама које се мењају, тако и са списком који додаје и уклања ставке. Наравно, они раде подједнако добро и са статичким подацима.

Поглавље Преглед елемента списка у ГТК документацији садржи више информација о елементима списка.

Неки примери су приказани у овом поглављу. Постоји још примера у директоријуму listmodelviews међу примерима gtkmm-documentation-а.

### Модел података

Модел података је класа која примењује прочеље Gio::ListModel. Примери таквих класа су Gio::ListStore (не треба га мешати са застарелим Gtk::ListStore), Gtk:StringList, Gtk:DirectoryList и Pango::FontMap.

Елементи у моделу се називају ставке. Све ставке су примерци подкласе Glib::Object. На пример, можете имати ColumnView са једном колоном целог броја и једном текстуалном колоном, овако:

    class ModelColumns : public Glib::Object
    {
    public:
      int m_col_id;
      Glib::ustring m_col_name;

      static Glib::RefPtr<ModelColumns> create(
        int col_id, const Glib::ustring& col_name)
      {
        return Glib::make_refptr_for_instance<ModelColumns>(
          new ModelColumns(col_id, col_name));
      }

    protected:
      ModelColumns(int col_id, const Glib::ustring& col_name)
      : m_col_id(col_id), m_col_name(col_name)
      {}
    };

    Glib::RefPtr<Gio::ListStore<ModelColumns>> m_ListStore;

Свака ставка у моделу има положај који је неозначени цео број који описује где се у моделу ставка налази. Прва ставка у моделу је на положају 0. Положај ставке се наравно може променити како се друге ставке додају у модел или уклањају из њега.

Gio::ListStore референца

StringList референца

DirectoryList референца

### Модел избора

Модел избора је класа која примењује прочеље Gtk::SelectionModel. Можете бирати између NoSelection, SingleSelection и MultiSelection.

NoSelection референца

SingleSelection референца

MultiSelection референца

### Фабрика

Подаци из модела података се додају у преглед помоћу фабрике, која је подкласа ListItemFactory. Постоји само једна таква подкласа у gtkmm-у, SignalListItemFactory. Подаци из модела се додају у преглед помоћу руковалаца сигналима који су повезани на SignalListItemFactory.

SignalListItemFactory референца

### Преглед

Преглед је елемент који приказује податке модела и омогућава кориснику да комуницира са њим. Преглед може приказати све колоне модела, или само неке, и може их приказати на различите начине.

Важан захтев за прегледе (нарочито прегледе дугих спискова) је да морају знати које ставке нису видљиве како би се могле поново искористити. Прегледи то постижу применом прочеља Scrollable и очекивањем да буду постављени директно у ScrolledWindow.

Постоје различити елементи прегледа које можете изабрати.

#### ListView

ListView приказује једнодимензиони списак са једном колоном.

Референце

##### Пример

ListView

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handlers:
      void on_button_quit();
      void on_setup_label(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item);

      // Child widgets:
      Gtk::Box m_VBox;
      Gtk::Label m_Heading;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::ListView m_ListView;
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;

      Glib::RefPtr<Gtk::StringList> m_StringList;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Heading("<b>Name</b>", Gtk::Align::START),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::ListView (Gtk::StringList) example");
      set_default_size(300, 200);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      // Add the ListView, inside a ScrolledWindow, with the heading above
      // and the button underneath.
      m_Heading.set_use_markup();
      m_VBox.append(m_Heading);
      m_ScrolledWindow.set_child(m_ListView);

      // Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(5);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_quit));

      // Create the list model:
      m_StringList = Gtk::StringList::create({"Billy Bob", "Joey Jojo", "Rob McRoberts"});

      // Set list model and selection model.
      auto selection_model = Gtk::SingleSelection::create(m_StringList);
      selection_model->set_autoselect(false);
      selection_model->set_can_unselect(true);
      m_ListView.set_model(selection_model);
      m_ListView.add_css_class("data-table"); // high density table

      // Add the factory for the ListView's single column.
      auto factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_setup_label));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_name));
      m_ListView.set_factory(factory);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_setup_label(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      list_item->set_child(*Gtk::make_managed<Gtk::Label>("", Gtk::Align::START));
    }

    void ExampleWindow::on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto pos = list_item->get_position();
      if (pos == GTK_INVALID_LIST_POSITION)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(m_StringList->get_string(pos));
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### GridView

GridView приказује дводимензиону мрежу.

Референце

##### Пример

GridView

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>
    #include <gdkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      class ModelColumns;
      // Signal handlers:
      void on_button_quit();
      void on_item_activated(unsigned int position);
      void on_selection_changed();
      void on_setup_listitem(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_listitem(const Glib::RefPtr<Gtk::ListItem>& list_item);
      int on_model_sort(const Glib::RefPtr<const ModelColumns>& a, const Glib::RefPtr<const ModelColumns>& b);

      void add_entry(const std::string& filename, const Glib::ustring& description);

      // A Gio::ListStore stores filename, description and texture.
      class ModelColumns : public Glib::Object
      {
      public:
        std::string m_filename;
        Glib::ustring  m_description;
        Glib::RefPtr<Gdk::Texture> m_texture;

        static Glib::RefPtr<ModelColumns> create(const std::string& filename,
          const Glib::ustring& description, const Glib::RefPtr<Gdk::Texture>& texture)
        {
          return Glib::make_refptr_for_instance<ModelColumns>(
            new ModelColumns(filename, description, texture));
        }

      protected:
        ModelColumns(const std::string& filename, const Glib::ustring& description,
          const Glib::RefPtr<Gdk::Texture>& texture)
        : m_filename(filename), m_description(description), m_texture(texture)
        { }
      }; // ModelColumns

      Glib::RefPtr<Gio::ListStore<ModelColumns>> m_data_model;
      Glib::RefPtr<Gtk::SingleSelection> m_selection_model;
      Glib::RefPtr<Gtk::SignalListItemFactory> m_factory;

      // Child widgets:
      Gtk::Box m_VBox;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::GridView m_GridView;
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <array>
    #include <iostream>

    namespace
    {
      struct GridEntry
      {
        std::string m_filename;
        Glib::ustring m_description;
      };

      std::array<GridEntry, 8> entries =
      {
        GridEntry{"mozilla-firefox.png", "<b>Mozilla Firefox</b> Logo"},
        GridEntry{"xmms.xpm",            "<b>XMMS</b> Logo"},
        GridEntry{"gnome-dice-1.svg",    "<b>Gnome Dice 1</b> Logo"},
        GridEntry{"gnome-dice-2.svg",    "<b>Gnome Dice 2</b> Logo"},
        GridEntry{"gnome-dice-3.svg",    "<b>Gnome Dice 3</b> Logo"},
        GridEntry{"gnome-dice-4.svg",    "<b>Gnome Dice 4</b> Logo"},
        GridEntry{"gnome-dice-5.svg",    "<b>Gnome Dice 5</b> Logo"},
        GridEntry{"gnome-dice-6.svg",    "<b>Gnome Dice 6</b> Logo"}
      };

    } // anonymous namespace

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::GridView (Gio::ListStore) example");
      set_default_size(400, 400);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      // Add the GridView inside a ScrolledWindow, with the button underneath:
      m_ScrolledWindow.set_child(m_GridView);

      // Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(6);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_quit));

      // Create the data model:
      m_data_model = Gio::ListStore<ModelColumns>::create();
      m_selection_model = Gtk::SingleSelection::create(m_data_model);
      m_selection_model->set_autoselect(false);

      m_factory = Gtk::SignalListItemFactory::create();
      m_factory->signal_setup().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_setup_listitem));
      m_factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_listitem));

      // Fill the Gio::ListStore's data model and sort it.
      for (const auto& entry : entries)
        add_entry(entry.m_filename, entry.m_description);
      m_data_model->sort(sigc::mem_fun(*this, &ExampleWindow::on_model_sort));

      m_GridView.set_model(m_selection_model);
      m_GridView.set_factory(m_factory);

      m_GridView.signal_activate().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_item_activated));
      m_selection_model->property_selected().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_selection_changed));
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_setup_listitem(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      // Each ListItem contains a vertical Box with an Image and a Label.
      auto vBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
      auto picture = Gtk::make_managed<Gtk::Picture>();
      picture->set_can_shrink(false);
      picture->set_halign(Gtk::Align::CENTER);
      picture->set_valign(Gtk::Align::END);
      vBox->append(*picture);
      vBox->append(*Gtk::make_managed<Gtk::Label>());
      list_item->set_child(*vBox);
    }

    void ExampleWindow::on_bind_listitem(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto vBox = dynamic_cast<Gtk::Box*>(list_item->get_child());
      if (!vBox)
        return;
      auto picture = dynamic_cast<Gtk::Picture*>(vBox->get_first_child());
      if (!picture)
        return;
      auto label = dynamic_cast<Gtk::Label*>(picture->get_next_sibling());
      if (!label)
        return;
      picture->set_paintable(col->m_texture);
      label->set_markup(col->m_description);
    }

    void ExampleWindow::on_item_activated(unsigned int position)
    {
      auto col = m_data_model->get_item(position);
      if (!col)
        return;

      const std::string filename = col->m_filename;
      const Glib::ustring description = col->m_description;

      std::cout  << "Item activated: filename=" << filename
        << ", description=" << description << std::endl;
    }

    void ExampleWindow::on_selection_changed()
    {
      auto position = m_selection_model->get_selected();
      auto col = m_data_model->get_item(position);
      if (!col)
        return;

      const std::string filename = col->m_filename;
      const Glib::ustring description = col->m_description;

      std::cout  << "Selection Changed to: filename=" << filename
        << ", description=" << description << std::endl;
    }

    int ExampleWindow::on_model_sort(const Glib::RefPtr<const ModelColumns>& a,
      const Glib::RefPtr<const ModelColumns>& b)
    {
      return (a->m_description < b->m_description) ? -1 : ((a->m_description > b->m_description) ? 1 : 0);
    }

    void ExampleWindow::add_entry(const std::string& filename,
            const Glib::ustring& description )
    {
      try
      {
         auto pixbuf = Gdk::Pixbuf::create_from_file(filename);
         auto texture = Gdk::Texture::create_for_pixbuf(pixbuf);
         m_data_model->append(ModelColumns::create(filename, description, texture));
      }
      catch (const Gdk::PixbufError& ex)
      {
        std::cerr << "Gdk::PixbufError: " << ex.what() << std::endl;
      }
      catch (const Glib::FileError& ex)
      {
        std::cerr << "Glib::FileError: " << ex.what() << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### ColumnView

ColumnView приказује једнодимензиони списак са једном или више колона.

Референце

##### Пример

ColumnView

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handlers:
      void on_button_quit();
      void on_setup_label(const Glib::RefPtr<Gtk::ListItem>& list_item, Gtk::Align halign);
      void on_setup_progressbar(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_id(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_number(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_percentage(const Glib::RefPtr<Gtk::ListItem>& list_item);

      // A Gio::ListStore item.
      class ModelColumns : public Glib::Object
      {
      public:
        unsigned int m_col_id;
        Glib::ustring m_col_name;
        short m_col_number;
        int m_col_percentage;

        static Glib::RefPtr<ModelColumns> create(unsigned int col_id,
          const Glib::ustring& col_name, short col_number, int col_percentage)
        {
          return Glib::make_refptr_for_instance<ModelColumns>(
            new ModelColumns(col_id, col_name, col_number, col_percentage));
        }

      protected:
        ModelColumns(unsigned int col_id, const Glib::ustring& col_name,
          short col_number, int col_percentage)
        : m_col_id(col_id), m_col_name(col_name), m_col_number(col_number),
          m_col_percentage(col_percentage)
        {}
      }; // ModelColumns

      // Child widgets:
      Gtk::Box m_VBox;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::ColumnView m_ColumnView;
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;

      Glib::RefPtr<Gio::ListStore<ModelColumns>> m_ListStore;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::ColumnView (Gio::ListStore) example");
      set_default_size(500, 250);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      // Add the ColumnView, inside a ScrolledWindow, with the button underneath:
      m_ScrolledWindow.set_child(m_ColumnView);

      // Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(5);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_quit));

      // Create the List model:
      m_ListStore = Gio::ListStore<ModelColumns>::create();
      m_ListStore->append(ModelColumns::create(1, "Billy Bob", 10, 15));
      m_ListStore->append(ModelColumns::create(2, "Joey Jojo", 20, 40));
      m_ListStore->append(ModelColumns::create(3, "Rob McRoberts", 30, 70));

      // Set list model and selection model.
      auto selection_model = Gtk::SingleSelection::create(m_ListStore);
      selection_model->set_autoselect(false);
      selection_model->set_can_unselect(true);
      m_ColumnView.set_model(selection_model);
      m_ColumnView.add_css_class("data-table"); // high density table

      // Make the columns reorderable.
      // This is not necessary, but it's nice to show the feature.
      m_ColumnView.set_reorderable(true);

      // Add the ColumnView's columns:

      // Id column
      auto factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
        &ExampleWindow::on_setup_label), Gtk::Align::END));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_id));
      auto column = Gtk::ColumnViewColumn::create("ID", factory);
      m_ColumnView.append_column(column);

      // Name column
      factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
        &ExampleWindow::on_setup_label), Gtk::Align::START));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_name));
      column = Gtk::ColumnViewColumn::create("Name", factory);
      m_ColumnView.append_column(column);

      // Number column
      factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
        &ExampleWindow::on_setup_label), Gtk::Align::END));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_number));
      column = Gtk::ColumnViewColumn::create("Formatted number", factory);
      m_ColumnView.append_column(column);

      // Percentage column
      factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_setup_progressbar));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_percentage));
      column = Gtk::ColumnViewColumn::create("Some percentage", factory);
      m_ColumnView.append_column(column);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_setup_label(
      const Glib::RefPtr<Gtk::ListItem>& list_item, Gtk::Align halign)
    {
      list_item->set_child(*Gtk::make_managed<Gtk::Label>("", halign));
    }

    void ExampleWindow::on_setup_progressbar(
      const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto progressbar = Gtk::make_managed<Gtk::ProgressBar>();
      progressbar->set_show_text(true);
      list_item->set_child(*progressbar);
    }

    void ExampleWindow::on_bind_id(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(Glib::ustring::format(col->m_col_id));
    }

    void ExampleWindow::on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(col->m_col_name);
    }

    void ExampleWindow::on_bind_number(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      // 10 digits, using leading zeroes.
      label->set_text(Glib::ustring::sprintf("%010d", col->m_col_number));
    }

    void ExampleWindow::on_bind_percentage(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto progressbar = dynamic_cast<Gtk::ProgressBar*>(list_item->get_child());
      if (!progressbar)
        return;
      progressbar->set_fraction(col->m_col_percentage * 0.01);
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Ређање

Списак се може поређати његовим обавијањем у SortListModel. Постоје два начина да се ово уради.

- У ColumnView-у, добавите ColumnViewSorter из ColumnView-а и поставите га у SortListModel. Поставите Sorter за сваку ColumnViewColumn. Тада корисник ваше апликације може поређати ставке кликањем на заглавље колоне.

- У било ком прегледу, поставите Sorter као што је StringSorter или NumericSorter у SortListModel.

SortListModel референце

StringSorter референце

NumericSorter референце

#### Пример

Сортирани модел списка

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handlers:
      void on_button_nosort();
      void on_button_sortheadings();
      void on_button_sortlength();
      void on_button_quit();
      void on_setup_label(const Glib::RefPtr<Gtk::ListItem>& list_item, Gtk::Align halign);
      void on_bind_id(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item);

      // A Gio::ListStore item.
      class ModelColumns : public Glib::Object
      {
      public:
        unsigned int m_col_id;
        Glib::ustring m_col_name;

        static Glib::RefPtr<ModelColumns> create(int col_id, const Glib::ustring& col_name)
        {
          return Glib::make_refptr_for_instance<ModelColumns>(
            new ModelColumns(col_id, col_name));
        }

      protected:
        ModelColumns(int col_id, const Glib::ustring& col_name)
        : m_col_id(col_id), m_col_name(col_name)
        {}
      }; // ModelColumns

      // Child widgets:
      Gtk::Box m_VBox;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::ColumnView m_ColumnView;
      Gtk::Box m_ButtonBox;
      Gtk::ToggleButton m_Button_NoSort;
      Gtk::ToggleButton m_Button_SortHeadings;
      Gtk::ToggleButton m_Button_SortLength;
      Gtk::Button m_Button_Quit;

      Glib::RefPtr<Gtk::SingleSelection> m_SelectionModel;
      Glib::RefPtr<Gio::ListStore<ModelColumns>> m_ListStore;
      Glib::RefPtr<Gtk::SortListModel> m_SortListModel;
      Glib::RefPtr<Gtk::ColumnViewColumn> m_IdColumn;
      Glib::RefPtr<Gtk::ColumnViewColumn> m_NameColumn;
      Glib::RefPtr<Gtk::NumericSorter<Glib::ustring::size_type>> m_LengthSorter;
      Glib::RefPtr<Gtk::NumericSorter<unsigned int>> m_IdSorter;
      Glib::RefPtr<Gtk::StringSorter> m_NameSorter;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_NoSort("Don't Sort"),
      m_Button_SortHeadings("Sort Headings"),
      m_Button_SortLength("Sort Length"),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::ColumnView (Gtk::SortListModel) example");
      set_default_size(400, 350);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      // Add the ColumnView, inside a ScrolledWindow, with the buttons underneath:
      m_ScrolledWindow.set_child(m_ColumnView);

      // Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.set_margin(5);
      m_ButtonBox.set_spacing(10);
      m_ButtonBox.append(m_Button_NoSort);
      m_ButtonBox.append(m_Button_SortHeadings);
      m_ButtonBox.append(m_Button_SortLength);
      m_ButtonBox.append(m_Button_Quit);
      m_Button_NoSort.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_nosort));
      m_Button_SortHeadings.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_sortheadings));
      m_Button_SortLength.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_sortlength));
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_quit));

      // Gtk::CheckButton and Gtk::ToggleButton have set_group() methods.
      // They act as radio buttons, if they are included in a group.
      m_Button_SortHeadings.set_group(m_Button_NoSort);
      m_Button_SortLength.set_group(m_Button_NoSort);

      // Create the List model.
      m_ListStore = Gio::ListStore<ModelColumns>::create();
      m_ListStore->append(ModelColumns::create(1, "Billy Bob"));
      m_ListStore->append(ModelColumns::create(11, "Billy Bob Junior"));
      m_ListStore->append(ModelColumns::create(12, "Sue Bob"));
      m_ListStore->append(ModelColumns::create(2, "Joey Jojo"));
      m_ListStore->append(ModelColumns::create(3, "Rob McRoberts"));
      m_ListStore->append(ModelColumns::create(31, "Xavier McRoberts"));

      // Create the sort model.
      auto size_expression = Gtk::ClosureExpression<Glib::ustring::size_type>::create(
        [](const Glib::RefPtr<Glib::ObjectBase>& item)->Glib::ustring::size_type
        {
          const auto col = std::dynamic_pointer_cast<ModelColumns>(item);
          return col ? col->m_col_name.size() : 0;
        });
      m_LengthSorter = Gtk::NumericSorter<Glib::ustring::size_type>::create(size_expression);
      m_LengthSorter->set_sort_order(Gtk::SortType::ASCENDING);
      m_SortListModel = Gtk::SortListModel::create(m_ListStore, m_LengthSorter);

      // Set list model and selection model.
      m_SelectionModel = Gtk::SingleSelection::create(m_ListStore);
      m_SelectionModel->set_autoselect(false);
      m_SelectionModel->set_can_unselect(true);
      m_ColumnView.set_model(m_SelectionModel);
      m_ColumnView.add_css_class("data-table"); // high density table

      // Make the columns reorderable.
      // This is not necessary, but it's nice to show the feature.
      m_ColumnView.set_reorderable(true);

      // Add the ColumnView's columns:

      // Id column
      auto factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
        &ExampleWindow::on_setup_label), Gtk::Align::END));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_id));
      m_IdColumn = Gtk::ColumnViewColumn::create("ID", factory);
      m_ColumnView.append_column(m_IdColumn);
      auto uint_expression = Gtk::ClosureExpression<unsigned int>::create(
        [](const Glib::RefPtr<Glib::ObjectBase>& item)->unsigned int
        {
          const auto col = std::dynamic_pointer_cast<ModelColumns>(item);
          return col ? col->m_col_id : 0;
        });
      m_IdSorter = Gtk::NumericSorter<unsigned int>::create(uint_expression);

      // Name column
      factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
        &ExampleWindow::on_setup_label), Gtk::Align::START));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_name));
      m_NameColumn = Gtk::ColumnViewColumn::create("Name", factory);
      m_ColumnView.append_column(m_NameColumn);
      auto ustring_expression = Gtk::ClosureExpression<Glib::ustring>::create(
        [](const Glib::RefPtr<Glib::ObjectBase>& item)->Glib::ustring
        {
          const auto col = std::dynamic_pointer_cast<ModelColumns>(item);
          return col ? col->m_col_name : "";
        });
      m_NameSorter = Gtk::StringSorter::create(ustring_expression);

      // Show the list model without sorting.
      m_Button_NoSort.set_active(true);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_nosort()
    {
      if (m_Button_NoSort.get_active())
      {
        m_SelectionModel->set_model(m_ListStore);
        m_IdColumn->set_sorter({});
        m_NameColumn->set_sorter({});
      }
    }

    void ExampleWindow::on_button_sortheadings()
    {
      if (m_Button_SortHeadings.get_active())
      {
        m_SortListModel->set_sorter(m_ColumnView.get_sorter());
        m_SelectionModel->set_model(m_SortListModel);
        m_IdColumn->set_sorter(m_IdSorter);
        m_NameColumn->set_sorter(m_NameSorter);
      }
    }

    void ExampleWindow::on_button_sortlength()
    {
      if (m_Button_SortLength.get_active())
      {
        m_SortListModel->set_sorter(m_LengthSorter);
        m_SelectionModel->set_model(m_SortListModel);
        m_IdColumn->set_sorter({});
        m_NameColumn->set_sorter({});
      }
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_setup_label(
      const Glib::RefPtr<Gtk::ListItem>& list_item, Gtk::Align halign)
    {
      list_item->set_child(*Gtk::make_managed<Gtk::Label>("", halign));
    }

    void ExampleWindow::on_bind_id(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(Glib::ustring::format(col->m_col_id));
    }

    void ExampleWindow::on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(col->m_col_name);
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Филтрирање

Списак се може филтрирати његовим обавијањем у FilterListModel. Поставите Filter као што је StringFilter или BoolFilter на FilterListModel.

Референце за FilterListModel

Референце за StringFilter

Референце за BoolFilter

#### Пример

Филтрирани модел списка

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handlers:
      void on_button_showall();
      void on_button_showj();
      void on_button_showm();
      void on_button_quit();
      void on_setup_label(const Glib::RefPtr<Gtk::ListItem>& list_item, Gtk::Align halign);
      void on_bind_id(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item);

      // A Gio::ListStore item.
      class ModelColumns : public Glib::Object
      {
      public:
        unsigned int m_col_id;
        Glib::ustring m_col_name;

        static Glib::RefPtr<ModelColumns> create(int col_id, const Glib::ustring& col_name)
        {
          return Glib::make_refptr_for_instance<ModelColumns>(
            new ModelColumns(col_id, col_name));
        }

      protected:
        ModelColumns(int col_id, const Glib::ustring& col_name)
        : m_col_id(col_id), m_col_name(col_name)
        {}
      }; // ModelColumns

      // Child widgets:
      Gtk::Box m_VBox;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::ColumnView m_ColumnView;
      Gtk::Box m_ButtonBox;
      Gtk::ToggleButton m_Button_ShowAll;
      Gtk::ToggleButton m_Button_ShowJ;
      Gtk::ToggleButton m_Button_ShowM;
      Gtk::Button m_Button_Quit;

      Glib::RefPtr<Gtk::SingleSelection> m_SelectionModel;
      Glib::RefPtr<Gio::ListStore<ModelColumns>> m_ListStore;
      Glib::RefPtr<Gtk::FilterListModel> m_FilterListModel;
      Glib::RefPtr<Gtk::StringFilter> m_StringFilter;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_ShowAll("Show All"),
      m_Button_ShowJ("Show J"),
      m_Button_ShowM("Show M"),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::ColumnView (Gtk::FilterListModel) example");
      set_default_size(400, 350);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      // Add the ColumnView, inside a ScrolledWindow, with the buttons underneath:
      m_ScrolledWindow.set_child(m_ColumnView);

      // Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.set_margin(5);
      m_ButtonBox.set_spacing(10);
      m_ButtonBox.append(m_Button_ShowAll);
      m_ButtonBox.append(m_Button_ShowJ);
      m_ButtonBox.append(m_Button_ShowM);
      m_ButtonBox.append(m_Button_Quit);
      m_Button_ShowAll.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_showall));
      m_Button_ShowJ.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_showj));
      m_Button_ShowM.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_showm));
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_quit));

      // Gtk::CheckButton and Gtk::ToggleButton have set_group() methods.
      // They act as radio buttons, if they are included in a group.
      m_Button_ShowJ.set_group(m_Button_ShowAll);
      m_Button_ShowM.set_group(m_Button_ShowAll);

      // Create the List model.
      m_ListStore = Gio::ListStore<ModelColumns>::create();
      m_ListStore->append(ModelColumns::create(1, "Billy Bob"));
      m_ListStore->append(ModelColumns::create(11, "Billy Bob Junior"));
      m_ListStore->append(ModelColumns::create(12, "Sue Bob"));
      m_ListStore->append(ModelColumns::create(2, "Joey Jojo"));
      m_ListStore->append(ModelColumns::create(3, "Rob McRoberts"));
      m_ListStore->append(ModelColumns::create(31, "Xavier McRoberts"));

      // Create the filter model.
      auto expression = Gtk::ClosureExpression<Glib::ustring>::create(
        [](const Glib::RefPtr<Glib::ObjectBase>& item)->Glib::ustring
        {
          const auto col = std::dynamic_pointer_cast<ModelColumns>(item);
          return col ? col->m_col_name : "";
        });
      m_StringFilter = Gtk::StringFilter::create(expression);
      m_StringFilter->set_ignore_case(false);
      m_StringFilter->set_match_mode(Gtk::StringFilter::MatchMode::SUBSTRING);
      m_FilterListModel = Gtk::FilterListModel::create(m_ListStore, m_StringFilter);

      // Set list model and selection model.
      m_SelectionModel = Gtk::SingleSelection::create(m_ListStore);
      m_SelectionModel->set_autoselect(false);
      m_SelectionModel->set_can_unselect(true);
      m_ColumnView.set_model(m_SelectionModel);
      m_ColumnView.add_css_class("data-table"); // high density table

      // Make the columns reorderable.
      // This is not necessary, but it's nice to show the feature.
      m_ColumnView.set_reorderable(true);

      // Add the ColumnView's columns:

      // Id column
      auto factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
        &ExampleWindow::on_setup_label), Gtk::Align::END));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_id));
      auto column = Gtk::ColumnViewColumn::create("ID", factory);
      m_ColumnView.append_column(column);

      // Name column
      factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
        &ExampleWindow::on_setup_label), Gtk::Align::START));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_name));
      column = Gtk::ColumnViewColumn::create("Name", factory);
      m_ColumnView.append_column(column);

      // Show the list model without filtering.
      m_Button_ShowAll.set_active(true);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_showall()
    {
      if (m_Button_ShowAll.get_active())
        m_SelectionModel->set_model(m_ListStore);
    }

    void ExampleWindow::on_button_showj()
    {
      if (m_Button_ShowJ.get_active())
      {
       m_StringFilter->set_search("J");
        m_SelectionModel->set_model(m_FilterListModel);
      }
    }

    void ExampleWindow::on_button_showm()
    {
      if (m_Button_ShowM.get_active())
      {
       m_StringFilter->set_search("M");
        m_SelectionModel->set_model(m_FilterListModel);
      }
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_setup_label(
      const Glib::RefPtr<Gtk::ListItem>& list_item, Gtk::Align halign)
    {
      list_item->set_child(*Gtk::make_managed<Gtk::Label>("", halign));
    }

    void ExampleWindow::on_bind_id(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(Glib::ustring::format(col->m_col_id));
    }

    void ExampleWindow::on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(col->m_col_name);
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Приказивање стабала

Док је застарели TreeView пружао уграђену подршку за стабла, виџети списка, а посебно Gio::ListModel, то не чине. Међутим, gtkmm пружа функционалност како би стабла изгледала и понашала се као спискови за људе који и даље желе да приказују спискове. Ово се постиже коришћењем TreeListModel-а за изравнавање стабла у списак. Виџет TreeExpander се затим може користити унутар ставке списка како би се корисницима омогућило да рашире и скупе редове.

Референце за TreeListModel

Референце за TreeExpander

#### Пример

Модел списка стабла

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handlers:
      void on_button_quit();
      void on_setup_id(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_setup_name(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_id(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item);

      Glib::RefPtr<Gio::ListModel> create_model(
        const Glib::RefPtr<Glib::ObjectBase>& item = {});

      // A Gio::ListStore item.
      class ModelColumns : public Glib::Object
      {
      public:
        int m_col_id;
        Glib::ustring m_col_name;

        static Glib::RefPtr<ModelColumns> create(int col_id, const Glib::ustring& col_name)
        {
          return Glib::make_refptr_for_instance<ModelColumns>(
            new ModelColumns(col_id, col_name));
        }

      protected:
        ModelColumns(int col_id, const Glib::ustring& col_name)
        : m_col_id(col_id), m_col_name(col_name)
        {}
      }; // ModelColumns

      // Child widgets:
      Gtk::Box m_VBox;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::ColumnView m_ColumnView;
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;

      Glib::RefPtr<Gtk::TreeListModel> m_TreeListModel;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::ColumnView (Gtk::TreeListModel) example");
      set_default_size(300, 350);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      // Add the ColumnView, inside a ScrolledWindow, with the button underneath:
      m_ScrolledWindow.set_child(m_ColumnView);

      // Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(5);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_quit));

      // Create the root model:
      auto root = create_model();

      // Set list model and selection model.
      // passthrough must be false when Gtk::TreeExpander is used in the view.
      m_TreeListModel = Gtk::TreeListModel::create(root,
        sigc::mem_fun(*this, &ExampleWindow::create_model),
        /* passthrough */ false, /* autoexpand */ false);
      auto selection_model = Gtk::MultiSelection::create(m_TreeListModel);
      m_ColumnView.set_model(selection_model);
      m_ColumnView.add_css_class("data-table"); // high density table

      // Make the columns reorderable.
      // This is not necessary, but it's nice to show the feature.
      m_ColumnView.set_reorderable(true);

      // Add the ColumnView's columns:

      // Id column
      auto factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_setup_id));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_id));
      auto column = Gtk::ColumnViewColumn::create("ID", factory);
      m_ColumnView.append_column(column);

      // Name column
      factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_setup_name));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_name));
      column = Gtk::ColumnViewColumn::create("Name", factory);
      m_ColumnView.append_column(column);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    Glib::RefPtr<Gio::ListModel> ExampleWindow::create_model(
      const Glib::RefPtr<Glib::ObjectBase>& item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(item);
      auto result = Gio::ListStore<ModelColumns>::create();
      if (!col)
      {
        // Top names
        result->append(ModelColumns::create(1, "Billy Bob"));
        result->append(ModelColumns::create(2, "Joey Jojo"));
        result->append(ModelColumns::create(3, "Rob McRoberts"));
      }
      else
      {
        switch (col->m_col_id)
        {
        case 1:
          result->append(ModelColumns::create(11, "Billy Bob Junior"));
          result->append(ModelColumns::create(12, "Sue Bob"));
          break;
        case 3:
          result->append(ModelColumns::create(31, "Xavier McRoberts"));
          break;
        }
      }

      // If result is empty, it's a leaf in the tree, i.e. an item without children.
      // Returning an empty RefPtr (not a RefPtr with an empty Gio::ListModel)
      // signals that the item is not expandable.
      return (result->get_n_items() > 0) ? result : Glib::RefPtr<Gio::ListModel>();
    }

    void ExampleWindow::on_setup_id(
      const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      // Each ListItem contains a TreeExpander, which contains a Label.
      // The Label shows the ModelColumns::m_col_id. That's done in on_bind_id().
      auto expander = Gtk::make_managed<Gtk::TreeExpander>();
      auto label = Gtk::make_managed<Gtk::Label>();
      label->set_halign(Gtk::Align::END);
      expander->set_child(*label);
      list_item->set_child(*expander);
    }

    void ExampleWindow::on_setup_name(
      const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      list_item->set_child(*Gtk::make_managed<Gtk::Label>("", Gtk::Align::START));
    }

    void ExampleWindow::on_bind_id(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      // When TreeListModel::property_passthrough() is false, ListItem::get_item()
      // is a TreeListRow. TreeExpander needs the TreeListRow.
      // The ModelColumns item is returned by TreeListRow::get_item().
      auto row = std::dynamic_pointer_cast<Gtk::TreeListRow>(list_item->get_item());
      if (!row)
        return;
      auto col = std::dynamic_pointer_cast<ModelColumns>(row->get_item());
      if (!col)
        return;
      auto expander = dynamic_cast<Gtk::TreeExpander*>(list_item->get_child());
      if (!expander)
        return;
      expander->set_list_row(row);
      auto label = dynamic_cast<Gtk::Label*>(expander->get_child());
      if (!label)
        return;
      label->set_text(Glib::ustring::format(col->m_col_id));
    }

    void ExampleWindow::on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto row = std::dynamic_pointer_cast<Gtk::TreeListRow>(list_item->get_item());
      if (!row)
        return;
      auto col = std::dynamic_pointer_cast<ModelColumns>(row->get_item());
      if (!col)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(col->m_col_name);
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

## Виџет TreeView

Gtk::TreeView је застарео од верзије gtkmm 4.10. У новом коду, користите Gtk::ListView за спискове и Gtk::ColumnView за табеларне спискове.

Виџет Gtk::TreeView може да садржи спискове или стабла података, у колонама.

### Модел

Сваки Gtk::TreeView има придружени Gtk::TreeModel, који садржи податке приказане у TreeView-у. Сваки Gtk::TreeModel може користити више од једног Gtk::TreeView-а. На пример, ово омогућава да се исти основни подаци приказују и уређују на 2 различита начина у исто време. Или 2 приказа могу приказивати различите колоне из истих података модела, на исти начин на који 2 SQL упита (или „приказа“) могу приказивати различита поља из исте табеле базе података.

Иако теоретски можете имплементирати сопствени модел, обично ћете користити класе модела ListStore или TreeStore.

Референца

#### ListStore, за редове

ListStore садржи једноставне редове података, а сваки ред нема подређених ставки.

TreeView — ListStore

Референца

#### TreeStore, за хијерархију

TreeStore садржи редове података, а сваки ред може имати подређене редове.

TreeView — TreeStore

Упута

#### Колоне модела

Разред TreeModelColumnRecord се користи за праћење колона и њихових врста података. Додајете примерке TreeModelColumn у ColumnRecord а затим користите те TreeModelColumn-е приликом добављања и подешавања података у редовима модела. Вероватно ће вам бити згодно да изведете нови TreeModelColumnRecord који има ваше примерке TreeModelColumn као податке чланове.

    class ModelColumns : public Gtk::TreeModelColumnRecord
    {
    public:

      ModelColumns()
        { add(m_col_text); add(m_col_number); }

      Gtk::TreeModelColumn<Glib::ustring> m_col_text;
      Gtk::TreeModelColumn<int> m_col_number;
    };

    ModelColumns m_Columns;

Наводите ColumnRecord приликом стварања модела, овако:

    Glib::RefPtr<Gtk::ListStore> refListStore =
        Gtk::ListStore::create(m_Columns);

Како TreeModelColumnRecord описује структуру, а не податке, може се делити међу више модела, што је пожељно због ефикасности. Међутим, примерак (као што је овде `m_Columns`) обично не би требало да буде статички, јер га често треба покренути након што је glibmm покренут. Најбоље решење је да га учините лењо покренутим јединцем, тако да ће бити изграђен на захтев, кад год му први модел приступи.

#### Додавање редова

Додајте редове у модел помоћу метода append(), prepend() или insert().

    auto iter = m_refListStore->append();

Можете разрешити итератор да бисте добили ред:

    auto row = *iter;

##### Додавање садржаних редова

Модели Gtk::TreeStore могу имати садржане ставке. Додајте их помоћу метода append(), prepend() или insert(), овако:

    auto iter_child =
        m_refTreeStore->append(row.children());

#### Подешавање вредности

Можете користити преоптерећење operator[] да подесите податке за одређену колону у реду, наводећи TreeModelColumn који је коришћен за стварање модела.

    row[m_Columns.m_col_text] = "sometext";

#### Добављање вредности

Можете користити преоптерећење operator[] да добавите податке у одређеној колони у реду, наводећи TreeModelColumn који је коришћен за стварање модела.

    auto strText = row[m_Columns.m_col_text];
    auto number = row[m_Columns.m_col_number];

Преводилац ће се побунити ако користите неприкладну врсту. На пример, ово би створило грешку преводиоца:

    //грешка преводиоца — нема претварања из ustring у int.
    int number = row[m_Columns.m_col_text];

#### „Скривене“ колоне

Можда ћете желети да придружите додатне податке сваком реду. Ако је тако, само их додајте као колону модела, али их немојте додавати у приказ.

### Преглед

Приказ је стварни елемент (Gtk::TreeView) који приказује податке модела (Gtk::TreeModel) и омогућава кориснику да узајамно делује са њим. Приказ може да покаже све колоне модела, или само неке, и може да их прикаже на различите начине.

Референце

#### Коришћење модела

Можете навести Gtk::TreeModel приликом изградње Gtk::TreeView, или можете користити метод set_model(), овако:

    m_TreeView.set_model(m_refListStore);

#### Додавање колона приказа

Можете користити метод append_column() да кажете приказу да треба да прикаже одређене колоне модела, одређеним редоследом, са одређеним насловом колоне.

    m_TreeView.append_column("Поруке", m_Columns.m_col_text);

Када користите ово једноставно преоптерећење append_column(), TreeView ће приказати податке модела са одговарајућим CellRenderer-ом. На пример, ниске и бројеви се приказују у једноставном елементу Gtk::Entry, а логичке вредности у Gtk::CheckButton. Ово је обично оно што вам треба. За друге врсте колона морате или да повежете повратни позив који претвара вашу врсту у текстуалну представу, помоћу TreeViewColumn::set_cell_data_func(), или да изведете произвољни CellRenderer. Имајте на уму да (unsigned) short није подржан по подразумеваним вредностима — уместо тога можете користити (unsigned) int или (unsigned) long као врсту колоне.

#### Више од једне колоне модела по колони приказа

Да бисте исцртали више од једне колоне модела у колони приказа, морате ручно да направите елемент TreeView::Column и користите pack_start() да му додате колоне модела.

Затим користите append_column() да додате колону приказа у приказ. Приметите да је Gtk::TreeView::append_column() преоптерећен тако да прихвата или већ изграђен елемент Gtk::TreeView::Column, или само TreeModelColumn из којег ствара одговарајући елемент Gtk::TreeView::Column.

Ево неког примера кода који има иконицу сличице и текстуални назив у истој колони:

    auto pColumn = Gtk::make_managed<Gtk::TreeView::Column>("Назив иконице");

    // m_columns.icon и m_columns.iconname су колоне у моделу.
    // pColumn је колона у TreeView-у:
    pColumn->pack_start(m_columns.icon, /* expand= */ false);
    pColumn->pack_start(m_columns.iconname);

    m_TreeView.append_column(*pColumn);

#### Навођење детаља CellRenderer-а

Подразумевани CellRenderer-и и њихово подразумевано понашање ће обично бити довољни, али повремено вам може затребати прецизнија контрола. На пример, овај пример кода из `gtkmm/demos/gtk-demo/example_treeview_treestore.cc`, додаје Gtk::CellRenderer виџет и налаже му да исцрта податке из различитих колона модела кроз различите аспекте његовог изгледа.

    auto cols_count = m_TreeView.append_column_editable("Alex", m_columns.alex);
    auto pColumn = m_TreeView.get_column(cols_count-1);
    if(pColumn)
    {
      auto pRenderer = static_cast<Gtk::CellRendererToggle*>(pColumn->get_first_cell());
      pColumn->add_attribute(pRenderer->property_visible(), m_columns.visible);
      pColumn->add_attribute(pRenderer->property_activatable(), m_columns.world);

Такође се можете повезати на сигнале CellRenderer-а како бисте открили корисничке радње. На пример:

    auto pRenderer = Gtk::make_managed<Gtk::CellRendererToggle>();
    pRenderer->signal_toggled().connect(
        sigc::bind( sigc::mem_fun(*this,
            &Example_TreeView_TreeStore::on_cell_toggled), m_columns.dave)
    );

#### Измењиве ћелије

##### Automatically-stored editable cells

Ћелије у TreeView-у корисник може мењати на лицу места. Да бисте ово омогућили, користите методе Gtk::TreeView insert_column_editable() и append_column_editable() уместо insert_column() и append_column(). Када се ове ћелије уреде, нове вредности ће одмах бити сачуване у моделу. Имајте на уму да су ове методе шаблони који се могу инстанцирати само за једноставне типове колона као што су Glib::ustring, int и long.

##### Implementing custom logic for editable cells

Међутим, можда нећете желети да се нове вредности одмах сачувају. На пример, можда желите да ограничите унос на одређене знакове или опсеге вредности.

Да бисте то постигли, требало би да користите обичне методе Gtk::TreeView insert_column() и append_column(), а затим користите get_column_cell_renderer() да бисте добили Gtk::CellRenderer који користи та колона.

Затим би требало да кастујете тај Gtk::CellRenderer* у специфични CellRenderer који очекујете, како бисте могли да користите специфичан АПИ.

На пример, за CellRendererText, поставили бисте својство ћелије editable на тачно, овако:

    cell->property_editable() = true;

За CellRendererToggle, уместо тога бисте поставили својство activatable.

Затим се можете повезати на одговарајући сигнал „edited“. На пример, повежите се на Gtk::CellRendererText::signal_edited() или Gtk::CellRendererToggle::signal_toggled(). Ако колона садржи више од једног CellRenderer-а, мораћете да користите Gtk::TreeView::get_column(), а затим да позовете get_cells() на тој колони приказа.

У свом руковаоцу сигналом, требало би да испитате нову вредност и затим је сачувате у моделу ако је то прикладно за вашу апликацију.

### Итерирање кроз редове модела

Gtk::TreeModel обезбеђује контејнер својих садржалаца у стилу C++ стандардне библиотеке, преко методе children(). Можете користити познате методе begin() и end() за увећавање итератора, овако:

    auto children = refModel->children();
    for (auto iter = children.begin(), end = children.end(); iter != end; ++iter)
    {
      auto row = *iter;
      //Do something with the row - see above for set/get.
    }

Ако увек желите да итерирате кроз цео опсег, могућа је много сажетија синтакса коришћењем C++ `for` петље засноване на опсегу:

    for (auto row: refModel->children())
    {
      //Урадите нешто са редом — погледајте изнад за постављање/добављање.
    }

#### Садржаоци реда

Када користите Gtk::TreeStore, редови могу имати садржане редове, који заузврат могу имати сопствене садржаоце. Користите Gtk::TreeModel::Row::children() да бисте добили контејнер садржаних Row (редова):

    Gtk::TreeModel::Children children = row.children();

### Избор

Да бисте сазнали које редове је корисник изабрао, добавите објекат Gtk::TreeView::Selection из TreeView-а, на овај начин:

    auto refTreeSelection = m_TreeView.get_selection();

#### Једноструки или вишеструки избор

По подразумеваним подешавањима, могу се изабрати само појединачни редови, али можете дозволити вишеструки избор постављањем режима, овако:

    refTreeSelection->set_mode(Gtk::SelectionMode::MULTIPLE);

#### Изабрани редови

За једноструки избор, можете једноставно позвати get_selected(), овако:

    auto iter = refTreeSelection->get_selected();
    if(iter) //Ако је било шта изабрано
    {
      auto row = *iter;
      //Урадите нешто са редом.
    }

За вишеструки избор, потребно је да позовете get_selected_rows() или да дефинишете повратни позив и проследите га функцији selected_foreach(), selected_foreach_path() или selected_foreach_iter(), овако:

    refTreeSelection->selected_foreach_iter(
        sigc::mem_fun(*this, &TheClass::selected_row_callback) );

    void TheClass::selected_row_callback(
        const Gtk::TreeModel::const_iterator& iter)
    {
      auto row = *iter;
      //Урадите нешто са редом.
    }

#### Сигнал „changed“ (измењено)

Да бисте одговорили на корисниково кликање на ред или опсег редова, повежите се са сигналом овако:

    refTreeSelection->signal_changed().connect(
        sigc::mem_fun(*this, &Example_IconTheme::on_selection_changed)
    );

#### Спречавање избора реда

Можда корисник не би требало да буде у могућности да изабере сваку ставку у вашој листи или стаблу. На пример, у gtk-demo-у, можете изабрати демо да бисте видели изворни код, али нема никаквог смисла изабрати категорију демоа.

Да бисте управљали тиме који редови могу бити изабрани, користите метод set_select_function(), обезбеђујући sigc::slot повратни позив. На пример:

    m_refTreeSelection->set_select_function( sigc::mem_fun(*this,
        &DemoWindow::select_function) );

а затим

    bool DemoWindow::select_function(
        const Glib::RefPtr<Gtk::TreeModel>& model,
        const Gtk::TreeModel::Path& path, bool)
    {
      const auto iter = model->get_iter(path);
      return iter->children().empty(); // дозволи само избор чворова листова
    }

#### Мењање избора

Да бисте променили избор, наведите Gtk::TreeModel::iterator или Gtk::TreeModel::Row, овако:

    auto row = m_refModel->children()[5]; //Шести ред.
    if(row)
      refTreeSelection->select(row.get_iter());

или

    auto iter = m_refModel->children().begin()
    if(iter)
      refTreeSelection->select(iter);

### Ређање

Стандардни модели стабла (TreeStore и ListStore) потичу из TreeSortable, па нуде функционалност ређања. На пример, позовите set_sort_column() да бисте поређали модел према наведеној колони. Или доставите функцију повратног позива у set_sort_func() да бисте применили сложенији алгоритам ређања.

TreeSortable референца

#### Ређање кликом на колоне

Да би корисник могао да кликне на заглавље колоне TreeView-а ради ређања садржаја TreeView-а, позовите Gtk::TreeView::Column::set_sort_column(), достављајући колону модела према којој модел треба да буде поређан када се кликне на заглавље. На пример:

    auto pColumn = treeview.get_column(0);
    if(pColumn)
      pColumn->set_sort_column(m_columns.m_col_id);

#### Независно поређани прегледи истог модела

TreeView већ омогућава да прикажете исти TreeModel у два TreeView елемента. Ако вам је потребно да један од ових прегледа стабла ређа модел другачије од другог, онда треба да користите TreeModelSort уместо само, на пример, Gtk::TreeViewColumn::set_sort_column(). TreeModelSort је модел који садржи други модел, представљајући поређану верзију тог модела. На пример, можете додати поређану верзију модела у TreeView овако:

    auto sorted_model = Gtk::TreeModelSort::create(model);
    sorted_model->set_sort_column(columns.m_col_name, Gtk::SortType::ASCENDING);
    treeview.set_model(sorted_model);

Имајте на уму, међутим, да ће TreeView пружити итераторе за поређани модел. Морате их претворити у итераторе за основни подређени модел да бисте извршили радње над тим моделом. На пример:

    void ExampleWindow::on_button_delete()
    {
      auto refTreeSelection = m_treeview.get_selection();
      if(refTreeSelection)
      {
        auto sorted_iter = m_refTreeSelection->get_selected();
        if(sorted_iter)
        {
          auto iter = m_refModelSort->convert_iter_to_child_iter(sorted_iter);
          m_refModel->erase(iter);
        }
      }
    }

TreeModelSort референца

### Превуци и пусти

Gtk::TreeView већ примењује једноставно превлачење и спуштање када се користи са моделима Gtk::ListStore или Gtk::TreeStore (од гтк-а 4.8). Ако је потребно, такође вам омогућава да примените сложеније понашање када се ставке превлаче и спуштају, користећи обичан Превлачење и спуштање API.

#### Редови са променљивим редоследом

Ако позовете Gtk::TreeView::set_reorderable() тада се ставке вашег TreeView-а могу премештати унутар самог прегледа стабла. Ово је приказано у примеру TreeStore.

Међутим, ово вам не омогућава никакву контролу над тим које ставке се могу превлачити и где се могу спустити. Ако вам је потребна та додатна контрола, тада можете направити изведени `Gtk::TreeModel` из `Gtk::TreeStore` или `Gtk::ListStore` и преклопити виртуелне методе `Gtk::TreeDragSource::row_draggable_vfunc()` и `Gtk::TreeDragDest::row_drop_possible_vfunc()`. Можете испитати достављене `Gtk::TreeModel::Path` путање и дозволити или онемогућити превлачење или спуштање враћањем `true` или `false`.

Ово је приказано у примеру drag_and_drop.

### Искачући контекстни мени

Многим људима је потребно да примене контекстне меније на десни клик за TreeView прегледе, па ћемо овде објаснити како то да урадите да бисмо вам уштедели мало времена. То је углавном исто као и обичан контекстни мени, као што је описано у поглављу о менијима. Користите Gtk::GestureClick за откривање клика мишем.

Ово је приказано у примеру искачућег контекстног менија. У том примеру се користи изведени TreeView, али то није неопходно.

### Примери

Неки TreeView примери су приказани овде. Има још примера у директоријуму прегледа стабла међу примерима gtkmm-documentation документације.

Ако ни ListStore ни TreeStore нису прикладни за ваш програм, погледајте пример произвољног TreeModel-а. Он показује како можете направити сопствену изведбу TreeModel прочеља.

#### ListStore

Овај пример има Gtk::TreeView елемент, са Gtk::ListStore моделом.

TreeView — ListStore

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_quit();

      //Tree model columns:
      class ModelColumns : public Gtk::TreeModel::ColumnRecord
      {
      public:

        ModelColumns()
        { add(m_col_id); add(m_col_name); add(m_col_number); add(m_col_percentage);}

        Gtk::TreeModelColumn<unsigned int> m_col_id;
        Gtk::TreeModelColumn<Glib::ustring> m_col_name;
        Gtk::TreeModelColumn<short> m_col_number;
        Gtk::TreeModelColumn<int> m_col_percentage;
      };

      ModelColumns m_Columns;

      //Child widgets:
      Gtk::Box m_VBox;

      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::TreeView m_TreeView;
      Glib::RefPtr<Gtk::ListStore> m_refTreeModel;

      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include <iostream>
    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::TreeView (ListStore) example");
      set_default_size(400, 200);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      //Add the TreeView, inside a ScrolledWindow, with the button underneath:
      m_ScrolledWindow.set_child(m_TreeView);

      //Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(5);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit) );

      //Create the Tree model:
      m_refTreeModel = Gtk::ListStore::create(m_Columns);
      m_TreeView.set_model(m_refTreeModel);

      //Fill the TreeView's model
      auto row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 1;
      row[m_Columns.m_col_name] = "Billy Bob";
      row[m_Columns.m_col_number] = 10;
      row[m_Columns.m_col_percentage] = 15;

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 2;
      row[m_Columns.m_col_name] = "Joey Jojo";
      row[m_Columns.m_col_number] = 20;
      row[m_Columns.m_col_percentage] = 40;

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 3;
      row[m_Columns.m_col_name] = "Rob McRoberts";
      row[m_Columns.m_col_number] = 30;
      row[m_Columns.m_col_percentage] = 70;

      //Add the TreeView's view columns:
      //This number will be shown with the default numeric formatting.
      m_TreeView.append_column("ID", m_Columns.m_col_id);
      m_TreeView.append_column("Name", m_Columns.m_col_name);

      m_TreeView.append_column_numeric("Formatted number", m_Columns.m_col_number,
              "%010d" /* 10 digits, using leading zeroes. */);

      //Display a progress bar instead of a decimal number:
      auto cell = Gtk::make_managed<Gtk::CellRendererProgress>();
      int cols_count = m_TreeView.append_column("Some percentage", *cell);
      auto pColumn = m_TreeView.get_column(cols_count - 1);
      if(pColumn)
      {
        pColumn->add_attribute(cell->property_value(), m_Columns.m_col_percentage);
      }

      //Make all the columns reorderable:
      //This is not necessary, but it's nice to show the feature.
      //You can use TreeView::set_column_drag_function() to more
      //finely control column drag and drop.
      for(guint i = 0; i < 2; i++)
      {
        auto column = m_TreeView.get_column(i);
        column->set_reorderable();
      }
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### TreeStore

Овај пример је врло сличан примеру за ListStore, али уместо њега користи модел Gtk::TreeStore и додаје садржаоце у редове.

TreeView — TreeStore

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_quit();
      void on_treeview_row_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column);

      //Tree model columns:
      class ModelColumns : public Gtk::TreeModel::ColumnRecord
      {
      public:

        ModelColumns()
        { add(m_col_id); add(m_col_name); }

        Gtk::TreeModelColumn<int> m_col_id;
        Gtk::TreeModelColumn<Glib::ustring> m_col_name;
      };

      ModelColumns m_Columns;

      //Child widgets:
      Gtk::Box m_VBox;

      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::TreeView m_TreeView;
      Glib::RefPtr<Gtk::TreeStore> m_refTreeModel;

      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include <iostream>
    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::TreeView (TreeStore) example");
      set_default_size(400, 200);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      //Add the TreeView, inside a ScrolledWindow, with the button underneath:
      m_ScrolledWindow.set_child(m_TreeView);

      //Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(5);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit) );

      //Create the Tree model:
      m_refTreeModel = Gtk::TreeStore::create(m_Columns);
      m_TreeView.set_model(m_refTreeModel);

      //All the items to be reordered with drag-and-drop:
      m_TreeView.set_reorderable();

      //Fill the TreeView's model
      auto row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 1;
      row[m_Columns.m_col_name] = "Billy Bob";

      auto childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 11;
      childrow[m_Columns.m_col_name] = "Billy Bob Junior";

      childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 12;
      childrow[m_Columns.m_col_name] = "Sue Bob";

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 2;
      row[m_Columns.m_col_name] = "Joey Jojo";

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 3;
      row[m_Columns.m_col_name] = "Rob McRoberts";

      childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 31;
      childrow[m_Columns.m_col_name] = "Xavier McRoberts";

      //Add the TreeView's view columns:
      m_TreeView.append_column("ID", m_Columns.m_col_id);
      m_TreeView.append_column("Name", m_Columns.m_col_name);

      //Connect signal:
      m_TreeView.signal_row_activated().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_treeview_row_activated) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_treeview_row_activated(const Gtk::TreeModel::Path& path,
            Gtk::TreeViewColumn* /* column */)
    {
      const auto iter = m_refTreeModel->get_iter(path);
      if(iter)
      {
        const auto row = *iter;
        std::cout << "Row activated: ID=" << row[m_Columns.m_col_id] << ", Name="
            << row[m_Columns.m_col_name] << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Измењиве ћелије

Овај пример је истоветан примеру за ListStore, али користи TreeView::append_column_editable() уместо TreeView::append_column().

Преглед стабла — измењива поља

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_quit();

      void treeviewcolumn_validated_on_cell_data(Gtk::CellRenderer* renderer, const Gtk::TreeModel::const_iterator& iter);
      void cellrenderer_validated_on_editing_started(Gtk::CellEditable* cell_editable, const Glib::ustring& path);
      void cellrenderer_validated_on_edited(const Glib::ustring& path_string, const Glib::ustring& new_text);
      void on_message_response(int response_id, Gtk::MessageDialog* dialog);

      //Tree model columns:
      class ModelColumns : public Gtk::TreeModel::ColumnRecord
      {
      public:

        ModelColumns()
        { add(m_col_id); add(m_col_name); add(m_col_foo); add(m_col_number); add(m_col_number_validated); }

        Gtk::TreeModelColumn<unsigned int> m_col_id;
        Gtk::TreeModelColumn<Glib::ustring> m_col_name;
        Gtk::TreeModelColumn<bool> m_col_foo;
        Gtk::TreeModelColumn<int> m_col_number;
        Gtk::TreeModelColumn<int> m_col_number_validated;
      };

      ModelColumns m_Columns;

      //Child widgets:
      Gtk::Box m_VBox;

      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::TreeView m_TreeView;
      Glib::RefPtr<Gtk::ListStore> m_refTreeModel;

      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;

      //For the validated column:
      //You could also use a CellRendererSpin or a CellRendererProgress:
      Gtk::CellRendererText m_cellrenderer_validated;
      Gtk::TreeView::Column m_treeviewcolumn_validated;
      bool m_validate_retry;
      Glib::ustring m_invalid_text_for_retry;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include "examplewindow.h"

    using std::sprintf;
    using std::strtol;

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Quit("Quit"),
      m_validate_retry(false)
    {
      set_title("Gtk::TreeView Editable Cells example");
      set_default_size(400, 200);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      //Add the TreeView, inside a ScrolledWindow, with the button underneath:
      m_ScrolledWindow.set_child(m_TreeView);

      //Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(5);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit) );

      //Create the Tree model:
      m_refTreeModel = Gtk::ListStore::create(m_Columns);
      m_TreeView.set_model(m_refTreeModel);

      //Fill the TreeView's model
      auto row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 1;
      row[m_Columns.m_col_name] = "Billy Bob";
      row[m_Columns.m_col_foo] = true;
      row[m_Columns.m_col_number] = 10;

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 2;
      row[m_Columns.m_col_name] = "Joey Jojo";
      row[m_Columns.m_col_foo] = true;
      row[m_Columns.m_col_number] = 20;

      row = *(m_refTreeModel->append());

      row[m_Columns.m_col_id] = 3;
      row[m_Columns.m_col_name] = "Rob McRoberts";
      row[m_Columns.m_col_foo] = false;
      row[m_Columns.m_col_number] = 30;

      //Add the TreeView's view columns:
      //We use the *_editable convenience methods for most of these,
      //because the default functionality is enough:
      m_TreeView.append_column_editable("ID", m_Columns.m_col_id);
      m_TreeView.append_column_editable("Name", m_Columns.m_col_name);
      m_TreeView.append_column_editable("foo", m_Columns.m_col_foo);
      m_TreeView.append_column_numeric_editable("Number", m_Columns.m_col_number,
              "%010d");

      //For this column, we create the CellRenderer ourselves, and connect our own
      //signal handlers, so that we can validate the data that the user enters, and
      //control how it is displayed.
      m_treeviewcolumn_validated.set_title("validated (<10)");
      m_treeviewcolumn_validated.pack_start(m_cellrenderer_validated);
      m_TreeView.append_column(m_treeviewcolumn_validated);

      //Tell the view column how to render the model values:
      m_treeviewcolumn_validated.set_cell_data_func(m_cellrenderer_validated,
              sigc::mem_fun(*this,
                  &ExampleWindow::treeviewcolumn_validated_on_cell_data) );

      //Make the CellRenderer editable, and handle its editing signals:
      m_cellrenderer_validated.property_editable() = true;

      m_cellrenderer_validated.signal_editing_started().connect(
              sigc::mem_fun(*this,
            &ExampleWindow::cellrenderer_validated_on_editing_started) );

      m_cellrenderer_validated.signal_edited().connect( sigc::mem_fun(*this,
                  &ExampleWindow::cellrenderer_validated_on_edited) );

      //If this was a CellRendererSpin then you would have to set the adjustment:
      //m_cellrenderer_validated.property_adjustment() = m_spin_adjustment;
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::treeviewcolumn_validated_on_cell_data(
            Gtk::CellRenderer* /* renderer */,
            const Gtk::TreeModel::const_iterator& iter)
    {
      //Get the value from the model and show it appropriately in the view:
      if(iter)
      {
        const auto row = *iter;
        int model_value = row[m_Columns.m_col_number_validated];

        //This is just an example.
        //In this case, it would be easier to use append_column_editable() or
        //append_column_numeric_editable()
        char buffer[32];
        sprintf(buffer, "%d", model_value);

        auto view_text = buffer;
        m_cellrenderer_validated.property_text() = view_text;
      }
    }

    void ExampleWindow::cellrenderer_validated_on_editing_started(
            Gtk::CellEditable* cell_editable, const Glib::ustring& /* path */)
    {
      //Start editing with previously-entered (but invalid) text,
      //if we are allowing the user to correct some invalid data.
      if(m_validate_retry)
      {
        //This is the CellEditable inside the CellRenderer.
        auto celleditable_validated = cell_editable;

        //It's usually an Entry, at least for a CellRendererText:
        auto pEntry = dynamic_cast<Gtk::Entry*>(celleditable_validated);
        if(pEntry)
        {
          pEntry->set_text(m_invalid_text_for_retry);
          m_validate_retry = false;
          m_invalid_text_for_retry.clear();
        }
      }
    }

    void ExampleWindow::cellrenderer_validated_on_edited(
            const Glib::ustring& path_string,
            const Glib::ustring& new_text)
    {
      Gtk::TreePath path(path_string);

      //Convert the inputed text to an integer, as needed by our model column:
      char* pchEnd = nullptr;
      int new_value = strtol(new_text.c_str(), &pchEnd, 10);

      if(new_value >= 10)
      {
        //Prevent entry of numbers higher than 10.

        //Tell the user:
        auto dialog = new Gtk::MessageDialog(*this,
                "The number must be less than 10. Please try again.",
                false, Gtk::MessageType::ERROR, Gtk::ButtonsType::OK, true /* modal */);
        dialog->signal_response().connect(sigc::bind(
          sigc::mem_fun(*this, &ExampleWindow::on_message_response), dialog));

        dialog->set_visible(true);

        //Start editing again, with the bad text, so that the user can correct it.
        //A real application should probably allow the user to revert to the
        //previous text.

        //Set the text to be used in the start_editing signal handler:
        m_invalid_text_for_retry = new_text;
        m_validate_retry = true;

        //Start editing again, when the message dialog has been closed:
        m_TreeView.set_cursor(path, m_treeviewcolumn_validated,
                m_cellrenderer_validated, true /* start_editing */);
      }
      else
      {
        //Get the row from the path:
        auto iter = m_refTreeModel->get_iter(path);
        if(iter)
        {
          auto row = *iter;

          //Put the new value in the model:
          row[m_Columns.m_col_number_validated] = new_value;
        }
      }
    }

    void ExampleWindow::on_message_response(int /* response_id */, Gtk::MessageDialog* dialog)
    {
      delete dialog;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Превуци и пусти

Овај пример је веома сличан примеру за TreeStore, али има 2 додатне колоне које указују на то да ли се ред може превлачити и да ли може да прими превучене редове. Користи изведени Gtk::TreeStore који преписује виртуелне функције као што је описано у одељку Превлачење и спуштање у прегледу стабла.

Преглед стабла — Превлачење и спуштање

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>
    #include "treemodel_dnd.h"

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_quit();

      //Child widgets:
      Gtk::Box m_VBox;

      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::TreeView m_TreeView;
      Glib::RefPtr<TreeModel_Dnd> m_refTreeModel;

      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `treemodel_dnd.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_TREEMODEL_DND_H
    #define GTKMM_EXAMPLE_TREEMODEL_DND_H

    #include <gtkmm.h>

    class TreeModel_Dnd : public Gtk::TreeStore
    {
    protected:
      TreeModel_Dnd();

    public:

      //Tree model columns:
      class ModelColumns : public Gtk::TreeModel::ColumnRecord
      {
      public:

        ModelColumns()
        { add(m_col_id); add(m_col_name); add(m_col_draggable); add(m_col_receivesdrags); }

        Gtk::TreeModelColumn<int> m_col_id;
        Gtk::TreeModelColumn<Glib::ustring> m_col_name;
        Gtk::TreeModelColumn<bool> m_col_draggable;
        Gtk::TreeModelColumn<bool> m_col_receivesdrags;
      };

      ModelColumns m_Columns;

      static Glib::RefPtr<TreeModel_Dnd> create();

    protected:
      //Overridden virtual functions:
      bool row_draggable_vfunc(const Gtk::TreeModel::Path& path) const override;
      bool row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Glib::ValueBase& value) const override;
    };

    #endif //GTKMM_EXAMPLE_TREEMODEL_DND_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include <iostream>
    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Quit("_Quit", true)
    {
      set_title("Gtk::TreeView (Drag and Drop) example");
      set_default_size(400, 200);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      //Add the TreeView, inside a ScrolledWindow, with the button underneath:
      m_ScrolledWindow.set_child(m_TreeView);

      //Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(5);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit) );

      //Create the Tree model:
      //Use our derived model, which overrides some Gtk::TreeDragDest and
      //Gtk::TreeDragSource virtual functions:
      //The columns are declared in the overridden TreeModel.
      m_refTreeModel = TreeModel_Dnd::create();
      m_TreeView.set_model(m_refTreeModel);

      //Enable Drag-and-Drop of TreeView rows:
      //See also the derived TreeModel's *_vfunc overrides.
      m_TreeView.enable_model_drag_source();
      m_TreeView.enable_model_drag_dest();

      //Fill the TreeView's model
      auto row = *(m_refTreeModel->append());
      row[m_refTreeModel->m_Columns.m_col_id] = 1;
      row[m_refTreeModel->m_Columns.m_col_name] = "Billy Bob";
      row[m_refTreeModel->m_Columns.m_col_draggable] = true;
      row[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

      auto childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_refTreeModel->m_Columns.m_col_id] = 11;
      childrow[m_refTreeModel->m_Columns.m_col_name] = "Billy Bob Junior";
      childrow[m_refTreeModel->m_Columns.m_col_draggable] = true;
      childrow[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

      childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_refTreeModel->m_Columns.m_col_id] = 12;
      childrow[m_refTreeModel->m_Columns.m_col_name] = "Sue Bob";
      childrow[m_refTreeModel->m_Columns.m_col_draggable] = true;
      childrow[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

      row = *(m_refTreeModel->append());
      row[m_refTreeModel->m_Columns.m_col_id] = 2;
      row[m_refTreeModel->m_Columns.m_col_name] = "Joey Jojo";
      row[m_refTreeModel->m_Columns.m_col_draggable] = true;
      row[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

      row = *(m_refTreeModel->append());
      row[m_refTreeModel->m_Columns.m_col_id] = 3;
      row[m_refTreeModel->m_Columns.m_col_name] = "Rob McRoberts";
      row[m_refTreeModel->m_Columns.m_col_draggable] = true;
      row[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

      childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_refTreeModel->m_Columns.m_col_id] = 31;
      childrow[m_refTreeModel->m_Columns.m_col_name] = "Xavier McRoberts";
      childrow[m_refTreeModel->m_Columns.m_col_draggable] = true;
      childrow[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

      //Add the TreeView's view columns:
      m_TreeView.append_column("ID", m_refTreeModel->m_Columns.m_col_id);
      m_TreeView.append_column("Name", m_refTreeModel->m_Columns.m_col_name);
      m_TreeView.append_column_editable("Draggable",
              m_refTreeModel->m_Columns.m_col_draggable);
      m_TreeView.append_column_editable("Receives Drags",
              m_refTreeModel->m_Columns.m_col_receivesdrags);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `treemodel_dnd.cc` (For use with gtkmm 4)

    #include "treemodel_dnd.h"
    #include <iostream>

    TreeModel_Dnd::TreeModel_Dnd()
    {
      //We can't just call Gtk::TreeModel(m_Columns) in the initializer list
      //because m_Columns does not exist when the base class constructor runs.
      //And we can't have a static m_Columns instance, because that would be
      //instantiated before the gtkmm type system.
      //So, we use this method, which should only be used just after creation:
      set_column_types(m_Columns);
    }

    Glib::RefPtr<TreeModel_Dnd> TreeModel_Dnd::create()
    {
      return Glib::make_refptr_for_instance<TreeModel_Dnd>( new TreeModel_Dnd() );
    }

    bool
    TreeModel_Dnd::row_draggable_vfunc(const Gtk::TreeModel::Path& path) const
    {
      // Make the value of the "draggable" column determine whether this row can
      // be dragged:

      const const_iterator iter = get_iter(path);
      if(iter)
      {
        ConstRow row = *iter;
        bool is_draggable = row[m_Columns.m_col_draggable];
        return is_draggable;
      }

      return Gtk::TreeStore::row_draggable_vfunc(path);
    }

    bool
    TreeModel_Dnd::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest,
            const Glib::ValueBase& value) const
    {
      //Make the value of the "receives drags" column determine whether a row can be
      //dragged into it:

      //dest is the path that the row would have after it has been dropped:
      //But in this case we are more interested in the parent row:
      auto dest_parent = dest;
      bool dest_is_not_top_level = dest_parent.up();
      if(!dest_is_not_top_level || dest_parent.empty())
      {
        //The user wants to move something to the top-level.
        //Let's always allow that.
      }
      else
      {
        //Get an iterator for the row at this path:
        const const_iterator iter_dest_parent = get_iter(dest_parent);
        if(iter_dest_parent)
        {
          ConstRow row = *iter_dest_parent;
          bool receives_drags = row[m_Columns.m_col_receivesdrags];
          return receives_drags;
        }
      }

      // You could also examine the row being dragged (via value)
      // if you must look at both rows to see whether a drop should be allowed.
      // You could use
      //   Glib::RefPtr<const Gtk::TreeModel> model_dragged_row;
      //   Gtk::TreeModel::Path path_dragged_row;
      //   Gtk::TreeModel::Path::get_row_drag_data(value,
      //     model_dragged_row, path_dragged_row);

      return Gtk::TreeStore::row_drop_possible_vfunc(dest, value);
    }

#### Искачући контекстни мени

Овај пример је веома сличан примеру за ListStore, али изводи прилагођени TreeView како би инкапсулирао ко̂д модела стабла и ко̂д искачућег менија у нашој изведеној класи. Погледајте одељак Искачући контекстни мени прегледа стабла.

Преглед стабла — Искачући контекстни мени

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>
    #include "treeview_withpopup.h"

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_quit();

      //Child widgets:
      Gtk::Box m_VBox;

      Gtk::ScrolledWindow m_ScrolledWindow;
      TreeView_WithPopup m_TreeView;

      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `treeview_withpopup.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_TREEVIEW_WITHPOPUP_H
    #define GTKMM_EXAMPLE_TREEVIEW_WITHPOPUP_H

    #include <gtkmm.h>

    class TreeView_WithPopup : public Gtk::TreeView
    {
    public:
      TreeView_WithPopup();
      virtual ~TreeView_WithPopup();

    protected:
      // Signal handler for showing popup menu:
      void on_popup_button_pressed(int n_press, double x, double y);

      //Signal handler for popup menu items:
      void on_menu_file_popup_generic();

      //Tree model columns:
      class ModelColumns : public Gtk::TreeModel::ColumnRecord
      {
      public:

        ModelColumns()
        { add(m_col_id); add(m_col_name); }

        Gtk::TreeModelColumn<unsigned int> m_col_id;
        Gtk::TreeModelColumn<Glib::ustring> m_col_name;
      };

      ModelColumns m_Columns;

      //The Tree model:
      Glib::RefPtr<Gtk::ListStore> m_refTreeModel;

      Gtk::PopoverMenu m_MenuPopup;
    };

    #endif //GTKMM_EXAMPLE_TREEVIEW_WITHPOPUP_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include <iostream>
    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::TreeView (ListStore) example");
      set_default_size(400, 200);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      //Add the TreeView, inside a ScrolledWindow, with the button underneath:
      m_ScrolledWindow.set_child(m_TreeView);

      //Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(5);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `treeview_withpopup.cc` (For use with gtkmm 4)

    #include "treeview_withpopup.h"
    #include <iostream>

    TreeView_WithPopup::TreeView_WithPopup()
    {
      //Create the Tree model:
      m_refTreeModel = Gtk::ListStore::create(m_Columns);
      set_model(m_refTreeModel);

      //Fill the TreeView's model
      auto row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 1;
      row[m_Columns.m_col_name] = "right-click on this";

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 2;
      row[m_Columns.m_col_name] = "or this";

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 3;
      row[m_Columns.m_col_name] = "or this, for a popup context menu";

      //Add the TreeView's view columns:
      append_column("ID", m_Columns.m_col_id);
      append_column("Name", m_Columns.m_col_name);

      // Catch button press events:
      auto refGesture = Gtk::GestureClick::create();
      refGesture->set_button(GDK_BUTTON_SECONDARY);
      refGesture->signal_pressed().connect(
        sigc::mem_fun(*this, &TreeView_WithPopup::on_popup_button_pressed));
      add_controller(refGesture);

      // Fill popup menu:
      auto gmenu = Gio::Menu::create();
      gmenu->append("_Edit", "popup.edit");
      gmenu->append("_Process", "popup.process");
      gmenu->append("_Remove", "popup.remove");

      m_MenuPopup.set_parent(*this);
      m_MenuPopup.set_menu_model(gmenu);
      m_MenuPopup.set_has_arrow(false);

      // Create actions:
      auto refActionGroup = Gio::SimpleActionGroup::create();

      refActionGroup->add_action("edit",
        sigc::mem_fun(*this, &TreeView_WithPopup::on_menu_file_popup_generic));
      refActionGroup->add_action("process",
        sigc::mem_fun(*this, &TreeView_WithPopup::on_menu_file_popup_generic));
      refActionGroup->add_action("remove",
        sigc::mem_fun(*this, &TreeView_WithPopup::on_menu_file_popup_generic));

      insert_action_group("popup", refActionGroup);
    }

    TreeView_WithPopup::~TreeView_WithPopup()
    {
    }

    void TreeView_WithPopup::on_popup_button_pressed(int /* n_press */, double x, double y)
    {
      const Gdk::Rectangle rect(x, y, 1, 1);
      m_MenuPopup.set_pointing_to(rect);
      m_MenuPopup.popup();
    }

    void TreeView_WithPopup::on_menu_file_popup_generic()
    {
      std::cout << "A popup menu item was selected." << std::endl;

      auto refSelection = get_selection();
      if(refSelection)
      {
        auto iter = refSelection->get_selected();
        if(iter)
        {
          int id = (*iter)[m_Columns.m_col_id];
          std::cout << "  Selected ID=" << id << std::endl;
        }
      }
    }

## Елемент падајућег менија

Елемент DropDown је алтернатива за застарели ComboBox. Користи моделе списка уместо модела стабла, а садржај се приказује помоћу елемената уместо исцртавача ћелија.

Елемент DropDown нуди списак избора у падајућем менију. Ако је прикладно, може приказати додатне податке о свакој ставци, као што су текст, слика или дугме за потврду. Елемент DropDown опционално може имати Entry у падајућем менију, омогућавајући кориснику да претражује дугачак списак.

Списак се доставља преко Gio::ListModel, а подаци из овог модела се додају у преглед елемента DropDown помоћу руковалаца сигналима повезаних са SignalListItemFactory. Ово пружа флексибилност, али класа StringList пружа једноставнију текстуалну специјализацију у случају да та флексибилност није потребна.

Референца

### Модел

Модел за DropDown се може дефинисати и попунити потпуно исто као за ListView или ColumnView. Мора бити подкласа Glib::Object. На пример, можете имати DropDown са једном колоном за целе бројеве и једном за текст, овако:

    class ModelColumns : public Glib::Object
    {
    public:
      int m_col_id;
      Glib::ustring m_col_name;

      static Glib::RefPtr<ModelColumns> create(
        int col_id, const Glib::ustring& col_name)
      {
        return Glib::make_refptr_for_instance<ModelColumns>(
          new ModelColumns(col_id, col_name));
      }

    protected:
      ModelColumns(int col_id, const Glib::ustring& col_name)
      : m_col_id(col_id), m_col_name(col_name)
      {}
    };

    Glib::RefPtr<Gio::ListStore<ModelColumns>> m_ListStore;

Након додавања редова у овај модел, требало би да доставите модел елементу DropDown помоћу методе set_model(). Осим ако не користите модел StringList, такође морате да поставите ListItemFactory помоћу set_factory(). Ако желите да ставке у падајућем менију изгледају другачије од ставке у елементу DropDown, такође морате да поставите посебан ListItemFactory помоћу set_list_factory().

### Изабрана ставка

Да бисте сазнали коју је ставку, ако је има, корисник изабрао из елемента DropDown, позовите DropDown::get_selected(). Ово враћа unsigned int који можете користити да добијете изабране податке из модела. На пример, можете прочитати целобројну вредност ИД-а из модела, иако сте изабрали да у елементу DropDown прикажете само опис читљив људима. На пример:

    unsigned int sel = m_DropDown.get_selected();
    if (sel != GTK_INVALID_LIST_POSITION)
    {
      // Get the data for the selected row, using our knowledge of the list model:
      auto id = m_ListStore->get_item(sel).m_col_id;
      set_some_id_chosen(id); // Your own function.
    }
    else
      set_nothing_chosen(); // Your own function.

### Одговор на измене

Можда ћете морати да реагујете на сваку промену избора у DropDown елементу, на пример да бисте ажурирали друге виџете. Да бисте то урадили, требало би да се повежете на property_selected().signal_changed(). На пример:

    m_DropDown.property_selected().signal_changed().connect(
      sigc::mem_fun(*this, &ExampleWindow::on_dropdown_changed));

### Пример једноставне ниске

Једноставан падајући мени

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm/window.h>
    #include <gtkmm/dropdown.h>
    #include <gtkmm/stringlist.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handler:
      void on_dropdown_changed();

      // Child widget:
      Gtk::DropDown m_DropDown;

      Glib::RefPtr<Gtk::StringList> m_StringList;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    {
      set_title("DropDown example");

      set_child(m_DropDown);

      // Fill the dropdown:
      const std::vector<Glib::ustring> strings{
        "1 minute", "2 minutes", "5 minutes", "20 minutes"
      };
      m_StringList = Gtk::StringList::create(strings);
      m_DropDown.set_model(m_StringList);
      m_DropDown.set_selected(0);

      // Connect signal handler:
      m_DropDown.property_selected().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_dropdown_changed));
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_dropdown_changed()
    {
      const auto selected = m_DropDown.get_selected();
      std::cout << "DropDown changed: Row=" << selected
        << ", String=" << m_StringList->get_string(selected) << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Примери са пољем за претрагу

Падајући мени може да садржи Entry који омогућава претрагу ставки на списку. Позовите set_enable_search() и set_expression(). На пример:

    m_DropDown.set_enable_search(true);
    auto expression = Gtk::ClosureExpression<Glib::ustring>::create(
      sigc::mem_fun(*this, &ExampleWindow::get_col_name));
    m_DropDown.set_expression(expression);

    //-------
    Glib::ustring ExampleWindow::get_col_name(const Glib::RefPtr<Glib::ObjectBase>& item)
    {
      const auto col = std::dynamic_pointer_cast<ModelColumns>(item);
      return col ? col->m_col_name : "";
    }

#### Пример са ниском

Ниска за претрагу

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm/window.h>
    #include <gtkmm/dropdown.h>
    #include <gtkmm/stringlist.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handler:
      void on_dropdown_changed();

      // Child widget:
      Gtk::DropDown m_DropDown;

      Glib::RefPtr<Gtk::StringList> m_StringList;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/expression.h>
    #include <gtkmm/stringobject.h>
    #include <iostream>

    ExampleWindow::ExampleWindow()
    {
      set_title("Searchable DropDown example");

      set_child(m_DropDown);

      // Fill the dropdown:
      const std::vector<Glib::ustring> many_times{
        "1 minute", "2 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes",
        "25 minutes", "30 minutes", "35 minutes", "40 minutes", "45 minutes", "50 minutes",
        "55 minutes", "1 hour", "2 hours", "3 hours", "5 hours", "6 hours", "7 hours",
        "8 hours", "9 hours", "10 hours", "11 hours", "12 hours"
      };
      m_StringList = Gtk::StringList::create(many_times);
      m_DropDown.set_model(m_StringList);
      m_DropDown.set_selected(0);

      // Show a search entry.
      m_DropDown.set_enable_search(true);
      auto expression = Gtk::ClosureExpression<Glib::ustring>::create(
        [](const Glib::RefPtr<Glib::ObjectBase>& item)->Glib::ustring
        {
          // An item in a StringList is a StringObject.
          const auto string_object = std::dynamic_pointer_cast<Gtk::StringObject>(item);
          return string_object ? string_object->get_string() : "";
        });
      m_DropDown.set_expression(expression);

      // Connect signal handler:
      m_DropDown.property_selected().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_dropdown_changed));
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_dropdown_changed()
    {
      const auto selected = m_DropDown.get_selected();
      std::cout << "DropDown changed: Row=" << selected
        << ", String=" << m_StringList->get_string(selected) << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Пример са фонтом

Овај пример користи Pango::FontMap као свој модел. Ово је могуће јер Pango::FontMap примењује Gio::ListModel интерфејс. Наравно, уместо тога можете користити FontDialogButton.

Претрага фонта

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handler:
      void on_dropdown_changed();

      Glib::ustring get_family_name(const Glib::RefPtr<Glib::ObjectBase>& item);

      // Child widget:
      Gtk::DropDown m_DropDown;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <pangomm/cairofontmap.h>
    #include <iostream>
    //#include <typeinfo>

    ExampleWindow::ExampleWindow()
    {
      set_title("Searchable DropDown example");

      // Add the DropDown to the window.
      set_child(m_DropDown);

      // Show a search entry.
      m_DropDown.set_enable_search(true);
      auto expression = Gtk::ClosureExpression<Glib::ustring>::create(
        sigc::mem_fun(*this, &ExampleWindow::get_family_name));
      m_DropDown.set_expression(expression);

      // Pango::FontMap is a Gio::ListModel.
      auto model = Pango::CairoFontMap::get_default();
      m_DropDown.set_model(model);
      m_DropDown.set_selected(0);

      // Connect signal handler.
      m_DropDown.property_selected().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_dropdown_changed));
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_dropdown_changed()
    {
      const auto selected = m_DropDown.get_selected();
      const auto item = m_DropDown.get_selected_item();
      std::cout << "DropDown changed: Row=" << selected
        << ", Font=" << get_family_name(item) << std::endl;

      // Pango::FontMap is often a list of items of type Pango::FontFamily.
      // From the description of Pango::CairoFontMap: "The actual type of the font map
      // will depend on the particular font technology Cairo was compiled to use."
      // If get_family_name() does not return a family name, you can add
      // #include <typeinfo> and query the type of the items with:
      //   std::cout << typeid(*item.get()).name() << std::endl;
    }

    Glib::ustring ExampleWindow::get_family_name(const Glib::RefPtr<Glib::ObjectBase>& item)
    {
      auto family = std::dynamic_pointer_cast<Pango::FontFamily>(item);
      return family ? family->get_name() : "";
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Сложени пример

Ово је сложенији пример са два SignalListItemFactory објекта и њиховим обрађивачима сигнала. Овај пример би био једноставнији без поља за потврду у падајућем менију.

Претрага фонта

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handlers:
      void on_setup_selected_item(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_selected_item(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_setup_list_item(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_list_item(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_unbind_list_item(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_selected_item_changed(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_dropdown_changed();

      void create_model();
      void liststore_add_item(const Glib::ustring& title, const Glib::ustring& icon,
        const Glib::ustring& description);

      // A Gio::ListStore item.
      class ModelColumns : public Glib::Object
      {
      public:
        Glib::ustring m_title;
        Glib::ustring m_icon;
        Glib::ustring m_description;

        static Glib::RefPtr<ModelColumns> create(const Glib::ustring& title,
          const Glib::ustring& icon, const Glib::ustring& description)
        {
          return Glib::make_refptr_for_instance<ModelColumns>(
            new ModelColumns(title, icon, description));
        }

      protected:
        ModelColumns(const Glib::ustring& title, const Glib::ustring& icon,
          const Glib::ustring& description)
        : m_title(title), m_icon(icon), m_description(description)
        {}
      }; // ModelColumns

      // Child widget:
      Gtk::DropDown m_DropDown;

      Glib::RefPtr<Gio::ListStore<ModelColumns>> m_ListStore;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    {
      set_title("DropDown example");

      // Add the DropDown to the window.
      set_child(m_DropDown);

      // Factory for presenting the selected item.
      auto factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_setup_selected_item));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_selected_item));
      m_DropDown.set_factory(factory);

      // Factory for presenting the items in the dropdown list.
      factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_setup_list_item));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_list_item));
      factory->signal_unbind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_unbind_list_item));
      m_DropDown.set_list_factory(factory);

      // Create the model and fill it.
      create_model();
      m_DropDown.set_model(m_ListStore);
      m_DropDown.set_selected(0);

      // Connect signal handler:
      m_DropDown.property_selected().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_dropdown_changed));
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_dropdown_changed()
    {
      const auto selected = m_DropDown.get_selected();
      std::cout << "DropDown changed: Row=" << selected
        << ", Title=" << m_ListStore->get_item(selected)->m_title << std::endl;
    }

    void ExampleWindow::create_model()
    {
      m_ListStore = Gio::ListStore<ModelColumns>::create();

      liststore_add_item("Digital Output", "audio-card-symbolic", "Built-in Audio");
      liststore_add_item("Headphones", "audio-headphones-symbolic", "Built-in Audio");
      liststore_add_item("Digital Output", "audio-card-symbolic", "Thinkpad Tunderbolt 3 Dock USB Audio");
      liststore_add_item("Analog Output", "audio-card-symbolic", "Thinkpad Tunderbolt 3 Dock USB Audio");
    }

    void ExampleWindow::liststore_add_item(const Glib::ustring& title,
      const Glib::ustring& icon, const Glib::ustring& description)
    {
      m_ListStore->append(ModelColumns::create(title, icon, description));
    }

    void ExampleWindow::on_setup_selected_item(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 10);
      box->append(*Gtk::make_managed<Gtk::Image>());
      box->append(*Gtk::make_managed<Gtk::Label>());
      list_item->set_child(*box);
    }

    void ExampleWindow::on_bind_selected_item(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto box = dynamic_cast<Gtk::Box*>(list_item->get_child());
      if (!box)
        return;
      auto image = dynamic_cast<Gtk::Image*>(box->get_first_child());
      if (!image)
        return;
      image->set_from_icon_name(col->m_icon);
      auto label = dynamic_cast<Gtk::Label*>(image->get_next_sibling());
      if (!label)
        return;
      label->set_text(col->m_title);
    }

    void ExampleWindow::on_setup_list_item(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 10);
      auto vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 2);

      hbox->append(*Gtk::make_managed<Gtk::Image>());
      hbox->append(*vbox);
      auto title = Gtk::make_managed<Gtk::Label>();
      title->set_xalign(0.0);
      vbox->append(*title);
      auto description = Gtk::make_managed<Gtk::Label>();
      description->set_xalign(0.0);
      description->add_css_class("dim-label");
      vbox->append(*description);
      auto checkmark = Gtk::make_managed<Gtk::Image>();
      checkmark->set_from_icon_name("object-select-symbolic");
      checkmark->set_visible(false);
      hbox->append(*checkmark);

      list_item->set_child(*hbox);
    }

    void ExampleWindow::on_bind_list_item(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto hbox = dynamic_cast<Gtk::Box*>(list_item->get_child());
      if (!hbox)
        return;
      auto image = dynamic_cast<Gtk::Image*>(hbox->get_first_child());
      if (!image)
        return;
      image->set_from_icon_name(col->m_icon);
      auto vbox = dynamic_cast<Gtk::Box*>(image->get_next_sibling());
      if (!vbox)
        return;
      auto title = dynamic_cast<Gtk::Label*>(vbox->get_first_child());
      if (!title)
        return;
      title->set_text(col->m_title);
      auto description = dynamic_cast<Gtk::Label*>(title->get_next_sibling());
      if (!description)
        return;
      description->set_text(col->m_description);

      auto connection = m_DropDown.property_selected_item().signal_changed().connect(
        sigc::bind(sigc::mem_fun(*this, &ExampleWindow::on_selected_item_changed),
        list_item));
      list_item->set_data("connection", new sigc::connection(connection),
        Glib::destroy_notify_delete<sigc::connection>);
      on_selected_item_changed(list_item);
    }

    void ExampleWindow::on_unbind_list_item(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      if (auto connection = static_cast<sigc::connection*>(list_item->get_data("connection")))
      {
        connection->disconnect();
        list_item->set_data("connection", nullptr);
      }
    }

    void ExampleWindow::on_selected_item_changed(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto hbox = dynamic_cast<Gtk::Box*>(list_item->get_child());
      if (!hbox)
        return;
      auto checkmark = dynamic_cast<Gtk::Image*>(hbox->get_last_child());
      if (!checkmark)
        return;
      checkmark->set_visible(m_DropDown.get_selected_item() == list_item->get_item());
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

## Прозорчићи за избор

Gtk::ComboBox и Gtk::ComboBoxText су застарели почев од gtkmm 4.10. Користите Gtk::DropDown у новом ко̂ду.

Елемент ComboBox нуди списак (или дрво) избора у падајућем менију. Ако је прикладно, може приказати додатне податке о свакој ставци, као што су текст, слика, дугме са кућицом за означавање или трака напретка. Елемент ComboBox обично ограничава корисника на доступне изборе, али опционално може имати Entry, омогућавајући кориснику да унесе произвољан текст ако ниједан од доступних избора није одговарајући.

Списак се доставља преко TreeModel-а, а колоне из овог модела се додају у преглед ComboBox-а помоћу методе ComboBox::pack_start(). Ово пружа флексибилност и безбедност врста приликом превођења, али класа ComboBoxText обезбеђује једноставнију специјализацију засновану на тексту у случају да та флексибилност није потребна.

Референца

### Модел

Модел за ComboBox се може дефинисати и попунити исто као и за TreeView. На пример, можете извести ComboBox класу са једном колоном целог броја и једном текстуалном колоном, овако:

    class ModelColumns : public Gtk::TreeModel::ColumnRecord
    {
    public:
      ModelColumns()
      { add(m_col_id); add(m_col_name); }

      Gtk::TreeModelColumn<int> m_col_id;
      Gtk::TreeModelColumn<Glib::ustring> m_col_name;
    };

    ModelColumns m_columns;

Након додавања редова у овај модел, требало би да доставите модел ComboBox-у помоћу методе set_model(). Затим користите методе pack_start() или pack_end() да одредите које ће колоне бити приказане у ComboBox-у. Као и код TreeView-а, можете користити или подразумевани исцртавач ћелија прослеђивањем TreeModelColumn-а методама паковања, или можете инстанцирати одређени CellRenderer и одредити одређено мапирање помоћу add_attribute() или set_cell_data_func(). Имајте на уму да се ове методе налазе у базној класи CellLayout.

### Изабрана ставка

Да бисте сазнали коју је ставку, ако је има, корисник изабрао из ComboBox-а, позовите ComboBox::get_active(). Ово враћа TreeModel::iterator који можете дереференцирати у Row како бисте прочитали вредности у вашим колонама. На пример, можете прочитати вредност целог броја ID-а из модела, иако сте изабрали да у ComboBox-у прикажете само опис читљив људима. На пример:

    Gtk::TreeModel::iterator iter = m_Combo.get_active();
    if(iter)
    {
      auto row = *iter;

      //Добавите податке за изабрани ред, користећи наше знање
      //о моделу стабла:
      auto id = row[m_Columns.m_col_id];
      set_something_id_chosen(id); //Ваша сопствена функција.
    }
    else
      set_nothing_chosen(); //Ваша сопствена функција.

### Одговор на измене

Можда ћете морати да реагујете на сваку промену избора у ComboBox-у, на пример да бисте ажурирали друге елементе. Да бисте то урадили, требало би да обрадите сигнал `changed`. На пример:

    m_combo.signal_changed().connect( sigc::mem_fun(*this,
          &ExampleWindow::on_combo_changed) );

### Потпуни пример

ComboBox

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm/window.h>
    #include <gtkmm/comboboxtext.h>
    #include <gtkmm/liststore.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      void on_cell_data_extra(const Gtk::TreeModel::const_iterator& iter);

      //Signal handlers:
      void on_combo_changed();

      //Tree model columns:
      class ModelColumns : public Gtk::TreeModel::ColumnRecord
      {
      public:

        ModelColumns()
        { add(m_col_id); add(m_col_name); add(m_col_extra);}

        Gtk::TreeModelColumn<int> m_col_id;
        Gtk::TreeModelColumn<Glib::ustring> m_col_name;
        Gtk::TreeModelColumn<Glib::ustring> m_col_extra;
      };

      ModelColumns m_Columns;

      //Child widgets:
      Gtk::ComboBox m_Combo;
      Gtk::CellRendererText m_cell;
      Glib::RefPtr<Gtk::ListStore> m_refTreeModel;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    {
      set_title("ComboBox example");

      //Create the Tree model:
      //m_refTreeModel = Gtk::TreeStore::create(m_Columns);
      m_refTreeModel = Gtk::ListStore::create(m_Columns);
      m_Combo.set_model(m_refTreeModel);

      //Fill the ComboBox's Tree Model:
      auto iter = m_refTreeModel->append();
      auto row = *iter;
      row[m_Columns.m_col_id] = 1;
      row[m_Columns.m_col_name] = "Billy Bob";
      row[m_Columns.m_col_extra] = "something";
      m_Combo.set_active(iter);
      /*
      auto childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 11;
      childrow[m_Columns.m_col_name] = "Billy Bob Junior";

      childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 12;
      childrow[m_Columns.m_col_name] = "Sue Bob";
      */

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 2;
      row[m_Columns.m_col_name] = "Joey Jojo";
      row[m_Columns.m_col_extra] = "yadda";

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = 3;
      row[m_Columns.m_col_name] = "Rob McRoberts";
      row[m_Columns.m_col_extra] = "";

      /*
      childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 31;
      childrow[m_Columns.m_col_name] = "Xavier McRoberts";
      */

      //Add the model columns to the Combo (which is a kind of view),
      //rendering them in the default way:
      m_Combo.pack_start(m_Columns.m_col_id);
      m_Combo.pack_start(m_Columns.m_col_name);

      //An example of adding a cell renderer manually,
      //instead of using pack_start(model_column, Gtk::PackOptions::EXPAND_WIDGET)
      //so we have more control:
      m_Combo.set_cell_data_func(m_cell,
        sigc::mem_fun(*this, &ExampleWindow::on_cell_data_extra));
      m_Combo.pack_start(m_cell);

      //Add the ComboBox to the window.
      set_child(m_Combo);

      //Connect signal handler:
      m_Combo.signal_changed().connect( sigc::mem_fun(*this, &ExampleWindow::on_combo_changed) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_cell_data_extra(const Gtk::TreeModel::const_iterator& iter)
    {
      auto row = *iter;
      const Glib::ustring extra = row[m_Columns.m_col_extra];

      //Some arbitrary logic just to show that this is where you can do such things:

      //Transform the value, deciding how to represent it as text:
      if(extra.empty())
        m_cell.property_text() = "(none)";
      else
        m_cell.property_text() = "-" + extra + "-";

      //Change other cell renderer properties too:
      m_cell.property_foreground() = (extra == "yadda" ? "red" : "green");
    }

    void ExampleWindow::on_combo_changed()
    {
      const auto iter = m_Combo.get_active();
      if(iter)
      {
        const auto row = *iter;
        if(row)
        {
          //Get the data for the selected row, using our knowledge of the tree
          //model:
          int id = row[m_Columns.m_col_id];
          Glib::ustring name = row[m_Columns.m_col_name];

          std::cout << " ID=" << id << ", name=" << name << std::endl;
        }
      }
      else
        std::cout << "invalid iter" << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Једноставан текстуални пример

ComboBoxText

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm/window.h>
    #include <gtkmm/comboboxtext.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_combo_changed();

      //Child widgets:
      Gtk::ComboBoxText m_Combo;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    {
      set_title("ComboBoxText example");

      //Fill the combo:
      m_Combo.append("something");
      m_Combo.append("something else");
      m_Combo.append("something or other");
      m_Combo.set_active(1);

      set_child(m_Combo);

      //Connect signal handler:
      m_Combo.signal_changed().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_combo_changed) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_combo_changed()
    {
      Glib::ustring text = m_Combo.get_active_text();
      if(!(text.empty()))
        std::cout << "Combo changed: " << text << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### ComboBox са уносом

ComboBox може да садржи елемент Entry за унос произвољног текста, навођењем `true` за параметар конструктора `has_entry`.

#### Текстуална колона

Да би Entry могао да међудејствује са падајућим списком избора, морате навести која је од колона вашег модела текстуална колона, помоћу set_entry_text_column(). На пример:

    m_combo.set_entry_text_column(m_columns.m_col_name);

Када изаберете ставку из падајућег менија, вредност из ове колоне ће бити постављена у Entry.

#### Унос

Будући да корисник може да унесе произвољан текст, активни ред модела није довољан да нам каже који је текст корисник унео. Стога, треба да добавите елемент Entry методом ComboBox::get_entry() и позовете get_text() над њим.

#### Одговор на измене

Када корисник унесе произвољан текст, можда неће бити довољно повезати се на сигнал `changed`, који се емитује за сваки откуцани знак. Он се не емитује када корисник притисне тастер Enter. Притисак на тастер Enter или померање фокуса тастатуре на други елемент може сигнализирати да је корисник завршио са уносом текста. Да бисте били обавештени о овим догађајима, повежите се на сигнал `activate` елемента Entry (доступан од gtkmm 4.8), и додајте Gtk::EventControllerFocus и повежите се на његов сигнал `leave`, овако

    auto entry = m_Combo.get_entry();
    if (entry)
    {
      // Алтернативно се можете повезати на m_Combo.signal_changed().
      entry->signal_changed().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_entry_changed));
      entry->signal_activate().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_entry_activate));
      // Entry ће примати догађаје напуштања фокуса.
      auto controller = Gtk::EventControllerFocus::create();
      m_ConnectionFocusLeave = controller->signal_leave().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_entry_focus_leave));
      entry->add_controller(controller);
    }

Сигнали `changed` елемената ComboBox и Entry се оба емитују за сваку промену. Није битно на који ћете се повезати. Али EventControllerFocus мора бити додат у Entry.

#### Потпуни пример

ComboBox са уносом

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm/window.h>
    #include <gtkmm/combobox.h>
    #include <gtkmm/liststore.h>
    #include <gtkmm/version.h>

    #define HAS_SIGNAL_ACTIVATE GTKMM_CHECK_VERSION(4,7,1)

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_entry_changed();
    #if HAS_SIGNAL_ACTIVATE
      void on_entry_activate();
    #endif
      void on_entry_focus_leave();

      //Signal connection:
      sigc::connection m_ConnectionFocusLeave;

      //Tree model columns:
      class ModelColumns : public Gtk::TreeModel::ColumnRecord
      {
      public:

        ModelColumns()
        { add(m_col_id); add(m_col_name); }

        Gtk::TreeModelColumn<Glib::ustring> m_col_id; //The data to choose - this must be text.
        Gtk::TreeModelColumn<Glib::ustring> m_col_name;
      };

      ModelColumns m_Columns;

      //Child widgets:
      Gtk::ComboBox m_Combo;
      Glib::RefPtr<Gtk::ListStore> m_refTreeModel;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/eventcontrollerfocus.h>
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_Combo(true /* has_entry */)
    {
      set_title("ComboBox example");

      //Create the Tree model:
      //m_refTreeModel = Gtk::TreeStore::create(m_Columns);
      m_refTreeModel = Gtk::ListStore::create(m_Columns);
      m_Combo.set_model(m_refTreeModel);

      //Fill the ComboBox's Tree Model:
      auto row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = "1";
      row[m_Columns.m_col_name] = "Billy Bob";
      /*
      auto childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 11;
      childrow[m_Columns.m_col_name] = "Billy Bob Junior";

      childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 12;
      childrow[m_Columns.m_col_name] = "Sue Bob";
      */

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = "2";
      row[m_Columns.m_col_name] = "Joey Jojo";

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = "3";
      row[m_Columns.m_col_name] = "Rob McRoberts";

      /*
      childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 31;
      childrow[m_Columns.m_col_name] = "Xavier McRoberts";
      */

      //Add the model columns to the Combo (which is a kind of view),
      //rendering them in the default way:
      //This is automatically rendered when we use set_entry_text_column().
      //m_Combo.pack_start(m_Columns.m_col_id, Gtk::PackOptions::EXPAND_WIDGET);
      m_Combo.pack_start(m_Columns.m_col_name);

      m_Combo.set_entry_text_column(m_Columns.m_col_id);
      m_Combo.set_active(1);

      //Add the ComboBox to the window.
      set_child(m_Combo);

      //Connect signal handlers:
      auto entry = m_Combo.get_entry();
      if (entry)
      {
        // Alternatively you can connect to m_Combo.signal_changed().
        entry->signal_changed().connect(sigc::mem_fun(*this,
          &ExampleWindow::on_entry_changed));
    #if HAS_SIGNAL_ACTIVATE
        entry->signal_activate().connect(sigc::mem_fun(*this,
          &ExampleWindow::on_entry_activate));
    #endif
        // The Entry shall receive focus-leave events.
        auto controller = Gtk::EventControllerFocus::create();
        m_ConnectionFocusLeave = controller->signal_leave().connect(
          sigc::mem_fun(*this, &ExampleWindow::on_entry_focus_leave));
        entry->add_controller(controller);
      }
      else
        std::cout << "No Entry ???" << std::endl;
    }

    ExampleWindow::~ExampleWindow()
    {
      // The focus-leave signal may be emitted while m_Combo is being destructed.
      // The signal handler can generate critical messages, if it's called when
      // m_Combo has been partly destructed.
      m_ConnectionFocusLeave.disconnect();
    }

    void ExampleWindow::on_entry_changed()
    {
      auto entry = m_Combo.get_entry();
      if (entry)
      {
        std::cout << "on_entry_changed(): Row=" << m_Combo.get_active_row_number()
          << ", ID=" << entry->get_text() << std::endl;
      }
    }

    #if HAS_SIGNAL_ACTIVATE
    void ExampleWindow::on_entry_activate()
    {
      auto entry = m_Combo.get_entry();
      if (entry)
      {
        std::cout << "on_entry_activate(): Row=" << m_Combo.get_active_row_number()
          << ", ID=" << entry->get_text() << std::endl;
      }
    }
    #endif

    void ExampleWindow::on_entry_focus_leave()
    {
      auto entry = m_Combo.get_entry();
      if (entry)
      {
        std::cout << "on_entry_focus_leave(): Row=" << m_Combo.get_active_row_number()
          << ", ID=" << entry->get_text() << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Једноставан текстуални пример

ComboBoxText са уносом

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm/window.h>
    #include <gtkmm/comboboxtext.h>
    #include <gtkmm/version.h>

    #define HAS_SIGNAL_ACTIVATE GTKMM_CHECK_VERSION(4,7,1)

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_combo_changed();
    #if HAS_SIGNAL_ACTIVATE
      void on_entry_activate();
    #endif
      void on_entry_focus_leave();

      //Signal connection:
      sigc::connection m_ConnectionFocusLeave;

      //Child widgets:
      Gtk::ComboBoxText m_Combo;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/eventcontrollerfocus.h>
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_Combo(true /* has_entry */)
    {
      set_title("ComboBoxText example");

      //Fill the combo:
      m_Combo.append("something");
      m_Combo.append("something else");
      m_Combo.append("something or other");
      m_Combo.set_active(0);

      set_child(m_Combo);

      //Connect signal handlers:
      auto entry = m_Combo.get_entry();
      // Alternatively you can connect to entry->signal_changed().
      m_Combo.signal_changed().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_combo_changed) );

      if (entry)
      {
    #if HAS_SIGNAL_ACTIVATE
        entry->signal_activate().connect(sigc::mem_fun(*this,
          &ExampleWindow::on_entry_activate));
    #endif
        // The Entry shall receive focus-leave events.
        auto controller = Gtk::EventControllerFocus::create();
        m_ConnectionFocusLeave = controller->signal_leave().connect(
          sigc::mem_fun(*this, &ExampleWindow::on_entry_focus_leave));
        entry->add_controller(controller);
      }
      else
        std::cout << "No Entry ???" << std::endl;

      m_Combo.property_has_frame() = false;
    }

    ExampleWindow::~ExampleWindow()
    {
      // The focus-leave signal may be emitted while m_Combo is being destructed.
      // The signal handler can generate critical messages, if it's called when
      // m_Combo has been partly destructed.
      m_ConnectionFocusLeave.disconnect();
    }

    void ExampleWindow::on_combo_changed()
    {
      std::cout << "on_combo_changed(): Row=" << m_Combo.get_active_row_number()
        << ", Text=" << m_Combo.get_active_text() << std::endl;
    }

    #if HAS_SIGNAL_ACTIVATE
    void ExampleWindow::on_entry_activate()
    {
      std::cout << "on_entry_activate(): Row=" << m_Combo.get_active_row_number()
        << ", Text=" << m_Combo.get_active_text() << std::endl;
    }
    #endif

    void ExampleWindow::on_entry_focus_leave()
    {
      std::cout << "on_entry_focus_leave(): Row=" << m_Combo.get_active_row_number()
        << ", Text=" << m_Combo.get_active_text() << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

## TextView

Виџет TextView се може користити за приказ и уређивање великих количина форматираног текста. Попут TreeView-а, има дизајн модел/преглед. У овом случају TextBuffer је модел.

### Међумеморија

Gtk::TextBuffer је модел који садржи податке за Gtk::TextView, попут Gtk::TreeModel-а који користи Gtk::TreeView. Ово омогућава да два или више Gtk::TextView-а деле исту TextBuffer међумеморију, и омогућава да те међумеморије буду приказане мало другачије. Или бисте могли да одржавате неколико Gtk::TextBuffer међумеморија и изаберете да прикажете сваку у различито време у истом Gtk::TextView виџету.

TextView ствара сопствену подразумевану TextBuffer међумеморију, којој можете приступити путем методе get_buffer().

Референце

#### Итератори

Gtk::TextBuffer::iterator и Gtk::TextBuffer::const_iterator представљају положај између два знака у међумеморији текста. Кад год се међумеморија измени на начин који утиче на број знакова у њој, сви преостали итератори постају неважећи. Због тога се итератори не могу користити за очување положаја током измена међумеморије. Да бисте очували положај, користите Gtk::TextBuffer::Mark.

Референце

#### Ознаке и форматирање

##### Ознаке

Да бисте навели да неки текст у међумеморији треба да има специфично форматирање, морате дефинисати ознаку која ће чувати те информације о форматирању, а затим применити ту ознаку на област текста. На пример, да бисте дефинисали ознаку и њена својства:

    auto refTagMatch = Gtk::TextBuffer::Tag::create();
    refTagMatch->property_background() = "orange";

Можете навести име за Tag када користите методу create(), али то није неопходно.

Класа Tag има многа друга својства.

Референце

##### Табела ознака

Сваки Gtk::TextBuffer користи Gtk::TextBuffer::TagTable, који садржи Tag-ове за ту међумеморију. 2 или више TextBuffer-а могу делити исту TagTable. Када правите Tag-ове, требало би да их додате у TagTable. На пример:

    auto refTagTable = Gtk::TextBuffer::TagTable::create();
    refTagTable->add(refTagMatch);
    //Надајмо се да ће будуће издање gtkmm-а имати метод set_tag_table(),
    //за коришћење након стварања међумеморије.
    auto refBuffer = Gtk::TextBuffer::create(refTagTable);

Такође можете користити get_tag_table() да добијете, и можда измените, подразумевану TagTable TextBuffer-а уместо да је правите експлицитно.

Референце

##### Примена ознака

Ако сте направили Tag и додали га у TagTable, можете применити ту ознаку на део TextBuffer-а тако да се део текста приказује са тим форматирањем. Почетак и крај опсега текста одређујете навођењем Gtk::TextBuffer::iterator-а. На пример:

    refBuffer->apply_tag(refTagMatch, iterRangeStart, iterRangeStop);

Или можете навести ознаку при првом уметању текста:

    refBuffer->insert_with_tag(iter, "Неки текст", refTagMatch);

Можете применити више од једног Tag-а на исти текст, коришћењем apply_tag() више пута, или коришћењем insert_with_tags(). Tag-ови могу навести различите вредности за иста својства, али те сукобе можете решити коришћењем Tag::set_priority().

#### Ознаке

Итератори TextBuffer-а обично постају неважећи када се текст промени, али можете користити Gtk::TextBuffer::Mark да запамтите положај у таквим ситуацијама. На пример,

    auto refMark = refBuffer->create_mark(iter);

Затим можете касније користити метод get_iter() да направите итератор за нови положај Mark-а.

Постоје два уграђена Mark-а — `insert` и `selection_bound`, којима можете приступити помоћу метода TextBuffer-а get_insert() и get_selection_bound().

Референце

#### Преглед

Као што је већ поменуто, сваки TextView има TextBuffer, а један или више TextView-а могу делити исти TextBuffer.

Попут TreeView-а, вероватно би требало да поставите свој TextView унутар ScrolledWindow-а како бисте омогућили кориснику да види и креће се кроз целу текстуалну област помоћу трака за помицање.

Референце

##### Подразумевано форматирање

TextView има различите методе које вам омогућавају да промените приказ међумеморије за овај конкретан преглед. Неке од њих могу бити преклопљене Gtk::TextTag-овима у међумеморији, ако наводе исте ствари. На пример, set_left_margin(), set_right_margin(), set_indent(), итд.

##### Клизање

Gtk::TextView има различите scroll_to() методе. Оне вам омогућавају да осигурате да је одређени део међумеморије текста видљив. На пример, функција „Пронађи“ ваше апликације може користити Gtk::TextView::scroll_to() за приказ пронађеног текста.

### Елементи и ChildAnchors

Можете уградити елементе, као што су Gtk::Button дугмад, у текст. Сваком таквом садржаном елементу је потребан ChildAnchor. ChildAnchor-и су повезани са iterator-има. На пример, да бисте направили ослонац садржаног елемента на одређеном положају, користите Gtk::TextBuffer::create_child_anchor():

    auto refAnchor = refBuffer->create_child_anchor(iter);

Затим, да бисте додали елемент на тај положај, користите Gtk::TextView::add_child_at_anchor():

    m_TextView.add_child_at_anchor(m_Button, refAnchor);

Референца

### Примери

#### Једноставан пример

TextView

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:

      void fill_buffers();

      //Signal handlers:
      void on_button_quit();
      void on_button_buffer1();
      void on_button_buffer2();

      //Child widgets:
      Gtk::Box m_VBox;

      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::TextView m_TextView;

      Glib::RefPtr<Gtk::TextBuffer> m_refTextBuffer1, m_refTextBuffer2;

      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit, m_Button_Buffer1, m_Button_Buffer2;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Quit("_Quit", true),
      m_Button_Buffer1("Use buffer 1"),
      m_Button_Buffer2("Use buffer 2")
    {
      set_title("Gtk::TextView example");
      set_default_size(400, 200);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      //Add the TreeView, inside a ScrolledWindow, with the button underneath:
      m_ScrolledWindow.set_child(m_TextView);

      //Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);

      //append buttons:
      m_VBox.append(m_ButtonBox);

      m_Button_Buffer1.set_hexpand(true);
      m_Button_Buffer1.set_halign(Gtk::Align::END);
      m_ButtonBox.append(m_Button_Buffer1);
      m_ButtonBox.append(m_Button_Buffer2);
      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(5);
      m_ButtonBox.set_spacing(5);

      //Connect signals:
      m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit) );
      m_Button_Buffer1.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_buffer1) );
      m_Button_Buffer2.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_buffer2) );

      fill_buffers();
      on_button_buffer1();
    }

    void ExampleWindow::fill_buffers()
    {
      m_refTextBuffer1 = Gtk::TextBuffer::create();
      m_refTextBuffer1->set_text("This is the text from TextBuffer #1.");

      m_refTextBuffer2 = Gtk::TextBuffer::create();
      m_refTextBuffer2->set_text(
              "This is some alternative text, from TextBuffer #2.");

    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_button_buffer1()
    {
      m_TextView.set_buffer(m_refTextBuffer1);
    }

    void ExampleWindow::on_button_buffer2()
    {
      m_TextView.set_buffer(m_refTextBuffer2);
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Други примери

Пример у поглављу Интернационализација и локализација користи TextView.

Постоји сложенији пример међу демо програмима gtkmm-а.

## Изборници и траке алата

Постоје посебни АПИ-ји за изборнике и траке алата, али би обично требало да радите са њима заједно, стварајући Gio::SimpleAction радње на које се можете позивати и у изборницима и у тракама алата. На овај начин можете управљати активирањем радње уместо да одговарате на ставке изборника и траке алата засебно. Такође можете омогућити или онемогућити и ставку изборника и ставку траке алата путем радње. Gtk::Builder може да направи изборнике и траке алата.

Ово укључује коришћење класа Gio::SimpleActionGroup, Gio::SimpleAction и Gtk::Builder, од којих би све требало да буду инстанциране путем својих create() метода, које враћају RefPtr-ове.

### Радње

Прво направите Gio::SimpleAction радње и додајте их у Gio::SimpleActionGroup, помоћу Gio::ActionMap::add_action(). (Gio::ActionMap је основна класа за Gio::SimpleActionGroup.) Затим додајте групу радњи у ваш прозор помоћу Gtk::Widget::insert_action_group().

Аргументи за add_action() наводе назив радње, који се користи у ставкама изборника и дугмадима траке алата. Такође можете навести руковаоца сигналом када позивате add_action(). Овај руковалац сигналом ће бити позван када се радња активира било преко ставке изборника или дугмета траке алата.

На пример:

    m_refActionGroup = Gio::SimpleActionGroup::create();

    m_refActionGroup->add_action("new", sigc::mem_fun(*this, &ExampleWindow::on_action_file_new));
    m_refActionGroup->add_action("open", sigc::mem_fun(*this, &ExampleWindow::on_action_file_open));
    m_refActionGroup->add_action("quit", sigc::mem_fun(*this, &ExampleWindow::on_action_file_quit));

    insert_action_group("example", m_refActionGroup);

Ако користите Gtk::ApplicationWindow, не морате да правите сопствену групу радњи. Gio::ActionGroup и Gio::ActionMap су основни разреди за Gtk::ApplicationWindow.

### Трака изборника и трака са алаткама

Затим треба да направите Gtk::Builder. У овом тренутку је такође добра идеја да кажете апликацији да одговара на пречице тастатуре, коришћењем Gtk::Application::set_accel_for_action().

На пример,

    m_refBuilder = Gtk::Builder::create();

    app->set_accel_for_action("example.new", "<Primary>n");
    app->set_accel_for_action("example.quit", "<Primary>q");
    app->set_accel_for_action("example.copy", "<Primary>c");
    app->set_accel_for_action("example.paste", "<Primary>v");

Потом можете одредити стварни видљиви распоред изборника и трака са алаткама, и додати распоред корисничког прочеља у Builder. Ова „ниска корисничког прочеља“ користи ИксМЛ формат, у којем треба да наведете називе радњи које сте већ направили. На пример:

    Glib::ustring ui_info =
      "<interface>"
      "  <menu id='menubar'>"
      "    <submenu>"
      "      <attribute name='label' translatable='yes'>_File</attribute>"
      "      <section>"
      "        <item>"
      "          <attribute name='label' translatable='yes'>_New</attribute>"
      "          <attribute name='action'>example.new</attribute>"
      "        </item>"
      "      </section>"
      "      <section>"
      "        <item>"
      "          <attribute name='label' translatable='yes'>_Quit</attribute>"
      "          <attribute name='action'>example.quit</attribute>"
      "        </item>"
      "      </section>"
      "    </submenu>"
      "    <submenu>"
      "      <attribute name='label' translatable='yes'>_Edit</attribute>"
      "      <item>"
      "        <attribute name='label' translatable='yes'>_Copy</attribute>"
      "        <attribute name='action'>example.copy</attribute>"
      "      </item>"
      "      <item>"
      "        <attribute name='label' translatable='yes'>_Paste</attribute>"
      "        <attribute name='action'>example.paste</attribute>"
      "      </item>"
      "    </submenu>"
      "  </menu>"
      "</interface>";

    m_refBuilder->add_from_string(ui_info);
    m_refBuilder->add_from_resource("/toolbar/toolbar.ui");

Ово је место где наводимо називе ставки изборника онако како ће их корисници видети у изборнику. Због тога је ово место где треба да учините ниске преводивим, додавањем `translatable='yes'`.

Да бисте покренули Gtk::PopoverMenuBar и траку са алаткама (водоравну Gtk::Box) коју заправо можете приказати, треба да користите методе Builder::get_object() и Builder::get_widget(), а затим да додате елементе у контејнер. На пример:

    auto gmenu = m_refBuilder->get_object<Gio::Menu>("menubar");
    auto pMenuBar = Gtk::make_managed<Gtk::PopoverMenuBar>(gmenu);
    m_Box.append(*pMenuBar);

    auto toolbar = m_refBuilder->get_widget<Gtk::Box>("toolbar");
    m_Box.append(*toolbar);

### Искачући изборници

Menu изборници се обично само додају у прозор, али се такође могу привремено приказати као резултат клика дугметом миша. На пример, контекстни изборник може бити приказан када корисник кликне десним дугметом миша.

На пример:

    Glib::ustring ui_info =
      "<interface>"
      "  <menu id='menu-examplepopup'>"
      "    <section>"
      "      <item>"
      "        <attribute name='label' translatable='yes'>Edit</attribute>"
      "        <attribute name='action'>examplepopup.edit</attribute>"
      "      </item>"
      "      <item>"
      "        <attribute name='label' translatable='yes'>Process</attribute>"
      "        <attribute name='action'>examplepopup.process</attribute>"
      "      </item>"
      "      <item>"
      "        <attribute name='label' translatable='yes'>Remove</attribute>"
      "        <attribute name='action'>examplepopup.remove</attribute>"
      "      </item>"
      "    </section>"
      "  </menu>"
      "</interface>";

    m_refBuilder->add_from_string(ui_info);

    auto gmenu = m_refBuilder->get_object<Gio::Menu>("menu-examplepopup");
    m_MenuPopup.set_menu_model(gmenu);

Да бисте приказали искачући изборник, користите Gtk::GestureClick и повежите се на његов сигнал `pressed`. У руковаоцу сигналом, користите метод popup() разреда Gtk::PopoverMenu. На пример:

    void ExampleWindow::on_label_pressed(int /* n_press */, double x, double y)
    {
      const Gdk::Rectangle rect(x, y, 1, 1);
      m_MenuPopup.set_pointing_to(rect);
      m_MenuPopup.popup();
    }

### Gio::Resource и glib-compile-resources

Програми и библиотеке често садрже бинарне или текстуалне податке који су заправо део програма, пре него кориснички подаци. На пример Gtk::Builder `.ui` датотеке, слике почетног екрана, Gio::Menu ИксМЛ означавање, ЦСС датотеке, иконице, итд. Они се често испоручују као датотеке у `$datadir/appname`, или се ручно укључују као дословне ниске у коду.

Gio::Resource АПИ и програм glib-compile-resources пружају погодну и ефикасну алтернативу овоме, која има неке лепе особине. Датотеке одржавате као обичне датотеке, тако да их је лако уређивати, али се током изградње датотеке комбинују у бинарни комплет који је повезан у извршну датотеку. То значи да је учитавање датотека ресурса ефикасно (јер су већ у меморији, дељене са другим примерцима) и једноставно (нема потребе за провером ствари као што су У/И грешке или проналажење датотека у систему датотека). То такође олакшава прављење премостивих програма.

Комплете ресурса прави програм glib-compile-resources који узима ИксМЛ датотеку која описује комплет, и скуп датотека на које се ИксМЛ позива. Они се комбинују у бинарни комплет ресурса.

Gio::Resource Reference

Пример:

    <?xml version="1.0" encoding="UTF-8"?>
    <gresources>
      <gresource prefix="/toolbar">
        <file preprocess="xml-stripblanks">toolbar.ui</file>
        <file>rain.png</file>
      </gresource>
    </gresources>

Ово ће направити пакет ресурса са датотекама

- `/toolbar/toolbar.ui`

- `/toolbar/rain.png`

Затим можете користити glib-compile-resources да компајлирате xml у бинарни пакет који можете учитати помоћу Gio::Resource::create_from_file(). Међутим, чешће се користи аргумент --generate-source за прављење C изворне датотеке која се директно повезује са вашом апликацијом. Нпр.

    $ glib-compile-resources --target=resources.c --generate-source toolbar.gresource.xml

Након што је Gio::Resource направљен и регистрован, свим подацима у њему се може приступити глобално у процесу коришћењем АПИ позива као што су Gio::Resource::open_stream_from_global_resources() за проток података или Gio::Resource::lookup_data_in_global_resources() за добијање директног показивача на податке. Такође можете користити УРИ-је као што је resource:///toolbar/rain.png са Gio::File за приступ подацима ресурса.

Често вам није потребан примерак Gio::Resource, јер се подаци ресурса могу учитати методама као што су Gdk::Pixbuf::create_from_resource(), Gtk::Builder::add_from_resource() и Gtk::Image::set_from_resource().

### Примери

#### Пример менија апликације и главног менија

Овај програм садржи мени апликације, траку менија и траку алата. Класе су изведене из Gtk::Application и Gtk::ApplicationWindow.

Програм и главни мени

Изворни ко̂д

File: `exampleapplication.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPLICATION_H
    #define GTKMM_EXAMPLEAPPLICATION_H

    #include <gtkmm.h>

    class ExampleApplication : public Gtk::Application
    {
    protected:
      ExampleApplication();

    public:
      static Glib::RefPtr<ExampleApplication> create();

    protected:
      //Overrides of default signal handlers:
      void on_startup() override;
      void on_activate() override;

    private:
      void create_window();

      void on_menu_file_new_generic();
      void on_menu_file_quit();
      void on_menu_help_about();

      Glib::RefPtr<Gtk::Builder> m_refBuilder;
    };

    #endif /* GTKMM_EXAMPLEAPPLICATION_H */

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::ApplicationWindow
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_menu_others();

      void on_menu_choices(const Glib::ustring& parameter);
      void on_menu_choices_other(int parameter);
      void on_menu_toggle();

      //Child widgets:
      Gtk::Box m_Box;

      Glib::RefPtr<Gtk::Builder> m_refBuilder;

      //Two sets of choices:
      Glib::RefPtr<Gio::SimpleAction> m_refChoice;
      Glib::RefPtr<Gio::SimpleAction> m_refChoiceOther;

      Glib::RefPtr<Gio::SimpleAction> m_refToggle;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `exampleapplication.cc` (For use with gtkmm 4)

    #include "exampleapplication.h"
    #include "examplewindow.h"
    #include <iostream>

    ExampleApplication::ExampleApplication()
    : Gtk::Application("org.gtkmm.example.main_menu")
    {
      Glib::set_application_name("Main Menu Example");
    }

    Glib::RefPtr<ExampleApplication> ExampleApplication::create()
    {
      return Glib::make_refptr_for_instance<ExampleApplication>(new ExampleApplication());
    }

    void ExampleApplication::on_startup()
    {
      //Call the base class's implementation:
      Gtk::Application::on_startup();

      //Create actions for menus and toolbars.
      //We can use add_action() because Gtk::Application derives from Gio::ActionMap.

      //File|New sub menu:
      add_action("newstandard",
        sigc::mem_fun(*this, &ExampleApplication::on_menu_file_new_generic));

      add_action("newfoo",
        sigc::mem_fun(*this, &ExampleApplication::on_menu_file_new_generic));

      add_action("newgoo",
        sigc::mem_fun(*this, &ExampleApplication::on_menu_file_new_generic));

      //File menu:
      add_action("quit", sigc::mem_fun(*this, &ExampleApplication::on_menu_file_quit));

      //Help menu:
      add_action("about", sigc::mem_fun(*this, &ExampleApplication::on_menu_help_about));

      // Set accelerator keys:
      set_accel_for_action("app.newstandard", "<Primary>n");
      set_accel_for_action("app.quit", "<Primary>q");
      set_accel_for_action("win.copy", "<Primary>c");
      set_accel_for_action("win.paste", "<Primary>v");

      m_refBuilder = Gtk::Builder::create();

      //Layout the actions in a menubar and a menu:
      Glib::ustring ui_info =
        "<interface>"
        "  <!-- menubar -->"
        "  <menu id='menu-example'>"
        "    <submenu>"
        "      <attribute name='label' translatable='yes'>_File</attribute>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>New _Standard</attribute>"
        "          <attribute name='action'>app.newstandard</attribute>"
        "          <attribute name='accel'>&lt;Primary&gt;n</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>New _Foo</attribute>"
        "          <attribute name='action'>app.newfoo</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>New _Goo</attribute>"
        "          <attribute name='action'>app.newgoo</attribute>"
        "        </item>"
        "      </section>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_Quit</attribute>"
        "          <attribute name='action'>app.quit</attribute>"
        "          <attribute name='accel'>&lt;Primary&gt;q</attribute>"
        "        </item>"
        "      </section>"
        "    </submenu>"
        "    <submenu>"
        "      <attribute name='label' translatable='yes'>_Edit</attribute>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_Copy</attribute>"
        "          <attribute name='action'>win.copy</attribute>"
        "          <attribute name='accel'>&lt;Primary&gt;c</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_Paste</attribute>"
        "          <attribute name='action'>win.paste</attribute>"
        "          <attribute name='accel'>&lt;Primary&gt;v</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_Something</attribute>"
        "          <attribute name='action'>win.something</attribute>"
        "        </item>"
        "      </section>"
        "    </submenu>"
        "    <submenu>"
        "      <attribute name='label' translatable='yes'>_Choices</attribute>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>Choice _A</attribute>"
        "          <attribute name='action'>win.choice</attribute>"
        "          <attribute name='target'>a</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>Choice _B</attribute>"
        "          <attribute name='action'>win.choice</attribute>"
        "          <attribute name='target'>b</attribute>"
        "        </item>"
        "      </section>"
        "    </submenu>"
        "    <submenu>"
        "      <attribute name='label' translatable='yes'>_Other Choices</attribute>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>Choice 1</attribute>"
        "          <attribute name='action'>win.choiceother</attribute>"
        "          <attribute name='target' type='i'>1</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>Choice 2</attribute>"
        "          <attribute name='action'>win.choiceother</attribute>"
        "          <attribute name='target' type='i'>2</attribute>"
        "        </item>"
        "      </section>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>Some Toggle</attribute>"
        "          <attribute name='action'>win.sometoggle</attribute>"
        "        </item>"
        "      </section>"
        "    </submenu>"
        "    <submenu>"
        "      <attribute name='label' translatable='yes'>_Help</attribute>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_About Window</attribute>"
        "          <attribute name='action'>win.about</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_About App</attribute>"
        "          <attribute name='action'>app.about</attribute>"
        "        </item>"
        "      </section>"
        "    </submenu>"
        "  </menu>"
        "</interface>";

      try
      {
        m_refBuilder->add_from_string(ui_info);
      }
      catch (const Glib::Error& ex)
      {
        std::cerr << "Building menus failed: " << ex.what();
      }

      //Get the menubar and the app menu, and add them to the application:
      auto gmenu = m_refBuilder->get_object<Gio::Menu>("menu-example");
      if (!gmenu)
      {
        g_warning("GMenu not found");
      }
      else
      {
        set_menubar(gmenu);
      }
    }

    void ExampleApplication::on_activate()
    {
      //std::cout << "debug1: " << G_STRFUNC << std::endl;
      // The application has been started, so let's show a window.
      // A real application might want to reuse this window in on_open(),
      // when asked to open a file, if no changes have been made yet.
      create_window();
    }

    void ExampleApplication::create_window()
    {
      // Since gtkmm 4.8, a Gtk::Window can be managed. If managed and not created
      // by Gtk::Builder, it's deleted when its underlying C instance is destroyed.
      // The underlying C instance is destroyed when it's closed, unless
      // Gtk::Window::set_hide_on_close() has been called.
      auto win = Gtk::make_managed<ExampleWindow>();

      //Make sure that the application runs for as long this window is still open:
      add_window(*win);

      win->set_show_menubar();
      win->set_visible(true);
    }

    void ExampleApplication::on_menu_file_new_generic()
    {
      std::cout << "A File|New menu item was selected." << std::endl;
    }

    void ExampleApplication::on_menu_file_quit()
    {
      std::cout << G_STRFUNC << std::endl;
      quit(); // Not really necessary, when Gtk::Window::close() is called.

      // Gio::Application::quit() will make Gio::Application::run() return,
      // but it's a crude way of ending the program. The window is not removed
      // from the application. Neither the window's nor the application's
      // destructors will be called, because there will be remaining reference
      // counts in both of them. If we want the destructors to be called, we
      // must close the window.
      auto windows = get_windows();
      for (auto window : windows)
        window->close();
    }

    void ExampleApplication::on_menu_help_about()
    {
      std::cout << "Help|About App was selected." << std::endl;
    }

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : Gtk::ApplicationWindow(),
      m_Box(Gtk::Orientation::VERTICAL)
    {
      set_title("Main menu example");
      set_default_size(300, 100);

      // ExampleApplication displays the menubar. Other stuff, such as a toolbar,
      // is put into the box.
      set_child(m_Box);

      // Create actions for menus and toolbars.
      // We can use add_action() because Gtk::ApplicationWindow derives from Gio::ActionMap.
      // This Action Map uses a "win." prefix for the actions.
      // Therefore, for instance, "win.copy", is used in ExampleApplication::on_startup()
      // to layout the menu.

      //Edit menu:
      add_action("copy", sigc::mem_fun(*this, &ExampleWindow::on_menu_others));
      add_action("paste", sigc::mem_fun(*this, &ExampleWindow::on_menu_others));
      add_action("something", sigc::mem_fun(*this, &ExampleWindow::on_menu_others));

      //Choices menus, to demonstrate Radio items,
      //using our convenience methods for string and int radio values:
      m_refChoice = add_action_radio_string("choice",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_choices), "a");

      m_refChoiceOther = add_action_radio_integer("choiceother",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_choices_other), 1);

      m_refToggle = add_action_bool("sometoggle",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_toggle), false);

      //Help menu:
      add_action("about", sigc::mem_fun(*this, &ExampleWindow::on_menu_others));

      //Create the toolbar and add it to a container widget:

      m_refBuilder = Gtk::Builder::create();

      Glib::ustring ui_info =
        "<!-- Generated with glade 3.18.3 and then changed manually -->"
        "<interface>"
        "  <object class='GtkBox' id='toolbar'>"
        "    <property name='can_focus'>False</property>"
        "    <child>"
        "      <object class='GtkButton' id='toolbutton_new'>"
        "        <property name='can_focus'>False</property>"
        "        <property name='tooltip_text' translatable='yes'>New Standard</property>"
        "        <property name='action_name'>app.newstandard</property>"
        "        <property name='icon_name'>document-new</property>"
        "        <property name='hexpand'>False</property>"
        "        <property name='vexpand'>False</property>"
        "      </object>"
        "    </child>"
        "    <child>"
        "      <object class='GtkButton' id='toolbutton_quit'>"
        "        <property name='can_focus'>False</property>"
        "        <property name='tooltip_text' translatable='yes'>Quit</property>"
        "        <property name='action_name'>app.quit</property>"
        "        <property name='icon_name'>application-exit</property>"
        "        <property name='hexpand'>False</property>"
        "        <property name='vexpand'>False</property>"
        "      </object>"
        "    </child>"
        "  </object>"
        "</interface>";

      try
      {
        m_refBuilder->add_from_string(ui_info);
      }
      catch (const Glib::Error& ex)
      {
        std::cerr << "Building toolbar failed: " <<  ex.what();
      }

      auto toolbar = m_refBuilder->get_widget<Gtk::Box>("toolbar");
      if (!toolbar)
        g_warning("toolbar not found");
      else
        m_Box.append(*toolbar);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_menu_others()
    {
      std::cout << "A menu item was selected." << std::endl;
    }

    void ExampleWindow::on_menu_choices(const Glib::ustring& parameter)
    {
      //The radio action's state does not change automatically:
      m_refChoice->change_state(parameter);

      Glib::ustring message;
      if (parameter == "a")
        message = "Choice a was selected.";
      else
        message = "Choice b was selected.";

      std::cout << message << std::endl;
    }

    void ExampleWindow::on_menu_choices_other(int parameter)
    {
      //The radio action's state does not change automatically:
      m_refChoiceOther->change_state(parameter);

      Glib::ustring message;
      if (parameter == 1)
        message = "Choice 1 was selected.";
      else
        message = "Choice 2 was selected.";

      std::cout << message << std::endl;
    }

    void ExampleWindow::on_menu_toggle()
    {
      bool active = false;
      m_refToggle->get_state(active);

      //The toggle action's state does not change automatically:
      active = !active;
      m_refToggle->change_state(active);

      Glib::ustring message;
      if (active)
        message = "Toggle is active.";
      else
        message = "Toggle is not active.";

      std::cout << message << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "exampleapplication.h"

    int main(int argc, char* argv[])
    {
      auto application = ExampleApplication::create();

      // Start the application, showing the initial window,
      // and opening extra windows for any files that it is asked to open,
      // for instance as a command-line parameter.
      // run() will return when the last window has been closed by the user.
      const int status = application->run(argc, argv);
      return status;
    }

#### Пример главног менија

Овај програм садржи траку менија и траку алата. Класа је изведена из Gtk::Window.

Главни мени

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow(const Glib::RefPtr<Gtk::Application>& app);
      virtual ~ExampleWindow();

    private:
      //Signal handlers:
      //void on_action_file_new();
      void on_action_file_quit();
      void on_action_others();
      void on_action_toggle();

      //Child widgets:
      Gtk::Box m_Box;

      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      Glib::RefPtr<Gio::SimpleActionGroup> m_refActionGroup;
      Glib::RefPtr<Gio::SimpleAction> m_refActionRain;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

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

    ExampleWindow::ExampleWindow(const Glib::RefPtr<Gtk::Application>& app)
    : m_Box(Gtk::Orientation::VERTICAL)
    {
      set_title("main_menu example");
      set_default_size(200, 200);

      set_child(m_Box); //We can put a MenuBar at the top of the box and other stuff below it.

      //Define the actions:
      m_refActionGroup = Gio::SimpleActionGroup::create();

      // There are several ways of calling a function that takes a sigc::slot.
      // If the slot function is very short, it might be easy to skip the on_xxx()
      // method and put its contents directly in a lambda expression.
      m_refActionGroup->add_action("new",
        [] { std::cout << "A File|New menu item was selected.\n"; /* on_action_file_new() */});
      // With sigc::mem_fun() or (for non-member functions and static member functions)
      // sigc::ptr_fun(). The only way before C++11 introduced lambda expressions.
      m_refActionGroup->add_action("open",
        sigc::mem_fun(*this, &ExampleWindow::on_action_others) );

      // With a lambda expression. Does not disconnect automatically when ExampleWindow
      // is deleted, like sigc::mem_fun() does.
      m_refActionRain = m_refActionGroup->add_action_bool("rain",
        [this] { on_action_toggle(); }, false);

      m_refActionGroup->add_action("quit",
        sigc::mem_fun(*this, &ExampleWindow::on_action_file_quit) );

      // With a lambda expression and sigc::track_obj() or sigc::track_object().
      // Disconnects automatically like sigc::mem_fun().
    #if SIGCXX_MINOR_VERSION >= 4
      m_refActionGroup->add_action("cut",
        sigc::track_object([this] { on_action_others(); }, *this));
    #else
      m_refActionGroup->add_action("cut",
        sigc::track_obj([this] { on_action_others(); }, *this));
    #endif
      m_refActionGroup->add_action("copy",
        sigc::mem_fun(*this, &ExampleWindow::on_action_others) );
      m_refActionGroup->add_action("paste",
        sigc::mem_fun(*this, &ExampleWindow::on_action_others) );

      insert_action_group("example", m_refActionGroup);

      //Define how the actions are presented in the menus and toolbars:
      m_refBuilder = Gtk::Builder::create();

      //Layout the actions in a menubar and toolbar:
      const Glib::ustring ui_info =
        "<interface>"
        "  <menu id='menubar'>"
        "    <submenu>"
        "      <attribute name='label' translatable='yes'>_File</attribute>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_New</attribute>"
        "          <attribute name='action'>example.new</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_Open</attribute>"
        "          <attribute name='action'>example.open</attribute>"
        "        </item>"
        "      </section>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>Rain</attribute>"
        "          <attribute name='action'>example.rain</attribute>"
        "        </item>"
        "      </section>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_Quit</attribute>"
        "          <attribute name='action'>example.quit</attribute>"
        "        </item>"
        "      </section>"
        "    </submenu>"
        "    <submenu>"
        "      <attribute name='label' translatable='yes'>_Edit</attribute>"
        "      <item>"
        "        <attribute name='label' translatable='yes'>_Cut</attribute>"
        "        <attribute name='action'>example.cut</attribute>"
        "      </item>"
        "      <item>"
        "        <attribute name='label' translatable='yes'>_Copy</attribute>"
        "        <attribute name='action'>example.copy</attribute>"
        "      </item>"
        "      <item>"
        "        <attribute name='label' translatable='yes'>_Paste</attribute>"
        "        <attribute name='action'>example.paste</attribute>"
        "      </item>"
        "    </submenu>"
        "  </menu>"
        "</interface>";

      // Set accelerator keys:
      app->set_accel_for_action("example.new", "<Primary>n");
      app->set_accel_for_action("example.open", "<Primary>o");
      app->set_accel_for_action("example.quit", "<Primary>q");
      app->set_accel_for_action("example.cut", "<Primary>x");
      app->set_accel_for_action("example.copy", "<Primary>c");
      app->set_accel_for_action("example.paste", "<Primary>v");

      try
      {
        m_refBuilder->add_from_string(ui_info);
        m_refBuilder->add_from_resource("/toolbar/toolbar.ui");
      }
      catch(const Glib::Error& ex)
      {
        std::cerr << "Building menus and toolbar failed: " <<  ex.what();
      }

      //Get the menubar:
      auto gmenu = m_refBuilder->get_object<Gio::Menu>("menubar");
      if (!gmenu)
        g_warning("GMenu not found");
      else
      {
        auto pMenuBar = Gtk::make_managed<Gtk::PopoverMenuBar>(gmenu);

        //Add the PopoverMenuBar to the window:
        m_Box.append(*pMenuBar);
      }

      //Get the toolbar and add it to a container widget:
      auto toolbar = m_refBuilder->get_widget<Gtk::Box>("toolbar");
      if (!toolbar)
        g_warning("toolbar not found");
      else
        m_Box.append(*toolbar);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_action_file_quit()
    {
      close();
    }

    //void ExampleWindow::on_action_file_new()
    //{
    //   std::cout << "A File|New menu item was selected." << std::endl;
    //}

    void ExampleWindow::on_action_others()
    {
      std::cout << "A menu item was selected." << std::endl;
    }

    void ExampleWindow::on_action_toggle()
    {
      std::cout << "The toggle menu item was selected." << std::endl;

      bool active = false;
      m_refActionRain->get_state(active);

      //The toggle action's state does not change automatically:
      active = !active;
      m_refActionRain->change_state(active);

      Glib::ustring message;
      if(active)
        message = "Toggle is active.";
      else
        message = "Toggle is not active";

      std::cout << message << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include <gtkmm.h>
    #include "examplewindow.h"

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv, app);
    }

File: `toolbar.gresource.xml` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <gresources>
      <gresource prefix="/toolbar">
        <file preprocess="xml-stripblanks">toolbar.ui</file>
        <file>rain.png</file>
      </gresource>
    </gresources>

#### Пример искачућег менија

Искачући мени

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    #include <memory>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow(const Glib::RefPtr<Gtk::Application>& app);
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_label_pressed(int n_press, double x, double y);

      void on_menu_file_popup_generic();

      //Child widgets:
      Gtk::Box m_Box;
      Gtk::Label m_Label;
      Gtk::PopoverMenu m_MenuPopup;

      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      Glib::RefPtr<Gtk::GestureClick> m_refGesture;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow(const Glib::RefPtr<Gtk::Application>& app)
    : m_Box(Gtk::Orientation::VERTICAL),
      m_Label("Right-click to see the popup menu.")
    {
      set_title("popup example");
      set_default_size(200, 200);

      set_child(m_Box);

      // Catch button_press events:
      m_Box.append(m_Label);
      m_Label.set_expand();
      m_refGesture = Gtk::GestureClick::create();
      m_refGesture->set_button(GDK_BUTTON_SECONDARY);
      m_refGesture->signal_pressed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_label_pressed));
      m_Label.add_controller(m_refGesture);

      //Create actions:
      auto refActionGroup = Gio::SimpleActionGroup::create();

      //File|New sub menu:
      //These menu actions would normally already exist for a main menu, because a
      //context menu should not normally contain menu items that are only available
      //via a context menu.

      refActionGroup->add_action("edit",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_popup_generic));

      refActionGroup->add_action("process",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_popup_generic));

      refActionGroup->add_action("remove",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_popup_generic));

      insert_action_group("examplepopup", refActionGroup);

      // Set accelerator keys:
      app->set_accel_for_action("examplepopup.edit", "<Primary>e");
      app->set_accel_for_action("examplepopup.process", "<Primary>p");
      app->set_accel_for_action("examplepopup.remove", "<Primary>r");

      m_refBuilder = Gtk::Builder::create();

      //Layout the actions in a menubar and toolbar:
      Glib::ustring ui_info =
        "<interface>"
        "  <menu id='menu-examplepopup'>"
        "    <section>"
        "      <item>"
        "        <attribute name='label' translatable='yes'>Edit</attribute>"
        "        <attribute name='action'>examplepopup.edit</attribute>"
        "      </item>"
        "      <item>"
        "        <attribute name='label' translatable='yes'>Process</attribute>"
        "        <attribute name='action'>examplepopup.process</attribute>"
        "      </item>"
        "      <item>"
        "        <attribute name='label' translatable='yes'>Remove</attribute>"
        "        <attribute name='action'>examplepopup.remove</attribute>"
        "      </item>"
        "    </section>"
        "  </menu>"
        "</interface>";

      try
      {
        m_refBuilder->add_from_string(ui_info);
      }
      catch(const Glib::Error& ex)
      {
        std::cerr << "building menus failed: " <<  ex.what();
      }

      //Get the menu:
      auto gmenu = m_refBuilder->get_object<Gio::Menu>("menu-examplepopup");
      if(!gmenu)
        g_warning("GMenu not found");

      m_MenuPopup.set_parent(m_Label);
      m_MenuPopup.set_menu_model(gmenu);
      m_MenuPopup.set_has_arrow(false);
    }

    ExampleWindow::~ExampleWindow()
    {
      m_MenuPopup.unparent();
    }

    void ExampleWindow::on_menu_file_popup_generic()
    {
       std::cout << "A popup menu item was selected." << std::endl;
    }

    void ExampleWindow::on_label_pressed(int /* n_press */, double x, double y)
    {
      const Gdk::Rectangle rect(x, y, 1, 1);
      m_MenuPopup.set_pointing_to(rect);
      m_MenuPopup.popup();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv, app);
    }

## Поравнања

gtkmm има различите елементе који се могу визуелно подешавати мишем или тастатуром, као што су елементи Range (описани у одељку Елементи опсега). Постоји и неколико елемената који приказују неки подесиви део веће области, као што је елемент Viewport. Ови елементи имају објекте Gtk::Adjustment који изражавају овај заједнички део њиховог АПИ-ја.

Како би програми могли да реагују на измене, на пример када корисник помери траку клизача, Gtk::Adjustment има сигнал `value_changed`. Затим можете користити метод get_value() да бисте сазнали нову вредност.

### Стварање поравнања

Gtk::Adjustment се ствара својим методом create() који изгледа овако:

    Glib::RefPtr<Gtk::Adjustment> Gtk::Adjustment::create(
      double value,
      double lower,
      double upper,
      double step_increment = 1,
      double page_increment = 10,
      double page_size = 0);

Аргумент value је почетна вредност поравнања, која обично одговара највишем или најлевљем положају подесивог елемента. Аргументи lower и upper одређују могући опсег вредности које поравнање може да садржи. Аргумент step_increment одређује мањи од два корака за који корисник може да промени вредност, док је page_increment већи. Аргумент page_size обично на неки начин одговара видљивој области елемента за померање. Аргумент upper се користи за представљање најниже или најдесније координате унутар садржаја елемента за померање.

### Коришћење поравнања на лак начин

Подесиви елементи се грубо могу поделити на оне који користе и захтевају специфичне јединице за ове вредности, и на оне који их третирају као произвољне бројеве.

Група која третира вредности као произвољне бројеве укључује елементе Range (Scrollbar и Scale), елемент ScaleButton и елемент SpinButton. Ове елементе обично „подешава“ директно корисник помоћу миша или тастатуре. Они ће третирати вредности lower и upper поравнања као опсег унутар којег корисник може да мења value поравнања. Подразумевано, они ће мењати само value поравнања.

Друга група укључује елемент Viewport и елемент ScrolledWindow. Сви ови елементи користе вредности у пикселима за своја поравнања. Они се такође обично подешавају индиректно помоћу трака клизача. Иако сви елементи који користе поравнања могу или да направе сопствена поравнања или да користе она која ви доставите, углавном ћете желети да допустите овој специфичној категорији елемената да сама направи своја поравнања.

Ако делите објекат поравнања између елемената Scrollbar и TextView, померање траке клизача ће аутоматски прилагодити елемент TextView. То можете подесити овако:

    // прави сопствена поравнања
    Gtk::TextView textview;
    // користи новостворено поравнање и за траку клизача
    Gtk::Scrollbar vscrollbar(textview.get_vadjustment(), Gtk::Orientation::VERTICAL);

### Унутрашњост поравнања

У реду, рећи ћете, то је лепо, али шта ако желим да направим сопствене руковаоце који ће реаговати када корисник подеси елемент Range или SpinButton. Да бисте приступили вредности Gtk::Adjustment, можете користити методе get_value() и set_value():

Као што је раније поменуто, Gtk::Adjustment може да емитује сигнале. Ово је, наравно, начин на који се ажурирања дешавају аутоматски када делите објекат Adjustment између елемента Scrollbar и другог подесивог елемента; сви подесиви елементи повезују руковаоце сигналима на сигнал `value_changed` свог поравнања, што може да уради и ваш програм.

Тако, на пример, ако имате елемент Scale и желите да промените ротацију слике кад год се њена вредност промени, направили бисте руковалац сигналом овако:

    void cb_rotate_picture(MyPicture* picture)
    {
      picture->set_rotation(adj->get_value());
    ...

и повежите га на подешавање елемента размере овако:

    adj->signal_value_changed().connect(sigc::bind<MyPicture*>(sigc::mem_fun(*this,
        &cb_rotate_picture), picture));

Шта ако елемент поново конфигурише поља upper или lower свог Adjustment-а, као када корисник дода више текста у елемент текста? У овом случају, он емитује сигнал `changed`.

Елементи Range обично повезују руковаоца на овај сигнал, који мења њихов изглед како би одразио промену — на пример, величина клизача у траци клизача ће се повећати или смањити у обрнутој пропорцији са разликом између вредности lower и upper свог Adjustment-а.

Вероватно вам никада неће требати да прикачите руковаоца на овај сигнал, осим ако не пишете нову врсту елемента опсега.

    adjustment->signal_changed();

## Прозорчићи

Gtk::Dialog и класе изведене из ње су застареле од верзије gtkmm 4.10. Оне се и даље могу користити у gtkmm4 апликацијама, под условом да GTKMM_DISABLE_DEPRECATED и GDKMM_DISABLE_DEPRECATED нису дефинисани. Неке од класа прозорчића су замењене класама које су доступне од верзије gtkmm 4.10.

Примери у овом поглављу користе класе које су доступне од верзије gtkmm 4.10. Слични примери са застарелим класама су доступни у грани gtkmm-4-0 у гит ризници.

Прозорчићи се користе као секундарни прозори, за пружање специфичних информација или за постављање питања. Прозори Gtk::Dialog садрже неколико унапред спакованих елемената како би се осигурала доследност, и сигнал `response` који се емитује када корисник одбаци прозорче.

Постоји неколико изведених класа Dialog које би вам могле бити корисне. Gtk::MessageDialog се користи за већину једноставних обавештења. Али у другим приликама ће вам можда требати да изведете сопствену класу прозорчета како бисте пружили сложенију функционалност.

Да бисте спаковали елементе у произвољно прозорче, требало би да их спакујете у Gtk::Box, који је доступан преко get_content_area(). Да бисте само додали Button на дно Dialog-а, можете користити метод add_button().

Руковалац сигнала `response` прима `int`. Ово може бити вредност из Gtk::ResponseType ако је корисник затворио прозорче кликом на стандардно дугме, или би то могла бити произвољна вредност одговора коју сте навели приликом коришћења add_button().

Да бисте приказали прозорче, позовите set_visible(true). Ако ће се исти примерак прозорчета приказивати неколико пута, морате такође позвати set_hide_on_close(), иначе ће прозорче бити уништено када се затвори. Повежите се на сигнал `response` ако желите да знате које дугме је притиснуто. Руковалац сигнала `response` је такође место где треба да сакријете прозорче.

Референце

### AlertDialog и MessageDialog

MessageDialog (застарело од gtkmm 4.10) и AlertDialog (доступно од gtkmm 4.10) су помоћне класе, које се користе за прављење једноставних, стандардних прозорчића за поруке, са поруком и дугмићима за одговор корисника.

AlertDialog референце

MessageDialog референце

#### Пример

AlertDialog

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_button_info_clicked();
      void on_button_question_clicked();
      void on_question_dialog_finish(const Glib::RefPtr<Gio::AsyncResult>& result);

      //Child widgets:
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Info;
      Gtk::Button m_Button_Question;

      Glib::RefPtr<Gtk::AlertDialog> m_pDialog;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/messagedialog.h>
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_ButtonBox(Gtk::Orientation::VERTICAL),
      m_Button_Info("Show Info AlertDialog"),
      m_Button_Question("Show Question AlertDialog")
    {
      set_title("Gtk::AlertDialog example");

      set_child(m_ButtonBox);

      m_ButtonBox.append(m_Button_Info);
      m_Button_Info.set_expand(true);
      m_Button_Info.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_info_clicked) );

      m_ButtonBox.append(m_Button_Question);
      m_Button_Question.set_expand(true);
      m_Button_Question.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_question_clicked) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_info_clicked()
    {
      if (!m_pDialog)
        m_pDialog = Gtk::AlertDialog::create();
      else
      {
        // Reset values that may have been set by on_button_question_clicked().
        m_pDialog->set_buttons({});
        m_pDialog->set_default_button(-1);
        m_pDialog->set_cancel_button(-1);
      }

      m_pDialog->set_message("This is an INFO AlertDialog.");
      m_pDialog->set_detail("And this is the secondary text that explains things.");
      m_pDialog->show(*this);
    }

    void ExampleWindow::on_button_question_clicked()
    {
      if (!m_pDialog)
        m_pDialog = Gtk::AlertDialog::create();
      m_pDialog->set_message("This is a QUESTION AlertDialog.");
      m_pDialog->set_detail("And this is the secondary text that explains things.");
      m_pDialog->set_buttons({"Cancel", "Retry", "OK"});
      m_pDialog->set_default_button(2); // OK button or Return key
      m_pDialog->set_cancel_button(0); // Cancel button or Escape key
      m_pDialog->choose(*this,
        sigc::mem_fun(*this, &ExampleWindow::on_question_dialog_finish));
    }

    void ExampleWindow::on_question_dialog_finish(const Glib::RefPtr<Gio::AsyncResult>& result)
    {
      try
      {
        const int response_id = m_pDialog->choose_finish(result);
        switch (response_id)
        {
        case 0:
          std::cout << "Cancel clicked." << std::endl;
          break;
        case 1:
          std::cout << "Retry clicked." << std::endl;
          break;
        case 2:
          std::cout << "OK clicked." << std::endl;
          break;
        default:
          std::cout << "Unexpected response: " << response_id << std::endl;
          break;
        }
      }
      catch (const Gtk::DialogError& err)
      {
        // Can be thrown by m_pDialog->choose_finish(result).
        std::cout << "DialogError, " << err.what() << std::endl;
      }
      catch (const Glib::Error& err)
      {
        std::cout << "Unexpected exception. " << err.what() << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### FileDialog и FileChooserDialog

FileChooserDialog (застарело од верзије gtkmm 4.10) и FileDialog (доступно од верзије gtkmm 4.10) су погодни за коришћење са ставкама изборника „Отвори“ или „Сачувај“.

Већина корисних метода чланица за FileChooserDialog се заправо налазе у основној класи Gtk::FileChooser.

FileDialog референца

FileChooserDialog референца

#### Пример

FileDialog

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_button_file_clicked();
      void on_button_folder_clicked();
      void on_file_dialog_finish(const Glib::RefPtr<Gio::AsyncResult>& result,
        const Glib::RefPtr<Gtk::FileDialog>& dialog);
      void on_folder_dialog_finish(const Glib::RefPtr<Gio::AsyncResult>& result,
        const Glib::RefPtr<Gtk::FileDialog>& dialog);

      //Child widgets:
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_File;
      Gtk::Button m_Button_Folder;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_ButtonBox(Gtk::Orientation::VERTICAL),
      m_Button_File("Choose File"),
      m_Button_Folder("Choose Folder")
    {
      set_title("Gtk::FileDialog example");

      set_child(m_ButtonBox);

      m_ButtonBox.append(m_Button_File);
      m_Button_File.set_expand(true);
      m_Button_File.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_file_clicked) );

      m_ButtonBox.append(m_Button_Folder);
      m_Button_Folder.set_expand(true);
      m_Button_Folder.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_folder_clicked) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_file_clicked()
    {
      auto dialog = Gtk::FileDialog::create();

      // Add filters, so that only certain file types can be selected:
      auto filters = Gio::ListStore<Gtk::FileFilter>::create();

      auto filter_text = Gtk::FileFilter::create();
      filter_text->set_name("Text files");
      filter_text->add_mime_type("text/plain");
      filters->append(filter_text);

      auto filter_cpp = Gtk::FileFilter::create();
      filter_cpp->set_name("C/C++ files");
      filter_cpp->add_mime_type("text/x-c");
      filter_cpp->add_mime_type("text/x-c++");
      filter_cpp->add_mime_type("text/x-c-header");
      filters->append(filter_cpp);

      auto filter_any = Gtk::FileFilter::create();
      filter_any->set_name("Any files");
      filter_any->add_pattern("*");
      filters->append(filter_any);

      dialog->set_filters(filters);

      // Show the dialog and wait for a user response:
      dialog->open(sigc::bind(sigc::mem_fun(
        *this, &ExampleWindow::on_file_dialog_finish), dialog));
    }

    void ExampleWindow::on_file_dialog_finish(const Glib::RefPtr<Gio::AsyncResult>& result,
      const Glib::RefPtr<Gtk::FileDialog>& dialog)
    {
      // Handle the response:
      try
      {
        auto file = dialog->open_finish(result);

        // Notice that this is a std::string, not a Glib::ustring.
        auto filename = file->get_path();
        std::cout << "File selected: " <<  filename << std::endl;
      }
      catch (const Gtk::DialogError& err)
      {
        // Can be thrown by dialog->open_finish(result).
        std::cout << "No file selected. " << err.what() << std::endl;
      }
      catch (const Glib::Error& err)
      {
        std::cout << "Unexpected exception. " << err.what() << std::endl;
      }
    }

    void ExampleWindow::on_button_folder_clicked()
    {
      auto dialog = Gtk::FileDialog::create();

      // Show the dialog and wait for a user response:
      dialog->select_folder(sigc::bind(sigc::mem_fun(
        *this, &ExampleWindow::on_folder_dialog_finish), dialog));
    }

    void ExampleWindow::on_folder_dialog_finish(const Glib::RefPtr<Gio::AsyncResult>& result,
      const Glib::RefPtr<Gtk::FileDialog>& dialog)
    {
      // Handle the response:
      try
      {
        auto folder = dialog->select_folder_finish(result);
        std::cout << "Folder selected: " << folder->get_path() << std::endl;
      }
      catch (const Gtk::DialogError& err)
      {
        // Can be thrown by dialog->select_folder_finish(result).
        std::cout << "No folder selected. " << err.what() << std::endl;
      }
      catch (const Glib::Error& err)
      {
        std::cout << "Unexpected exception. " << err.what() << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### ColorDialog и ColorChooserDialog

ColorChooserDialog (застарело од верзије gtkmm 4.10) и ColorDialog (доступно од верзије gtkmm 4.10) омогућавају кориснику да изабере боју. ColorButton (застарело од верзије gtkmm 4.10) и ColorDialogButton (доступно од верзије gtkmm 4.10) отварају прозорче за избор боје када се кликне на њих.

ColorDialog референца

ColorChooserDialog референца

#### Пример

ColorDialog

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_color_button_color_set();
      void on_button_dialog_clicked();
      void on_dialog_finish(const Glib::RefPtr<Gio::AsyncResult>& result);

      //Draw function:
      void on_drawing_area_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);

      //Child widgets:
      Gtk::Box m_VBox;
      Gtk::ColorDialogButton m_ColorDialogButton;
      Gtk::Button m_Button_Dialog;
      Gtk::DrawingArea m_DrawingArea; //To show the color.

      Glib::RefPtr<Gtk::ColorDialog> m_pDialog;
      Gdk::RGBA m_Color;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL, 5),
      m_Button_Dialog("Choose Color")
    {
      set_title("Gtk::ColorDialog example");
      set_default_size(200, 200);

      m_pDialog = Gtk::ColorDialog::create();
      m_ColorDialogButton.set_dialog(m_pDialog);

      set_child(m_VBox);

      m_VBox.append(m_ColorDialogButton);
      m_ColorDialogButton.property_rgba().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_color_button_color_set));

      m_VBox.append(m_Button_Dialog);
      m_Button_Dialog.signal_clicked().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_button_dialog_clicked) );

      //Set start color:
      m_Color.set_red(0.0);
      m_Color.set_green(0.0);
      m_Color.set_blue(1.0);
      m_Color.set_alpha(1.0); //opaque
      m_ColorDialogButton.set_rgba(m_Color);

      m_VBox.append(m_DrawingArea);
      m_DrawingArea.set_expand(true);
      m_DrawingArea.set_draw_func(sigc::mem_fun(*this, &ExampleWindow::on_drawing_area_draw));
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_color_button_color_set()
    {
      //Store the chosen color:
      m_Color = m_ColorDialogButton.get_rgba();
      m_DrawingArea.queue_draw();
    }

    void ExampleWindow::on_button_dialog_clicked()
    {
      m_pDialog->choose_rgba(*this, m_Color,
        sigc::mem_fun(*this, &ExampleWindow::on_dialog_finish));
    }

    void ExampleWindow::on_dialog_finish(const Glib::RefPtr<Gio::AsyncResult>& result)
    {
      try
      {
        // If this call changes the color, it will trigger a call to
        // on_color_button_color_set().
        m_ColorDialogButton.set_rgba(m_pDialog->choose_rgba_finish(result));

        // This is what you should do if m_ColorDialogButton and
        // on_color_button_color_set() did not exist:
        //m_Color = m_pDialog->choose_rgba_finish(result);
        //m_DrawingArea.queue_draw();
      }
      catch (const Gtk::DialogError& err)
      {
        // Can be thrown by m_pDialog->choose_rgba_finish(result).
        std::cout << "No color selected. " << err.what() << std::endl;
      }
      catch (const Glib::Error& err)
      {
        std::cout << "Unexpected exception. " << err.what() << std::endl;
      }
    }

    void ExampleWindow::on_drawing_area_draw(const Cairo::RefPtr<Cairo::Context>& cr, int, int)
    {
      Gdk::Cairo::set_source_rgba(cr, m_Color);
      cr->paint();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### FontDialog и FontChooserDialog

FontChooserDialog (застарело од верзије gtkmm 4.10) и FontDialog (доступно од верзије gtkmm 4.10) омогућавају кориснику да изабере фонт. FontButton (застарело од верзије gtkmm 4.10) и FontDialogButton (доступно од верзије gtkmm 4.10) отварају прозорче за избор фонта када се кликне на њих.

FontDialog референца

FontChooserDialog референца

#### Пример

Прозорче за избор фонта

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_font_button_font_set();
      void on_button_dialog_clicked();
      void on_dialog_finish(const Glib::RefPtr<Gio::AsyncResult>& result);

      //Child widgets:
      Gtk::Box m_ButtonBox;
      Gtk::FontDialogButton m_FontDialogButton;
      Gtk::Button m_Button_Dialog;

      Glib::RefPtr<Gtk::FontDialog> m_pDialog;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_ButtonBox(Gtk::Orientation::VERTICAL),
      m_Button_Dialog("Choose Font")
    {
      set_title("Gtk::FontDialog example");

      set_child(m_ButtonBox);

      m_pDialog = Gtk::FontDialog::create();

      m_ButtonBox.append(m_FontDialogButton);
      m_FontDialogButton.set_dialog(m_pDialog);
      m_FontDialogButton.set_font_desc(Pango::FontDescription("Sans 10"));
      m_FontDialogButton.set_expand(true);
      m_FontDialogButton.set_use_font(true);
      m_FontDialogButton.set_use_size(true);
      m_FontDialogButton.property_font_desc().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_font_button_font_set));

      m_ButtonBox.append(m_Button_Dialog);
      m_Button_Dialog.set_expand(true);
      m_Button_Dialog.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_dialog_clicked));
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_font_button_font_set()
    {
      auto font_name = m_FontDialogButton.get_font_desc().to_string();
      std::cout << "Font chosen: " << font_name << std::endl;
    }

    void ExampleWindow::on_button_dialog_clicked()
    {
      // Get the previously selected font description from the FontDialogButton.
      m_pDialog->choose_font(*this,
        sigc::mem_fun(*this, &ExampleWindow::on_dialog_finish),
        m_FontDialogButton.get_font_desc());
    }

    void ExampleWindow::on_dialog_finish(const Glib::RefPtr<Gio::AsyncResult>& result)
    {
      try
      {
        // If this call changes the font, it will trigger a call to
        // on_font_button_font_set().
        m_FontDialogButton.set_font_desc(m_pDialog->choose_font_finish(result));
      }
      catch (const Gtk::DialogError& err)
      {
        // Can be thrown by m_pDialog->choose_font_finish(result).
        std::cout << "No font selected. " << err.what() << std::endl;
      }
      catch (const Glib::Error& err)
      {
        std::cout << "Unexpected exception. " << err.what() << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Немодално прозорче „О програму“

AboutDialog нуди једноставан начин за приказ података о програму, као што су његов логотип, назив, ауторска права, веб-страница и лиценца.

Већина прозорчића у овом поглављу су модални, односно они замрзавају остатак апликације док су приказани. Такође је могуће направити немодално прозорче, које не замрзава остале прозоре у апликацији. Следећи пример приказује немодално прозорче AboutDialog. Ово можда није врста прозорчета коју бисте обично учинили немодалним, али немодални прозорчићи могу бити корисни у другим случајевима. Нпр. прозорче за претрагу и замену у gedit-у је немодално.

Референце

#### Пример

Прозорче „О програму“

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_clicked();

      //Child widgets:
      Gtk::Box m_VBox;
      Gtk::Label m_Label;
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button;
      Gtk::AboutDialog m_Dialog;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Label("The AboutDialog is non-modal. "
        "You can select parts of this text while the AboutDialog is shown."),
      m_ButtonBox(Gtk::Orientation::VERTICAL),
      m_Button("Show AboutDialog")
    {
      set_title("Gtk::AboutDialog example");
      set_default_size(400, 150);

      set_child(m_VBox);

      m_VBox.append(m_Label);
      m_Label.set_expand(true);
      m_Label.set_wrap(true);
      m_Label.set_selectable(true);

      m_VBox.append(m_ButtonBox);
      m_ButtonBox.append(m_Button);
      m_Button.set_expand(true);
      m_Button.set_halign(Gtk::Align::CENTER);
      m_Button.set_valign(Gtk::Align::CENTER);
      m_Button.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_clicked) );

      m_Dialog.set_transient_for(*this);
      m_Dialog.set_hide_on_close();

      m_Dialog.set_logo(Gdk::Texture::create_from_resource("/about/gtkmm_logo.gif"));
      m_Dialog.set_program_name("Example application");
      m_Dialog.set_version("1.0.0");
      m_Dialog.set_copyright("Murray Cumming");
      m_Dialog.set_comments("This is just an example application.");
      m_Dialog.set_license("LGPL");

      m_Dialog.set_website("https://gtkmm.gnome.org");
      m_Dialog.set_website_label("gtkmm website");

      std::vector<Glib::ustring> list_authors;
      list_authors.push_back("Murray Cumming");
      list_authors.push_back("Somebody Else");
      list_authors.push_back("AN Other");
      m_Dialog.set_authors(list_authors);

      m_Button.grab_focus();
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_clicked()
    {
      m_Dialog.set_visible(true);

      //Bring it to the front, in case it was already shown:
      m_Dialog.present();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `aboutdialog.gresource.xml` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <gresources>
      <gresource prefix="/about">
        <file>gtkmm_logo.gif</file>
      </gresource>
    </gresources>

### Произвољно прозорче

Када ниједна од унапред дефинисаних класа прозорчића не одговара вашим потребама, можете направити сопствено прозорче извођењем класе из Window и попунити је елементима који су вам потребни.

Референце прозора

#### Пример

Прозорче прозора

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>
    #include "namedialog.h"

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_button_clicked();
      void on_dialog_response(const Glib::ustring& response);
      void on_dialog_hidden();

      //Child widgets:
      Gtk::Button m_Button;

      NameDialog m_Dialog;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `namedialog.h` (For use with gtkmm 4)

    #ifndef GTKMM_NAME_DIALOG_H_
    #define GTKMM_NAME_DIALOG_H_

    #include <gtkmm.h>

    class NameDialog : public Gtk::Window
    {
    public:
      NameDialog();
      ~NameDialog() override;

      void buttons_clicked_connect(const sigc::slot<void(const Glib::ustring&)>& slot);
      Glib::ustring get_entry1() const;
      Glib::ustring get_entry2() const;

    protected:
      //Member widgets:
      Gtk::Grid m_Grid;
      Gtk::Image m_Image;
      Gtk::Label m_Label1;
      Gtk::Label m_Label2;
      Gtk::Entry m_Entry1;
      Gtk::Entry m_Entry2;
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_OK;
      Gtk::Button m_Button_Cancel;
    };

    #endif /* GTKMM_NAME_DIALOG_H_ */

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_Button("Show Dialog"),
      m_Dialog()
    {
      set_title("Custom Dialog example");

      set_child(m_Button);
      m_Button.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_clicked));

      m_Dialog.set_default_size(250, 100);
      m_Dialog.set_transient_for(*this);
      m_Dialog.set_modal();
      m_Dialog.set_hide_on_close();
      m_Dialog.buttons_clicked_connect(
        sigc::mem_fun(*this, &ExampleWindow::on_dialog_response));
      m_Dialog.signal_hide().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_dialog_hidden));
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_clicked()
    {
      m_Dialog.set_visible(true);
    }

    void ExampleWindow::on_dialog_response(const Glib::ustring& response)
    {
      m_Dialog.set_visible(false);

      if (response == "OK")
        std::cout << "Name: " << m_Dialog.get_entry1() << " "
          << m_Dialog.get_entry2() << std::endl;
      else
        std::cout << response << " button clicked" << std::endl;
    }

    void ExampleWindow::on_dialog_hidden()
    {
      std::cout << "Dialog hidden" << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `namedialog.cc` (For use with gtkmm 4)

    NameDialog::NameDialog()
    : m_Label1("_First name", true),
      m_Label2("_Second name", true),
      m_ButtonBox(Gtk::Orientation::HORIZONTAL, 5),
      m_Button_OK("_OK", true),
      m_Button_Cancel("_Cancel", true)
    {
      set_destroy_with_parent(true);

      set_title("Name Dialog");
      set_child(m_Grid);

      m_Grid.set_row_spacing(4);
      m_Grid.set_column_spacing(4);
      m_Grid.set_expand(true);

      m_Image.set_from_icon_name("dialog-question");
      m_Image.set_icon_size(Gtk::IconSize::LARGE);
      m_Grid.attach(m_Image, 0, 0, 1, 2);

      m_Grid.attach(m_Label1, 1, 0);
      m_Grid.attach(m_Entry1, 2, 0);
      m_Label1.set_mnemonic_widget(m_Entry1);

      m_Grid.attach(m_Label2, 1, 1);
      m_Grid.attach(m_Entry2, 2, 1);
      m_Label2.set_mnemonic_widget(m_Entry2);

      m_Grid.attach(m_ButtonBox, 0, 2, 3, 1);
      m_ButtonBox.set_halign(Gtk::Align::END);
      m_ButtonBox.append(m_Button_OK);
      m_ButtonBox.append(m_Button_Cancel);
    }

    void NameDialog::buttons_clicked_connect(
      const sigc::slot<void(const Glib::ustring&)>& slot)
    {
      m_Button_OK.signal_clicked().connect(sigc::bind(slot, "OK"));
      m_Button_Cancel.signal_clicked().connect(sigc::bind(slot, "Cancel"));
    }

    Glib::ustring NameDialog::get_entry1() const
    {
      return m_Entry1.get_text();
    }

    Glib::ustring NameDialog::get_entry2() const
    {
      return m_Entry2.get_text();
    }

    NameDialog::~NameDialog()
    {
    }

## Елемент DrawingArea

Елемент DrawingArea је празан прозор који вам даје слободу да креирате било коју графику коју желите. Уз ту слободу долази и одговорност исцртавања на елементу. Када се елемент први пут прикаже, или када се прекрије па поново открије, потребно је да се поново исцрта. Већина елемената има код за ово, али DrawingArea нема, што вам омогућава да напишете сопствену функцију исцртавања како бисте одредили како ће садржај елемента бити исцртан. То се постиже постављањем функције исцртавања позивом функције чланице set_draw_func().

ГТК користи Cairo АПИ за цртање. Са gtkmm-ом, можете користити cairomm Ц++ АПИ за cairo.

Можете цртати веома софистициране облике користећи Cairo, али су методе за то прилично основне. Cairo пружа методе за цртање правих линија, кривих линија и лукова (укључујући кругове). Ови основни облици се могу комбиновати за прављење сложенијих облика и путања које се могу попунити пуним бојама, преливима, шарама и другим стварима. Поред тога, Cairo може да изврши сложене трансформације, композицију слика и исцртавање умекшаног текста.

Каиро и Панго

Иако Каиро може да исцртава текст, он није замишљен као замена за Панго. Панго је бољи избор ако треба да извршите напредније исцртавање текста као што је преламање или скраћивање текста. Цртање текста помоћу Каира треба радити само ако је текст део графике.

У овом одељку водича, покрићемо основни Каиро модел цртања, описати сваки од основних елемената цртања детаљније (са примерима), а затим представити једноставан програм који користи Каиро за цртање произвољног елемента часовника.

### Каиро модел цртања

Основни концепт цртања у Каиру укључује дефинисање „невидљивих“ путања, а затим њихово исцртавање или попуњавање како би постале видљиве.

Да бисте извршили било какво цртање у gtkmm-у помоћу Каира, прво морате добити објекат Cairo::Context. Ова класа садржи све параметре графичког стања који описују како се цртање обавља. Ово укључује информације као што су ширина линије, боја, површина на којој се црта и многе друге ствари. Ово омогућава стварним функцијама цртања да примају мање аргумената како би се поједноставило прочеље. Обично користите Cairo::Context који добијете као улазне податке функцији цртања коју постављате позивом set_draw_func(). Такође је могуће направити Cairo::Context позивањем функција Gdk::Surface::create_cairo_context() и Gdk::CairoContext::cairo_create(). Будући да су Каиро контексти објекти са бројем референци, cairo_create() враћа објекат Cairo::RefPtr<Cairo::Context>. (Имајте на уму разлику између Gdk::CairoContext и Cairo::Context.)

Следећи пример показује како да подесите Каиро контекст са црвеном бојом исцртавања и ширином 2. Све функције цртања које користе овај контекст користиће ова подешавања.

    Gtk::DrawingArea myArea;
    auto gdkCairoContext = myArea.get_surface()->create_cairo_context();
    auto myContext = gdkCairoContext->cairo_create();
    myContext->set_source_rgb(1.0, 0.0, 0.0);
    myContext->set_line_width(2.0);

Сваки Cairo::Context је повезан са одређеном Gdk::Surface површином, тако да први ред горњег примера прави Gtk::DrawingArea елемент, а следећа два реда користе његову придружену Gdk::Surface површину за прављење Cairo::Context објекта. Последња два реда мењају графичко стање контекста.

Постоји низ променљивих графичког стања које се могу поставити за Каиро контекст. Најчешћи атрибути контекста су боја (коришћењем set_source_rgb() или set_source_rgba() за провидне боје), ширина линије (коришћењем set_line_width()), шара испрекидане линије (коришћењем set_dash()), стил завршетка линије (коришћењем set_line_cap()), стил споја линије (коришћењем set_line_join()) и стилови фонта (коришћењем set_font_size(), set_font_face() и других). Постоје и многа друга подешавања, као што су матрице трансформације, правила попуњавања, да ли да се врши умекшавање ивица и друга. За даље информације, погледајте cairomm АПИ документацију.

Тренутно стање Cairo::Context објекта може бити сачувано на унутрашњи стек сачуваних стања и касније враћено на стање у којем је било када сте га сачували. Да бисте то урадили, користите методе save() и restore(). Ово може бити корисно ако треба привремено да промените ширину линије и боју (или било које друго графичко подешавање) како бисте нешто нацртали, а затим се вратили на претходна подешавања. У овој ситуацији, могли бисте позвати Cairo::Context::save(), променити графичка подешавања, нацртати линије, а затим позвати Cairo::Context::restore() да бисте повратили првобитно графичко стање. Вишеструки позиви save() и restore() могу бити угнежђени; сваки позив restore() враћа стање из одговарајућег упареног save() позива.

Добра је пракса ставити све измене графичког стања између позива функција save()/restore(). На пример, ако имате функцију која прима Cairo::Context референцу као аргумент, могли бисте је применити на следећи начин:

    void doSomething(const Cairo::RefPtr<Cairo::Context>& context, int x)
    {
        context->save();
        // change graphics state
        // perform drawing operations
        context->restore();
    }

Функција цртања коју поставите позивом set_draw_func() позива се са Каиро контекстом који треба да користите за цртање у Gtk::DrawingArea елементу. Није неопходно чувати и враћати овај Каиро контекст у функцији цртања.

### Цртање правих линија

Сада када разумемо основе Каиро графичке библиотеке, скоро смо спремни да почнемо са цртањем. Почећемо са најједноставнијим елементом цртања: правом линијом. Али прво морате знати мало о Каировом координатном систему. Почетак Каировог координатног система налази се у горњем левом углу прозора са позитивним x вредностима удесно и позитивним y вредностима наниже.

Будући да је Каиро графичка библиотека написана са подршком за више излазних циљева (X систем прозора, PNG слике, OpenGL итд.), постоји разлика између координата корисничког простора и простора уређаја. Мапирање између ова два координатна система је подразумевано један према један, тако да се целобројне вредности мапирају отприлике на пикселе на екрану, али се ово подешавање може прилагодити по жељи. Понекад може бити корисно променити размеру координата тако да пуна ширина и висина прозора буду у опсегу од 0 до 1 („јединични квадрат“) или неко друго мапирање које одговара вашем програму. Ово се може урадити помоћу функције Cairo::Context::scale().

#### Пример

У овом примеру, конструисаћемо мали али потпуно функционалан gtkmm програм и нацртати неке линије у прозору. Линије се цртају прављењем путање а затим њеним исцртавањем. Путања се прави помоћу функција Cairo::Context::move_to() и Cairo::Context::line_to(). Функција move_to() је слична чину подизања оловке са папира и њеног постављања на друго место — линија се не црта између тачке у којој сте били и тачке у коју сте се померили. Да бисте нацртали линију између две тачке, користите функцију line_to().

Након што завршите са прављењем путање, још увек нисте нацртали ништа видљиво. Да би путања постала видљива, морате користити функцију stroke() која ће исцртати тренутну путању са ширином линије и стилом наведеним у вашем Cairo::Context објекту. Након исцртавања, тренутна путања ће бити обрисана тако да можете почети са следећом путањом.

Многе Каиро функције цртања имају _preserve() варијанту. Обично функције цртања као што су clip(), fill() или stroke() бришу тренутну путању. Ако користите _preserve() варијанту, тренутна путања ће бити задржана тако да можете користити исту путању са следећом функцијом цртања.

Област за цртање — линије

Изворни код

File: `myarea.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_MYAREA_H
    #define GTKMM_EXAMPLE_MYAREA_H

    #include <gtkmm/drawingarea.h>

    class MyArea : public Gtk::DrawingArea
    {
    public:
      MyArea();
      virtual ~MyArea();

    protected:
      void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);
    };

    #endif // GTKMM_EXAMPLE_MYAREA_H

File: `main.cc` (For use with gtkmm 4)

    #include "myarea.h"
    #include <gtkmm/application.h>
    #include <gtkmm/window.h>

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

    protected:
      MyArea m_area;
    };

    ExampleWindow::ExampleWindow()
    {
      set_title("DrawingArea");
      set_child(m_area);
    }

    int main(int argc, char** argv)
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `myarea.cc` (For use with gtkmm 4)

    #include "myarea.h"
    #include <cairomm/context.h>

    MyArea::MyArea()
    {
      set_draw_func(sigc::mem_fun(*this, &MyArea::on_draw));
    }

    MyArea::~MyArea()
    {
    }

    void MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
    {
      // coordinates for the center of the window
      int xc, yc;
      xc = width / 2;
      yc = height / 2;

      cr->set_line_width(10.0);

      // draw red lines out from the center of the window
      cr->set_source_rgb(0.8, 0.0, 0.0);
      cr->move_to(0, 0);
      cr->line_to(xc, yc);
      cr->line_to(0, height);
      cr->move_to(xc, yc);
      cr->line_to(width, yc);
      cr->stroke();
    }

Овај програм садржи једну класу, MyArea, која је подкласа Gtk::DrawingArea и садржи функцију чланицу on_draw(). Ова функција постаје функција за цртање позивом set_draw_func() у конструктору класе MyArea. on_draw() се затим позива кад год је потребно поново исцртати слику у области за цртање. Прослеђује јој се Cairo::RefPtr показивач на Cairo::Context који користимо за цртање. Сам код за цртање поставља боју коју желимо да користимо за цртање помоћу set_source_rgb() која прима аргументе који дефинишу црвену, зелену и плаву компоненту жељене боје (важеће вредности су између 0 и 1). Након постављања боје, направили смо нову путању користећи функције move_to() и line_to(), а затим исцртали ову путању помоћу stroke().

Цртање релативним координатама

У горњем примеру смо све нацртали користећи апсолутне координате. Такође можете цртати користећи релативне координате. За праву линију, то се ради помоћу функције Cairo::Context::rel_line_to().

#### Стилови линија

Поред цртања основних правих линија, постоји низ ствари које можете прилагодити код линије. Већ сте видели примере постављања боје и ширине линије, али постоје и други.

Ако сте нацртали низ линија које чине путању, можда ћете желети да их спојите на одређени начин. Каиро нуди три различита начина за спајање линија: угаоно (Miter), косо (Bevel) и заобљено (Round). Они су приказани испод:

Различите врсте спојева у Каиру

Стил споја линија се поставља помоћу функције Cairo::Context::set_line_join().

Крајеви линија такође могу имати различите стилове. Подразумевани стил је да линија почиње и зауставља се тачно у одредишним тачкама линије. Ово се зове оштар (Butt) завршетак. Друге опције су заобљено (Round — користи заобљени завршетак, са центром круга у крајњој тачки) или квадратно (Square — користи квадратни завршетак, са центром квадрата у крајњој тачки). Ово подешавање се поставља помоћу функције Cairo::Context::set_line_cap().

Постоје и друге ствари које можете прилагодити, укључујући прављење испрекиданих линија и друго. За више информација погледајте документацију за Каиро АПИ. Испрекидане линије су приказане у примеру у одељку Цртање закривљених линија.

#### Цртање танких линија

Ако покушате да нацртате линије ширине једног пиксела, можда ћете приметити да линија понекад испадне замућена и шира него што би требало да буде. То се дешава зато што ће Каиро покушати да црта са изабране позиције на обе стране (по пола на сваку), па ако сте позиционирани тачно на пресеку пиксела, а желите линију ширине једног пиксела, Каиро ће покушати да искористи половину сваког суседног пиксела, што није могуће (пиксел је најмања могућа јединица). Ово се дешава када је ширина линије непаран број пиксела (не само један пиксел).

Трик је у томе да се позиционирате у средину пиксела где желите да линија буде исцртана, чиме се гарантује добијање жељених резултата. Погледајте Каиро ЧПП.

Област за цртање — танке линије

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm/window.h>
    #include <gtkmm/box.h>
    #include <gtkmm/checkbutton.h>
    #include "myarea.h"

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_toggled();

    private:
      Gtk::Box m_HBox;
      MyArea m_Area_Lines;
      Gtk::CheckButton m_Button_FixLines;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `myarea.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_MYAREA_H
    #define GTKMM_EXAMPLE_MYAREA_H

    #include <gtkmm/drawingarea.h>

    class MyArea : public Gtk::DrawingArea
    {
    public:
      MyArea();
      virtual ~MyArea();

      void fix_lines(bool fix = true);

    protected:
      void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);

    private:
      double m_fix;
    };

    #endif // GTKMM_EXAMPLE_MYAREA_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_HBox(Gtk::Orientation::HORIZONTAL),
      m_Button_FixLines("Fix lines")
    {
      set_title("Thin lines example");

      m_HBox.append(m_Area_Lines);
      m_HBox.append(m_Button_FixLines);

      set_child(m_HBox);

      m_Button_FixLines.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_toggled));

      // Synchonize the drawing in m_Area_Lines with the state of the toggle button.
      on_button_toggled();
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_toggled()
    {
      m_Area_Lines.fix_lines(m_Button_FixLines.get_active());
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `myarea.cc` (For use with gtkmm 4)

    #include "myarea.h"

    MyArea::MyArea()
    : m_fix (0)
    {
      set_content_width(200);
      set_content_height(100);
      set_draw_func(sigc::mem_fun(*this, &MyArea::on_draw));
    }

    MyArea::~MyArea()
    {
    }

    void MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
    {
      cr->set_line_width(1.0);

      // draw one line, every two pixels
      // without the 'fix', you won't notice any space between the lines,
      // since each one will occupy two pixels (width)
      for (int i = 0; i < width; i += 2)
      {
        cr->move_to(i + m_fix, 0);
        cr->line_to(i + m_fix, height);
      }

      cr->stroke();
    }

    // Toogle between both values (0 or 0.5)
    void MyArea::fix_lines(bool fix)
    {
      // to get the width right, we have to draw in the middle of the pixel
      m_fix = fix ? 0.5 : 0.0;

      // force the redraw of the image
      queue_draw();
    }

### Цртање кривих линија

Поред цртања правих линија, Каиро вам омогућава да лако цртате криве линије (технички кубни Безјеров сп-крива) користећи функције Cairo::Context::curve_to() и Cairo::Context::rel_curve_to(). Ове функције узимају координате за одредишну тачку као и координате за две „контролне“ тачке. Ово је најбоље објашњено на примеру, па хајде да уронимо у то.

#### Пример

Ова једноставна апликација црта криву помоћу Каира и приказује контролне тачке за сваки крај криве.

Површина за цртање — Крива

Изворни код

File: `myarea.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_MYAREA_H
    #define GTKMM_EXAMPLE_MYAREA_H

    #include <gtkmm/drawingarea.h>

    class MyArea : public Gtk::DrawingArea
    {
    public:
      MyArea();
      virtual ~MyArea();

    protected:
      void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);
    };

    #endif // GTKMM_EXAMPLE_MYAREA_H

File: `main.cc` (For use with gtkmm 4)

    #include "myarea.h"
    #include <gtkmm/application.h>
    #include <gtkmm/window.h>

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

    protected:
      MyArea m_area;
    };

    ExampleWindow::ExampleWindow()
    {
      set_title("DrawingArea");
      set_child(m_area);
    }

    int main(int argc, char** argv)
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `myarea.cc` (For use with gtkmm 4)

    #include "myarea.h"
    #include <cairomm/context.h>
    #include <vector>

    MyArea::MyArea()
    {
      set_draw_func(sigc::mem_fun(*this, &MyArea::on_draw));
    }

    MyArea::~MyArea()
    {
    }

    void MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
    {
      const double x0=0.1, y0=0.5, // start point
                   x1=0.4, y1=0.9, // control point #1
                   x2=0.6, y2=0.1, // control point #2
                   x3=0.9, y3=0.5; // end point
      const std::vector<double> dashes = {0.08, 0.05};

      // scale to unit square (0 to 1 width and height)
      cr->scale(width, height);

      // draw curve
      cr->set_line_width(0.05);
      cr->move_to(x0, y0);
      cr->curve_to(x1, y1, x2, y2, x3, y3);
      cr->stroke();

      // show first control point (red)
      cr->set_line_width(0.03);
      cr->set_dash(dashes, 0.0);
      cr->set_source_rgba(1.0, 0.2, 0.2, 0.6);
      cr->set_line_cap(Cairo::Context::LineCap::ROUND);
      cr->move_to(x1, y1);
      cr->line_to (x0, y0);
      cr->stroke();

      // show second control point (blue)
      cr->set_source_rgba(0.2, 0.2, 1.0, 0.6);
      cr->set_line_cap(Cairo::Context::LineCap::BUTT);
      cr->move_to(x2, y2);
      cr->line_to (x3, y3);
      cr->stroke();
    }

Једина разлика између овог примера и примера са правом линијом је у функцији on_draw(), али овде је уведено неколико нових концепата и функција, па хајде да их укратко испитамо.

Позивамо Cairo::Context::scale(), прослеђујући ширину и висину површине за цртање. Ово мења размеру координатног система корисничког простора тако да су и ширина и висина елемента једнаке 1,0 „јединица“. Нема посебног разлога за промену размере координатног система у овом случају, али понекад то може олакшати операције цртања.

Позив Cairo::Context::curve_to() би требало да буде прилично јасан сам по себи. Први пар координата дефинише контролну тачку за почетак криве. Други скуп координата дефинише контролну тачку за крај криве, а последњи скуп координата дефинише одредишну тачку. Да би концепт контролних тачака био мало лакши за визуелизацију, повучена је линија од сваке контролне тачке до крајње тачке на кривој са којом је повезана. Имајте на уму да су ове линије контролних тачака провидне. Ово се постиже варијантом set_source_rgb() која се зове set_source_rgba(). Ова функција узима четврти аргумент који одређује алфа вредност боје (важеће вредности су између 0 и 1).

Линије контролних тачака су испрекидане линије. Ово су само две могуће варијације. Испрекидане линије могу бити сложеније. На пример, цртице могу имати различите дужине као код линија са цртицама и тачкама.

### Цртање лукова и кругова

Са Каиром, иста функција се користи за цртање лукова, кругова или елипси: Cairo::Context::arc(). Ова функција узима пет аргумената. Прва два су координате средишње тачке лука, трећи аргумент је полупречник лука, а последња два аргумента дефинишу почетни и крајњи угао лука. Сви углови су дефинисани у радијанима, тако да је цртање круга исто што и цртање лука од 0 до 2 * M_PI радијана. Угао 0 је у смеру позитивне X осе (у корисничком простору). Угао од M_PI/2 радијана (90 степени) је у смеру позитивне Y осе (у корисничком простору). Углови се повећавају у смеру од позитивне X осе према позитивној Y оси. Дакле, са подразумеваном матрицом преображаја, углови се повећавају у смеру казаљке на сату. (Запамтите да позитивна Y оса показује надоле.)

Да бисте нацртали елипсу, можете променити размеру тренутне матрице преображаја за различите износе у X и Y смеровима. На пример, да бисте нацртали елипсу са средиштем у `x`, `y` и величином `width`, `height`:

    context->save();
    context->translate(x, y);
    context->scale(width / 2.0, height / 2.0);
    context->arc(0.0, 0.0, 1.0, 0.0, 2 * M_PI);
    context->restore();

#### Пример

Ево примера једноставног програма који црта лук, круг и елипсу у површини за цртање.

Површина за цртање — Лукови

Изворни код

File: `myarea.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_MYAREA_H
    #define GTKMM_EXAMPLE_MYAREA_H

    #include <gtkmm/drawingarea.h>

    class MyArea : public Gtk::DrawingArea
    {
    public:
      MyArea();
      virtual ~MyArea();

    protected:
      void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);
    };

    #endif // GTKMM_EXAMPLE_MYAREA_H

File: `main.cc` (For use with gtkmm 4)

    #include "myarea.h"
    #include <gtkmm/application.h>
    #include <gtkmm/window.h>

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

    protected:
      MyArea m_area;
    };

    ExampleWindow::ExampleWindow()
    {
      set_title("DrawingArea");
      set_child(m_area);
    }

    int main(int argc, char** argv)
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `myarea.cc` (For use with gtkmm 4)

    #include "myarea.h"
    #include <cairomm/context.h>
    #include <cmath>

    MyArea::MyArea()
    {
      set_draw_func(sigc::mem_fun(*this, &MyArea::on_draw));
    }

    MyArea::~MyArea()
    {
    }

    void MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
    {
      const int lesser = std::min(width, height);

      // coordinates for the center of the window
      const int xc = width / 2;
      const int yc = height / 2;

      cr->set_line_width(lesser * 0.02);  // outline thickness changes
                                          // with window size

      // first draw a simple unclosed arc
      cr->save();
      cr->arc(width / 3.0, height / 4.0, lesser / 4.0, -(M_PI / 5.0), M_PI);
      cr->close_path();   // line back to start point
      cr->set_source_rgb(0.0, 0.8, 0.0);
      cr->fill_preserve();
      cr->restore();  // back to opaque black
      cr->stroke();   // outline it

      // now draw a circle
      cr->save();
      cr->arc(xc, yc, lesser / 4.0, 0.0, 2.0 * M_PI); // full circle
      cr->set_source_rgba(0.0, 0.0, 0.8, 0.6);    // partially translucent
      cr->fill_preserve();
      cr->restore();  // back to opaque black
      cr->stroke();

      // and finally an ellipse
      double ex, ey, ew, eh;
      // center of ellipse
      ex = xc;
      ey = 3.0 * height / 4.0;
      // ellipse dimensions
      ew = 3.0 * width / 4.0;
      eh = height / 3.0;

      cr->save();

      cr->translate(ex, ey);  // make (ex, ey) == (0, 0)
      cr->scale(ew / 2.0, eh / 2.0);  // for width: ew / 2.0 == 1.0
                                      // for height: eh / 2.0 == 1.0

      cr->arc(0.0, 0.0, 1.0, 0.0, 2 * M_PI);  // 'circle' centered at (0, 0)
                                              // with 'radius' of 1.0

      cr->set_source_rgba(0.8, 0.0, 0.0, 0.7);
      cr->fill_preserve();
      cr->restore();  // back to opaque black
      cr->stroke();
    }

Постоји неколико ствари које треба приметити у вези са овим кодним примером. Опет, једина стварна разлика између овог примера и претходних је функција on_draw(), па ћемо се ограничити на ту функцију. Поред тога, први део функције је скоро идентичан претходним примерима, па ћемо прескочити тај део.

Имајте на уму да смо у овом случају скоро све изразили кроз висину и ширину прозора, укључујући и ширину линија. Због тога, када промените величину прозора, све се сразмерно мења са прозором. Такође имајте на уму да у функцији постоје три одељка за цртање и сваки је обмотан паром save()/restore() тако да се враћамо у познато стање након сваког цртања.

Одељак за цртање лука уводи једну нову функцију, close_path(). Ова функција ће заправо нацртати праву линију од тренутне тачке назад до прве тачке на путањи. Међутим, постоји значајна разлика између позивања close_path() и ручног цртања линије назад до почетне тачке. Ако користите close_path(), линије ће бити лепо спојене. Ако уместо тога користите line_to(), линије ће се завршити у истој тачки, али Каиро неће извршити никакво посебно спајање.

Цртање супротно смеру казаљке на сату

Функција Cairo::Context::arc_negative() је потпуно иста као Cairo::Context::arc(), али углови иду у супротном смеру.

### Цртање текста

#### Цртање текста помоћу Панга

Текст се исцртава путем Панго распореда. Најлакши начин за прављење Pango::Layout објекта је коришћење Gtk::Widget::create_pango_layout(). Након што се направи, распоредом се може управљати на различите начине, укључујући промену текста, фонта итд. На крају, распоред се може исцртати коришћењем методе Pango::Layout::show_in_cairo_context().

#### Пример

Ево примера програма који исцртава неки текст, а неки од њега наопачке. Поглавље о штампању садржи још један пример исцртавања текста.

Површина за цртање — Текст

Изворни код

File: `myarea.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_MYAREA_H
    #define GTKMM_EXAMPLE_MYAREA_H

    #include <gtkmm/drawingarea.h>

    class MyArea : public Gtk::DrawingArea
    {
    public:
      MyArea();
      virtual ~MyArea();

    protected:
      void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);

    private:
      void draw_rectangle(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);
      void draw_text(const Cairo::RefPtr<Cairo::Context>& cr, int rectangle_width, int rectangle_height);

    };

    #endif // GTKMM_EXAMPLE_MYAREA_H

File: `main.cc` (For use with gtkmm 4)

    #include "myarea.h"
    #include <gtkmm/application.h>
    #include <gtkmm/window.h>

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

    protected:
      MyArea m_area;
    };

    ExampleWindow::ExampleWindow()
    {
      set_title("Drawing text example");
      set_child(m_area);
    }

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `myarea.cc` (For use with gtkmm 4)

    #include "myarea.h"

    MyArea::MyArea()
    {
      set_draw_func(sigc::mem_fun(*this, &MyArea::on_draw));
    }

    MyArea::~MyArea()
    {
    }

    void MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
    {
      const int rectangle_width = width;
      const int rectangle_height = height / 2;

      // Draw a black rectangle
      cr->set_source_rgb(0.0, 0.0, 0.0);
      draw_rectangle(cr, rectangle_width, rectangle_height);

      // and some white text
      cr->set_source_rgb(1.0, 1.0, 1.0);
      draw_text(cr, rectangle_width, rectangle_height);

      // flip the image vertically
      // see http://www.cairographics.org/documentation/cairomm/reference/classCairo_1_1Matrix.html
      // the -1 corresponds to the yy part (the flipping part)
      // the height part is a translation (we could have just called cr->translate(0, height) instead)
      // it's height and not height / 2, since we want this to be on the second part of our drawing
      // (otherwise, it would draw over the previous part)
      Cairo::Matrix matrix(1.0, 0.0, 0.0, -1.0, 0.0, height);

      // apply the matrix
      cr->transform(matrix);

      // white rectangle
      cr->set_source_rgb(1.0, 1.0, 1.0);
      draw_rectangle(cr, rectangle_width, rectangle_height);

      // black text
      cr->set_source_rgb(0.0, 0.0, 0.0);
      draw_text(cr, rectangle_width, rectangle_height);
    }

    void MyArea::draw_rectangle(const Cairo::RefPtr<Cairo::Context>& cr,
                                int width, int height)
    {
      cr->rectangle(0, 0, width, height);
      cr->fill();
    }

    void MyArea::draw_text(const Cairo::RefPtr<Cairo::Context>& cr,
                           int rectangle_width, int rectangle_height)
    {
      // https://gnome.pages.gitlab.gnome.org/pangomm/classPango_1_1FontDescription.html
      Pango::FontDescription font;

      font.set_family("Monospace");
      font.set_weight(Pango::Weight::BOLD);

      // https://gnome.pages.gitlab.gnome.org/pangomm/classPango_1_1Layout.html
      auto layout = create_pango_layout("Hi there!");

      layout->set_font_description(font);

      int text_width;
      int text_height;

      //get the text dimensions (it updates the variables -- by reference)
      layout->get_pixel_size(text_width, text_height);

      // Position the text in the middle
      cr->move_to((rectangle_width-text_width)/2, (rectangle_height-text_height)/2);

      layout->show_in_cairo_context(cr);
    }

### Цртање слика

Постоји метода за цртање из Gdk::Pixbuf у Cairo::Context. Gdk::Pixbuf бафер је користан омотач око збирке пиксела, који се могу читати из датотека и којима се може управљати на различите начине.

Вероватно најчешћи начин прављења Gdk::Pixbuf објеката је коришћење Gdk::Pixbuf::create_from_file() или Gdk::Pixbuf::create_from_resource(), који могу учитати датотеку слике, као што је png датотека, у пиксбаф спреман за исцртавање.

Gdk::Pixbuf се може исцртати постављањем као изворни шаблон Каиро контекста помоћу Gdk::Cairo::set_source_pixbuf(). Затим нацртајте слику помоћу Cairo::Context::paint() (за цртање целе слике), или Cairo::Context::rectangle() и Cairo::Context::fill() (за попуњавање наведеног правоугаоника). set_source_pixbuf() није члан класе Cairo::Context. Она узима Cairo::Context као свој први параметар.

Ево малог дела кода који све то повезује: (Имајте на уму да обично не бисте учитавали слику сваки пут у руковаоцу сигнала за цртање! То је овде приказано само да би све било на једном месту.)

    void MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
    {
      auto image = Gdk::Pixbuf::create_from_file("myimage.png");
      // Draw the image at 110, 90, except for the outermost 10 pixels.
      Gdk::Cairo::set_source_pixbuf(cr, image, 100, 80);
      cr->rectangle(110, 90, image->get_width()-20, image->get_height()-20);
      cr->fill();
    }

#### Пример

Ево примера једноставног програма који исцртава слику. Програм учитава слику из датотеке ресурса. Погледајте одељак Gio::Resource и glib-compile-resources. Користите glib-compile-resources за компајлирање ресурса у C изворну датотеку која се може компајлирати и повезати са C++ кодом. Нпр.

    $ glib-compile-resources --target=resources.c --generate-source image.gresource.xml

Површина за цртање — Слика

Изворни код

File: `myarea.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_MYAREA_H
    #define GTKMM_EXAMPLE_MYAREA_H

    #include <gtkmm/drawingarea.h>
    #include <gdkmm/pixbuf.h>

    class MyArea : public Gtk::DrawingArea
    {
    public:
      MyArea();
      virtual ~MyArea();

    protected:
      void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);

      Glib::RefPtr<Gdk::Pixbuf> m_image;
    };

    #endif // GTKMM_EXAMPLE_MYAREA_H

File: `main.cc` (For use with gtkmm 4)

    #include "myarea.h"
    #include <gtkmm/application.h>
    #include <gtkmm/window.h>

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

    protected:
      MyArea m_area;
    };

    ExampleWindow::ExampleWindow()
    {
      set_title("DrawingArea");
      set_default_size(300, 200);
      set_child(m_area);
    }

    int main(int argc, char** argv)
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `myarea.cc` (For use with gtkmm 4)

    #include "myarea.h"
    #include <cairomm/context.h>
    #include <giomm/resource.h>
    #include <gdkmm/general.h> // set_source_pixbuf()
    #include <glibmm/fileutils.h>
    #include <iostream>

    MyArea::MyArea()
    {
      try
      {
        // The fractal image has been created by the XaoS program.
        // http://xaos.sourceforge.net
        m_image = Gdk::Pixbuf::create_from_resource("/image/fractal_image.png");
      }
      catch(const Gio::ResourceError& ex)
      {
        std::cerr << "ResourceError: " << ex.what() << std::endl;
      }
      catch(const Gdk::PixbufError& ex)
      {
        std::cerr << "PixbufError: " << ex.what() << std::endl;
      }

      // Show at least a quarter of the image.
      if (m_image)
      {
        set_content_width(m_image->get_width()/2);
        set_content_height(m_image->get_height()/2);
      }

      // Set the draw function.
      set_draw_func(sigc::mem_fun(*this, &MyArea::on_draw));
    }

    MyArea::~MyArea()
    {
    }

    void MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
    {
      if (!m_image)
        return;

      // Draw the image in the middle of the drawing area, or (if the image is
      // larger than the drawing area) draw the middle part of the image.
      Gdk::Cairo::set_source_pixbuf(cr, m_image,
        (width - m_image->get_width())/2, (height - m_image->get_height())/2);
      cr->paint();
    }

File: `image.gresource.xml` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <gresources>
      <gresource prefix="/image">
        <file>fractal_image.png</file>
      </gresource>
    </gresources>

### Пример апликације: Прављење сата помоћу Каира

Сада када смо покрили основе цртања помоћу Каира, хајде да покушамо да све то повежемо и направимо једноставну апликацију која заправо нешто ради. Следећи пример користи Каиро за прављење произвољног Clock елемента. Сат има секундну, минутну и сатну казаљку, и ажурира се сваке секунде.

Изворни код

File: `clock.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_CLOCK_H
    #define GTKMM_EXAMPLE_CLOCK_H

    #include <gtkmm/drawingarea.h>

    class Clock : public Gtk::DrawingArea
    {
    public:
      Clock();
      virtual ~Clock();

    protected:
      void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);

      bool on_timeout();

      double m_radius;
      double m_line_width;
    };

    #endif // GTKMM_EXAMPLE_CLOCK_H

File: `clock.cc` (For use with gtkmm 4)

    #include <ctime>
    #include <cmath>
    #include <cairomm/context.h>
    #include <glibmm/main.h>
    #include "clock.h"

    Clock::Clock()
    : m_radius(0.42), m_line_width(0.05)
    {
      Glib::signal_timeout().connect( sigc::mem_fun(*this, &Clock::on_timeout), 1000 );
      set_draw_func(sigc::mem_fun(*this, &Clock::on_draw));
    }

    Clock::~Clock()
    {
    }

    void Clock::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
    {
      // scale to unit square and translate (0, 0) to be (0.5, 0.5), i.e.
      // the center of the window
      cr->scale(width, height);
      cr->translate(0.5, 0.5);
      cr->set_line_width(m_line_width);

      cr->save();
      cr->set_source_rgba(0.337, 0.612, 0.117, 0.9);   // green
      cr->paint();
      cr->restore();
      cr->arc(0, 0, m_radius, 0, 2 * M_PI);
      cr->save();
      cr->set_source_rgba(1.0, 1.0, 1.0, 0.8);
      cr->fill_preserve();
      cr->restore();
      cr->stroke_preserve();
      cr->clip();

      //clock ticks
      for (int i = 0; i < 12; i++)
      {
        double inset = 0.05;

        cr->save();
        cr->set_line_cap(Cairo::Context::LineCap::ROUND);

        if(i % 3 != 0)
        {
          inset *= 0.8;
          cr->set_line_width(0.03);
        }

        cr->move_to(
          (m_radius - inset) * cos (i * M_PI / 6),
          (m_radius - inset) * sin (i * M_PI / 6));
        cr->line_to (
          m_radius * cos (i * M_PI / 6),
          m_radius * sin (i * M_PI / 6));
        cr->stroke();
        cr->restore(); /* stack-pen-size */
      }

      // store the current time
      time_t rawtime;
      time(&rawtime);
      struct tm * timeinfo = localtime (&rawtime);

      // compute the angles of the indicators of our clock
      double minutes = timeinfo->tm_min * M_PI / 30;
      double hours = timeinfo->tm_hour * M_PI / 6;
      double seconds= timeinfo->tm_sec * M_PI / 30;

      cr->save();
      cr->set_line_cap(Cairo::Context::LineCap::ROUND);

      // draw the seconds hand
      cr->save();
      cr->set_line_width(m_line_width / 3);
      cr->set_source_rgba(0.7, 0.7, 0.7, 0.8); // gray
      cr->move_to(0, 0);
      cr->line_to(sin(seconds) * (m_radius * 0.9),
        -cos(seconds) * (m_radius * 0.9));
      cr->stroke();
      cr->restore();

      // draw the minutes hand
      cr->set_source_rgba(0.117, 0.337, 0.612, 0.9);   // blue
      cr->move_to(0, 0);
      cr->line_to(sin(minutes + seconds / 60) * (m_radius * 0.8),
        -cos(minutes + seconds / 60) * (m_radius * 0.8));
      cr->stroke();

      // draw the hours hand
      cr->set_source_rgba(0.337, 0.612, 0.117, 0.9);   // green
      cr->move_to(0, 0);
      cr->line_to(sin(hours + minutes / 12.0) * (m_radius * 0.5),
        -cos(hours + minutes / 12.0) * (m_radius * 0.5));
      cr->stroke();
      cr->restore();

      // draw a little dot in the middle
      cr->arc(0, 0, m_line_width / 3.0, 0, 2 * M_PI);
      cr->fill();
    }

    bool Clock::on_timeout()
    {
      // force our program to redraw the entire clock.
      queue_draw();
      return true;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "clock.h"
    #include <gtkmm/application.h>
    #include <gtkmm/window.h>

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

    protected:
      Clock m_clock;
    };

    ExampleWindow::ExampleWindow()
    {
      set_title("Cairomm Clock");
      set_child(m_clock);
    }

    int main(int argc, char** argv)
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

Као и раније, скоро све занимљиве ствари се обављају у функцији исцртавања on_draw(). Пре него што заронимо у функцију исцртавања, приметимо да конструктор за елемент Clock повезује функцију руковаоца on_timeout() са одбројавачем са периодом истека од 1000 милисекунди (1 секунда). То значи да ће on_timeout() бити позивана једном у секунди. Једина одговорност ове функције је да поништи прозор како би gtkmm био приморан да га поново исцрта.

Сада погледајмо код који обавља стварно цртање. Први одељак on_draw() би до сада требало да буде прилично познат. Овај пример поново мења размеру координатног система тако да буде јединични квадрат како би било лакше нацртати сат као проценат величине прозора, тако да се аутоматски прилагођава када се промени величина прозора. Надаље, координатни систем је померен тако да се координата (0, 0) налази у самом центру прозора.

Функција Cairo::Context::paint() се овде користи за постављање боје позадине прозора. Ова функција не узима аргументе и попуњава тренутну површину (или исечени део површине) тренутно активном изворном бојом. Након постављања боје позадине прозора, цртамо круг за обрис сата, попуњавамо га белом бојом, а затим исцртавамо обрис црном бојом. Приметите да обе ове радње користе варијанту _preserve како би се сачувала тренутна путања, а затим се та иста путања исеца како бисмо били сигурни да наше следеће линије не изађу ван обриса сата.

Након цртања обриса, идемо око сата и цртамо цртице за сваки сат, са већом цртицом на 12, 3, 6 и 9. Сада смо коначно спремни да имплементирамо функционалност мерења времена сата, што једноставно укључује добављање тренутних вредности за сате, минуте и секунде, и цртање казаљки под исправним угловима.

## Превуци и пусти

Управљачи догађајима Gtk::DragSource и Gtk::DropTarget имају методе и сигнале који се користе за превлачење и спуштање.

### Извори и циљеви

Ствари се превлаче са `извора` да би се спустиле на `циљеве`. Сваки извор и циљ има податке о форматима података које може да пошаље или прими, које обезбеђује Gdk::ContentFormats. Циљ спуштања ће прихватити превучену ставку само ако обоје деле компатибилан формат. Тада ће бити емитовани одговарајући сигнали, који говоре руковаоцима сигнала који формат је коришћен.

Објекти Gdk::ContentFormats садрже податке о доступним GType врстама и миме врстама (медијским врстама).

### Методе

Елементи се могу препознати као извори или циљеви коришћењем управљача догађајима Gtk::DragSource и Gtk::DropTarget.

    auto source = Gtk::DragSource::create();
    m_source_widget.add_controller(source);

Неке методе DragSource:

- `void set_content(const Glib::RefPtr<Gdk::ContentProvider>& content)`: Поставља достављача садржаја на извор превлачења.

- `void set_actions(Gdk::DragAction actions)`: Поставља радње на извор превлачења. На пример `Gdk::DragAction::COPY | Gdk::DragAction::MOVE`.

- `void set_icon(const Glib::RefPtr<const Gdk::Paintable>& paintable, int hot_x, int hot_y)`: Поставља објекат за исцртавање који ће се користити као иконица током операција превлачења и спуштања.

    auto target = Gtk::DropTarget::create(gtype, actions);
    m_target_widget.add_controller(target);

Неки начини за DropTarget:

- `void set_gtypes(const std::vector<GType>& types)`: Поставља подржане врсте за овај циљ спуштања.

- `void set_actions(Gdk::DragAction actions)`: Поставља радње које овај циљ спуштања подржава.

- `Glib::ValueBase get_value() const`: Добавља тренутне податке за спуштање, као Glib::Value.

- `void reject()`: Одбија текућу радњу спуштања. Ову функцију треба користити када се одлаже одлука о томе да ли да се прихвати превлачење или не до након читања података.

### Сигнали

Када одредиште спуштања прихвати превучену ставку, биће емитовани одређени сигнали, у зависности од тога која је радња изабрана. На пример, корисник је можда држао тастер Shift да би навео `move` (премештање) уместо `copy` (копирање). Запамтите да корисник може изабрати само оне радње које сте навели у вашим позивима функцијама Gtk::DragSource::set_actions() и Gtk::DropTarget::set_actions().

Изворни елемент ће емитовати ове DragSource сигнале:

- `drag_begin`: Обезбеђује Gdk::Drag.

- `prepare`: Треба да врати Gdk::ContentProvider, са подацима који ће се користити за превлачење које управо почиње.

- `drag_end`: Обезбеђује Gdk::Drag, и bool који каже да ли је превлачење извршавало `move` (премештање) и да ли податке треба обрисати.

- `drag_cancel`: Емитује се на извору превлачења када превлачење не успе.

Циљни елемент ће емитовати ове DropTarget сигнале:

- `enter`: Обезбеђује координате. Треба да врати жељени Gdk::DragAction.

- `motion`: Обезбеђује координате. Треба да врати жељени Gdk::DragAction.

- `leave`: Емитује се на месту спуштања када показивач напусти елемент.

- `accept`: Обезбеђује Gdk::Drop. Можете позвати метод status() објекта Gdk::Drop да назначите које ће радње бити прихваћене.

- `drop`: Обезбеђује податке који се спуштају и координате. Треба да врати bool који указује на то да ли је спуштање прихваћено.

Следећи сигнали позивају само једног руковаоца сигналом када се емитују. Када повежете руковаоца на такав сигнал, ваш руковалац сигналом мора бити позван пре (уместо) подразумеваног руковаоца, иначе неће бити позван. Поставите параметар `after` у connect() на `false`.

- Gtk::DragSource::signal_prepare()

- Gtk::DropTarget::signal_enter()

- Gtk::DropTarget::signal_motion()

- Gtk::DropTarget::signal_accept()

- Gtk::DropTarget::signal_drop()

### Пример

Ево веома једноставног примера који демонстрира радњу превлачења и спуштања `Copy` (умножи):

Превуци и пусти

Изворни ко̂д

File: `dndwindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_DNDWINDOW_H
    #define GTKMM_EXAMPLE_DNDWINDOW_H

    #include <gdkmm/drop.h>
    #include <gtkmm/box.h>
    #include <gtkmm/label.h>
    #include <gtkmm/window.h>
    #include <gtkmm/button.h>

    class DnDWindow : public Gtk::Window
    {

    public:
      DnDWindow();
      virtual ~DnDWindow();

    protected:
      //Signal handlers:
      Glib::RefPtr<Gdk::ContentProvider> on_label_drag_prepare_data(double x, double y);
      bool on_button_drop_drop_data(const Glib::ValueBase& value, double x, double y);

      //Member widgets:
      Gtk::Box m_HBox;
      Gtk::Label m_Label_Drag;
      Gtk::Button m_Button_Drop;
    };

    #endif // GTKMM_EXAMPLE_DNDWINDOW_H

File: `dndwindow.cc` (For use with gtkmm 4)

    #include "dndwindow.h"
    #include <gdkmm/contentprovider.h>
    #include <gtkmm/dragsource.h>
    #include <gtkmm/droptarget.h>
    #include <iostream>

    DnDWindow::DnDWindow()
    : m_Label_Drag("Drag Here\n"),
      m_Button_Drop("Drop here\n")
    {
      set_title("DnD example");

      set_child(m_HBox);

      //Drag site:

      //Make m_Label_Drag a DnD drag source:
      auto source = Gtk::DragSource::create();
      source->set_actions(Gdk::DragAction::COPY);
      source->signal_prepare().connect(
        sigc::mem_fun(*this, &DnDWindow::on_label_drag_prepare_data), false);
      m_Label_Drag.add_controller(source);

      m_HBox.append(m_Label_Drag);
      m_Label_Drag.set_expand(true);

      //Drop site:

      //Make m_Button_Drop a DnD drop destination:
      const GType ustring_type = Glib::Value<Glib::ustring>::value_type();
      auto target = Gtk::DropTarget::create(ustring_type, Gdk::DragAction::COPY);
      target->signal_drop().connect(
        sigc::mem_fun(*this, &DnDWindow::on_button_drop_drop_data), false);
      m_Button_Drop.add_controller(target);

      m_HBox.append(m_Button_Drop);
      m_Button_Drop.set_expand(true);
    }

    DnDWindow::~DnDWindow()
    {
    }

    // In this simple example where just a small amount of data is copied,
    // it would be reasonable to store the ContentProvider in the DragSource.
    // Then this signal handler would be unnecessary.
    Glib::RefPtr<Gdk::ContentProvider> DnDWindow::on_label_drag_prepare_data(double, double)
    {
      Glib::Value<Glib::ustring> ustring_value;
      ustring_value.init(ustring_value.value_type());
      ustring_value.set("I'm Data!");
      return Gdk::ContentProvider::create(ustring_value);
    }

    bool DnDWindow::on_button_drop_drop_data(const Glib::ValueBase& value, double, double)
    {
      if (G_VALUE_HOLDS(value.gobj(), Glib::Value<Glib::ustring>::value_type()))
      {
        // We got the value type that we expected.
        Glib::Value<Glib::ustring> ustring_value;
        ustring_value.init(value.gobj());
        const Glib::ustring dropped_string = ustring_value.get();

        std::cout << "Received \"" << dropped_string << "\" in button " << std::endl;
        return true;
      }
      else
      {
        std::cout << "Received unexpected data type \""
          << G_VALUE_TYPE_NAME(value.gobj()) << "\" in button " << std::endl;
        return false;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "dndwindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<DnDWindow>(argc, argv);
    }

Постоји сложенији пример у examples/others/dnd.

## Остава

Једноставна функционалност умножавања и убацивања текста је обезбеђена бесплатно од стране елемената као што су Gtk::Entry и Gtk::TextView, али ће вам можда требати посебан ко̂д за рад са вашим сопственим форматима података. На пример, програм за цртање би захтевао посебан ко̂д како би омогућио умножавање и убацивање унутар прегледа или између докумената.

Примерак оставе можете добити помоћу Gtk::Widget::get_clipboard() или Gdk::Display::get_clipboard().

Ваш програм не мора да чека на операције са оставом, нарочито између времена када корисник изабере „Умножи“ а затим касније изабере „Убаци“. Многе методе класе Gdk::Clipboard примају sigc::slot-ове који одређују методе повратног позива. Када је Gdk::Clipboard спреман, он ће позвати ове методе, достављајући захтеване податке.

Референце

### Формати

Различити програми садрже различите врсте података, и они могу учинити те податке доступним у различитим форматима. gtkmm назива ове врсте података `format`-има (записима).

На пример, gedit може да достави и прими `text/plain` МИМЕ врсту, тако да можете убацити податке у gedit из било ког програма који доставља тај формат. Или два различита програма за обраду слика могу достављати и примати различите формате слика. Све док један програм може да прими један од формата који други доставља, моћи ћете да умножавате податке из једног у други.

Подаци у остави могу бити у различитим бинарним форматима. Ово поглавље, као и примери, претпостављају да су подаци 8-битни текст. То би нам омогућило да користимо ИксМЛ формат за податке у остави. Међутим, то вероватно не би било прикладно за бинарне податке као што су слике.

АПИ за превлачење и спуштање користи исти механизам. Вероватно би требало да користите исте формате података и за оставу и за радње превлачења и спуштања.

### Копирај

Када корисник затражи умножавање неких података, требало би да умножите податке у Clipboard (оставу). На пример,

    void ExampleWindow::on_button_copy()
    {
      get_clipboard()->set_text("example_custom_target");
    }

### Налепи

Када корисник затражи убацивање података из Clipboard-а (оставе), требало би да захтевате одређени формат и обезбедите методу повратног позива која ће бити позвана са стварним подацима. На пример:

    void ExampleWindow::on_button_paste()
    {
      get_clipboard()->read_text_async(sigc::mem_fun(*this,
                  &ExampleWindow::on_clipboard_received));
    }

Ево примера методе повратног позива:

    void ExampleWindow::on_clipboard_received(Glib::RefPtr<Gio::AsyncResult>& result)
    {
      auto text = get_clipboard()->read_text_finish(result);
      //Do something with the pasted data.
    }

#### Откривање доступних формата

Да бисте сазнали који су формати тренутно доступни у остави за убацивање, позовите метод get_formats(). Затим позовите метод Gdk::ContentFormats да бисте сазнали да ли је доступан формат који ваша апликација подржава.

### Примери

#### Једноставно

Овај пример омогућава копирање и убацивање података специфичних за апликацију, користећи стандардни текстуални формат. Иако је ово једноставно, није идеално јер не идентификује податке оставе као податке одређене врсте.

Остава — једноставно

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_copy();
      void on_button_paste();

      void on_clipboard_text_received(Glib::RefPtr<Gio::AsyncResult>& result);

      Glib::ustring m_strData;

      //Child widgets:
      Gtk::Box m_VBox;

      Gtk::Label m_Label;

      Gtk::Grid m_Grid;
      Gtk::ToggleButton m_ButtonA1, m_ButtonA2, m_ButtonB1, m_ButtonB2;

      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Copy, m_Button_Paste;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Label("Select cells in the grid, click Copy, then open a second "
            "instance of this example to try pasting the copied data."),
      m_ButtonA1("A1"), m_ButtonA2("A2"), m_ButtonB1("B1"), m_ButtonB2("B2"),
      m_Button_Copy("_Copy", /* mnemonic= */ true), m_Button_Paste("_Paste", true)
    {
      set_title("Gtk::Clipboard example");

      m_VBox.set_margin(12);
      set_child(m_VBox);

      m_VBox.append(m_Label);

      //Fill Grid:
      m_VBox.append(m_Grid);
      m_Grid.set_expand(true);
      m_Grid.set_row_homogeneous(true);
      m_Grid.set_column_homogeneous(true);
      m_Grid.attach(m_ButtonA1, 0, 0);
      m_Grid.attach(m_ButtonA2, 1, 0);
      m_Grid.attach(m_ButtonB1, 0, 1);
      m_Grid.attach(m_ButtonB2, 1, 1);

      //Add ButtonBox to bottom:
      m_VBox.append(m_ButtonBox);
      m_VBox.set_spacing(6);

      //Fill ButtonBox:
      m_ButtonBox.append(m_Button_Copy);
      m_Button_Copy.set_hexpand(true);
      m_Button_Copy.set_halign(Gtk::Align::END);
      m_Button_Copy.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_copy) );
      m_ButtonBox.append(m_Button_Paste);
      m_Button_Paste.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_paste) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_copy()
    {
      //Build a string representation of the stuff to be copied:
      //Ideally you would use XML, with an XML parser here:
      m_strData = m_ButtonA1.get_active() ? "1" : "0";
      m_strData += m_ButtonA2.get_active() ? "1" : "0";
      m_strData += m_ButtonB1.get_active() ? "1" : "0";
      m_strData += m_ButtonB2.get_active() ? "1" : "0";

      // Gdk::Clipboard::set_text() does not take a copy of the text.
      // The text can only be pasted (in this program or in some other program)
      // for as long as it exists.
      get_clipboard()->set_text(m_strData);
    }

    void ExampleWindow::on_button_paste()
    {
      //Tell the clipboard to call our method when it is ready:
      get_clipboard()->read_text_async(sigc::mem_fun(*this,
                  &ExampleWindow::on_clipboard_text_received));
    }

    void ExampleWindow::on_clipboard_text_received(Glib::RefPtr<Gio::AsyncResult>& result)
    {
      Glib::ustring text;
      try
      {
        text = get_clipboard()->read_text_finish(result);
      }
      catch (const Glib::Error& err)
      {
        // Print an error about why pasting failed.
        // Usually you probably want to ignore such failures,
        // but for demonstration purposes, we show the error.
        std::cout << "Pasting failed: " << err.what() << std::endl;
      }

      //See comment in on_button_copy() about this silly clipboard format.
      if(text.size() >= 4)
      {
        m_ButtonA1.set_active( text[0] == '1' );
        m_ButtonA2.set_active( text[1] == '1' );
        m_ButtonB1.set_active( text[2] == '1' );
        m_ButtonB2.set_active( text[3] == '1' );
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      // Gio::Application::Flags::NON_UNIQUE because it shall be possible to run
      // several instances of this application simultaneously.
      auto app = Gtk::Application::create(
        "org.gtkmm.example", Gio::Application::Flags::NON_UNIQUE);

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

#### Идеално

Ово је као једноставан пример, али он

1. Дефинише произвољни циљ оставе, иако је формат и даље текстуални.

2. Користи сигнал Gdk::ContentFormats::signal_changed() и онемогућава дугме „Убаци“ ако не може да користи ништа из оставе.

Остава — идеално

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_copy();
      void on_button_paste();

      void on_clipboard_content_changed();

      void on_clipboard_received(Glib::RefPtr<Gio::AsyncResult>& result);
      void on_clipboard_received_status(Glib::RefPtr<Gio::AsyncResult>& result);

      void update_paste_status(); //Disable the paste button if there is nothing to paste.

      Glib::ustring m_strData;

      //Child widgets:
      Gtk::Box m_VBox;

      Gtk::Label m_Label;

      Gtk::Grid m_Grid;
      Gtk::ToggleButton m_ButtonA1, m_ButtonA2, m_ButtonB1, m_ButtonB2;

      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Copy, m_Button_Paste;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>
    #include <string>

    namespace
    {

    const char example_format_custom[] = "gtkmmclipboardexample";

    } // anonymous namespace

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Label("Select cells in the grid, click Copy, then open a second instance "
              "of this example to try pasting the copied data.\nOr try pasting the "
              "text representation into gedit."),
      m_ButtonA1("A1"), m_ButtonA2("A2"), m_ButtonB1("B1"), m_ButtonB2("B2"),
      m_Button_Copy("_Copy", /* mnemonic= */ true), m_Button_Paste("_Paste", true)
    {
      set_title("Gtk::Clipboard example");

      m_VBox.set_margin(12);
      set_child(m_VBox);

      m_VBox.append(m_Label);

      //Fill Grid:
      m_VBox.append(m_Grid);
      m_Grid.set_expand(true);
      m_Grid.set_row_homogeneous(true);
      m_Grid.set_column_homogeneous(true);
      m_Grid.attach(m_ButtonA1, 0, 0);
      m_Grid.attach(m_ButtonA2, 1, 0);
      m_Grid.attach(m_ButtonB1, 0, 1);
      m_Grid.attach(m_ButtonB2, 1, 1);

      //Add ButtonBox to bottom:
      m_VBox.append(m_ButtonBox);
      m_VBox.set_spacing(6);

      //Fill ButtonBox:
      m_ButtonBox.append(m_Button_Copy);
      m_Button_Copy.set_hexpand(true);
      m_Button_Copy.set_halign(Gtk::Align::END);
      m_Button_Copy.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_copy) );
      m_ButtonBox.append(m_Button_Paste);
      m_Button_Paste.signal_clicked().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_button_paste) );

      //Connect a signal handler that will be called when the contents of
      //the clipboard change.
      get_clipboard()->signal_changed().connect(sigc::mem_fun(*this,
                  &ExampleWindow::on_clipboard_content_changed));

      update_paste_status();
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_copy()
    {
      //Build a string representation of the stuff to be copied:
      //Ideally you would use XML, with an XML parser here:
      m_strData = example_format_custom;
      m_strData += m_ButtonA1.get_active() ? " A1" : "";
      m_strData += m_ButtonA2.get_active() ? " A2" : "";
      m_strData += m_ButtonB1.get_active() ? " B1" : "";
      m_strData += m_ButtonB2.get_active() ? " B2" : "";

      // Gdk::Clipboard::set_text() does not take a copy of the text.
      // The text can only be pasted (in this program or in some other program)
      // for as long as it exists.
      get_clipboard()->set_text(m_strData);
    }

    void ExampleWindow::on_button_paste()
    {
      //Tell the clipboard to call our method when it is ready:
      get_clipboard()->read_text_async(sigc::mem_fun(*this,
                  &ExampleWindow::on_clipboard_received));
    }

    void ExampleWindow::on_clipboard_content_changed()
    {
      update_paste_status();
    }

    void ExampleWindow::on_clipboard_received(Glib::RefPtr<Gio::AsyncResult>& result)
    {
      Glib::ustring text;
      try
      {
        text = get_clipboard()->read_text_finish(result);
      }
      catch (const Glib::Error& err)
      {
        // Print an error about why pasting failed.
        // Usually you probably want to ignore such failures,
        // but for demonstration purposes, we show the error.
        std::cout << "Pasting failed: " << err.what() << std::endl;
      }

      if (text.find(example_format_custom) == 0)
      {
        // It's the expected format.
        m_ButtonA1.set_active(text.find("A1") != std::string::npos);
        m_ButtonA2.set_active(text.find("A2") != std::string::npos);
        m_ButtonB1.set_active(text.find("B1") != std::string::npos);
        m_ButtonB2.set_active(text.find("B2") != std::string::npos);
      }
      else
      {
        // Unexpected format. Disable the Paste button.
        std::cout << "Unexpected pasted text: \"" << text << "\"" << std::endl;
        m_Button_Paste.set_sensitive(false);
      }
    }

    void ExampleWindow::update_paste_status()
    {
      // Disable the paste button if there is nothing to paste.
      get_clipboard()->read_text_async(sigc::mem_fun(*this,
                  &ExampleWindow::on_clipboard_received_status));
    }

    void ExampleWindow::on_clipboard_received_status(Glib::RefPtr<Gio::AsyncResult>& result)
    {
      Glib::ustring text;
      try
      {
        text = get_clipboard()->read_text_finish(result);
      }
      catch (const Glib::Error&)
      {
      }

      const bool bPasteIsPossible = text.find(example_format_custom) == 0;

      // Enable/Disable the Paste button appropriately:
      m_Button_Paste.set_sensitive(bPasteIsPossible);
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      // Gio::Application::Flags::NON_UNIQUE because it shall be possible to run
      // several instances of this application simultaneously.
      auto app = Gtk::Application::create(
        "org.gtkmm.example", Gio::Application::Flags::NON_UNIQUE);

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

## Штампање

На нивоу развоја апликација, gtkmm-ов API за штампање пружа прозорчиће који су доследни кроз апликације и омогућава коришћење Cairo-вог заједничког API-ја за цртање, са Pango-вођеним исцртавањем текста. У изведби овог заједничког API-ја, користе се позадинци специфични за платформу и драјвери специфични за штампач.

### PrintOperation

Примарни објекат је Gtk::PrintOperation, додељен за сваку радњу штампања. Да бисте управљали цртањем странице, повежите се на његове сигнале или наследите из њега и замените подразумеване виртуелне руковаоце сигнала. PrintOperation аутоматски управља свим подешавањима која утичу на петљу штампања.

#### Сигнали

Метод PrintOperation::run() покреће петљу штампања, током које се емитују разни сигнали:

- `begin_print`: Морате управљати овим сигналом, јер овде правите и подешавате Pango::Layout користећи достављени Gtk::PrintContext, и рашчлањујете ваш излаз штампања на странице.

- `paginate`: Пагинација је потенцијално спора, па ако треба да је пратите, можете позвати метод PrintOperation::set_show_progress() и управљати овим сигналом.

- За сваку страницу коју треба исцртати, емитују се следећи сигнали:

  - `request_page_setup`: Пружа PrintContext, број странице и Gtk::PageSetup. Управљајте овим сигналом ако треба да измените подешавање странице на основи појединачних страница.

  - Морате обрадити овај сигнал, који обезбеђује PrintContext и број странице. PrintContext треба користити за прављење Cairo::Context у којем треба исцртати достављену страницу. За исцртавање текста, прођите кроз Pango::Layout који сте направили у руковаоцу `begin_print`.

- Руковалац за њега је безбедно место за ослобађање свих ресурса везаних за PrintOperation. Ако имате сопствену класу која наслеђује PrintOperation, природно је једноставније то учинити у деструктору.

- Овај сигнал се емитује када је штампање завршено, што значи када су подаци за штампу послати у ред чекања. Имајте на уму да достављени `Gtk::PrintOperation::Result` може указати на то да је дошло до грешке. У сваком случају вероватно желите да обавестите корисника о коначном стању.

- Емитује се кад год се промени стање посла штампања, све док се не заврши. Позовите метод PrintOperation::set_track_print_status() да бисте пратили стање посла након слања у ред чекања. Да бисте видели стање, користите get_status() или get_status_string().

Референце

### Подешавање странице

Класа PrintOperation има метод под називом set_default_page_setup() који бира подразумевану величину папира, усмерење и маргине. Да бисте приказали прозорче за подешавање странице из вашег програма, користите метод Gtk::run_page_setup_dialog(), који враћа објекат Gtk::PageSetup са изабраним подешавањима. Користите овај објекат да ажурирате PrintOperation и да приступите изабраној величини папира Gtk::PaperSize, усмерењу `Gtk::PageOrientation` и маргинама специфичним за штампач.

Требало би да сачувате изабрано Gtk::PageSetup како бисте могли поново да га користите ако се прозорче за подешавање странице поново прикаже.

На пример,

    // Унутар класе која наслеђује Gtk::Window и чува m_refPageSetup
    // и m_refSettings као чланове...
    auto new_page_setup = Gtk::run_page_setup_dialog(*this, m_refPageSetup, m_refSettings);
    m_refPageSetup = new_page_setup;

Референце

Координатни систем Каира, у руковаоцу `draw_page`, аутоматски се ротира према тренутном усмерењу странице. Обично је унутар маргина штампача, али то можете променити путем метода PrintOperation::set_use_full_page(). Подразумевана јединица мере су пиксели уређаја. Да бисте изабрали друге јединице, користите метод PrintOperation::set_unit().

### Исцртавање текста

Исцртавање текста се врши помоћу Панго-а. Објекат Pango::Layout за штампање треба направити позивањем метода PrintContext::create_pango_layout(). Објекат PrintContext такође обезбеђује метрику странице, путем get_width() и get_height(). Број страница се може поставити помоћу PrintOperation::set_n_pages(). Да бисте заиста исцртали Панго текст у `on_draw_page`, набавите Cairo::Context помоћу PrintContext::get_cairo_context() и прикажите Pango::LayoutLine линије које се појављују унутар захтеваног броја странице.

Погледајте пример како се тачно то може урадити.

### Асинхроне операције

По подразумеваној вредности, PrintOperation::run() се враћа када се операција штампања заврши. Ако треба да покренете неблокирајућу операцију штампања, позовите PrintOperation::set_allow_async(). Имајте на уму да set_allow_async() није подржано на свим платформама, међутим сигнал `done` ће и даље бити емитован.

run() може вратити `PrintOperation::Result::IN_PROGRESS`. Да бисте пратили стање и обрадили резултат или грешку, треба да имплементирате руковаоце сигнала за сигнале `done` и `status_changed`:

На пример,

    // у методу класе ExampleWindow...
    auto op = PrintOperation::create();
    // ...подесите op...
    op->signal_done().connect(sigc::bind(sigc::mem_fun(
      *this, &ExampleWindow::on_printoperation_done), op));
    // покрените op

Друго, проверите да ли постоји грешка и повежите се на сигнал `status_changed`. На пример:

    void ExampleWindow::on_printoperation_done(Gtk::PrintOperation::Result result,
      const Glib::RefPtr<PrintOperation>& op)
    {
      if (result == Gtk::PrintOperation::Result::ERROR)
        //обавести корисника
      else if (result == Gtk::PrintOperation::Result::APPLY)
        //Ажурирај PrintSettings онима који су коришћени у овој операцији штампања

      if (! op->is_finished())
        op->signal_status_changed().connect(sigc::bind(sigc::mem_fun(
          *this, &ExampleWindow::on_printoperation_status_changed), op));
    }

На крају, проверите стање. На пример,

    void ExampleWindow::on_printoperation_status_changed(const Glib::RefPtr<PrintOperation>& op)
    {
      if (op->is_finished())
        //посао штампања је завршен
      else
        //добави стање помоћу get_status() или get_status_string()

      //ажурирај корисничко прочеље
    }

### Извези у ПДФ

Опција „Штампај у датотеку“ је доступна у прозорчету за штампање, без потребе за додатном имплементацијом. Међутим, понекад је корисно направити пдф датотеку директно из кода. На пример,

    auto op = Gtk::PrintOperation::create();
    // ...подеси op...
    op->set_export_filename("test.pdf");
    auto res = op->run(Gtk::PrintOperation::Action::EXPORT);

### Проширивање прозорчета за штампање

Можете додати произвољни језичак у прозорче за штампање:

- Поставите наслов језичка преко PrintOperation::set_custom_tab_label(), направите нови елемент и вратите га из руковаоца сигнала `create_custom_widget`. Вероватно ћете желети да ово буде елемент садржалац, упакован са неким другим елементима.

- Добавите податке из елемената у руковаоцу сигнала `custom_widget_apply`.

Иако сигнал `custom_widget_apply` обезбеђује елемент који сте претходно направили, ради поједностављења можете задржати елементе за које очекујете да садрже неки кориснички унос као чланове класе. На пример, рецимо да имате Gtk::Entry под називом `m_Entry` као члана ваше класе CustomPrintOperation:

    Gtk::Widget* CustomPrintOperation::on_create_custom_widget()
    {
      set_custom_tab_label("Мој произвољни језичак");

      auto hbox = new Gtk::Box(Gtk::Orientation::HORIZONTAL, 8);
      hbox->set_margin(6);

      auto label = Gtk::make_managed<Gtk::Label>("Унесите неки текст: ");
      hbox->append(*label);

      hbox->append(m_Entry);

      return hbox;
    }

    void CustomPrintOperation::on_custom_widget_apply(Gtk::Widget* /* widget */)
    {
      auto user_input = m_Entry.get_text();
      //...
    }

Пример у examples/book/printing/advanced ово демонстрира.

### Преглед

Изворно ГТК прозорче за штампање има дугме за преглед, али преглед можете покренути и директно из апликације:

    // у класи која наслеђује Gtk::Window...
    auto op = PrintOperation::create();
    // ...подеси op...
    op->run(Gtk::PrintOperation::Action::PREVIEW, *this);

На Јуниксу, подразумевани руковалац прегледа користи спољни програм за прегледање. На Виндоузу ће бити приказано изворно прозорче за преглед. Ако је потребно, можете премостити ово понашање и обезбедити произвољно прозорче за преглед. Погледајте пример који се налази у /examples/book/printing/advanced.

### Прозорче за штампање

Од gtkmm 4.14 Gtk::PrintDialog је алтернатива за Gtk::PrintOperation. PrintDialog користи исте класе PageSetup и PrintSettings као и PrintOperation. Исцртавање помоћу Каира и Пангоа је такође слично.

### Примери

#### Једноставно

Следећи пример демонстрира како одштампати неки унос из корисничког прочеља користећи PrintOperation. Показује како применити `on_begin_print` и `on_draw_page`, као и како пратити стање штампања и ажурирати подешавања штампања.

Штампање — једноставно

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    // This file is part of the printing/simple and printing/advanced examples

    #include <gtkmm.h>

    class PrintFormOperation;

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow(const Glib::RefPtr<Gtk::Application>& app);
      virtual ~ExampleWindow();

    protected:

      void build_main_menu(const Glib::RefPtr<Gtk::Application>& app);

      void print_or_preview(Gtk::PrintOperation::Action print_action);

      //PrintOperation signal handlers.
      //We handle these so can get necessary information to update the UI or print settings.
      //Our derived PrintOperation class also overrides some default signal handlers.
      void on_printoperation_status_changed();
      void on_printoperation_done(Gtk::PrintOperation::Result result);

      //Action signal handlers:
      void on_menu_file_new();
      void on_menu_file_page_setup();
      void on_menu_file_print_preview();
      void on_menu_file_print();
      void on_menu_file_quit();

      //Printing-related objects:
      Glib::RefPtr<Gtk::PageSetup> m_refPageSetup;
      Glib::RefPtr<Gtk::PrintSettings> m_refSettings;
      Glib::RefPtr<PrintFormOperation> m_refPrintFormOperation;

      //Child widgets:
      Gtk::Box m_VBox;
      Gtk::Grid m_Grid;

      Gtk::Label m_NameLabel;
      Gtk::Entry m_NameEntry;

      Gtk::Label m_SurnameLabel;
      Gtk::Entry m_SurnameEntry;

      Gtk::Label m_CommentsLabel;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::TextView m_TextView;

      Glib::RefPtr<Gtk::TextBuffer> m_refTextBuffer;

      Gtk::Label m_Statusbar;

      Glib::RefPtr<Gtk::Builder> m_refBuilder;

      Glib::RefPtr<Gtk::AlertDialog> m_refDialog;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `printformoperation.h` (For use with gtkmm 4)

    #ifndef GTKMM_PRINT_FORM_OPERATION_H
    #define GTKMM_PRINT_FORM_OPERATION_H

    #include <gtkmm.h>
    #include <pangomm.h>
    #include <vector>

    //We derive our own class from PrintOperation,
    //so we can put the actual print implementation here.
    class PrintFormOperation : public Gtk::PrintOperation
    {
     public:
      static Glib::RefPtr<PrintFormOperation> create();
      virtual ~PrintFormOperation();

      void set_name(const Glib::ustring& name) { m_Name = name; }
      void set_comments(const Glib::ustring& comments) { m_Comments = comments; }

     protected:
      PrintFormOperation();

      //PrintOperation default signal handler overrides:
      void on_begin_print(const Glib::RefPtr<Gtk::PrintContext>& context) override;
      void on_draw_page(const Glib::RefPtr<Gtk::PrintContext>& context, int page_nr) override;

      Glib::ustring m_Name;
      Glib::ustring m_Comments;
      Glib::RefPtr<Pango::Layout> m_refLayout;
      std::vector<int> m_PageBreaks; // line numbers where a page break occurs
    };

    #endif // GTKMM_PRINT_FORM_OPERATION_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include "printformoperation.h"
    #include <iostream>

    ExampleWindow::ExampleWindow(const Glib::RefPtr<Gtk::Application>& app)
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_NameLabel("Name"),
      m_SurnameLabel("Surname"),
      m_CommentsLabel("Comments")
    {
      m_refPageSetup = Gtk::PageSetup::create();
      m_refSettings = Gtk::PrintSettings::create();

      set_title("gtkmm Printing Example");
      set_default_size(400, 300);

      set_child(m_VBox);

      build_main_menu(app);

      m_VBox.append(m_Grid);

      //Arrange the widgets inside the grid:
      m_Grid.set_expand(true);
      m_Grid.set_row_spacing(5);
      m_Grid.set_column_spacing(5);
      m_Grid.attach(m_NameLabel, 0, 0);
      m_Grid.attach(m_NameEntry, 1, 0);

      m_Grid.attach(m_SurnameLabel, 0, 1);
      m_Grid.attach(m_SurnameEntry, 1, 1);

      //Add the TextView, inside a ScrolledWindow:
      m_ScrolledWindow.set_child(m_TextView);

      //Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);

      m_Grid.attach(m_CommentsLabel, 0, 2);
      m_Grid.attach(m_ScrolledWindow, 1, 2);
      m_ScrolledWindow.set_expand(true);

      m_refTextBuffer = Gtk::TextBuffer::create();
      m_TextView.set_buffer(m_refTextBuffer);

      m_Statusbar.set_hexpand(true);
      m_VBox.append(m_Statusbar);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::build_main_menu(const Glib::RefPtr<Gtk::Application>& app)
    {
      //Create actions for menus and toolbars:
      auto refActionGroup = Gio::SimpleActionGroup::create();

      //File menu:
      refActionGroup->add_action("new",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_new));

      refActionGroup->add_action("pagesetup",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_page_setup));

      refActionGroup->add_action("printpreview",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_print_preview));

      refActionGroup->add_action("print",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_print));

      refActionGroup->add_action("quit",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_quit));

      insert_action_group("example", refActionGroup);

      // When the menubar is a child of a Gtk::Window, keyboard accelerators are not
      // automatically fetched from the Gio::Menu.
      // See the examples/book/menus/main_menu example for an alternative way of
      // adding the menubar when using Gtk::ApplicationWindow.
      app->set_accel_for_action("example.new", "<Primary>n");
      app->set_accel_for_action("example.print", "<Primary>p");
      app->set_accel_for_action("example.quit", "<Primary>q");

      m_refBuilder = Gtk::Builder::create();

      // Layout the actions in a menubar:
      Glib::ustring ui_menu_info =
        "<interface>"
        "  <menu id='menu-example'>"
        "    <submenu>"
        "      <attribute name='label' translatable='yes'>_File</attribute>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_New</attribute>"
        "          <attribute name='action'>example.new</attribute>"
        "        </item>"
        "      </section>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>Page _Setup...</attribute>"
        "          <attribute name='action'>example.pagesetup</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>Print Preview</attribute>"
        "          <attribute name='action'>example.printpreview</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_Print...</attribute>"
        "          <attribute name='action'>example.print</attribute>"
        "        </item>"
        "      </section>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_Quit</attribute>"
        "          <attribute name='action'>example.quit</attribute>"
        "        </item>"
        "      </section>"
        "    </submenu>"
        "  </menu>"
        "</interface>";

      try
      {
        m_refBuilder->add_from_string(ui_menu_info);
      }
      catch(const Glib::Error& ex)
      {
        std::cerr << "building menus failed: " << ex.what();
      }

      // Layout the actions in a toolbar:
      Glib::ustring ui_toolbar_info =
        "<!-- Generated with glade 3.18.3 and then changed manually -->"
        "<interface>"
          "<object class='GtkBox' id='toolbar'>"
            "<property name='can_focus'>False</property>"
            "<property name='spacing'>3</property>"
            "<child>"
              "<object class='GtkButton' id='toolbutton_new'>"
                "<property name='can_focus'>False</property>"
                "<property name='tooltip_text' translatable='yes'>New</property>"
                "<property name='action_name'>example.new</property>"
                "<property name='icon_name'>document-new</property>"
                "<property name='hexpand'>False</property>"
                "<property name='vexpand'>False</property>"
              "</object>"
            "</child>"
            "<child>"
              "<object class='GtkButton' id='toolbutton_print'>"
                "<property name='can_focus'>False</property>"
                "<property name='tooltip_text' translatable='yes'>Print</property>"
                "<property name='action_name'>example.print</property>"
                "<property name='icon_name'>document-print</property>"
                "<property name='hexpand'>False</property>"
                "<property name='vexpand'>False</property>"
              "</object>"
            "</child>"
            "<child>"
              "<object class='GtkSeparator' id='separator1'>"
                "<property name='can_focus'>False</property>"
                "<property name='hexpand'>False</property>"
                "<property name='vexpand'>False</property>"
              "</object>"
            "</child>"
            "<child>"
              "<object class='GtkButton' id='toolbutton_quit'>"
                "<property name='can_focus'>False</property>"
                "<property name='tooltip_text' translatable='yes'>Quit</property>"
                "<property name='action_name'>example.quit</property>"
                "<property name='icon_name'>application-exit</property>"
                "<property name='hexpand'>False</property>"
                "<property name='vexpand'>False</property>"
              "</object>"
            "</child>"
          "</object>"
        "</interface>";

      try
      {
        m_refBuilder->add_from_string(ui_toolbar_info);
      }
      catch(const Glib::Error& ex)
      {
        std::cerr << "building toolbar failed: " << ex.what();
      }

      // Get the menubar and add it to a container widget:
      auto object = m_refBuilder->get_object("menu-example");
      auto gmenu = std::dynamic_pointer_cast<Gio::Menu>(object);
      if (!gmenu)
        g_warning("GMenu not found");
      else
      {
        auto pMenuBar = Gtk::make_managed<Gtk::PopoverMenuBar>(gmenu);

        // Add the PopoverMenuBar to the window:
        m_VBox.append(*pMenuBar);
      }

      // Get the toolbar and add it to a container widget:
      auto toolbar = m_refBuilder->get_widget<Gtk::Box>("toolbar");
      if (!toolbar)
        g_warning("toolbar not found");
      else
        m_VBox.append(*toolbar);
    }

    void ExampleWindow::on_printoperation_status_changed()
    {
      Glib::ustring status_msg;

      if (m_refPrintFormOperation->is_finished())
      {
        status_msg = "Print job completed.";
      }
      else
      {
        //You could also use get_status().
        status_msg = m_refPrintFormOperation->get_status_string();
      }

      m_Statusbar.set_text(status_msg);
    }

    void ExampleWindow::on_printoperation_done(Gtk::PrintOperation::Result result)
    {
      //Printing is "done" when the print data is spooled.

      if (result == Gtk::PrintOperation::Result::ERROR)
      {
        if (!m_refDialog)
          m_refDialog = Gtk::AlertDialog::create("Error printing form");
        m_refDialog->show(*this);
      }
      else if (result == Gtk::PrintOperation::Result::APPLY)
      {
        //Update PrintSettings with the ones used in this PrintOperation:
        m_refSettings = m_refPrintFormOperation->get_print_settings();
      }

      if (!m_refPrintFormOperation->is_finished())
      {
        //We will connect to the status-changed signal to track status
        //and update a status bar. In addition, you can, for example,
        //keep a list of active print operations, or provide a progress dialog.
        m_refPrintFormOperation->signal_status_changed().connect(sigc::mem_fun(*this,
                        &ExampleWindow::on_printoperation_status_changed));
      }
    }

    void ExampleWindow::print_or_preview(Gtk::PrintOperation::Action print_action)
    {
      //Create a new PrintOperation with our PageSetup and PrintSettings:
      //(We use our derived PrintOperation class)
      m_refPrintFormOperation = PrintFormOperation::create();

      m_refPrintFormOperation->set_name(m_NameEntry.get_text() + " " + m_SurnameEntry.get_text());
      m_refPrintFormOperation->set_comments(m_refTextBuffer->get_text(false /*Don't include hidden*/));
      // In the printing/advanced example, the font will be set through a custom tab
      // in the print dialog.

      m_refPrintFormOperation->set_track_print_status();
      m_refPrintFormOperation->set_default_page_setup(m_refPageSetup);
      m_refPrintFormOperation->set_print_settings(m_refSettings);

      m_refPrintFormOperation->signal_done().connect(sigc::mem_fun(*this,
                      &ExampleWindow::on_printoperation_done));
      try
      {
        m_refPrintFormOperation->run(print_action /* print or preview */, *this);
      }
      catch (const Gtk::PrintError& ex)
      {
        //See documentation for exact Gtk::PrintError error codes.
        std::cerr << "An error occurred while trying to run a print operation:"
            << ex.what() << std::endl;
      }
    }

    void ExampleWindow::on_menu_file_new()
    {
      //Clear entries and textview:
      m_NameEntry.set_text("");
      m_SurnameEntry.set_text("");
      m_refTextBuffer->set_text("");
      m_TextView.set_buffer(m_refTextBuffer);
    }

    void ExampleWindow::on_menu_file_page_setup()
    {
      //Show the page setup dialog, asking it to start with the existing settings:
      auto new_page_setup =
          Gtk::run_page_setup_dialog(*this, m_refPageSetup, m_refSettings);

      //Save the chosen page setup dialog for use when printing, previewing, or
      //showing the page setup dialog again:
      m_refPageSetup = new_page_setup;
    }

    void ExampleWindow::on_menu_file_print_preview()
    {
      print_or_preview(Gtk::PrintOperation::Action::PREVIEW);
    }

    void ExampleWindow::on_menu_file_print()
    {
      print_or_preview(Gtk::PrintOperation::Action::PRINT_DIALOG);
    }

    void ExampleWindow::on_menu_file_quit()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv, app);
    }

File: `printformoperation.cc` (For use with gtkmm 4)

    #include "printformoperation.h"

    PrintFormOperation::PrintFormOperation()
    {
    }

    PrintFormOperation::~PrintFormOperation()
    {
    }

    Glib::RefPtr<PrintFormOperation> PrintFormOperation::create()
    {
      return Glib::make_refptr_for_instance<PrintFormOperation>(new PrintFormOperation());
    }

    void PrintFormOperation::on_begin_print(
            const Glib::RefPtr<Gtk::PrintContext>& print_context)
    {
      //Create and set up a Pango layout for PrintData based on the passed
      //PrintContext: We then use this to calculate the number of pages needed, and
      //the lines that are on each page.
      m_refLayout = print_context->create_pango_layout();

      Pango::FontDescription font_desc("sans 12");
      m_refLayout->set_font_description(font_desc);

      const double width = print_context->get_width();
      const double height = print_context->get_height();

      m_refLayout->set_width(static_cast<int>(width * Pango::SCALE));

      //Set and mark up the text to print:
      Glib::ustring marked_up_form_text;
      marked_up_form_text += "<b>Name</b>: " + m_Name + "\n\n";
      marked_up_form_text += "<b>Comments</b>: " + m_Comments;

      m_refLayout->set_markup(marked_up_form_text);

      //Set the number of pages to print by determining the line numbers
      //where page breaks occur:
      const int line_count = m_refLayout->get_line_count();

      Glib::RefPtr<Pango::LayoutLine> layout_line;
      double page_height = 0;

      for (int line = 0; line < line_count; ++line)
      {
        Pango::Rectangle ink_rect, logical_rect;

        layout_line = m_refLayout->get_line(line);
        layout_line->get_extents(ink_rect, logical_rect);

        const double line_height = logical_rect.get_height() / 1024.0;

        if (page_height + line_height > height)
        {
          m_PageBreaks.push_back(line);
          page_height = 0;
        }

        page_height += line_height;
      }

      set_n_pages(m_PageBreaks.size() + 1);
    }

    void PrintFormOperation::on_draw_page(
            const Glib::RefPtr<Gtk::PrintContext>& print_context, int page_nr)
    {
      //Decide which lines we need to print in order to print the specified page:
      int start_page_line = 0;
      int end_page_line = 0;

      if(page_nr == 0)
      {
        start_page_line = 0;
      }
      else
      {
        start_page_line = m_PageBreaks[page_nr - 1];
      }

      if(page_nr < static_cast<int>(m_PageBreaks.size()))
      {
        end_page_line = m_PageBreaks[page_nr];
      }
      else
      {
        end_page_line = m_refLayout->get_line_count();
      }

      //Get a Cairo Context, which is used as a drawing board:
      Cairo::RefPtr<Cairo::Context> cairo_ctx = print_context->get_cairo_context();

      //We'll use black letters:
      cairo_ctx->set_source_rgb(0, 0, 0);

      //Render Pango LayoutLines over the Cairo context:
      auto iter = m_refLayout->get_iter();

      double start_pos = 0;
      int line_index = 0;

      do
      {
        if(line_index >= start_page_line)
        {
          auto layout_line = iter.get_line();
          auto logical_rect = iter.get_line_logical_extents();
          int baseline = iter.get_baseline();

          if (line_index == start_page_line)
          {
            start_pos = logical_rect.get_y() / 1024.0;
          }

          cairo_ctx->move_to(logical_rect.get_x() / 1024.0,
            baseline / 1024.0 - start_pos);

          layout_line->show_in_cairo_context(cairo_ctx);
        }

        line_index++;
      }
      while(line_index < end_page_line && iter.next_line());
    }

#### Прозорче за штампање

Следећи пример показује како се штампа неки унос из корисничког прочеља коришћењем класе PrintDialog. Корисничко прочеље је слично претходном примеру.

Штампање — PrintDialog

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class PrintFormDialog;

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow(const Glib::RefPtr<Gtk::Application>& app);
      ~ExampleWindow() override;

    protected:
      void build_main_menu(const Glib::RefPtr<Gtk::Application>& app);

      // Action signal handlers:
      void on_menu_file_new();
      void on_menu_file_print_setup();
      void on_menu_file_print();
      void on_menu_file_quit();

      // Printing-related objects:
      Glib::RefPtr<PrintFormDialog> m_refPrintFormDialog;

      // Child widgets:
      Gtk::Box m_VBox;
      Gtk::Grid m_Grid;

      Gtk::Label m_NameLabel;
      Gtk::Entry m_NameEntry;

      Gtk::Label m_SurnameLabel;
      Gtk::Entry m_SurnameEntry;

      Gtk::Label m_CommentsLabel;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::TextView m_TextView;

      Glib::RefPtr<Gtk::TextBuffer> m_refTextBuffer;

      Glib::RefPtr<Gtk::Builder> m_refBuilder;
    };
    #endif //GTKMM_EXAMPLEWINDOW_H

File: `printformdialog.h` (For use with gtkmm 4)

    #ifndef GTKMM_PRINT_FORM_DIALOG_H
    #define GTKMM_PRINT_FORM_DIALOG_H

    #include <gtkmm.h>
    #include <pangomm.h>
    #include <vector>

    #define HAS_PRINT_DIALOG GTKMM_CHECK_VERSION(4,13,1)

    #if HAS_PRINT_DIALOG
    // We derive our own class from PrintDialog,
    // so we can put the actual print implementation here.
    class PrintFormDialog : public Gtk::PrintDialog
    {
    public:
      static Glib::RefPtr<PrintFormDialog> create();
      virtual ~PrintFormDialog();

      void set_name(const Glib::ustring& name) { m_Name = name; }
      void set_comments(const Glib::ustring& comments) { m_Comments = comments; }
      void do_setup(Gtk::Window* parent);
      void do_print(Gtk::Window* parent);

    protected:
      PrintFormDialog();

      // PrintDialog callback slots:
      void on_setup_finish(const Glib::RefPtr<Gio::AsyncResult>& result);
      void on_print_finish(const Glib::RefPtr<Gio::AsyncResult>& result);
      Cairo::ErrorStatus write_func(const unsigned char* data, unsigned int length,
        Glib::RefPtr<Gio::OutputStream>& stream);
      void do_printing(const Cairo::RefPtr<Cairo::Context>& cairo_ctx);

      Glib::ustring m_Name;
      Glib::ustring m_Comments;
      Glib::RefPtr<Pango::Layout> m_refLayout;
      Glib::RefPtr<Gtk::PrintSetup> m_refPrintSetup;
    };
    #endif // HAS_PRINT_DIALOG

    #endif // GTKMM_PRINT_FORM_DIALOG_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include "printformdialog.h"
    #include <iostream>

    ExampleWindow::ExampleWindow(const Glib::RefPtr<Gtk::Application>& app)
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_NameLabel("Name"),
      m_SurnameLabel("Surname"),
      m_CommentsLabel("Comments")
    {
      set_title("gtkmm PrintDialog Example");
      set_default_size(400, 300);

    #if HAS_PRINT_DIALOG
      // Create a PrintDialog. (We use our derived PrintDialog class.)
      m_refPrintFormDialog = PrintFormDialog::create();
    #endif // HAS_PRINT_DIALOG

      set_child(m_VBox);

      build_main_menu(app);

      m_VBox.append(m_Grid);

      // Arrange the widgets inside the grid:
      m_Grid.set_expand(true);
      m_Grid.set_row_spacing(5);
      m_Grid.set_column_spacing(5);
      m_Grid.attach(m_NameLabel, 0, 0);
      m_Grid.attach(m_NameEntry, 1, 0);

      m_Grid.attach(m_SurnameLabel, 0, 1);
      m_Grid.attach(m_SurnameEntry, 1, 1);

      // Add the TextView, inside a ScrolledWindow:
      m_ScrolledWindow.set_child(m_TextView);

      // Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);

      m_Grid.attach(m_CommentsLabel, 0, 2);
      m_Grid.attach(m_ScrolledWindow, 1, 2);
      m_ScrolledWindow.set_expand(true);

      m_refTextBuffer = Gtk::TextBuffer::create();
      m_TextView.set_buffer(m_refTextBuffer);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::build_main_menu(const Glib::RefPtr<Gtk::Application>& app)
    {
      // Create actions for menus and toolbars:
      auto refActionGroup = Gio::SimpleActionGroup::create();

      // File menu:
      refActionGroup->add_action("new",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_new));

      refActionGroup->add_action("printsetup",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_print_setup));

      refActionGroup->add_action("print",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_print));

      refActionGroup->add_action("quit",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_quit));

      insert_action_group("example", refActionGroup);

      // When the menubar is a child of a Gtk::Window, keyboard accelerators are not
      // automatically fetched from the Gio::Menu.
      // See the examples/book/menus/main_menu example for an alternative way of
      // adding the menubar when using Gtk::ApplicationWindow.
      app->set_accel_for_action("example.new", "<Primary>n");
      app->set_accel_for_action("example.print", "<Primary>p");
      app->set_accel_for_action("example.quit", "<Primary>q");

      m_refBuilder = Gtk::Builder::create();

      // Layout the actions in a menubar:
      Glib::ustring ui_menu_info =
        "<interface>"
        "  <menu id='menu-example'>"
        "    <submenu>"
        "      <attribute name='label' translatable='yes'>_File</attribute>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_New</attribute>"
        "          <attribute name='action'>example.new</attribute>"
        "        </item>"
        "      </section>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>Print _Setup...</attribute>"
        "          <attribute name='action'>example.printsetup</attribute>"
        "        </item>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_Print...</attribute>"
        "          <attribute name='action'>example.print</attribute>"
        "        </item>"
        "      </section>"
        "      <section>"
        "        <item>"
        "          <attribute name='label' translatable='yes'>_Quit</attribute>"
        "          <attribute name='action'>example.quit</attribute>"
        "        </item>"
        "      </section>"
        "    </submenu>"
        "  </menu>"
        "</interface>";

      try
      {
        m_refBuilder->add_from_string(ui_menu_info);
      }
      catch(const Glib::Error& ex)
      {
        std::cerr << "building menus failed: " << ex.what();
      }

      // Layout the actions in a toolbar:
      Glib::ustring ui_toolbar_info =
        "<!-- Generated with glade 3.18.3 and then changed manually -->"
        "<interface>"
          "<object class='GtkBox' id='toolbar'>"
            "<property name='can_focus'>False</property>"
            "<property name='spacing'>3</property>"
            "<child>"
              "<object class='GtkButton' id='toolbutton_new'>"
                "<property name='can_focus'>False</property>"
                "<property name='tooltip_text' translatable='yes'>New</property>"
                "<property name='action_name'>example.new</property>"
                "<property name='icon_name'>document-new</property>"
                "<property name='hexpand'>False</property>"
                "<property name='vexpand'>False</property>"
              "</object>"
            "</child>"
            "<child>"
              "<object class='GtkButton' id='toolbutton_print'>"
                "<property name='can_focus'>False</property>"
                "<property name='tooltip_text' translatable='yes'>Print</property>"
                "<property name='action_name'>example.print</property>"
                "<property name='icon_name'>document-print</property>"
                "<property name='hexpand'>False</property>"
                "<property name='vexpand'>False</property>"
              "</object>"
            "</child>"
            "<child>"
              "<object class='GtkSeparator' id='separator1'>"
                "<property name='can_focus'>False</property>"
                "<property name='hexpand'>False</property>"
                "<property name='vexpand'>False</property>"
              "</object>"
            "</child>"
            "<child>"
              "<object class='GtkButton' id='toolbutton_quit'>"
                "<property name='can_focus'>False</property>"
                "<property name='tooltip_text' translatable='yes'>Quit</property>"
                "<property name='action_name'>example.quit</property>"
                "<property name='icon_name'>application-exit</property>"
                "<property name='hexpand'>False</property>"
                "<property name='vexpand'>False</property>"
              "</object>"
            "</child>"
          "</object>"
        "</interface>";

      try
      {
        m_refBuilder->add_from_string(ui_toolbar_info);
      }
      catch(const Glib::Error& ex)
      {
        std::cerr << "building toolbar failed: " << ex.what();
      }

      // Get the menubar and add it to a container widget:
      auto object = m_refBuilder->get_object("menu-example");
      auto gmenu = std::dynamic_pointer_cast<Gio::Menu>(object);
      if (!gmenu)
        g_warning("GMenu not found");
      else
      {
        auto pMenuBar = Gtk::make_managed<Gtk::PopoverMenuBar>(gmenu);

        // Add the PopoverMenuBar to the window:
        m_VBox.append(*pMenuBar);
      }

      // Get the toolbar and add it to a container widget:
      auto toolbar = m_refBuilder->get_widget<Gtk::Box>("toolbar");
      if (!toolbar)
        g_warning("toolbar not found");
      else
        m_VBox.append(*toolbar);
    }

    void ExampleWindow::on_menu_file_new()
    {
      // Clear entries and textview:
      m_NameEntry.set_text("");
      m_SurnameEntry.set_text("");
      m_refTextBuffer->set_text("");
      m_TextView.set_buffer(m_refTextBuffer);
    }

    void ExampleWindow::on_menu_file_print_setup()
    {
    #if HAS_PRINT_DIALOG
      m_refPrintFormDialog->do_setup(this);
    #else
      std::cout << "Gtk::PrintDialog is not available.\n";
    #endif // HAS_PRINT_DIALOG
    }

    void ExampleWindow::on_menu_file_print()
    {
    #if HAS_PRINT_DIALOG
      m_refPrintFormDialog->set_name(m_NameEntry.get_text() + " " + m_SurnameEntry.get_text());
      m_refPrintFormDialog->set_comments(m_refTextBuffer->get_text(false /*Don't include hidden*/));
      m_refPrintFormDialog->do_print(this);
    #else
      std::cout << "Gtk::PrintDialog is not available.\n";
    #endif // HAS_PRINT_DIALOG
    }

    void ExampleWindow::on_menu_file_quit()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv, app);
    }

File: `printformdialog.cc` (For use with gtkmm 4)

    #include "printformdialog.h"
    #include <iostream>

    #if HAS_PRINT_DIALOG
    PrintFormDialog::PrintFormDialog()
    {
      set_page_setup(Gtk::PageSetup::create());
      set_print_settings(Gtk::PrintSettings::create());
    }

    PrintFormDialog::~PrintFormDialog()
    {
    }

    Glib::RefPtr<PrintFormDialog> PrintFormDialog::create()
    {
      return Glib::make_refptr_for_instance<PrintFormDialog>(new PrintFormDialog());
    }

    void PrintFormDialog::do_setup(Gtk::Window* parent)
    {
      set_title("Print setup");
      if (parent)
        setup(*parent, sigc::mem_fun(*this, &PrintFormDialog::on_setup_finish));
      else
        setup(sigc::mem_fun(*this, &PrintFormDialog::on_setup_finish));
    }

    void PrintFormDialog::do_print(Gtk::Window* parent)
    {
      set_title("Printing");
      if (parent)
        print(*parent, sigc::mem_fun(*this, &PrintFormDialog::on_print_finish));
      else
        print(sigc::mem_fun(*this, &PrintFormDialog::on_print_finish));
    }

    void PrintFormDialog::on_setup_finish(const Glib::RefPtr<Gio::AsyncResult>& result)
    {
      try
      {
        m_refPrintSetup = setup_finish(result);
        if (m_refPrintSetup)
        {
          set_page_setup(m_refPrintSetup->get_page_setup());
          set_print_settings(m_refPrintSetup->get_print_settings());
        }
      }
      catch (const Gtk::DialogError& err)
      {
        // Can be thrown by setup_finish(result).
        std::cout << "No setup selected. " << err.what() << std::endl;
      }
      catch (const Glib::Error& err)
      {
        std::cout << "Unexpected exception. " << err.what() << std::endl;
      }
    }

    void PrintFormDialog::on_print_finish(const Glib::RefPtr<Gio::AsyncResult>& result)
    {
      Glib::RefPtr<Gio::OutputStream> outputStream;
      try
      {
        outputStream = print_finish(result);
      }
      catch (const Gtk::DialogError& err)
      {
        // Can be thrown by print_finish(result).
        std::cout << "No output stream. " << err.what() << std::endl;
        return;
      }
      catch (const Glib::Error& err)
      {
        std::cout << "Unexpected exception. " << err.what() << std::endl;
        return;
      }
      const double paper_width = get_page_setup()->get_paper_width(Gtk::Unit::POINTS);
      const double paper_height = get_page_setup()->get_paper_height(Gtk::Unit::POINTS);
      auto cairo_surface = Cairo::PdfSurface::create_for_stream(
        sigc::bind(sigc::mem_fun(*this, &PrintFormDialog::write_func), outputStream),
        paper_width, paper_height);
      auto cairo_ctx = Cairo::Context::create(cairo_surface);

      do_printing(cairo_ctx);

      // cairo_surface->finish() calls write_func(),
      // which must be done before the stream is closed.
      cairo_surface->finish();
      try
      {
        outputStream->close();
      }
      catch (const Glib::Error& err)
      {
        std::cout << "Error closing stream. " << err.what() << std::endl;
      }
    }

    Cairo::ErrorStatus PrintFormDialog::write_func(const unsigned char* data,
      unsigned int length, Glib::RefPtr<Gio::OutputStream>& stream)
    {
      try
      {
        gsize bytes_written = 0;
        stream->write_all(data, length, bytes_written);
      }
      catch (const Glib::Error& err)
      {
        std::cout << "Error writing to stream. " << err.what() << std::endl;
        return CAIRO_STATUS_WRITE_ERROR;
      }
      return CAIRO_STATUS_SUCCESS;
    }
    void PrintFormDialog::do_printing(const Cairo::RefPtr<Cairo::Context>& cairo_ctx)
    {
      m_refLayout = Pango::Layout::create(cairo_ctx);
      m_refLayout->set_font_description(Pango::FontDescription("sans 12"));

      const auto page_setup = get_page_setup();
      const double page_width = page_setup->get_page_width(Gtk::Unit::POINTS);
      const double page_height = page_setup->get_page_height(Gtk::Unit::POINTS);
      const double left_margin = page_setup->get_left_margin(Gtk::Unit::POINTS);
      const double top_margin = page_setup->get_top_margin(Gtk::Unit::POINTS);
      m_refLayout->set_width(static_cast<int>(page_width * Pango::SCALE));
      m_refLayout->set_wrap(Pango::WrapMode::WORD_CHAR);
      cairo_ctx->translate(left_margin, top_margin);

      // Set and mark up the text to print:
      Glib::ustring marked_up_form_text =
        "<b>Name</b>: " + m_Name + "\n\n" +
        "<b>Comments</b>: " + m_Comments;
      m_refLayout->set_markup(marked_up_form_text);

      // We'll use black letters:
      cairo_ctx->set_source_rgb(0, 0, 0);

      // Render Pango LayoutLines over the Cairo context:
      const double inverse_pango_scale = 1.0 / Pango::SCALE;
      double page_y = 0.0;
      auto iter = m_refLayout->get_iter();
      double start_y_pos = iter.get_line_logical_extents().get_y() * inverse_pango_scale;
      do
      {
        const auto layout_line = iter.get_line();
        const auto logical_rect = iter.get_line_logical_extents();
        const double line_height = logical_rect.get_height() * inverse_pango_scale;

        if (page_y + line_height > page_height)
        {
          // Start a new page.
          cairo_ctx->show_page();
          page_y = 0.0;
          start_y_pos = logical_rect.get_y() * inverse_pango_scale;
        }
        page_y += line_height;

        cairo_ctx->move_to(logical_rect.get_x() * inverse_pango_scale,
          iter.get_baseline() * inverse_pango_scale - start_y_pos);
        layout_line->show_in_cairo_context(cairo_ctx);
      }
      while (iter.next_line());

      cairo_ctx->show_page();
    }
    #endif // HAS_PRINT_DIALOG

## Скоро коришћени документи

gtkmm пружа лак начин за управљање недавно коришћеним документима. Ова функционалност је примењена у класи Gtk::RecentManager.

Свака ставка на списку недавно коришћених датотека је идентификована својим УРИ-јем и може имати придружене метаподатке. Метаподаци се могу користити за одређивање начина на који датотека треба да буде приказана, опис датотеке, њену миме врсту, који програм ју је регистровао, да ли је приватна за програм који ју је регистровао и неколико других ствари.

### Управник недавних ставки

RecentManager делује као база података недавно коришћених датотека. Ову класу користите за регистровање нових датотека, уклањање датотека са списка или за тражење недавно коришћених датотека. Постоји један списак недавно коришћених датотека по кориснику.

Можете направити нови RecentManager, али ћете највероватније само желети да користите подразумевани. Референцу на подразумевани RecentManager можете добити помоћу get_default().

#### Додавање ставки на списак недавних датотека

Да бисте додали нову датотеку на списак недавних докумената, у најједноставнијем случају, потребно је само да наведете УРИ. На пример:

    auto recent_manager = Gtk::RecentManager::get_default();
    recent_manager->add_item(uri);

Ако желите да региструјете датотеку са метаподацима, можете проследити параметар RecentManager::Data методу add_item(). Метаподаци који се могу поставити на одређену ставку датотеке су следећи:

- `app_exec`: Линија наредбе која ће се користити за покретање овог ресурса. Ова ниска може да садржи излазне знакове „f“ и „u“ који ће бити замењени путањом датотеке ресурса и УРИ-јем, респективно

- `app_name`: Назив програма који је регистровао ресурс

- `description`: Кратак опис ресурса као УТФ-8 кодирана ниска

- `display_name`: Назив ресурса који ће се користити за приказ као УТФ-8 кодирана ниска

- `groups`: Списак група придружених овој ставци. Групе су у суштини произвољне ниске повезане са одређеним ресурсом. Могу се посматрати као „категорије“ (као што су „е-пошта“, „графика“, итд.) или ознаке за ресурс.

- `is_private`: Да ли овај ресурс треба да буде видљив само програмима који су га регистровали или не

- `mime_type`: MIME врста ресурса

Поред додавања ставки на списак, можете такође потражити ставке са списка и изменити их или уклонити.

#### Тражење ставки на списку недавних датотека

За тражење недавно коришћених датотека, RecentManager обезбеђује неколико функција. Да бисте потражили одређену ставку према њеној УРИ адреси, можете користити функцију lookup_item(), која ће вратити класу RecentInfo. Ако наведена УРИ адреса не постоји на списку недавних датотека, lookup_item() избацује изузетак RecentManagerError. На пример:

    Glib::RefPtr<Gtk::RecentInfo> info;
    try
    {
      info = recent_manager->lookup_item(uri);
    }
    catch(const Gtk::RecentManagerError& ex)
    {
      std::cerr << "RecentManagerError: " << ex.what() << std::endl;
    }
    if (info)
    {
      // ставка је пронађена
    }

Објекат RecentInfo је у суштини објекат који садржи све метаподатке о једној недавно коришћеној датотеци. Овај објекат можете користити за тражење било ког од својстава наведених изнад.

Ако не желите да тражите одређену УРИ адресу, већ уместо тога желите да добијете списак свих недавно коришћених ставки, RecentManager обезбеђује функцију get_items(). Повратна вредност ове функције је std::vector свих недавно коришћених датотека. Следећи код показује како можете добити списак недавно коришћених датотека:

    auto info_list = recent_manager->get_items();

Максимална старост ставки на списку недавно коришћених датотека може се поставити помоћу Gtk::Settings::property_gtk_recent_files_max_age(). Подразумевана вредност: 30 дана.

#### Мењање списка недавних датотека

Може се десити да требате да измените списак недавних датотека. На пример, ако је датотека премештена или преименована, можда ћете морати да ажурирате место датотеке на списку недавних датотека како она не би показивала на погрешно место. Место ставке можете ажурирати коришћењем move_item().

Поред промене УРИ адресе датотеке, такође можете уклонити ставке са списка, било једну по једну или чишћењем свих одједном. Прво се постиже помоћу remove_item(), а друго помоћу purge_items().

Функције move_item(), remove_item() и purge_items() немају утицаја на стварне датотеке на које УРИ адресе упућују, оне само мењају списак недавних датотека.

### Бирач датотека и прозорче за датотеке

FileChooser је интерфејс који могу применити елементи који приказују списак датотека. gtkmm обезбеђује три уграђене имплементације за бирање недавних датотека или других датотека: FileChooserWidget, FileChooserDialog и FileChooserNative.

FileChooserWidget је једноставан елемент за приказивање списка недавно коришћених датотека или других датотека. FileChooserWidget је основни градивни блок за FileChooserDialog, али га можете уградити у свој кориснички интерфејс ако желите.

FileChooser и класе које га имплементирају су застареле од верзије gtkmm 4.10. Замењене су класом FileDialog, која је доступна од верзије gtkmm 4.10.

#### Једноставан пример за FileDialog

Испод је приказан једноставан пример како се користи класа FileDialog у програму. Овај једноставан програм има траку изборника са ставком изборника File Dialog. Када изаберете ову ставку изборника, појављује се прозорче које приказује списак датотека. Ако изаберете Recent у бочној површи, приказује се списак недавно коришћених датотека.

Ако први пут користите програм који користи радни оквир недавних датотека, прозорче може бити празно у почетку. У супротном, требало би да прикаже списак недавно коришћених докумената које су регистровале друге апликације.

Након бирања ставке изборника File Dialog и ставке бочне површи Recent, требало би да видите нешто слично следећем прозору.

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow(const Glib::RefPtr<Gtk::Application>& app);
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_menu_file_files_dialog();
      void on_menu_file_quit();
      void on_menu_file_new();
      void on_dialog_finish(Glib::RefPtr<Gio::AsyncResult>& result);

      //Child widgets:
      Gtk::Box m_Box;

      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      Glib::RefPtr<Gio::SimpleActionGroup> m_refActionGroup;

      Glib::RefPtr<Gtk::RecentManager> m_refRecentManager;
      Glib::RefPtr<Gtk::FileDialog> m_refFileDialog;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow(const Glib::RefPtr<Gtk::Application>& app)
    : m_Box(Gtk::Orientation::VERTICAL),
      m_refRecentManager(Gtk::RecentManager::get_default())
    {
      set_title("Recent files example");
      set_default_size(300, 150);

      //We can put a PopoverMenuBar at the top of the box and other stuff below it.
      set_child(m_Box);

      //Create actions for menus and toolbars:
      m_refActionGroup = Gio::SimpleActionGroup::create();

      //File menu:
      m_refActionGroup->add_action("new",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_new));

      //A menu item to open the file dialog:
      m_refActionGroup->add_action("files-dialog",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_files_dialog));

      m_refActionGroup->add_action("quit",
        sigc::mem_fun(*this, &ExampleWindow::on_menu_file_quit) );

      insert_action_group("example", m_refActionGroup);

      m_refBuilder = Gtk::Builder::create();

      // When the menubar is a child of a Gtk::Window, keyboard accelerators are not
      // automatically fetched from the Gio::Menu.
      // See the examples/book/menus/main_menu example for an alternative way of
      // adding the menubar when using Gtk::ApplicationWindow.
      app->set_accel_for_action("example.new", "<Primary>n");
      app->set_accel_for_action("example.files-dialog", "<Primary>o");
      app->set_accel_for_action("example.quit", "<Primary>q");

      //Layout the actions in a menubar and a toolbar:
      const char* ui_info =
        "<interface>"
        "  <menu id='menubar'>"
        "    <submenu>"
        "      <attribute name='label' translatable='yes'>_File</attribute>"
        "      <item>"
        "        <attribute name='label' translatable='yes'>_New</attribute>"
        "        <attribute name='action'>example.new</attribute>"
        "        <attribute name='accel'>&lt;Primary&gt;n</attribute>"
        "      </item>"
        "      <item>"
        "        <attribute name='label' translatable='yes'>File _Dialog</attribute>"
        "        <attribute name='action'>example.files-dialog</attribute>"
        "        <attribute name='accel'>&lt;Primary&gt;o</attribute>"
        "      </item>"
        "      <item>"
        "        <attribute name='label' translatable='yes'>_Quit</attribute>"
        "        <attribute name='action'>example.quit</attribute>"
        "        <attribute name='accel'>&lt;Primary&gt;q</attribute>"
        "      </item>"
        "    </submenu>"
        "  </menu>"
        "  <object class='GtkBox' id='toolbar'>"
        "    <property name='can_focus'>False</property>"
        "    <child>"
        "      <object class='GtkButton' id='toolbutton_new'>"
        "        <property name='can_focus'>False</property>"
        "        <property name='tooltip_text' translatable='yes'>New</property>"
        "        <property name='action_name'>example.new</property>"
        "        <property name='icon_name'>document-new</property>"
        "        <property name='hexpand'>False</property>"
        "        <property name='vexpand'>False</property>"
        "      </object>"
        "    </child>"
        "    <child>"
        "      <object class='GtkButton' id='toolbutton_quit'>"
        "        <property name='can_focus'>False</property>"
        "        <property name='tooltip_text' translatable='yes'>Quit</property>"
        "        <property name='action_name'>example.quit</property>"
        "        <property name='icon_name'>application-exit</property>"
        "        <property name='hexpand'>False</property>"
        "        <property name='vexpand'>False</property>"
        "      </object>"
        "    </child>"
        "  </object>"
        "</interface>";

      try
      {
        m_refBuilder->add_from_string(ui_info);
      }
      catch(const Glib::Error& ex)
      {
        std::cerr << "building menubar and toolbar failed: " <<  ex.what();
      }

      //Get the menubar and toolbar widgets, and add them to a container widget:
      auto object = m_refBuilder->get_object("menubar");
      auto gmenu = std::dynamic_pointer_cast<Gio::Menu>(object);
      if (gmenu)
      {
        //Menubar:
        auto pMenubar = Gtk::make_managed<Gtk::PopoverMenuBar>(gmenu);
        m_Box.append(*pMenubar);
      }
      else
        g_warning("GMenu not found");

      auto pToolbar = m_refBuilder->get_widget<Gtk::Box>("toolbar");
      if (pToolbar)
        //Toolbar:
        m_Box.append(*pToolbar);
      else
        g_warning("toolbar not found");
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_menu_file_new()
    {
      std::cout << " New File" << std::endl;
    }

    void ExampleWindow::on_menu_file_quit()
    {
      close(); // Closes the main window to stop the app->make_window_and_run().
    }

    void ExampleWindow::on_menu_file_files_dialog()
    {
      if (!m_refFileDialog)
      {
        m_refFileDialog = Gtk::FileDialog::create();
        m_refFileDialog->set_modal(true);
      }
      m_refFileDialog->open(*this, sigc::mem_fun(*this, &ExampleWindow::on_dialog_finish));
    }

    void ExampleWindow::on_dialog_finish(Glib::RefPtr<Gio::AsyncResult>& result)
    {
      Glib::RefPtr<Gio::File> file;
      try
      {
        file = m_refFileDialog->open_finish(result);
      }
      catch (const Gtk::DialogError& err)
      {
        std::cout << "No file selected, " << err.what() << std::endl;
        return;
      }

      auto selected_uri = file->get_uri();
      std::cout << "URI selected = " << selected_uri << std::endl;
      std::cout << (m_refRecentManager->has_item(selected_uri) ? "A" : "Not a")
        << " recently used file" << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv, app);
    }

Конструктор за ExampleWindow прави изборник и траку алата користећи Builder (погледајте за више информација). Затим додаје изборник и траку алата у прозор.

#### Филтрирање датотека

За било коју од класа FileChooser, ако не желите да прикажете све ставке на списку датотека, можете филтрирати списак да приказује само оне које желите. Списак можете филтрирати уз помоћ класе FileFilter. Ова класа вам омогућава да филтрирате датотеке према њиховом имену (add_pattern()) или њиховој миме врсти (add_mime_type()).

Након што сте направили и подесили филтер да се поклапа само са ставкама које желите, можете применити филтер на виџет бирача помоћу функције FileChooser::add_filter().

## Догађаји тастатуре и миша

Сигнали догађаја се разликују на неке начине од осталих сигнала. Ове разлике су описане у одељку Сигнали догађаја у додатку. Овде ћемо користити догађаје тастатуре и догађаје миша да покажемо како се догађаји могу користити у програму.

### Преглед

Кад год притиснете или отпустите тастер, емитује се догађај. Можете додати контролер догађаја и повезати руковаоца сигналом за обраду таквих догађаја.

Руковалац сигналом догађаја ће примити аргументе који зависе од врсте догађаја. За догађаје притиска тастера аргументи су (guint `keyval`, guint `keycode`, Gdk::ModifierType `state`). Као што је описано у додатку, руковалац сигналом догађаја притиска тастера враћа bool вредност, како би указао да је сигнал у потпуности обрађен (`true`) или да дозволи простирање догађаја (`false`).

Да бисте одредили који тастер је притиснут или отпуштен, прочитате вредност аргумента `keyval` и упоредите је са константом у датотеци заглавља `<gdk/gdkkeysyms.h>`. Стања тастера модификатора (shift, ctrl, итд.) су доступна као битовске заставице у `state`.

Ево једноставног примера:

    bool MyClass::on_key_pressed(guint keyval, guint, Gdk::ModifierType state)
    {
      if (keyval == GDK_KEY_1 &&
        (state & (Gdk::ModifierType::SHIFT_MASK | Gdk::ModifierType::CONTROL_MASK |
         Gdk::ModifierType::ALT_MASK)) == Gdk::ModifierType::ALT_MASK)
      {
        handle_alt_press();
        return true;
      }
      return false;
    }

    // in MyClass constructor
    auto controller = Gtk::EventControllerKey::create();
    controller->signal_key_pressed().connect(
      sigc::mem_fun(*this, &MyClass::on_key_pressed), false);
    add_controller(controller);

#### Пример

У овом примеру постоје три пречице на тастатури: Alt+1 бира прво радио дугме, Alt+2 бира друго, а тастер Esc затвара прозор.

Догађаји тастатуре — једноставно

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:

      ExampleWindow();
      virtual ~ExampleWindow();

    private:
      // Signal handler:
      bool on_window_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state);

      Gtk::Box m_container;
      Gtk::CheckButton m_first;
      Gtk::CheckButton m_second;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    {
      set_title("Keyboard Events");
      m_container.set_margin(10);
      set_child(m_container);

      // Radio buttons:
      m_first.set_label("First");
      m_second.set_label("Second");

      m_second.set_group(m_first);
      m_first.set_active();

      // Main Container:
      m_container.set_orientation(Gtk::Orientation::HORIZONTAL);
      m_container.append(m_first);
      m_container.append(m_second);

      // Events.
      auto controller = Gtk::EventControllerKey::create();
      controller->signal_key_pressed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_window_key_pressed), false);
      add_controller(controller);
    }

    bool ExampleWindow::on_window_key_pressed(guint keyval, guint, Gdk::ModifierType state)
    {
      //Gdk::ModifierType::ALT_MASK -> the 'Alt' key(mask)
      //GDK_KEY_1 -> the '1' key
      //GDK_KEY_2 -> the '2' key

      //select the first radio button, when we press alt + 1
      if((keyval == GDK_KEY_1) &&
        (state & (Gdk::ModifierType::SHIFT_MASK | Gdk::ModifierType::CONTROL_MASK | Gdk::ModifierType::ALT_MASK)) == Gdk::ModifierType::ALT_MASK)
      {
        m_first.set_active();
        //returning true, cancels the propagation of the event
        return true;
      }
      else if((keyval == GDK_KEY_2) &&
        (state & (Gdk::ModifierType::SHIFT_MASK | Gdk::ModifierType::CONTROL_MASK | Gdk::ModifierType::ALT_MASK)) == Gdk::ModifierType::ALT_MASK)
      {
        //and the second radio button, when we press alt + 2
        m_second.set_active();
        return true;
      }
      else if(keyval == GDK_KEY_Escape)
      {
        //close the window, when the 'esc' key is pressed
        close();
        return true;
      }

      //the event has not been handled
      return false;
    }

    ExampleWindow::~ExampleWindow()
    {
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Простирање догађаја

Као што је описано у додатку, сигнали догађаја се простиру у 3 фазе:

1. Фаза хватања – извршава се од највишег нивоа наниже до елемента догађаја.

2. Циљна фаза – извршава се само на елементу догађаја.

3. Фаза мехура – извршава се од елемента догађаја навише до највишег нивоа.

Догађај тастатуре се прво шаље прозору највишег нивоа (Gtk::Window), где ће бити проверен за било које пречице тастатуре које могу бити постављене (тастери убрзавачи и мнемоници, који се користе за бирање ставки менија са тастатуре). Након овога (и под претпоставком да догађај није обрађен), он се простире наниже док не стигне до елемента који има фокус тастатуре. Догађај ће се затим простирати навише док не стигне до елемента највишег нивоа, или док не зауставите простирање враћањем `true` из руковаоца догађајем.

Имајте на уму да након отказивања догађаја, ниједна друга функција неће бити позвана (чак и ако је из истог елемента).

#### Пример

У овом примеру постоји 9 EventControllerKey објеката, по 3 у сваком од Gtk::Window, Gtk::Box и Gtk::Label. У сваком од елемената постоји један управљач догађајима за сваку фазу простирања.

Сврха овог примера је да прикаже кораке које догађај предузима када је емитован.

Када пишете у натпису, биће емитован догађај притиска тастера, који ће ићи прво, у фази хватања, до прозора највишег нивоа (Gtk::Window), затим наниже до његовог детета, кутије, па до детета кутије, натписа са фокусом тастатуре. У циљној фази догађај иде само до елемента са фокусом тастатуре (натпис). У фази мехура догађај иде прво до елемента са фокусом тастатуре (натпис), затим до његовог родитеља (кутија), па до родитеља кутије (прозор). Ако се догађај простире свим путем доле до натписа и затим горе до прозора без заустављања, текст који пишете ће се појавити у Label изнад Label у којем пишете.

Догађаји тастатуре — Простирање догађаја

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EVENT_PROPAGATION_H
    #define GTKMM_EVENT_PROPAGATION_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:

      ExampleWindow();
      ~ExampleWindow() override;

    private:
      // Signal handlers:
      bool label2_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state, const Glib::ustring& phase);
      bool box_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state, const Glib::ustring& phase);
      bool window_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state, const Glib::ustring& phase);

      bool m_first = true;
      Gtk::Box m_container;
      Gtk::Frame m_frame;
      Gtk::Label m_label1 {"A label"};
      Gtk::Label m_label2 {"Write here"};
      Gtk::CheckButton m_checkbutton_can_propagate_down {"Can propagate down in the capture phase"};
      Gtk::CheckButton m_checkbutton_can_propagate_up {"Can propagate up in the bubble phase"};
    };

    #endif //GTKMM_EVENT_PROPAGATION_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    {
      set_title("Event Propagation");
      m_container.set_margin(10);
      set_child(m_container);

      m_frame.set_child(m_label2);
      m_label2.set_selectable();
      m_checkbutton_can_propagate_down.set_active();
      m_checkbutton_can_propagate_up.set_active();

      // Main container
      m_container.set_orientation(Gtk::Orientation::VERTICAL);
      m_container.append(m_label1);
      m_container.append(m_frame);
      m_container.append(m_checkbutton_can_propagate_down);
      m_container.append(m_checkbutton_can_propagate_up);

      // Event controllers
      const bool after = false; // Run before or after the default signal handlers.

      // Called in the capture phase of the event handling.
      auto controller = Gtk::EventControllerKey::create();
      controller->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
      controller->signal_key_pressed().connect(
        sigc::bind(sigc::mem_fun(*this, &ExampleWindow::label2_key_pressed), "capture"), after);
      m_label2.add_controller(controller);

      controller = Gtk::EventControllerKey::create();
      controller->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
      controller->signal_key_pressed().connect(
        sigc::bind(sigc::mem_fun(*this, &ExampleWindow::box_key_pressed), "capture"), after);
      m_container.add_controller(controller);

      controller = Gtk::EventControllerKey::create();
      controller->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
      controller->signal_key_pressed().connect(
        sigc::bind(sigc::mem_fun(*this, &ExampleWindow::window_key_pressed), "capture"), after);
      add_controller(controller);

      // Called in the target phase of the event handling.
      controller = Gtk::EventControllerKey::create();
      controller->set_propagation_phase(Gtk::PropagationPhase::TARGET);
      controller->signal_key_pressed().connect(
        sigc::bind(sigc::mem_fun(*this, &ExampleWindow::label2_key_pressed), "target"), after);
      m_label2.add_controller(controller);

      controller = Gtk::EventControllerKey::create();
      controller->set_propagation_phase(Gtk::PropagationPhase::TARGET);
      controller->signal_key_pressed().connect(
        sigc::bind(sigc::mem_fun(*this, &ExampleWindow::box_key_pressed), "target"), after);
      m_container.add_controller(controller);

      controller = Gtk::EventControllerKey::create();
      controller->set_propagation_phase(Gtk::PropagationPhase::TARGET);
      controller->signal_key_pressed().connect(
        sigc::bind(sigc::mem_fun(*this, &ExampleWindow::window_key_pressed), "target"), after);
      add_controller(controller);

      // Called in the bubble phase of the event handling.
      // This is the default, if set_propagation_phase() is not called.
      controller = Gtk::EventControllerKey::create();
      controller->set_propagation_phase(Gtk::PropagationPhase::BUBBLE);
      controller->signal_key_pressed().connect(
        sigc::bind(sigc::mem_fun(*this, &ExampleWindow::label2_key_pressed), "bubble"), after);
      m_label2.add_controller(controller);

      controller = Gtk::EventControllerKey::create();
      controller->set_propagation_phase(Gtk::PropagationPhase::BUBBLE);
      controller->signal_key_pressed().connect(
        sigc::bind(sigc::mem_fun(*this, &ExampleWindow::box_key_pressed), "bubble"), after);
      m_container.add_controller(controller);

      controller = Gtk::EventControllerKey::create();
      controller->set_propagation_phase(Gtk::PropagationPhase::BUBBLE);
      controller->signal_key_pressed().connect(
        sigc::bind(sigc::mem_fun(*this, &ExampleWindow::window_key_pressed), "bubble"), after);
      add_controller(controller);
    }

    // By changing the return value we allow, or don't allow, the event to propagate to other elements.
    bool ExampleWindow::label2_key_pressed(guint keyval, guint, Gdk::ModifierType, const Glib::ustring& phase)
    {
      std::cout << "Label,  " << phase << " phase" << std::endl;

      if (phase == "bubble")
      {
        const gunichar unichar = gdk_keyval_to_unicode(keyval);
        if (unichar != 0)
        {
          if (m_first)
          {
            m_label2.set_label("");
            m_first = false;
          }
          if (unichar == '\b')
            m_label2.set_label("");
          else
          {
            const Glib::ustring newchar(1, unichar);
            m_label2.set_label(m_label2.get_label() + newchar);
          }
        }

        if (!m_checkbutton_can_propagate_up.get_active())
          return true; // Don't propagate
      }
      return false;
    }

    bool ExampleWindow::box_key_pressed(guint, guint, Gdk::ModifierType, const Glib::ustring& phase)
    {
      std::cout << "Box,    " << phase << " phase" << std::endl;

      // Let it propagate
      return false;
    }

    // This will set the second label's text in the first label every time a key is pressed.
    bool ExampleWindow::window_key_pressed(guint, guint, Gdk::ModifierType, const Glib::ustring& phase)
    {
      if (phase == "capture")
        std::cout << std::endl;
      std::cout << "Window, " << phase << " phase";

      // Checking if the second label is on focus, otherwise the label would get
      // changed by pressing keys on the window (when the label is not on focus),
      // even if m_checkbutton_can_propagate_up wasn't active.
      if (phase == "bubble" && m_label2.has_focus())
      {
        m_label1.set_label(m_label2.get_label());
        std::cout << ", " << m_label2.get_label();
      }
      std::cout << std::endl;

      if (phase == "capture" && !m_checkbutton_can_propagate_down.get_active())
        return true; // Don't propagate
      return false;
    }

    ExampleWindow::~ExampleWindow()
    {
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Догађаји миша

Догађаји миша су слични догађајима тастатуре. Постоје неке разлике, ипак.

- Користите Gtk::GestureClick уместо Gtk::EventControllerKey.

- Постоје и друге класе догађаја које се могу користити за руковање догађајима миша, на пример Gtk::EventControllerMotion и Gtk::EventControllerScroll.

- Многи руковаоци сигналима не враћају `bool`. Не можете зауставити те догађаје од простирања.

Класе догађаја које рукују догађајима миша су такође корисне за руковање догађајима са екрана на додир. Постоје такође многе подкласе Gtk::EventController које су само (или углавном) корисне за екране на додир. Примери: Gtk::GestureRotate, Gtk::GestureZoom, Gtk::GestureSwipe, Gtk::GestureLongPress.

Референца

#### Пример

Ово је проширена верзија једноставног примера догађаја тастатуре.

Догађаји тастатуре и миша

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    private:
      // Signal handlers:
      void on_window_mouse_pressed(int n_press, double x, double y);
      void on_first_button_mouse_pressed(int n_press, double x, double y);
      void on_second_button_mouse_pressed(int n_press, double x, double y);
      void on_third_button_mouse_pressed(int n_press, double x, double y);
      void on_third_button_clicked();
      void on_mouse_released(int n_press, double x, double y);
      void on_mouse_motion(double x, double y);
      bool on_mouse_scroll(double dx, double dy);
      bool on_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state);

      Gtk::Box m_box;
      Gtk::CheckButton m_first;
      Gtk::CheckButton m_second;
      Gtk::Button m_third;

      Glib::RefPtr<Gtk::GestureClick> m_window_click;
      Glib::RefPtr<Gtk::GestureClick> m_first_button_click;
      Glib::RefPtr<Gtk::GestureClick> m_second_button_click;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <map>
    #include <iostream>

    namespace
    {
    std::map<unsigned int, Glib::ustring> mouse_buttons
    {
      {0, "No button"},
      {GDK_BUTTON_PRIMARY, "Primary"},
      {GDK_BUTTON_MIDDLE, "Middle"},
      {GDK_BUTTON_SECONDARY, "Secondary"},
    };
    }

    ExampleWindow::ExampleWindow()
    : m_first("First"),
      m_second("Second"),
      m_third("Click me")
    {
      set_title("Keyboard and Mouse Events");
      m_box.set_margin(10);
      set_child(m_box);

      // Radio buttons
      m_second.set_group(m_first);
      m_first.set_active();

      // Main container
      m_box.set_orientation(Gtk::Orientation::HORIZONTAL);
      m_box.append(m_first);
      m_box.append(m_second);
      m_box.append(m_third);

      // Keyboard events
      auto key_controller = Gtk::EventControllerKey::create();
      key_controller->signal_key_pressed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_key_pressed), false);
      add_controller(key_controller);

      // Mouse events, window
      auto motion_controller = Gtk::EventControllerMotion::create();
      motion_controller->signal_motion().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_mouse_motion));
      add_controller(motion_controller);

      m_window_click = Gtk::GestureClick::create();
      m_window_click->set_button(0); // All mouse buttons
      m_window_click->signal_pressed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_window_mouse_pressed));
      add_controller(m_window_click);

      auto mouse_click = Gtk::GestureClick::create();
      mouse_click->set_button(GDK_BUTTON_PRIMARY);
      mouse_click->signal_released().connect(sigc::mem_fun(*this, &ExampleWindow::on_mouse_released));
      add_controller(mouse_click);

      auto mouse_scroll = Gtk::EventControllerScroll::create();
      mouse_scroll->set_flags(Gtk::EventControllerScroll::Flags::VERTICAL);
      // Handle mouse wheel rotations
      mouse_scroll->signal_scroll().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_mouse_scroll), true);
      add_controller(mouse_scroll);

      // Mouse events, buttons
      m_first_button_click = Gtk::GestureClick::create();
      m_first_button_click->set_button(GDK_BUTTON_PRIMARY);
      m_first_button_click->signal_pressed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_first_button_mouse_pressed));
      m_first.add_controller(m_first_button_click);

      m_second_button_click = Gtk::GestureClick::create();
      m_second_button_click->set_button(GDK_BUTTON_PRIMARY);
      m_second_button_click->signal_pressed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_second_button_mouse_pressed));
      m_second.add_controller(m_second_button_click);

      mouse_click = Gtk::GestureClick::create();
      // This mouse event must be handled in the capture phase.
      // The GtkButton C class handles the event in the capture phase and
      // does not let it propagate to the bubble phase where events are
      // handled by default.
      mouse_click->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
      mouse_click->set_button(GDK_BUTTON_PRIMARY);
      mouse_click->signal_pressed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_third_button_mouse_pressed));
      m_third.add_controller(mouse_click);
      m_third.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_third_button_clicked));
    }

    void ExampleWindow::on_window_mouse_pressed(int n_press, double x, double y)
    {
      const auto current_button = mouse_buttons[m_window_click->get_current_button()];
      std::cout << "Mouse pressed in window: " << current_button
        << ", " << n_press << ", " << x << ", " << y << std::endl;
    }

    void ExampleWindow::on_first_button_mouse_pressed(int n_press, double x, double y)
    {
      const auto current_button = mouse_buttons[m_first_button_click->get_current_button()];
      std::cout << "Mouse pressed in first button: " << current_button
        << ", " << n_press << ", " << x << ", " << y << std::endl;
    }

    void ExampleWindow::on_second_button_mouse_pressed(int n_press, double x, double y)
    {
      const auto current_button = mouse_buttons[m_second_button_click->get_current_button()];
      std::cout << "Mouse pressed in second button: " << current_button
        << ", " << n_press << ", " << x << ", " << y << std::endl;
    }

    void ExampleWindow::on_third_button_mouse_pressed(int n_press, double x, double y)
    {
      std::cout << "Mouse pressed in third button: "
        << n_press << ", " << x << ", " << y << std::endl;
    }

    void ExampleWindow::on_third_button_clicked()
    {
      std::cout << "Third button clicked" << std::endl;
    }

    void ExampleWindow::on_mouse_released(int n_press, double x, double y)
    {
      std::cout << "Mouse released: " << n_press << ", " << x << ", " << y << std::endl;
    }

    void ExampleWindow::on_mouse_motion(double x, double y)
    {
      std::cout << "Mouse motion: "  << x << ", " << y << std::endl;
    }

    bool ExampleWindow::on_mouse_scroll(double dx, double dy)
    {
      std::cout << "Mouse scroll: "  << dx << ", " << dy << std::endl;
      return true; // handled
    }

    bool ExampleWindow::on_key_pressed(guint keyval, guint, Gdk::ModifierType state)
    {
      // Gdk::ModifierType::ALT_MASK -> the 'Alt' key(mask)
      // GDK_KEY_1 -> the '1' key
      // GDK_KEY_2 -> the '2' key

      // Select the first radio button, when we press Alt + 1
      if ((keyval == GDK_KEY_1) &&
        (state & (Gdk::ModifierType::SHIFT_MASK | Gdk::ModifierType::CONTROL_MASK | Gdk::ModifierType::ALT_MASK)) == Gdk::ModifierType::ALT_MASK)
      {
        m_first.set_active();
        // Returning true, cancels the propagation of the event
        return true;
      }
      else if ((keyval == GDK_KEY_2) &&
        (state & (Gdk::ModifierType::SHIFT_MASK | Gdk::ModifierType::CONTROL_MASK | Gdk::ModifierType::ALT_MASK)) == Gdk::ModifierType::ALT_MASK)
      {
        // and the second radio button, when we press Alt + 2
        m_second.set_active();
        return true;
      }
      else if (keyval == GDK_KEY_Escape)
      {
        // Close the window, when the 'Esc' key is pressed
        close();
        return true;
      }

      // The event has not been handled
      return false;
    }

    ExampleWindow::~ExampleWindow()
    {
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

## Временски рокови, У/И и функције мировања

### Временски рокови

Можда се питате како да натерате gtkmm да ради користан посао док мирује. На срећу, имате неколико опција. Коришћењем следећих метода можете направити метод временског рока који ће бити позиван сваких неколико милисекунди.

    sigc::connection Glib::SignalTimeout::connect(const sigc::slot<bool()>& slot,
                                          unsigned int interval, int priority = Glib::PRIORITY_DEFAULT);

Први аргумент је slot за који желите да буде позван када дође до истека времена. Други аргумент је број милисекунди између позива тог метода. Добијате објекат sigc::connection који се може користити за деактивирање везе помоћу његовог метода disconnect():

    my_connection.disconnect();

Други начин уништавања везе је ваш руковалац сигналом. Он мора бити врсте sigc::slot<bool()>. Као што видите из дефиниције, ваш руковалац сигналом мора да врати вредност врсте `bool`. Дефиниција примера метода би могла изгледати овако:

    bool MyCallback() { std::cout << "Hello World!\
    " << std::endl; return true; }

Метод временског рока можете зауставити враћањем вредности `false` из вашег руковаоца сигналом. Стога, ако желите да се ваш метод позива више пута, треба да врати `true`.

Ево примера ове технике:

Изворни ко̂д

File: `timerexample.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_TIMEREXAMPLE_H
    #define GTKMM_EXAMPLE_TIMEREXAMPLE_H

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

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

    protected:
      // signal handlers
      void on_button_add_timer();
      void on_button_delete_timer();
      void on_button_quit();

      // This is the callback function the timeout will call
      bool on_timeout(int timer_number);

      // Member data:

      Gtk::Box m_Box;
      Gtk::Button m_ButtonAddTimer, m_ButtonDeleteTimer, m_ButtonQuit;

      // Keep track of the timers being added:
      int m_timer_number;

      // These two constants are initialized in the constructor's member initializer:
      const int count_value;
      const int timeout_value;

      // STL map for storing our connections
      std::map<int, sigc::connection> m_timers;

      // STL map for storing our timer values.
      // Each timer counts back from COUNT_VALUE to 0 and is removed when it reaches 0
      std::map<int, int> m_counters;
    };

    #endif // GTKMM_EXAMPLE_TIMEREXAMPLE_H

File: `main.cc` (For use with gtkmm 4)

    #include "timerexample.h"
    #include <gtkmm/application.h>

    int main (int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      return app->make_window_and_run<TimerExample>(argc, argv);
    }

File: `timerexample.cc` (For use with gtkmm 4)

    #include "timerexample.h"

    TimerExample::TimerExample() :
      m_Box(Gtk::Orientation::HORIZONTAL, 10),
      m_ButtonAddTimer("_Add", true),
      m_ButtonDeleteTimer("_Remove", true),
      m_ButtonQuit("_Quit", true),
      m_timer_number(0), // start numbering the timers at 0
      count_value(5), // each timer will count down 5 times before disconnecting
      timeout_value(1500) // 1500 ms = 1.5 seconds
    {
      m_Box.set_margin(10);
      set_child(m_Box);
      m_Box.append(m_ButtonAddTimer);
      m_Box.append(m_ButtonDeleteTimer);
      m_Box.append(m_ButtonQuit);
      m_ButtonAddTimer.set_expand();
      m_ButtonDeleteTimer.set_expand();
      m_ButtonQuit.set_expand();

      // Connect the three buttons:
      m_ButtonQuit.signal_clicked().connect(sigc::mem_fun(*this,
                  &TimerExample::on_button_quit));
      m_ButtonAddTimer.signal_clicked().connect(sigc::mem_fun(*this,
                  &TimerExample::on_button_add_timer));
      m_ButtonDeleteTimer.signal_clicked().connect(sigc::mem_fun(*this,
                  &TimerExample::on_button_delete_timer));
    }

    void TimerExample::on_button_quit()
    {
      close();
    }

    void TimerExample::on_button_add_timer()
    {
      // Creation of a new object prevents long lines and shows us a little
      // how slots work.  We have 0 parameters and bool as a return value
      // after calling sigc::bind.
      sigc::slot<bool()> my_slot = sigc::bind(sigc::mem_fun(*this,
                  &TimerExample::on_timeout), m_timer_number);

      // This is where we connect the slot to the Glib::signal_timeout()
      auto conn = Glib::signal_timeout().connect(my_slot,
              timeout_value);

      // Remember the connection:
      m_timers[m_timer_number] = conn;

      // Initialize timer count:
      m_counters[m_timer_number] = count_value + 1;

      // Print some info to the console for the user:
      std::cout << "added timeout " << m_timer_number++ << std::endl;
    }

    void TimerExample::on_button_delete_timer()
    {
      // any timers?
      if(m_timers.empty())
      {
        // no timers left
        std::cout << "Sorry, there are no timers left." << std::endl;
      }
      else
      {
        // get the number of the first timer
        int timer_number = m_timers.begin()->first;

        // Give some info to the user:
        std::cout << "manually disconnecting timer " << timer_number
            << std::endl;

        // Remove the entry in the counter values
        m_counters.erase(timer_number);

        // Diconnect the signal handler:
        m_timers[timer_number].disconnect();

        // Forget the connection:
        m_timers.erase(timer_number);
      }
    }

    bool TimerExample::on_timeout(int timer_number)
    {
      // Print the timer:
      std::cout << "This is timer " << timer_number;

      // decrement and check counter value
      if (--m_counters[timer_number] == 0)
      {
        std::cout << " being disconnected" <<  std::endl;

        // delete the counter entry in the STL MAP
        m_counters.erase(timer_number);

        // delete the connection entry in the STL MAP
        m_timers.erase(timer_number);

        // Note that we do not have to explicitly call disconnect() on the
        // connection since Gtk::Main does this for us when we return false.
        return false;
      }

      // Print the timer value
      std::cout << " - " << m_counters[timer_number] << "/"
          << count_value << std::endl;

     // Keep going (do not disconnect yet):
      return true;
    }

### Надгледање У/И

Згодна функција Глиба (једне од библиотека на којима се заснива gtkmm) је могућност да он уместо вас проверава податке на описнику датотеке. Ово је нарочито корисно за мрежне програме. За ово се користи следећи метод:

    sigc::connection Glib::SignalIO::connect(const sigc::slot<bool(Glib::IOCondition)>& slot,
                                     Glib::PollFD::fd_t fd, Glib::IOCondition condition,
                                     int priority = Glib::PRIORITY_DEFAULT);

Први аргумент је место (slot) за које желите да буде позвано када се наведени догађај (погледајте аргумент 3) деси на описнику датотеке који наведете помоћу другог аргумента. Аргумент три може бити један или више (користећи `|`) од:

- Glib::IOCondition::IO_IN — Позовите ваш метод када буду спремни подаци за читање на вашем описнику датотеке.

- Glib::IOCondition::IO_OUT — Позовите ваш метод када описник датотеке буде спреман за писање.

- Glib::IOCondition::IO_PRI – Позовите ваш метод када описник датотеке има хитне податке за читање.

- Glib::IOCondition::IO_ERR – Позовите ваш метод када се догоди грешка на описнику датотеке.

- Glib::IOCondition::IO_HUP – Позовите ваш метод при прекиду везе (веза је обично прекинута код спојки и утичница).

Повратна вредност је sigc::connection која се може користити за заустављање надгледања овог описника датотеке коришћењем његовог метода disconnect(). Руковалац сигнала slot треба бити објављен на следећи начин:

    bool input_callback(Glib::IOCondition condition);

где је condition као што је горе наведено. Као и обично, место се прави помоћу `sigc::mem_fun()` (за метод члана објекта) или `sigc::ptr_fun()` (за функцију). Ламбда израз се може користити ако вам није потребно аутоматско прекидање везе које `sigc::mem_fun()` обезбеђује када објекат изађе из опсега.

Следи мали пример. Да бисте користили пример, само га извршите из терминала; он не прави прозор. Направиће спојку под називом `testfifo` у текућем директоријуму. Затим покрените другу шкољку и извршите `echo "Hello" > testfifo`. Пример ће исписати сваки ред који унесете док не извршите `echo "Q" > testfifo`.

Изворни ко̂д

File: `main.cc` (For use with gtkmm 4)

    #include <gtkmm/application.h>
    #include <glibmm/main.h>
    #include <glibmm/iochannel.h>
    #include <fcntl.h>
    #include <iostream>

    #include <unistd.h> //The SUN Forte compiler puts F_OK here.

    //The SUN Forte compiler needs these for mkfifo:
    #include <sys/types.h>
    #include <sys/stat.h>

    Glib::RefPtr<Gtk::Application> app;

    int read_fd;
    Glib::RefPtr<Glib::IOChannel> iochannel;

    /*
      send to the fifo with:
      echo "Hello" > testfifo

      quit the program with:
      echo "Q" > testfifo
    */

    // this will be our signal handler for read operations
    // it will print out the message sent to the fifo
    // and quit the program if the message was 'Q'.
    bool MyCallback(Glib::IOCondition io_condition)
    {
      if ((io_condition & Glib::IOCondition::IO_IN) != Glib::IOCondition::IO_IN)
      {
        std::cerr << "Invalid fifo response" << std::endl;
      }
      else
      {
       Glib::ustring buf;

       iochannel->read_line(buf);
       std::cout << buf;
       if (buf == "Q\n")
         app->quit();
      }
      return true;
    }

    int main(int argc, char *argv[])
    {
      app = Gtk::Application::create("org.gtkmm.example");

      if (access("testfifo", F_OK) == -1)
      {
        // fifo doesn't exist - create it
        #ifndef DONT_HAVE_MKFIFO
        if (mkfifo("testfifo", 0666) != 0)
        {
          std::cerr << "error creating fifo" << std::endl;
          return -1;
        }
        #else
          std::cerr << "error creating fifo: This platform does not have mkfifo()"
              << std::endl;
        #endif //DONT_HAVE_MKFIFO
      }

      // Although we will only read from the fifo, we open it in read/write mode.
      // Due to a peculiarity with the poll() system call, used deep down in glib,
      // this small program will use all available CPU time, if the fifo is opened
      // as O_RDONLY. See a discussion on the gtkmm-list, e.g.
      // https://mail.gnome.org/archives/gtkmm-list/2015-September/msg00034.html
      // and the link from there to stackoverflow.
      read_fd = open("testfifo", O_RDWR);
      if (read_fd == -1)
      {
        std::cerr << "error opening fifo" << std::endl;
        return -1;
      }

      // connect the signal handler
      Glib::signal_io().connect(
        [](Glib::IOCondition io_condition){ return MyCallback(io_condition); },
        read_fd, Glib::IOCondition::IO_IN);

      // Creates a iochannel from the file descriptor
      iochannel = Glib::IOChannel::create_from_fd(read_fd);

      // and last but not least - run the application main loop
      app->hold(); // keep the application running without a window
      app->run(argc, argv);

      // now remove the temporary fifo
      if(unlink("testfifo"))
        std::cerr << "error removing fifo" << std::endl;

      return 0;
    }

### Функције мировања

Ако желите да наведете метод који се позива када се ништа друго не дешава, користите следеће:

    sigc::connection  Glib::SignalIdle::connect(const sigc::slot<bool()>& slot,
                                        int priority = Glib::PRIORITY_DEFAULT_IDLE);

Ово узрокује да gtkmm позове наведени метод кад год се ништа друго не дешава. Можете додати приоритет (нижи бројеви су виши приоритети). Постоје два начина за уклањање руковаоца сигнала: позивањем disconnect() на објекту sigc::connection или враћањем вредности `false` у руковаоцу сигнала, који треба бити објављен на следећи начин:

    bool idleFunc();

Пошто је ово врло слично горе наведеним методима, ово објашњење би требало бити довољно за разумевање онога што се дешава. Ипак, ево малог примера:

Изворни ко̂д

File: `idleexample.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_IDLEEXAMPLE_H
    #define GTKMM_EXAMPLE_IDLEEXAMPLE_H

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

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

    protected:
      // Signal Handlers:
      bool on_timer();
      bool on_idle();
      void on_button_clicked();

      // Member data:
      Gtk::Box m_Box;
      Gtk::Button m_ButtonQuit;
      Gtk::ProgressBar m_ProgressBar_c;
      Gtk::ProgressBar m_ProgressBar_d;
    };

    #endif // GTKMM_EXAMPLE_IDLEEXAMPLE_H

File: `idleexample.cc` (For use with gtkmm 4)

    #include "idleexample.h"

    IdleExample::IdleExample() :
      m_Box(Gtk::Orientation::VERTICAL, 5),
      m_ButtonQuit("_Quit", true)
    {
      m_Box.set_margin(5);

      // Put buttons into container

      // Adding a few widgets:
      set_child(m_Box);
      m_Box.append(*Gtk::make_managed<Gtk::Label>("Formatting Windows drive C:"));
      m_Box.append(*Gtk::make_managed<Gtk::Label>("100 MB"));
      m_Box.append(m_ProgressBar_c);
      m_ProgressBar_c.set_expand();

      m_Box.append(*Gtk::make_managed<Gtk::Label>(""));

      m_Box.append(*Gtk::make_managed<Gtk::Label>("Formatting Windows drive D:"));
      m_Box.append(*Gtk::make_managed<Gtk::Label>("5000 MB"));
      m_Box.append(m_ProgressBar_d);
      m_ProgressBar_d.set_expand();

      auto hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,10);
      m_Box.append(*hbox);
      hbox->append(m_ButtonQuit);
      m_ButtonQuit.set_expand();
      m_ButtonQuit.set_halign(Gtk::Align::END);
      m_ButtonQuit.set_valign(Gtk::Align::END);

      // Connect the signal handlers:
      m_ButtonQuit.signal_clicked().connect( sigc::mem_fun(*this,
                  &IdleExample::on_button_clicked) );

      // formatting drive c in timeout signal handler - called once every 50ms
      Glib::signal_timeout().connect( sigc::mem_fun(*this, &IdleExample::on_timer),
              50 );

      // formatting drive d in idle signal handler - called as quickly as possible
      Glib::signal_idle().connect( sigc::mem_fun(*this, &IdleExample::on_idle) );
    }

    void IdleExample::on_button_clicked()
    {
      close();
    }

    // this timer callback function is executed once every 50ms (set in connection
    // above).  Use timeouts when speed is not critical. (ie periodically updating
    // something).
    bool IdleExample::on_timer()
    {
      double value = m_ProgressBar_c.get_fraction();

      // Update progressbar 1/500th each time:
      m_ProgressBar_c.set_fraction(value + 0.002);

      return value < 0.99;  // return false when done
    }

    // This idle callback function is executed as often as possible, hence it is
    // ideal for processing intensive tasks.
    bool IdleExample::on_idle()
    {
      double value = m_ProgressBar_d.get_fraction();

      // Update progressbar 1/5000th each time:
      m_ProgressBar_d.set_fraction(value + 0.0002);

      return value < 0.99;  // return false when done
    }

File: `main.cc` (For use with gtkmm 4)

    #include "idleexample.h"
    #include <gtkmm/application.h>

    int main (int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      return app->make_window_and_run<IdleExample>(argc, argv);
    }

Овај пример мало истиче разлику између метода мировања и истека времена. Ако су вам потребни методи који се позивају периодично, а брзина није врло важна, онда желите методе истека времена. Ако желите методе који се позивају што је чешће могуће (као што је израчунавање фрактала у позадини), онда користите методе мировања.

Пробајте да извршите пример и повећате оптерећење система. Горња трака напретка ће се стално повећавати; доња ће успорити.

## Управљање меморијом

### Елементи

#### Нормално C++ управљање меморијом

gtkmm омогућава програмеру да контролише животни век (то јест, стварање и уништавање) било ког елемента на исти начин као и било ког другог C++ објекта. Ова флексибилност вам омогућава да користите `new` и `delete` за динамичко стварање и уништавање објеката или да користите обичне чланове класе (који се аутоматски уништавају када се класа уништи) или да користите локалне примерке (који се уништавају када примерак изађе из опсега). Ова флексибилност није присутна у неким C++ ГУИ комплетима алата, који ограничавају програмера на само подскуп C++ могућности управљања меморијом.

Важна разлика у gtkmm-у 4.0 у односу на старије верзије је та што уништавање C++ елемента више не узрокује уништавање самог елемента ако се он налази унутар родитеља; у том случају C GtkWidget остаје жив. Ако сте се ослањали на то понашање у старијој верзији, у gtkmm-у 4.0 сада морате прво уклонити елемент из његовог родитеља. Погледајте Промене у gtkmm-у 4.0.

Ево неколико примера нормалног C++ управљања меморијом:

##### Елементи у опсегу класе

Ако програмеру није потребна динамичка алокација меморије, могу се користити аутоматски елементи у опсегу класе. Једна предност аутоматских елемената у опсегу класе је та што је управљање меморијом груписано на једном месту. Програмер не ризикује цурење меморије услед пропуста да се `delete`-ује елемент.

Главни недостатак коришћења елемената у опсегу класе је откривање имплементације класе уместо интерфејса класе у заглављу класе.

    #include <gtkmm/button.h>
    #include <gtkmm/window.h>
    class Foo : public Gtk::Window
    {
    private:
      Gtk::Button theButton;
      // биће уништено када се уништи објекат Foo
    };

##### Елементи у опсегу функције

Ако програмеру није потребан елемент у опсегу класе, може се користити и елемент у опсегу функције. Предности опсега функције у односу на опсег класе су повећана скривеност података и смањене зависности.

    {
      Gtk::Button aButton;
      aButton.set_visible(true);
      ...
      app->run();
    }

Међутим, ова техника је ретко корисна. Већина елемената се не може безбедно направити пре него што се апликација региструје или активира. Они се не могу безбедно обрисати након што се Gtk::Application::run() или Gtk::Application::make_window_and_run() врате.

##### Динамичка алокација помоћу new и delete

Обично ће програмер радије дозволити контејнерима да аутоматски униште своје садржаоце тако што ће их направити помоћу `Gtk::make_managed()` (види испод). Ово није строго неопходно, јер се могу користити и оператори `new` и `delete`, али савремени C++ стил их обесхрабрује у корист безбеднијих модела управљања меморијом, па је боље правити елементе помоћу `Gtk::make_managed()` и пустити њиховог родитеља да их уништи, него ручно вршити динамичку алокацију.

    auto pButton = new Gtk::Button("Test");
    // урадите нешто корисно са pButton
    delete pButton;

Овде програмер брише `pButton` како би спречио цурење меморије.

#### Управљани елементи

Алтернативно, можете пустити контејнер елемента да контролише када ће елемент бити уништен. У већини случајева, желите да елемент траје само онолико колико и контејнер у коме се налази. Да бисте делегирали управљање животним веком елемента његовом контејнеру, направите га помоћу `Gtk::make_managed()`, а затим га сместите у његов контејнер помоћу Gtk::Box::append() или сличног метода. Сада ће елемент бити уништен кад год се уништи његов контејнер.

##### Динамичка алокација помоћу make_managed() и append()

gtkmm пружа начине, укључујући функцију `make_managed()` и метод Gtk::Box::append(), за поједностављивање стварања и уништавања елемената чијим животним веком може управљати родитељ.

Сваки елемент осим прозора највишег нивоа мора бити додат у родитељски контејнер да би био приказан. Функција `manage()` означава елемент тако да када се тај елемент дода у родитељски контејнер, поменути контејнер постаје одговоран за брисање елемента, што значи да корисник више не мора то да ради. Првобитни начин прављења елемената чијим животним веком на овај начин управља њихов родитељ био је позивање `manage()`, прослеђујући резултат израза `new` који је направио динамички алоциран елемент.

Међутим, обично, када правите такав елемент, већ ћете знати да би његов родитељски контејнер требало да буде одговоран за његово уништавање. Поред тога, савремени C++ стил обесхрабрује употребу оператора `new`, који је био неопходан приликом прослеђивања новоствореног елемента функцији `manage()`. Стога је gtkmm додао `make_managed()`, који комбинује стварање и означавање помоћу `manage()` у један корак. Ово избегава потребу да пишете `new`, што је обесхрабрено у савременом C++ стилу, и јасније изражава намеру да се направи управљани елемент.

    MyContainer::MyContainer()
    {
      auto pButton = Gtk::make_managed<Gtk::Button>("Test");
      append(*pButton); //add *pButton to MyContainer
    }

Сада, када се објекти врсте MyContainer униште, дугме ће такође бити обрисано. Није више неопходно брисати `pButton` ради ослобађања меморије дугмета; његово брисање је поверено објекту MyContainer.

Ако никада нисте додали елемент у неки родитељски контејнер, ваша је одговорност да га обришете. Ако га додате у елемент контејнера, а касније га уклоните (на пример помоћу Gtk::Box::remove()), обрисаће га контејнер.

Наравно, контејнер највишег нивоа неће бити додат у други контејнер. Програмер је одговоран за уништавање контејнера највишег нивоа користећи неку од традиционалних C++ техника. Или можете пустити да Gtk::Application::make_window_and_run() направи прозор највишег нивоа и обрише га када се затвори.

### Дељени ресурси

Неки објекти, као што су Gdk::Pixbuf и Pango::Font, добијају се из дељеног складишта. Због тога не можете стварати сопствене примерке. Ове класе обично наслеђују из Glib::Object. Уместо да захтева од вас да референцирате и дереференцирате ове објекте, gtkmm користи паметни показивач Glib::RefPtr<>. Cairomm има свој паметни показивач, Cairo::RefPtr<>.

Објекти као што је Gdk::Pixbuf могу се створити само помоћу функције create(). На пример,

    auto pixbuf = Gdk::Pixbuf::create_from_file(filename);

Немате начина да добијете голи Gdk::Pixbuf. У примеру, `pixbuf` је паметни показивач, тако да можете урадити ово, слично обичном показивачу:

    auto width = 0;
    if(pixbuf)
    {
      width = pixbuf->get_width();
    }

Када `pixbuf` изађе из опсега, unref() ће се десити у позадини и више не морате да бринете о томе. Нема `new` па нема ни `delete`.

Ако копирате RefPtr, на пример

    auto pixbuf2 = pixbuf;

или ако га проследите као аргумент методе или повратну врсту, тада ће RefPtr обавити свако неопходно референцирање како би осигурао да примерак неће бити уништен све док последњи RefPtr не изађе из опсега.

Погледајте додатак за детаљне информације о RefPtr.

Ако желите да сазнате више о паметним показивачима, можете погледати ове књиге:

- Бјарне Струструп, „The C++ Programming Language“ четврто издање — одељак 34.3

- Николај М. Јосутис, „The C++ Standard Library“ — одељак 4.2

## Gtk::Builder

Иако можете користити C++ код за стварање и распоређивање елемената, то убрзо може постати заморно и често се понављати. Такође захтева поновно превођење програма да би се приказале измене. Апликација Cambalache омогућава вам да распоредите елементе на екрану и затим сачувате XML опис тог распореда. Ваша апликација тада може користити Gtk::Builder API за учитавање те XML датотеке у време извршавања и добијање показивача на специфично именоване примерке елемената.

Ово има следеће предности:

1. Потребно је мање C++ кода.

2. Измене корисничког прочеља се могу видети брже, па се прочеља могу побољшати.

3. Дизајнери без програмерских вештина могу да стварају и уређују корисничка прочеља.

И даље вам је потребан C++ код за руковање изменама корисничког прочеља које покрећу корисничке радње, али коришћење Gtk::Builder-а за распоред елемената омогућава вам да се усредсредите на имплементацију те функционалности.

Cambalache замењује програм Glade. Glade може да генерише XML датотеке које ће се користити са gtk3/gtkmm3, али не подржава gtk4/gtkmm4.

### Учитавање .ui датотеке

Gtk::Builder се мора користити путем Glib::RefPtr-а. Као и код свих таквих класа, морате користити метод create() да бисте га инстанцирали. На пример,

    auto builder = Gtk::Builder::create_from_file("basic.ui");

Ово ће инстанцирати прозоре дефинисане у `.ui` датотеци.

Да бисте инстанцирали само један прозор, или само један од садржаних елемената, можете навести назив елемента као други параметар. На пример,

    auto builder = Gtk::Builder::create_from_file("basic.ui", "treeview_products");

### Приступање елементима

Да бисте приступили елементу, на пример да бисте приказали прозорче, користите метод get_widget(), наводећи назив елемента. Овај назив треба да буде наведен у прозору Cambalache-а. Ако елемент не може бити пронађен, или је погрешне врсте, тада ће показивач бити постављен на `nullptr`.

Прозорчићи у овом поглављу су изведени из Gtk::Window јер је Gtk::Dialog застарео од gtkmm-а 4.10.

    auto pDialog = builder->get_widget<Gtk::Window>("DialogBasic");

Gtk::Builder проверава да ли постоји ништавни показивач и проверава да ли је елемент очекиване врсте, те ће приказати упозорења на линији наредби о томе.

Запамтите да не инстанцирате елемент помоћу get_widget(), већ само добијате показивач на онај који већ постоји. Увек ћете примити показивач на исти примерак када позовете get_widget() на истом Gtk::Builder-у, са истим називом елемента. Елементи се инстанцирају током Gtk::Builder::create_from_file().

get_widget() враћа садржане елементе којима се управља (погледајте поглавље Управљање меморијом), тако да ће они бити обрисани када се обрише њихов родитељски контејнер. Прозори (као што су прозорчићи) не могу бити управљани јер немају родитељски контејнер, тако да их морате обрисати у неком тренутку. Документација Gtk::Builder-а има више тога да каже о управљању меморијом различитих врста објеката.

Упутство

#### Пример

Овај једноставан пример показује како да учитате `.ui` датотеку у време извршавања и приступите елементима помоћу Gtk::Builder-а.

Изворни ко̂д

File: `main.cc` (For use with gtkmm 4)

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

    namespace
    {
    Gtk::Window* pDialog = nullptr;
    Glib::RefPtr<Gtk::Application> app;

    void on_button_clicked()
    {
      // When the application's only window is removed with
      // unset_application(), Gtk::Application::run() will end.
      if (pDialog)
        pDialog->unset_application();
    }

    void on_app_activate()
    {
      // Load the GtkBuilder file and instantiate its widgets:
      auto refBuilder = Gtk::Builder::create();
      try
      {
        refBuilder->add_from_file("basic.ui");
      }
      catch(const Glib::FileError& ex)
      {
        std::cerr << "FileError: " << ex.what() << std::endl;
        return;
      }
      catch(const Glib::MarkupError& ex)
      {
        std::cerr << "MarkupError: " << ex.what() << std::endl;
        return;
      }
      catch(const Gtk::BuilderError& ex)
      {
        std::cerr << "BuilderError: " << ex.what() << std::endl;
        return;
      }

      // Get the GtkBuilder-instantiated dialog:
      pDialog = refBuilder->get_widget<Gtk::Window>("DialogBasic");
      if (!pDialog)
      {
        std::cerr << "Could not get the dialog" << std::endl;
        return;
      }

      // Get the GtkBuilder-instantiated button, and connect a signal handler:
      auto pButton = refBuilder->get_widget<Gtk::Button>("quit_button");
      if (pButton)
        pButton->signal_clicked().connect([] () { on_button_clicked(); });

      // It's not possible to delete widgets after app->run() has returned.
      // Delete the dialog with its child widgets before app->run() returns.
      app->signal_window_removed().connect([](Gtk::Window* window) { delete window; });

      app->add_window(*pDialog);
      pDialog->set_visible(true);
    }
    } // anonymous namespace

    int main(int argc, char** argv)
    {
      app = Gtk::Application::create("org.gtkmm.example");

      // Instantiate a dialog when the application has been activated.
      // This can only be done after the application has been registered.
      // It's possible to call app->register_application() explicitly, but
      // usually it's easier to let app->run() do it for you.
      app->signal_activate().connect([] () { on_app_activate(); });

      return app->run(argc, argv);
    }

### Коришћење изведених елемената

Можете користити Cambalache и Gtk::Builder за распоређивање сопствених произвољних елемената изведених из gtkmm класа елемената. Ово одржава ваш ко̂д организованим и енкапсулираним, одвајајући декларативни приказ од пословне логике, избегавајући да већина вашег извора буде само подешавање својстава и паковање у контејнере.

Користите Gtk::Builder::get_widget_derived() на следећи начин:

    auto pDialog = Gtk::Builder::get_widget_derived<DerivedDialog>(builder, "DialogDerived");

Ваша изведена класа мора имати конструктор који прихвата показивач на основни Ц тип и примерак Gtk::Builder-а. Све релевантне класе gtkmm-а дефинишу свој основни Ц тип као BaseObjectType (Gtk::Window дефинише BaseObjectType као GtkWindow, на пример).

Морате позвати конструктор основне класе у листи иницијализације, обезбеђујући Ц показивач. На пример,

    DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder)
    : Gtk::Window(cobject)
    {
    }

Затим бисте могли да енкапсулирате манипулацију подређеним елементима у конструктору изведене класе, можда поново користећи get_widget() или get_widget_derived(). На пример,

    DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder)
    : Gtk::Window(cobject),
      m_builder(builder),
      // Дохватите Button инстанциран преко GtkBuilder-а и повежите руковаоца сигналом:
      m_pButton(m_builder->get_widget<Gtk::Button>("quit_button"))
    {
      if (m_pButton)
      {
        m_pButton->signal_clicked().connect( sigc::mem_fun(*this, &DerivedDialog::on_button_quit) );
      }
    }

Могуће је проследити додатне аргументе из get_widget_derived() у конструктор изведеног елемента. На пример, овај позив get_widget_derived()

    auto pDialog = Gtk::Builder::get_widget_derived<DerivedDialog>(builder, "DialogDerived", true);

може позвати овај конструктор

    DerivedDialog::DerivedDialog(BaseObjectType* cobject,
      const Glib::RefPtr<Gtk::Builder>& builder, bool warning)
    : Gtk::Window(cobject),
      m_builder(builder),
      m_pButton(m_builder->get_widget<Gtk::Button>("quit_button"))
    {
      // ....
    }

#### Gtk::Builder и Glib::Property

Ако ваш изведени елемент користи Glib::Property, ствар постаје мало компликованија. Изведени елемент који садржи Glib::Property чланове мора бити регистрован под сопственим именом у GType систему. Мора бити регистрован пре него што се позове било која од create_from_*() или add_from_*() метода, што значи да ћете можда морати да направите примерак свог изведеног елемента само да би његова класа била регистрована. Ваш изведени елемент мора имати конструктор који има параметре захтеване од стране get_widget_derived() и позива Glib::ObjectBase конструктор ради регистрације GType-а.

    DerivedButton::DerivedButton(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder)
    : Glib::ObjectBase("MyButton"), // GType име ће бити gtkmm__CustomObject_MyButton.
      Gtk::Button(cobject),
      prop_ustring(*this, "button-ustring"),
      prop_int(*this, "button-int", 10)
    {
      // ....
    }

Такође је могуће навести својства изведених елемената, декларисаних у C++ језику помоћу gtkmm-а, унутар `.ui` датотека и учитати/поставити их помоћу Gtk::Builder-а. Погледајте документацију за Gtk::Builder за више детаља о томе како ово постићи. Cambalache неће препознати таква својства сама по себи, али бисте требали бити у могућности да додате неколико линија ручно кодираног XML-а у XML ко̂д који је генерисао Cambalache.

#### Пример

Овај пример показује како да учитате `.ui` датотеку у време извршавања и приступите елементима преко изведених класа.

Изворни ко̂д

File: `derivedbutton.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_DERIVED_BUTTON_H
    #define GTKMM_EXAMPLE_DERIVED_BUTTON_H

    #include <gtkmm.h>

    class DerivedButton : public Gtk::Button
    {
    public:
      DerivedButton();
      DerivedButton(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder);
      virtual ~DerivedButton();

      // Provide proxies for the properties. The proxy allows you to connect to
      // the 'changed' signal, etc.
      Glib::PropertyProxy<Glib::ustring> property_ustring() { return prop_ustring.get_proxy(); }
      Glib::PropertyProxy<int> property_int() { return prop_int.get_proxy(); }

    private:
      Glib::Property<Glib::ustring> prop_ustring;
      Glib::Property<int> prop_int;
    };

    #endif //GTKMM_EXAMPLE_DERIVED_BUTTON_H

File: `deriveddialog.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_DERIVED_DIALOG_H
    #define GTKMM_EXAMPLE_DERIVED_DIALOG_H

    #include <gtkmm.h>
    #include "derivedbutton.h"

    class DerivedDialog : public Gtk::Window
    {
    public:
      DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder);
      DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder,
        bool is_glad);
      ~DerivedDialog() override;

    protected:
      //Signal handlers:
      void on_button_quit();

      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      DerivedButton* m_pButton;
    };

    #endif //GTKMM_EXAMPLE_DERIVED_DIALOG_H

File: `derivedbutton.cc` (For use with gtkmm 4)

    #include "derivedbutton.h"
    #include <iostream>

    // For creating a dummy object in main.cc.
    DerivedButton::DerivedButton()
    : Glib::ObjectBase("MyButton"),
      prop_ustring(*this, "button-ustring"),
      prop_int(*this, "button-int", 10)
    {
    }

    DerivedButton::DerivedButton(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& /* refBuilder */)
    : // To register custom properties, you must register a custom GType.  If
      // you don't know what that means, don't worry, just remember to add
      // this Glib::ObjectBase constructor call to your class' constructor.
      // The GType name will be gtkmm__CustomObject_MyButton.
      Glib::ObjectBase("MyButton"),
      Gtk::Button(cobject),
      // register the properties with the object and give them names
      prop_ustring(*this, "button-ustring"),
      // this one has a default value
      prop_int(*this, "button-int", 10)
    {
      // Register some handlers that will be called when the values of the
      // specified parameters are changed.
      property_ustring().signal_changed().connect(
        [](){ std::cout << "- ustring property changed!" << std::endl; }
      );
      property_int().signal_changed().connect(
        [](){ std::cout << "- int property changed!" << std::endl; }
      );
    }

    DerivedButton::~DerivedButton()
    {
    }

File: `deriveddialog.cc` (For use with gtkmm 4)

    #include "deriveddialog.h"
    #include <iostream>

    DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder)
    : Gtk::Window(cobject),
      m_refBuilder(refBuilder),
      m_pButton(nullptr)
    {
      // Get the GtkBuilder-instantiated Button, and connect a signal handler:
      m_pButton = Gtk::Builder::get_widget_derived<DerivedButton>(m_refBuilder, "quit_button");
      if (m_pButton)
      {
        m_pButton->signal_clicked().connect( sigc::mem_fun(*this, &DerivedDialog::on_button_quit) );
        std::cout << "ustring, int: " << m_pButton->property_ustring()
                  << ", " << m_pButton->property_int() << std::endl;
        m_pButton->property_int() = 99;
        std::cout << "ustring, int: " << m_pButton->property_ustring()
                  << ", " << m_pButton->property_int() << std::endl;
      }
    }

    // The first two parameters are mandatory in a constructor that will be called
    // from Gtk::Builder::get_widget_derived().
    // Additional parameters, if any, correspond to additional arguments in the call
    // to Gtk::Builder::get_widget_derived().
    DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder,
      bool is_glad)
    : DerivedDialog(cobject, refBuilder) // Delegate to the other constructor
    {
      // Show an icon.
      auto content_area = refBuilder->get_widget<Gtk::Box>("dialog-content_area");
      if (!content_area)
      {
        std::cerr << "Could not get the content area" << std::endl;
        return;
      }
      auto pImage = Gtk::make_managed<Gtk::Image>();
      pImage->set_from_icon_name(is_glad ? "face-smile" : "face-sad");
      pImage->set_icon_size(Gtk::IconSize::LARGE);
      pImage->set_expand();
      content_area->append(*pImage);
    }

    DerivedDialog::~DerivedDialog()
    {
    }

    void DerivedDialog::on_button_quit()
    {
      // When the application's only window is removed with
      // unset_application(), Gtk::Application::run() will end.
      unset_application();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "deriveddialog.h"
    #include <iostream>
    #include <cstring>

    namespace
    {
    bool show_icon = false;
    bool is_glad = true;

    DerivedDialog* pDialog = nullptr;
    Glib::RefPtr<Gtk::Application> app;

    void on_app_activate()
    {
      // Create a dummy instance before the call to refBuilder->add_from_file().
      // This creation registers DerivedButton's class in the GType system.
      // This is necessary because DerivedButton contains user-defined properties
      // (Glib::Property) and is created by Gtk::Builder.
      static_cast<void>(DerivedButton());

      // Load the GtkBuilder file and instantiate its widgets:
      auto refBuilder = Gtk::Builder::create();
      try
      {
        refBuilder->add_from_file("derived.ui");
      }
      catch(const Glib::FileError& ex)
      {
        std::cerr << "FileError: " << ex.what() << std::endl;
        return;
      }
      catch(const Glib::MarkupError& ex)
      {
        std::cerr << "MarkupError: " << ex.what() << std::endl;
        return;
      }
      catch(const Gtk::BuilderError& ex)
      {
        std::cerr << "BuilderError: " << ex.what() << std::endl;
        return;
      }

      // Get the GtkBuilder-instantiated dialog:
      if (show_icon)
        pDialog = Gtk::Builder::get_widget_derived<DerivedDialog>(refBuilder, "DialogDerived", is_glad);
      else
        pDialog = Gtk::Builder::get_widget_derived<DerivedDialog>(refBuilder, "DialogDerived");

      if (!pDialog)
      {
        std::cerr << "Could not get the dialog" << std::endl;
        return;
      }

      // It's not possible to delete widgets after app->run() has returned.
      // Delete the dialog with its child widgets before app->run() returns.
      app->signal_window_removed().connect([](Gtk::Window* window) { delete window; });

      app->add_window(*pDialog);
      pDialog->set_visible(true);
    }
    } // anonymous namespace

    int main(int argc, char** argv)
    {
      int argc1 = argc;
      if (argc > 1)
      {
        if (std::strcmp(argv[1], "--glad") == 0)
        {
          show_icon = true;
          is_glad = true;
          argc1 = 1; // Don't give the command line arguments to Gtk::Application.
        }
        else if (std::strcmp(argv[1], "--sad") == 0)
        {
          show_icon = true;
          is_glad = false;
          argc1 = 1; // Don't give the command line arguments to Gtk::Application.
        }
      }

      app = Gtk::Application::create("org.gtkmm.example");

      // Instantiate a dialog when the application has been activated.
      // This can only be done after the application has been registered.
      // It's possible to call app->register_application() explicitly, but
      // usually it's easier to let app->run() do it for you.
      app->signal_activate().connect([] () { on_app_activate(); });

      return app->run(argc1, argv);
    }

## Интернационализација и локализација

gtkmm програми могу лако подржати више језика, укључујући неевропске језике као што је кинески и језике који се пишу здесна налево као што је арапски. Исправно написан и преведен gtkmm програм ће користити одговарајући језик при извршавању на основу корисниковог окружења.

Можда нећете предвидети потребу за подршком додатним језицима, али то никада не можете искључити. И лакше је развити програм исправно од самог почетка него га касније прилагођавати.

Процес писања изворног кода који омогућава превођење назива се `интернационализација`, често скраћено на `i18n`. Процес `локализације`, понекад скраћено као `l10n`, обезбеђује преведени текст за друге језике, на основу тог изворног кода.

Главна активност у процесу интернационализације је проналажење ниски које корисници виде и њихово означавање за превођење. Не морате то да урадите све одједном — ако исправно подесите неопходну инфраструктуру пројекта, ваш програм ће радити нормално без обзира на то колико сте ниски обухватили.

Литерали ниски треба да буду укуцани у изворном коду на енглеском, али окружени макроом. Алатка gettext (или intltool) може затим издвојити означене ниске за превођење и заменити преведени текст при извршавању.

### Припрема вашег пројекта

У упутствима испод претпоставићемо да нећете користити gettext директно, већ intltool, који је написан посебно за `Гном`. intltool користи `gettext()`, који издваја ниске из изворног кода, али intltool такође може да комбинује ниске из других датотека, на пример из детаља изборника радне површине и ГКС датотека ресурса као што су `.ui` датотеке, у стандардне gettext `.pot/.po` датотеке.

Такође претпостављамо да користите autotools (automake и autoconf) за изградњу вашег пројекта (иако се autotools не препоручује за нове програме) и да користите `./autogen.sh` из gnome-common или сличну `autogen.sh` датотеку, која се, између осталог, брине о покретању intltool-а.

Ако користите meson (препоручено), погледајте поглавље Локализација у Мезоновом приручнику. Затим можете прескочити овај одељак.

Алтернатива за gnome-common-ов `autogen.sh` може изгледати овако:

    #! /bin/sh -e
    test -n "$srcdir" || srcdir=`dirname "$0"`
    test -n "$srcdir" || srcdir=.

    autoreconf --force --install --verbose --warnings=all "$srcdir"
    echo "Running intltoolize --copy --force --automake"
    intltoolize --copy --force --automake
    test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"

Направите поддиректоријум под називом `po` у кореном директоријуму вашег пројекта. Овај директоријум ће на крају садржати све ваше преводе. Унутар њега направите датотеку под називом `LINGUAS` и датотеку под називом `POTFILES.in`.

`LINGUAS` садржи азбучно поређан списак кодова који идентификују језике на које је ваш програм преведен (редови коментара који почињу са `#` се занемарују). Сваки код језика наведен у датотеци `LINGUAS` мора имати одговарајућу `.po` датотеку. Дакле, ако ваш програм има немачки и јапански превод, ваша `LINGUAS` датотека би изгледала овако:

    # keep this file sorted alphabetically, one language code per line
    de
    ja

(Поред тога, имали бисте датотеке `de.po` и `ja.po` у вашем `po` директоријуму које садрже немачки и јапански превод.)

`POTFILES.in` је списак путања до свих датотека које садрже ниске означене за превођење, почевши од кореног директоријума пројекта. Тако на пример, ако су извори вашег пројекта смештени у поддиректоријуму под називом `src` и имате две датотеке које садрже ниске које треба превести, ваша `POTFILES.in` датотека би могла изгледати овако:

    src/main.cc
    src/other.cc

Ако користите gettext директно, можете означити ниске за превођење само ако су у датотеци изворног кода. Међутим, ако користите intltool, можете означити ниске за превођење у разним другим форматима датотека, укључујући `.ui` датотеке, xml, .desktop датотеке и још неколико других. Дакле, ако сте дизајнирали део корисничког прочеља програма у xml датотекама, додајте и ваше `.ui` датотеке на списак у `POTFILES.in`.

Сада када постоји место за постављање ваших превода, потребно је да покренете intltool и gettext. Додајте следећи код у ваш `configure.ac`, замењујући „programname“ именом вашег програма:

    IT_PROG_INTLTOOL([0.35.0])

    GETTEXT_PACKAGE=programname
    AC_SUBST(GETTEXT_PACKAGE)
    AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"],
                       [The domain to use with gettext])
    AM_GNU_GETTEXT([external])
    AM_GNU_GETTEXT_VERSION([0.17])

    PROGRAMNAME_LOCALEDIR=[${datadir}/locale]
    AC_SUBST(PROGRAMNAME_LOCALEDIR)

Ова променљива `PROGRAMNAME_LOCALEDIR` ће се касније користити у датотеци `Makefile.am` за дефинисање макроа који ће се користити када покренете gettext у вашем изворном коду.

`AM_GLIB_GNU_GETTEXT` је био алтернатива за `AM_GNU_GETTEXT` и `AM_GNU_GETTEXT_VERSION`, али је `AM_GLIB_GNU_GETTEXT` сада застарео и не треба га користити у новом коду.

У Makefile.am највишег нивоа:

- Додајте `po` у променљиву `SUBDIRS`. Без овога, ваши преводи неће бити изграђени и инсталирани када градите програм

- Дефинишите `INTLTOOL_FILES` као:

      INTLTOOL_FILES = intltool-extract.in \
                       intltool-merge.in \
                       intltool-update.in

- Додајте `INTLTOOL_FILES` на списак датотека `EXTRA_DIST`. Ово осигурава да ће, када покренете `make dist`, ове датотеке бити укључене у изворну архиву (tarball).

- Ажурирајте ваш `DISTCLEANFILES`:

      DISTCLEANFILES = ... intltool-extract \
                       intltool-merge \
                       intltool-update \
                       po/.intltool-merge-cache

- У зависности од врста датотека које садрже ниске за превођење, додајте код као што је

      desktopdir = $(datadir)/applications
      desktop_in_files = programname.desktop.in
      desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
      @INTLTOOL_DESKTOP_RULE@

У вашем `src/Makefile.am`, ажурирајте ваш `AM_CPPFLAGS` да додате следећу дефиницију макроа препроцесора:

    AM_CPPFLAGS = ... -DPROGRAMNAME_LOCALEDIR=\"${PROGRAMNAME_LOCALEDIR}\"

Овај макро ће се користити када покренете `gettext` у вашем изворном коду.

### Означавање ниски за превођење

Литерали ниски треба да буду укуцани у изворном коду на енглеском, али треба да буду окружени позивом функције `gettext()`. Ове ниске ће бити извучене за превођење и преводи се могу користити током извршавања уместо оригиналних ниски на енглеском.

Пакет ГНУ gettext вам омогућава да означите ниске у изворном коду, извучете те ниске за превођење и користите преведене ниске у вашој апликацији.

Међутим, Глиб дефинише макрое подршке за `gettext()` који су краћи омотачи у облику једноставном за коришћење. Да бисте користили ове макрое, укључите `<glibmm/i18n.h>`, а затим, на пример, замените:

    display_message("Getting ready for i18n.");

са:

    display_message(_("Припрема за интернационализацију."));

Ради подсећања, могуће је направити датотеку која садржи све ниске које се појављују у вашем коду, чак и ако нису означене за превод, заједно са називом датотеке и референцама броја реда. Да бисте направили такву датотеку под називом `my-strings`, извршите следећу наредбу унутар директоријума изворног кода:

    xgettext -a -o my-strings --omit-header *.cc *.h

На крају, да бисте омогућили програму да користи превод за тренутни локал, додајте овај код на почетак ваше `main.cc` датотеке како бисте покренули gettext.

    bindtextdomain(GETTEXT_PACKAGE, PROGRAMNAME_LOCALEDIR);
    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    textdomain(GETTEXT_PACKAGE);

#### Како gettext ради

Скрипта intltool-update или xgettext извлачи ниске и ставља их у датотеку `mypackage.pot`. Преводиоци вашег програма стварају своје преводе тако што прво умноже ову `.pot` датотеку у `localename.po` датотеку. Локал одређује језик и кодирање за тај језик, укључујући записе датума и бројева. Касније, када се текст у вашем изворном коду измени, користи се скрипта msgmerge или intltool-update за ажурирање `localename.po` датотека из поново створене `.pot` датотеке.

Приликом инсталације, `.po` датотеке се претварају у бинарни облик (са проширењем `.mo`) и смештају у системски директоријум за датотеке локала, на пример `/usr/share/locale/`.

Када се програм покрене, библиотека gettext проверава системски директоријум да види да ли постоји `.mo` датотека за корисниково окружење локала (локал можете поставити, на пример, наредбом „export LANG=sr_RS.UTF-8“ из баш конзоле). Касније, када програм дође до позива `gettext`, он тражи превод одређене ниске. Ако га не пронађе, користи се изворна ниска.

#### Тестирање и додавање превода

Да бисте се уверили да сте добро урадили посао, можда ћете желети да додате превод за нови локал. Да бисте то урадили, идите у поддиректоријум `po` вашег пројекта и извршите следећу наредбу:

    intltool-update --pot --gettext-package programname

То ће направити датотеку под називом `programname.pot`. Сада умножите ту датотеку у `languagecode.po`, као што је `sr.po` или `de.po`. Такође додајте тај код језика у `LINGUAS`. `.po` датотека садржи заглавље и списак енглеских ниски, са простором за унос преведених ниски. Обавезно поставите кодирање `.po` датотеке на `UTF-8`.

    "Content-Type: text/plain; charset=UTF-8\
    "

Могуће је да ће одређене ниске бити означене као „нејасне“ (`fuzzy`) у `.po` датотеци. Ови преводи неће заменити изворну ниску. Да би се они појавили, једноставно уклоните ознаку `fuzzy`. Ознака `fuzzy` се појављује ако је садржај ниске измењен, али је место остало исто.

#### Ресурси

Више података о томе шта стоји иза процеса интернационализације и локализације представљено је и приказано у:

- Смернице за локализацију за програмере

- Intltool ПРОЧИТАЈМЕ

- Како користити Гит за Гномове преводиоце

- gettext упутство

### Очекује се UTF8

Правилно интернационализована апликација неће доносити претпоставке о броју бајтова у знаку. То значи да не би требало да користите аритметику показивача за кретање кроз знакове у ниски, и значи да не би требало да користите std::string или стандардне C функције као што је `strlen()` јер оне доносе исту претпоставку.

Међутим, вероватно већ избегавате чисте char* низове и аритметику показивача коришћењем std::string, па само треба да почнете да користите Glib::ustring уместо тога. Погледајте поглавље Основе о Glib::ustring.

#### Glib::ustring и std::iostreams

Нажалост, интеграција са стандардним iostreams није потпуно сигурна. gtkmm претвара Glib::ustring-ове у кодирање специфично за локал (које обично није UTF-8) ако их шаљете у ostream помоћу `operator<<`. Исто тако, преузимање Glib::ustring-ова из istream помоћу `operator>>` изазива конверзију у супротном смеру. Али ова шема пропада ако идете преко std::string, нпр. уносом текста из тока у std::string а затим имплицитним претварањем у Glib::ustring. Ако је ниска садржала не-ASCII знакове а тренутни локал није UTF-8 кодиран, резултат је оштећен Glib::ustring. Ово можете заобићи ручном конверзијом. На пример, за преузимање std::string из ostringstream:

    std::locale::global(std::locale("")); // Поставите глобални локал на корисников жељени локал.
                                          // Обично непотребно овде, јер Glib::init()
                                          // или Gtk::Application::create() то раде за вас.
    std::ostringstream output;
    output << percentage << " % done";
    label->set_text(Glib::locale_to_utf8(output.str()));

### Замке

Постоји неколико уобичајених грешака које бисте на крају и сами открили. Али овај одељак би вам могао помоћи да их избегнете.

#### Исте ниске, различита семантика

Понекад су две енглеске ниске идентичне али имају различита значења у различитим контекстима, па вероватно не би биле идентичне када се преведу. Пошто се енглеске ниске користе као кључеви за претрагу, ово изазива проблеме.

У овим случајевима, требало би да додате додатне знакове у ниске. На пример, користите `"jumps[noun]"` и `"jumps[verb]"` уместо само `"jumps"` и поново их одсеците изван позива `gettext`. Ако додате додатне знакове, требало би да додате и коментар за преводиоце пре позива `gettext`. Такви коментари ће бити приказани у `.po` датотекама. На пример:

    // напомена за преводиоце: не преводите део „[noun]“ — он је
    // овде само да би се ниска разликовала од друге ниске „jumps“
    text = strip(gettext("jumps[noun]"), "[noun]");

Ако користите Glib-ове макрое подршке, лакше је. Користите `C_()` уместо `_()`. На пример:

    GLib::ustring text(C_("noun", "jumps"));

#### Састављање ниски

C програмери користе `sprintf()` за састављање и спајање ниски. C++ преферира токове (streams), али нажалост, овај приступ отежава превођење, јер се сваки одломак текста преводи засебно, без омогућавања преводиоцима да их преуреде у складу са граматиком језика.

На пример, овај код би био проблематичан:

    std::cout << _("Current amount: ") << amount
              << _(" Future: ") << future << std::endl;

    label.set_text(_("Really delete ") + filename + _(" now?"));

Зато би требало или да избегавате ову ситуацију или да користите `Glib::ustring::compose()` који подржава синтаксу као што је:

    std::cout << Glib::ustring::compose(
                 _("Current amount: %1 Future: %2"), amount, future) << std::endl;

    label.set_text(Glib::ustring::compose(_("Really delete %1 now?"), filename));

#### Претпостављање приказане величине ниски

Никада не знате колико ће простора ниска заузети на екрану када се преведе. Врло је могуће да ће бити двоструко већа од изворне ниске на енглеском језику. Срећом, већина gtkmm елемената ће се раширити приликом извршавања на потребну величину.

#### Необичне речи

Требало би да избегавате криптичне скраћенице, жаргон или сленг. Они су обично тешки за превођење, а често су тешки за разумевање чак и изворним говорницима.

#### Коришћење не-АСКИ знакова у нискама

Тренутно, gettext не подржава не-АСКИ знакове (тј. било које знакове са кодом изнад 127) у изворном коду. На пример, не можете користити знак ауторских права (©).

Да бисте ово заобишли, могли бисте да напишете коментар у изворном коду непосредно пре ниске, говорећи преводиоцима да користе посебан знак ако је доступан у њиховим језицима. За енглески, тада бисте могли да направите превод за амерички енглески `en_US.po` који користи тај посебан знак.

### Добијање помоћи око превода

Ако је ваш програм слободан софтвер, постоји читав `GNOME` потпројекат посвећен помагању у изради превода, `GNOME` пројекат превођења.

Начин на који то функционише је да отпремите свој изворни код у гит ризницу којој преводиоци могу приступити, а затим поднесете извештај о проблему пројекту damned-lies и затражите да ваш програм буде додат на списак модула за превођење.

Затим се побрините да ажурирате датотеку `POTFILES.in` у `po/` поддиректоријуму (`intltool-update -m` може помоћи у томе) тако да преводиоци увек приступају ажурираним `myprogram.pot` датотекама, и једноставно замрзните ниске најмање неколико дана пре него што објавите ново издање. У зависности од броја ниски које ваш програм садржи и колико је популаран, преводи ће почети да пристижу као `.po` датотеке.

Имајте на уму да већина језичких тимова броји само 1-3 особе, па ако ваш програм садржи много ниски, може проћи неко време пре него што ико буде имао времена да га погледа. Такође, већина преводилаца не жели да губи своје време (превођење је веома захтеван задатак), па ако не оцене ваш пројекат као заиста озбиљан (у смислу да је углађен и да се одржава), могу одлучити да своје време потроше на неки други пројекат.

### Пример

Ево једноставног примера са преводивим нискама у C++ коду. Он показује шта можете да урадите ако не користите autotools или meson. То је такође пример коришћења Gtk::TextView елемента.

Овај програм може да подеси локалитет, ако је наведен у датотеци подешавања. Ово није нормално понашање. Већина програма прихвата локалитет који је корисник одабрао, на пример помоћу променљиве окружења.

Преводиве ниске

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

      // Configuration file, showing configured locale.
      static const std::string config_file_name;

    protected:
      void fill_text_tag_table();
      void fill_buffer();

      void on_dropdown_changed();

      // Child widgets
      Gtk::HeaderBar m_HeaderBar;
      Gtk::DropDown m_DropDown;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::TextView m_TextView;

      Glib::RefPtr<Gtk::StringList> m_StringList;
      Glib::RefPtr<Gtk::TextTagTable> m_refTextTagTable;
      Glib::RefPtr<Gtk::TextBuffer> m_refTextBuffer;
    };

    #endif // GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <fstream>
    #include <iostream>
    #include <glibmm/i18n.h>

    const std::string ExampleWindow::config_file_name("appconfig.cfg");

    ExampleWindow::ExampleWindow()
    {
      set_title(_("English i18n example"));
      set_default_size(500, 300);

      // Header bar
      m_HeaderBar.set_show_title_buttons(true);
      m_HeaderBar.pack_start(m_DropDown);

      // Set headerbar as titlebar
      set_titlebar(m_HeaderBar);

      // Fill the dropdown
      const std::vector<Glib::ustring> strings{
        "Locale", "en_US.UTF-8", "de_DE.UTF-8", "cs_CZ.UTF-8", "sv_SE.UTF-8"
      };
      m_StringList = Gtk::StringList::create(strings);
      m_DropDown.set_model(m_StringList);
      m_DropDown.set_selected(0);

      m_DropDown.property_selected().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_dropdown_changed));

      // Add the TextView, inside a ScrolledWindow.
      m_ScrolledWindow.set_child(m_TextView);
      set_child(m_ScrolledWindow);

      fill_text_tag_table();
      fill_buffer();
      m_TextView.set_buffer(m_refTextBuffer);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_dropdown_changed()
    {
      const auto selected = m_DropDown.get_selected();
      const auto text = m_StringList->get_string(selected);
      std::cout << "DropDown changed: " << text << std::endl;
      if (selected > 0)
      {
        std::ofstream myfile;
        myfile.open(config_file_name);
        if (myfile.is_open())
        {
          myfile << text << std::endl;
          myfile.close();
        }
        else
          std::cout << "Could not open file " << config_file_name << std::endl;
      }
    }

    void ExampleWindow::fill_text_tag_table()
    {
      m_refTextTagTable = Gtk::TextTagTable::create();
      auto textTag = Gtk::TextTag::create("plain-text");
      textTag->property_wrap_mode() = Gtk::WrapMode::WORD;
      m_refTextTagTable->add(textTag);

      textTag = Gtk::TextTag::create("script");
      textTag->property_wrap_mode() = Gtk::WrapMode::NONE;
      textTag->property_indent() = 20;
      textTag->property_family() = "Monospace";
      textTag->property_background() = "rgb(92%, 92%, 92%)"; // Light gray
      m_refTextTagTable->add(textTag);
    }

    void ExampleWindow::fill_buffer()
    {
      m_refTextBuffer = Gtk::TextBuffer::create(m_refTextTagTable);

      // Get beginning of buffer; each insertion will revalidate the
      // iterator to point to just after the inserted text.
      auto iter = m_refTextBuffer->begin();

      iter = m_refTextBuffer->insert_with_tag(iter,
        "To build myapp with messages in a different language, e.g. German,"
        " do this in a terminal window:\n\n",
        "plain-text");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "g++ -o myapp main.cc examplewindow.cc `pkg-config --cflags --libs gtkmm-4.0` -std=c++17\n\n",
        "script");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "Before you run myapp, create the files with translated texts:\n\n", "plain-text");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "mkdir po\n"
        "cd po\n"
        "cat >POTFILES.in <<EOF\n"
        "# List of source files containing translatable strings.\n"
        "main.cc\n"
        "examplewindow.cc\n"
        "EOF\n\n", "script");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "Export all quoted texts from source code: _(\"text\") to file myapp.pot:\n\n",
        "plain-text");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "intltool-update --pot --gettext-package myapp\n\n", "script");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "Create the de.po file:\n\n", "plain-text");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "msginit --no-translator --locale de_DE.UTF-8\n\n", "script");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "Now run poedit or some other editor, open file de.po and translate texts of messages."
        " Write the translations after 'msgstr'."
        " If charset is not UTF-8 in the .po file, change to UTF-8."
        " Then save the de.po file. (Later, to update the .po file, use ",
        "plain-text");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "intltool-update de", "script");
      iter = m_refTextBuffer->insert_with_tag(iter,
        ".) Still in the po directory, create myapp.mo from de.po:\n\n", "plain-text");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "mkdir --parents ../locale/de/LC_MESSAGES\n"
        "msgfmt --check --verbose --output-file ../locale/de/LC_MESSAGES/myapp.mo de.po\n\n",
        "script");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "Run myapp with specified language:\n\n", "plain-text");
      iter = m_refTextBuffer->insert_with_tag(iter,
        "cd ..\n"
        "LANG=de_DE.UTF-8 ./myapp\n\n", "script");
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <fstream>
    #include <iostream>
    #include <string>
    #include <locale>
    #include <clocale>
    #include <glibmm/i18n.h>

    const char* GETTEXT_PACKAGE = "myapp";
    const char* PROGRAMNAME_LOCALEDIR = "locale";

    int main(int argc, char* argv[])
    {
      std::ifstream myfile(ExampleWindow::config_file_name);
      if (myfile.is_open())
      {
        // Set the configured locale.
        std::string line;
        std::getline(myfile, line);
        std::cout <<"Preference settings: "<< line << std::endl;
        // Don't use the user's preferred locale.
        Glib::set_init_to_users_preferred_locale(false);
        Glib::setenv("LANG", line, true);
        std::cout << "getenv(LANG)= " << Glib::getenv("LANG") << '\n';
        std::setlocale(LC_ALL, line.c_str());
        myfile.close();
      }
      // If the configuration file does not exist, the user's preferred locale is used.

      bindtextdomain(GETTEXT_PACKAGE, PROGRAMNAME_LOCALEDIR);
      bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
      textdomain(GETTEXT_PACKAGE);

      // Gtk::Application::create() calls Glib::init(), which sets the C++ global locale.
      auto app = Gtk::Application::create("org.gtkmm.example");

      // On startup, when std::cout is created, the global locale is the "C" locale,
      // AKA the classic locale.
      std::cout << _("English App is Running") << ", "
        << std::cout.getloc().name() << " locale: " << 1234.56 << '\n';

      // Use the new global locale for future output to std::cout.
      std::cout.imbue(std::locale());

      std::cout << _("English App is Running") << ", "
        << std::cout.getloc().name() << " locale: " << 1234.56 << '\n';
      std::cout << "Force unicode German text: Gr\xC3\xBC\xC3\x9F Gott\n";

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

## Произвољни елементи

gtkmm омогућава веома лако извођење нових елемената наслеђивањем из постојеће класе елемента, било извођењем из контејнера и додавањем садржаних елемената, или извођењем из појединачног елемента и променом његовог понашања. Али повремено можете открити да већ не постоји одговарајућа почетна тачка. У овом случају, можете имплементирати елемент испочетка.

### Произвољни контејнери

Приликом извођења произвољног елемента контејнера директно из Gtk::Widget, требало би да преклопите следеће виртуелне методе:

- get_request_mode_vfunc(): Враћа који `Gtk::SizeRequestMode` контејнер преферира.

- measure_vfunc(): Израчунава минималну и природну ширину или висину контејнера.

- size_allocate_vfunc(): Поставља садржане елементе, на основу висине и ширине које је контејнер заправо добио.

Виртуелне методе get_request_mode_vfunc(), measure_vfunc() и size_allocate_vfunc() контролишу распоред садржаних елемената. На пример, ако ваш контејнер има 2 садржана елемента, један испод другог, ваш get_request_mode_vfunc() може захтевати распоред висине за ширину. Затим ваш measure_vfunc() може пријавити максимум ширина садржаних елемената када се тражи ширина, а може пријавити збир њихових висина када се тражи висина. Ако желите размак између садржаних елемената, онда бисте то такође додали ширини и висини. Контејнер вашег елемента ће користити овај резултат како би осигурао да ваш елемент добије довољно простора, а не мање. Испитивањем родитеља сваког елемента, и његовог родитеља, ова логика ће на крају одлучити о величини прозора највишег нивоа.

Није вам загарантовано да ћете добити `Gtk::SizeRequestMode` који захтевате. Стога measure_vfunc() мора вратити разумне вредности за све прихватљиве вредности својих улазних параметара. За опис параметара методе measure_vfunc() погледајте такође опис методе Gtk::Widget::measure(), која може бити боље документована од measure_vfunc().

size_allocate_vfunc() прима стварну висину и ширину коју је родитељски контејнер одлучио да додели вашем елементу. Ово може бити више од минимума, или чак више од природне величине, на пример ако је прозор највишег нивоа проширен. Можете изабрати да игноришете вишак простора и оставите празну област, или можете изабрати да проширите своје садржане елементе како би попунили простор, или можете изабрати да проширите размак између својих елемената. То је ваш контејнер, па ви одлучујете.

Ваш контејнер мора уклонити родитељство својим потомцима пре него што се основни C објекат ( gtkmm__GtkWidget) финализује. Ако се ваш контејнер користи као управљани елемент, он ће уклонити родитељство својим потомцима у руковаоцу Gtk::Widget::signal_destroy() (доступно од верзије gtkmm 4.8). Ако ваш контејнер није управљан, тај руковалац сигнала се не позива. Уместо тога, родитељство потомака ће бити уклоњено у C++ деструктору. Ако желите да ваш контејнер буде користан на оба начина, уклоните родитељство потомцима и у деструктору и у руковаоцу сигнала. Погледајте код примера.

#### Пример

Овај пример имплементира контејнер са садржаним елементима, један изнад другог. Наравно, у овом случају би било далеко једноставније само користити усправни Gtk::Box или Gtk::Grid.

Произвољни контејнер

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>
    #include "mycontainer.h"

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      //Signal handlers:
      void on_button_quit();

      //Child widgets:
      Gtk::Box m_VBox;
      MyContainer m_MyContainer;
      Gtk::Button m_Button_Child;
      Gtk::Label m_Label_Child;
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `mycontainer.h` (For use with gtkmm 4)

    #ifndef GTKMM_CUSTOM_CONTAINER_MYCONTAINER_H
    #define GTKMM_CUSTOM_CONTAINER_MYCONTAINER_H

    #include <gtkmm/widget.h>
    #include <gtkmm/version.h>

    #define HAS_SIGNAL_DESTROY GTKMM_CHECK_VERSION(4,7,1)

    class MyContainer : public Gtk::Widget
    {
    public:
      MyContainer();
      ~MyContainer() override;

      void append(Gtk::Widget& child);
      void prepend(Gtk::Widget& child);
      void remove(Gtk::Widget& child);

    protected:
      int get_nvis_children() const;

      //Overrides:
      Gtk::SizeRequestMode get_request_mode_vfunc() const override;
      void measure_vfunc(Gtk::Orientation orientation, int for_size, int& minimum, int& natural,
        int& minimum_baseline, int& natural_baseline) const override;
      void size_allocate_vfunc(int width, int height, int baseline) override;

    #if HAS_SIGNAL_DESTROY
      // Signal handler:
      void on_container_destroy();
    #endif
    };

    #endif //GTKMM_CUSTOM_CONTAINER_MYCONTAINER_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Child("Child button"),
      m_Label_Child("Child label", Gtk::Align::END, Gtk::Align::CENTER),
      m_Button_Quit("Quit")
    {
      set_title("Custom Container example");
      set_default_size(400, 200);

      m_VBox.set_margin(6);
      set_child(m_VBox);

      //Add the child widgets to the custom container:
      m_MyContainer.append(m_Button_Child);
      m_MyContainer.append(m_Label_Child);
      m_MyContainer.prepend(*Gtk::make_managed<Gtk::Label>(
        "First line\nSecond line\nThird line"));
      m_MyContainer.set_expand();
      m_VBox.append(m_MyContainer);

    #if HAS_SIGNAL_DESTROY
      // A managed custom container.
      auto container = Gtk::make_managed<MyContainer>();
      container->prepend(*Gtk::make_managed<Gtk::Label>("Second custom container"));
      container->set_expand();
      m_VBox.append(*container);
    #endif
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(6);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `mycontainer.cc` (For use with gtkmm 4)

    #include "mycontainer.h"

    #include <algorithm> // std::max()

    // This example container is a simplified vertical Box.
    //
    // It can't be used as a managed widget, managed by another container
    // unless Gtk::Widget::signal_destroy() exists.
    // It would cause an error like
    // Gtk-WARNING **: 08:31:48.137: Finalizing gtkmm__GtkWidget 0x561b777462c0, but it still has children left:

    MyContainer::MyContainer()
    {
    #if HAS_SIGNAL_DESTROY
      signal_destroy().connect(sigc::mem_fun(*this, &MyContainer::on_container_destroy));
    #endif
    }

    MyContainer::~MyContainer()
    {
      // If MyContainer is a managed widget, the underlying C object is destructed
      // before this C++ destructor is executed.
      if (!gobj())
        return;

      // If MyContainer is not a managed widget, unparent all children here.
      while (Widget* child = get_first_child())
        child->unparent();
    }

    #if HAS_SIGNAL_DESTROY
    // This signal handler is called only if MyContainer is a managed widget.
    void MyContainer::on_container_destroy()
    {
      while (Widget* child = get_first_child())
        child->unparent();
    }
    #endif

    // Get number of visible children.
    int MyContainer::get_nvis_children() const
    {
      int nvis_children = 0;
      for (const Widget* child = get_first_child(); child; child = child->get_next_sibling())
        if (child->get_visible())
          ++nvis_children;

      return nvis_children;
    }

    Gtk::SizeRequestMode MyContainer::get_request_mode_vfunc() const
    {
      return Gtk::SizeRequestMode::HEIGHT_FOR_WIDTH;
    }

    // Discover the total amount of minimum space and natural space needed by
    // this container and its children.
    void MyContainer::measure_vfunc(Gtk::Orientation orientation, int for_size,
      int& minimum, int& natural, int& minimum_baseline, int& natural_baseline) const
    {
      // Don't use baseline alignment.
      minimum_baseline = -1;
      natural_baseline = -1;

      minimum = 0;
      natural = 0;

      // Number of visible children.
      const int nvis_children = get_nvis_children();

      if (orientation == Gtk::Orientation::HORIZONTAL)
      {
        // Divide the height equally among the visible children.
        if (for_size > 0 && nvis_children > 0)
          for_size /= nvis_children;

        // Request a width equal to the width of the widest visible child.
      }

      for (const Widget* child = get_first_child(); child; child = child->get_next_sibling())
        if (child->get_visible())
        {
          int child_minimum, child_natural, ignore;
          child->measure(orientation, for_size, child_minimum, child_natural, ignore, ignore);
          minimum = std::max(minimum, child_minimum);
          natural = std::max(natural, child_natural);
        }

      if (orientation == Gtk::Orientation::VERTICAL)
      {
        // The allocated height will be divided equally among the visible children.
        // Request a height equal to the number of visible children times the height
        // of the highest child.
        minimum *= nvis_children;
        natural *= nvis_children;
      }
    }

    void MyContainer::size_allocate_vfunc(int width, int height, int baseline)
    {
      //Do something with the space that we have actually been given:
      //(We will not be given heights or widths less than we have requested, though
      //we might get more.)

      // Number of visible children.
      const int nvis_children = get_nvis_children();

      if (nvis_children <= 0)
      {
        // No visible child.
        return;
      }

      //Assign space to the children:
      Gtk::Allocation child_allocation;
      const int height_per_child = height / nvis_children;

      //Place the first visible child at the top-left:
      child_allocation.set_x(0);
      child_allocation.set_y(0);

      //Make it take up the full width available:
      child_allocation.set_width(width);
      child_allocation.set_height(height_per_child);

      //Divide the height equally among the visible children.
      for (Widget* child = get_first_child(); child; child = child->get_next_sibling())
        if (child->get_visible())
        {
          child->size_allocate(child_allocation, baseline);
          child_allocation.set_y(child_allocation.get_y() + height_per_child);
        }
    }

    void MyContainer::append(Gtk::Widget& child)
    {
       child.insert_at_end(*this);
    }

    void MyContainer::prepend(Gtk::Widget& child)
    {
       child.insert_at_start(*this);
    }

    void MyContainer::remove(Gtk::Widget& child)
    {
      child.unparent();
    }

### Произвољни елементи

Извођењем директно из Gtk::Widget можете директно обавити сво цртање за свој елемент, уместо да само распоређујете садржане елементе. На пример, Gtk::Label црта текст натписа, али то не чини коришћењем других елемената.

Када изводите из Gtk::Widget, требало би да преклопите следеће виртуелне методе. Методе означене са (опционо) не морају бити преклопљене у свим произвољним елементима. Методе основне класе могу бити одговарајуће.

- get_request_mode_vfunc(): (опционо) Враћа који `Gtk::SizeRequestMode` елемент преферира.

- measure_vfunc(): Израчунава минималну и природну ширину или висину елемента.

- size_allocate_vfunc(): (опционо) Поставља елемент, на основу висине и ширине које је заправо добио.

- on_realize(): (опционо)

- on_unrealize(): (опционо)

- on_map(): (опционо)

- on_unmap(): (опционо)

- snapshot_vfunc(): Направите чвор исцртавања, нпр. чвор Cairo::Context, и цртајте на њему.

Прве 3 методе у претходној табели су такође преклопљене у произвољним контејнерима. Оне су укратко описане у одељку Произвољни контејнери.

#### Функције за покретање класе и покретање примерка

Неке GTK функције, ако се уопште позивају, морају бити позване из функције за покретање класе. Неке друге GTK функције, ако се позивају, морају бити позване из функције за покретање примерка. Ако ваш произвољни елемент мора да позове било коју од тих функција, можете извести класу из Glib::ExtraClassInit и извести вашу произвољну класу из те класе. Пример назива произвољног CSS-а показује како се то ради.

#### Пример

Овај пример имплементира елемент који црта Пенроузове троуглове.

Произвољни елемент

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>
    #include "mywidget.h"

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_quit();

      //Child widgets:
      Gtk::Grid m_Grid;
      MyWidget m_MyWidgetS1;
      MyWidget m_MyWidgetS2;
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `mywidget.h` (For use with gtkmm 4)

    #ifndef GTKMM_CUSTOM_WIDGET_MYWIDGET_H
    #define GTKMM_CUSTOM_WIDGET_MYWIDGET_H

    #include <gtkmm/widget.h>
    #include <gtkmm/border.h>
    #include <gdkmm/rgba.h>

    class MyWidget : public Gtk::Widget
    {
    public:
      MyWidget();
      ~MyWidget() override;

    protected:
      // Overrides:
      Gtk::SizeRequestMode get_request_mode_vfunc() const override;
      void measure_vfunc(Gtk::Orientation orientation, int for_size, int& minimum, int& natural,
        int& minimum_baseline, int& natural_baseline) const override;
      void on_map() override;
      void on_unmap() override;
      void on_realize() override;
      void on_unrealize() override;
      void snapshot_vfunc(const Glib::RefPtr<Gtk::Snapshot>& snapshot) override;

      Gtk::Border m_padding;
      Gdk::RGBA m_foreground{0.0, 0.0, 1.0};
      Gdk::RGBA m_background{1.0, 0.0, 0.0};
    };

    #endif //GTKMM_CUSTOM_WIDGET_MYWIDGET_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_Button_Quit("Quit")
    {
      set_title("Custom Widget example");
      set_default_size(600, 400);

      m_Grid.set_margin(6);
      m_Grid.set_row_spacing(10);
      m_Grid.set_column_spacing(10);

      set_child(m_Grid);

      m_Grid.attach(m_MyWidgetS1, 0, 0);
      m_Grid.attach(m_MyWidgetS2, 1, 1);

      m_Grid.attach(m_ButtonBox, 0, 2, 2, 1);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(6);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this, &ExampleWindow::on_button_quit) );
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `mywidget.cc` (For use with gtkmm 4)

    #include "mywidget.h"
    #include <gdkmm/general.h>  // for Gdk::Cairo:: functions
    #include <gtkmm/snapshot.h>

    MyWidget::MyWidget()
    {
      // Expand, if there is extra space.
      set_expand(true);

      m_padding.set_left(5);
      m_padding.set_right(15);
      m_padding.set_top(10);
      m_padding.set_bottom(20);
    }

    MyWidget::~MyWidget()
    {
    }

    Gtk::SizeRequestMode MyWidget::get_request_mode_vfunc() const
    {
      //Accept the default value supplied by the base class.
      return Gtk::Widget::get_request_mode_vfunc();
    }

    //Discover the total amount of minimum space and natural space needed by
    //this widget.
    //Let's make this simple example widget always need minimum 60 by 50 and
    //natural 100 by 70.
    void MyWidget::measure_vfunc(Gtk::Orientation orientation, int /* for_size */,
      int& minimum, int& natural, int& minimum_baseline, int& natural_baseline) const
    {
      if (orientation == Gtk::Orientation::HORIZONTAL)
      {
        minimum = 60;
        natural = 100;
      }
      else
      {
        minimum = 50;
        natural = 70;
      }

      // Don't use baseline alignment.
      minimum_baseline = -1;
      natural_baseline = -1;
    }

    void MyWidget::on_map()
    {
      //Call base class:
      Gtk::Widget::on_map();
    }

    void MyWidget::on_unmap()
    {
      //Call base class:
      Gtk::Widget::on_unmap();
    }

    void MyWidget::on_realize()
    {
      //Call base class:
      Gtk::Widget::on_realize();
    }

    void MyWidget::on_unrealize()
    {
      //Call base class:
      Gtk::Widget::on_unrealize();
    }

    void MyWidget::snapshot_vfunc(const Glib::RefPtr<Gtk::Snapshot>& snapshot)
    {
      const auto allocation = get_allocation();
      const Gdk::Rectangle rect(0, 0, allocation.get_width(), allocation.get_height());

      // Create a cairo context to draw on.
      auto cr = snapshot->append_cairo(rect);

      // paint the background
      Gdk::Cairo::set_source_rgba(cr, m_background);
      Gdk::Cairo::add_rectangle_to_path(cr, rect);
      cr->fill();

      // draw the foreground
      const double scale_x = 0.001 * (allocation.get_width() - m_padding.get_left() - m_padding.get_right());
      const double scale_y = 0.001 * (allocation.get_height() - m_padding.get_top() - m_padding.get_bottom());
      Gdk::Cairo::set_source_rgba(cr, m_foreground);
      cr->translate(m_padding.get_left(), m_padding.get_top());
      cr->rectangle(0.0, 0.0, 1000.0*scale_x, 1000.0*scale_y);
      cr->move_to(155.*scale_x, 165.*scale_y);
      cr->line_to(155.*scale_x, 838.*scale_y);
      cr->line_to(265.*scale_x, 900.*scale_y);
      cr->line_to(849.*scale_x, 564.*scale_y);
      cr->line_to(849.*scale_x, 438.*scale_y);
      cr->line_to(265.*scale_x, 100.*scale_y);
      cr->line_to(155.*scale_x, 165.*scale_y);
      cr->move_to(265.*scale_x, 100.*scale_y);
      cr->line_to(265.*scale_x, 652.*scale_y);
      cr->line_to(526.*scale_x, 502.*scale_y);
      cr->move_to(369.*scale_x, 411.*scale_y);
      cr->line_to(633.*scale_x, 564.*scale_y);
      cr->move_to(369.*scale_x, 286.*scale_y);
      cr->line_to(369.*scale_x, 592.*scale_y);
      cr->move_to(369.*scale_x, 286.*scale_y);
      cr->line_to(849.*scale_x, 564.*scale_y);
      cr->move_to(633.*scale_x, 564.*scale_y);
      cr->line_to(155.*scale_x, 838.*scale_y);
      cr->stroke();
    }

### Називи произвољног CSS-а

Многи аспекти изгледа елемената су контролисани CSS (Cascading Style Sheet) датотекама. Помоћу CSS датотека можете бирати боју, фонт, дебљину линије, итд. Ако неким елементима дате сопствена имена или сопствене CSS класе, можете дефинисати CSS правила која се примењују само на те елементе, на пример одређену дугмад, без утицаја на друге сличне елементе.

#### Назив CSS чвора, назив елемента, назив CSS класе

Постоје три начина упућивања из елемента на податке у CSS датотеци:

- gtk_widget_class_set_css_name() се може позвати само из функције за покретање класе. Поставља назив CSS чвора за све примерке класе (GType). Погледајте одељак Функције за покретање класе и покретање примерка.

- Gtk::Widget::set_name() се може позвати из C++ конструктора. Поставља назив примерка елемента.

- Gtk::Widget::add_class_name() се може позвати из C++ constructor. Додаје назив CSS класе коју користи примерак елемента.

Следећи пример приказује дугме са сопственим називом CSS чвора, натпис са називом елемента и натпис који користи сопствену CSS класу.

#### Подаци о произвољном стилу

Да бисте додали стилски лист апликацији, користите једну од Gtk::CssProvider::load_from_*() метода. Затим га додајте помоћу Gtk::StyleProvider::add_provider_for_display() (доступно од gtkmm 4.10) или Gtk::StyleContext::add_provider_for_display(). Gtk::StyleContext такође садржи методе за читање неких података о стилу, али је ова класа застарела од gtkmm 4.10.

CSS датотеке су описане у документацији за GTK.

#### Пример

Овај пример спроводи дугме и два натписа са произвољним подацима о стилу.

Произвољни ЦСС назив

Изворни ко̂д

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>
    #include "mybutton.h"

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handlers:
      void on_button_quit();
      static void on_parsing_error(const Glib::RefPtr<const Gtk::CssSection>& section,
        const Glib::Error& error);

      // Child widgets:
      Gtk::Box m_VBox;
      MyButton m_Button_Child;
      Gtk::Label m_Label_Child1;
      Gtk::Label m_Label_Child2;
      Gtk::Box m_ButtonBox;
      Gtk::Button m_Button_Quit;

      Glib::RefPtr<Gtk::CssProvider> m_refCssProvider;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `mybutton.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_MYBUTTON_H
    #define GTKMM_EXAMPLE_MYBUTTON_H

    #include <gtkmm/button.h>
    #include "myextrainit.h"

    class MyButton : public MyExtraInit, public Gtk::Button
    {
    public:
      MyButton(const Glib::ustring& label, bool mnemonic=false);
      ~MyButton() override;
    };

    #endif //GTKMM_EXAMPLE_MYBUTTON_H

File: `myextrainit.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_MYEXTRAINIT_H
    #define GTKMM_EXAMPLE_MYEXTRAINIT_H

    #include <glibmm/extraclassinit.h>
    #include <glibmm/ustring.h>

    // Calls gtk_widget_class_set_css_name() in the class init function.
    class MyExtraInit : public Glib::ExtraClassInit
    {
    public:
      MyExtraInit(const Glib::ustring& css_name);

    private:
      Glib::ustring m_css_name;
    };

    #endif //GTKMM_EXAMPLE_MYEXTRAINIT_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>
    #include <gtkmm/version.h>

    #define HAS_STYLE_PROVIDER_ADD_PROVIDER_FOR_DISPLAY GTKMM_CHECK_VERSION(4,9,1)

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_Child("Button with custom CSS node name"),
      m_Label_Child1("Label with custom name"),
      m_Label_Child2("Label with custom CSS class"),
      m_Button_Quit("Quit")
    {
      set_title("Custom CSS name example");
      set_default_size(400, 200);

      m_VBox.set_margin(10);
      m_VBox.set_spacing(5);
      set_child(m_VBox);

      // There are 3 ways to set custom CSS data for a widget:
      // 1. The CSS name can be set for a GType with gtk_widget_class_set_css_name().
      // 2. The CSS name can be set for a widget instance with gtk_widget_set_name()
      //    (Gtk::Widget::set_name()).
      // 3. A CSS class can be set for a widget instance with
      //    gtk_widget_add_css_class() (Gtk::Widget::add_css_class()).
      //
      // gtk_widget_class_set_css_name(), if used, must be called in the class init
      // function. It has not been wrapped in a C++ function.
      // Gtk::Widget::set_name() and Gtk::Widget::add_css_class() can be called in
      // a C++ constructor.

      // CSS widget name, which must be used in the CSS file, preceded by "#".
      m_Label_Child1.set_name("my-label1");

      // CSS class name, which must be used in the CSS file, preceded by ".".
      m_Label_Child2.add_css_class("my-label2");

      // Add the child widgets to m_VBox:
      m_VBox.append(*Gtk::make_managed<Gtk::Label>("Ordinary label"));
      m_VBox.append(m_Button_Child);
      m_VBox.append(m_Label_Child1);
      m_VBox.append(m_Label_Child2);
      m_VBox.set_expand();
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_Button_Quit);
      m_ButtonBox.set_margin(6);
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_quit) );

      // Load extra CSS file.
      m_refCssProvider = Gtk::CssProvider::create();
    #if HAS_STYLE_PROVIDER_ADD_PROVIDER_FOR_DISPLAY
      Gtk::StyleProvider::add_provider_for_display(get_display(), m_refCssProvider,
        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    #else
      Gtk::StyleContext::add_provider_for_display(get_display(), m_refCssProvider,
        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    #endif

      m_refCssProvider->signal_parsing_error().connect(
        [](const auto& section, const auto& error)
        { on_parsing_error(section, error); }
      );
      m_refCssProvider->load_from_path("custom_gtkmm.css");
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_parsing_error(const Glib::RefPtr<const Gtk::CssSection>& section,
      const Glib::Error& error)
    {
      std::cerr << "on_parsing_error(): " << error.what() << std::endl;
      if (section)
      {
        const auto file = section->get_file();
        if (file)
        {
          std::cerr << "  URI = " << file->get_uri() << std::endl;
        }

        auto start_location = section->get_start_location();
        auto end_location = section->get_end_location();
        std::cerr << "  start_line = " << start_location.get_lines()+1
                  << ", end_line = " << end_location.get_lines()+1 << std::endl;
        std::cerr << "  start_position = " << start_location.get_line_chars()
                  << ", end_position = " << end_location.get_line_chars() << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

File: `mybutton.cc` (For use with gtkmm 4)

    #include "mybutton.h"
    #include <gtk/gtk.h> // For GTK_IS_BUTTON()
    #include <iostream>

    MyButton::MyButton(const Glib::ustring& label, bool mnemonic)
      :
      // The GType name will actually be gtkmm__CustomObject_MyButton
      Glib::ObjectBase("MyButton"),
      MyExtraInit("my-button"), // CSS node name, which must be used in the CSS file.
      Gtk::Button(label, mnemonic)
    {
      // This shows the GType name:
      std::cout << "GType name: " << G_OBJECT_TYPE_NAME(gobj()) << std::endl;

      // This shows that the GType still derives from GtkButton:
      std::cout << "Gtype is a GtkButton?: " << GTK_IS_BUTTON(gobj()) << std::endl;

      // The CSS name can be set either
      // - for a GType (in this case for your custom class) with gtk_widget_class_set_css_name(), or
      // - for a widget instance with gtk_widget_set_name() (Gtk::Widget::set_name()).
      //
      // gtk_widget_class_set_css_name(), if used, must be called in the class init function.
      // It has not been wrapped in a C++ function.
      // Gtk::Widget::set_name() can be called in a C++ constructor.
      //
      // Another alternative: The custom button inherits the CSS name "button" from
      // GtkButton. That name can be used in the CSS file.
    }

    MyButton::~MyButton()
    {
    }

File: `myextrainit.cc` (For use with gtkmm 4)

    #include "myextrainit.h"
    #include <gtkmm/widget.h>
    #include <gtk/gtk.h>

    namespace
    {
    using BaseObjectType = GtkWidget;
    using BaseClassType = GtkWidgetClass;

    // These callback functions are called from GLib (a C library).
    // They shall have C linkage. (Many compilers accept callback functions
    // with C++ linkage, but such a program has undefined behavior.)
    //
    // If you want the functions with C linkage to have internal linkage,
    // they must be declared 'static', even though they are defined in an anonymous
    // namespace. The compiler respects namespace declarations of functions
    // with C linkage, but the linker does not.
    extern "C"
    {
    // Extra class init function.
    static void class_init_function(void* g_class, void* class_data)
    {
      g_return_if_fail(GTK_IS_WIDGET_CLASS(g_class));

      const auto klass = static_cast<BaseClassType*>(g_class);
      const auto css_name = static_cast<Glib::ustring*>(class_data);

      gtk_widget_class_set_css_name(klass, css_name->c_str());
    }

    // Extra instance init function.
    static void instance_init_function(GTypeInstance* instance, void* /* g_class */)
    {
      g_return_if_fail(GTK_IS_WIDGET(instance));

      // Nothing to do here.
      // This extra instance init function just shows how such a function can
      // be added to a custom widget, if necessary.
    }
    } // extern "C"
    } // anonymous namespace

    MyExtraInit::MyExtraInit(const Glib::ustring& css_name)
    :
    Glib::ExtraClassInit(class_init_function, &m_css_name, instance_init_function),
    m_css_name(css_name)
    {
    }

File: `custom_gtkmm.css` (For use with gtkmm 4)

    /* Example of a CSS style sheet. */

    my-button {
      background-color: red;
      color:            blue;
      padding:          10px 15px 20px 5px; /* top right bottom left */
      border:           solid black 2px;
      border-radius:    8px;
    }
    my-button:active {
      background-color: rgb(0,255,0);
    }
    #my-label1 {
      background-color: rgb(180,180,180);
      color:            rgb(0,255,255);
      padding:          10px 15px 10px 25px; /* top right bottom left */
    }
    #my-label1:hover {
      background-color: rgb(220,220,220);
    }
    .my-label2 {
      background-color: rgb(180,180,100);
      color:            black;
      padding:          5px 15px 5px 25px; /* top right bottom left */
    }
    .my-label2:hover {
      background-color: rgb(220,220,140);
    }

## Вишенитни програми

### Ограничења

Потребна је пажња при писању програма заснованих на gtkmm-у који користе више нити извршавања, што произилази из чињенице да libsigc++, а посебно sigc::trackable, нису безбедни по питању нити. То је зато што ниједна од сложених интеракција које се дешавају иза сцене при коришћењу libsigc++-а није заштићена мутексом или другим средствима усаглашавања.

Ове интеракције произилазе из чињенице да ће, између осталог, класа која наслеђује из sigc::trackable, путем тог наслеђивања, имати std::list објекат који прати места направљена позивима функције `sigc::mem_fun()` која представља било који од њених нестатичких метода (тачније, чува списак повратних позива који ће поништити повезана места при њеном уништавању). Сваки sigc::slot објекат такође чува, путем sigc::slot_rep, сопствени sigc::trackable објекат како би пратио било које sigc::connection објекте о чијем престанку треба да обавести, а такође има и функцију за сопствено одјављивање из било ког sigc::trackable објекта при прекиду везе или уништавању. sigc::signal објекти такође чувају спискове места, који ће бити ажурирани позивом њивог connect() метода или позивима било ког sigc::connection објекта који се односи на такву везу.

#### Правила

Ово захтева поштовање одређеног броја правила при писању вишенитних програма коришћењем gtkmm-а. Она су наведена испод, али треба напоменути да је посебна пажња потребна при извођењу класа из sigc::trackable, јер су ефекти неинтуитивни (погледајте нарочито тачке 4 и 5 испод).

1. Користите Glib::Dispatcher за призивање gtkmm функција из радних нити (ово је детаљније обрађено у следећем одељку).

2. sigc::signal објекат треба сматрати власништвом нити која га је направила. Само та нит треба да повезује sigc::slot објекат са објектом сигнала, и само та нит треба да емитује (emit()) или позива operator()() над сигналом, или да поништава било који повезани sigc::slot објекат. Из тога следи (између осталог) да било који објекат сигнала који обезбеђује gtkmm елемент треба користити само у главној ГУИ нити, а било који објекат изведен из sigc::trackable чији су нестатички методи наведени местима повезаним са објектом сигнала треба уништавати само у тој нити.

3. Било који sigc::connection објекат треба сматрати власништвом нити у којој је позван метод који враћа sigc::connection објекат. Само та нит треба да позива sigc::connection методе над тим објектом.

4. sigc::slot објекат направљен позивом функције `sigc::mem_fun()` који упућује на метод класе изведене из sigc::trackable никада не би требало умножавати у другу нит, нити би га смела уништити нит различита од оне која га је направила.

5. Ако одређени објекат класе потиче из sigc::trackable, само једна нит треба да прави sigc::slot објекте који представљају било који од нестатичких метода класе позивањем функције `sigc::mem_fun()`. Прву нит која направи такво место треба сматрати власником релевантног објекта у сврху прављења даљих места која упућују на било који од његових нестатичких метода коришћењем те функције, или поништавања тих места прекидањем њихове везе или уништавањем пративог објекта.

6. Иако је сам glib безбедан по питању нити, било који glibmm омотачи који користе libsigc++ то неће бити. Тако на пример, само нит у којој се извршава главна петља треба да позива Glib::SignalIdle::connect(), Glib::SignalIO::connect(), Glib::SignalTimeout::connect(), Glib::SignalTimeout::connect_seconds за ту главну петљу, или да управља било којим sigc::connection објектом који они врате.

   Varijante connect*_once(), Glib::SignalIdle::connect_once(), Glib::SignalTimeout::connect_once(), Glib::SignalTimeout::connect_seconds_once(), безбедне су по питању нити у сваком случају где место није направљено позивом функције `sigc::mem_fun()` која представља метод класе изведене из sigc::trackable.

### Коришћење Glib::Dispatcher-а

Места повезана са sigc::signal објектима извршавају се у нити која позива emit() или operator()() над сигналом. Glib::Dispatcher се не понаша на овај начин: уместо тога, његова повезана места се извршавају у нити у којој је Glib::Dispatcher објекат изграђен (која мора имати glib главну петљу). Ако је Glib::Dispatcher објекат изграђен у главној ГУИ нити (која ће стога бити пријемна нит), било која радна нит може емитовати над њим и учинити да повезана места безбедно извршавају gtkmm функције.

Нека правила о безбедности нити при коришћењу Glib::Dispatcher-а и даље важе. Као што је поменуто, Glib::Dispatcher објекат мора бити изграђен у пријемној нити (нити у чијој главној петљи ће извршавати своја повезана места). По подразумеваним подешавањима ово је нит главног програма, иако постоји Glib::Dispatcher конструктор који може примити Glib::MainContext објекат било које нити која има главну петљу. Само пријемна нит треба да позива connect() над Glib::Dispatcher објектом, или да управља било којим повезаним sigc::connection објектом, осим ако се не примени додатно усаглашавање. Међутим, било која радна нит може безбедно емитовати над Glib::Dispatcher објектом без икаквог закључавања након што је пријемна нит повезала места, под условом да је изграђен пре него што је радна нит покренута (ако је изграђен након што је нит покренута, обично ће бити потребно додатно усаглашавање како би се осигурала видљивост).

Осим чињенице да се повезани слотови увек извршавају у нити примаоца, објекти Glib::Dispatcher су слични објектима sigc::signal<void()>. Стога не могу преносити невезане аргументе нити враћати вредност. Најбољи начин за пренос невезаних аргумената је помоћу безбедног (асинхроног) реда нити. У време писања glibmm га нема, иако ће га већина људи који пишу вишенитни код имати на располагању (релативно их је лако написати, иако постоје суптилности у комбиновању безбедности нити са јаком безбедношћу изузетака).

Објекат Glib::Dispatcher може бити емитован од стране нити примаоца као и од стране радне нити, иако би то требало радити у разумним границама. На системима налик јуниксу, објекти Glib::Dispatcher деле једну заједничку спојку, која би у теорији барем могла да се попуни на веома оптерећеном систему који покреће програм са веома великим бројем објеката Dispatcher у употреби. Ако би се спојка попунила пре него што главна петља нити примаоца добије прилику да чита из ње како би је испразнила, а нит примаоца покуша да емитује и тако пише у њу када је у том стању, нит примаоца би се блокирала при писању, што би довело до мртвог чвора. Тамо где нит примаоца треба да емитује, уместо тога се наравно може користити обичан објекат sigc::signal<void()>.

### Пример

Ово је пример програма са две нити, једном ГУИ нити, као у свим gtkmm програмима, и једном радном нити. Радна нит се прави када притиснете дугме `Start work`. Брише се када се посао заврши, када притиснете дугме `Stop work` или када притиснете дугме `Quit`.

Glib::Dispatcher се користи за слање обавештења из радне нити у ГУИ нит. Класа ExampleWorker садржи податке којима приступају обе нити. Ови подаци су заштићени помоћу std::mutex. Само ГУИ нит ажурира ГУИ.

Превођење и повезивање вишенитног програма може захтевати посебне опције преводиоца и повезивача. Ако користите g++ преводилац, додајте опцију `-pthread`. Други преводиоци могу захтевати друге опције. Ако градите помоћу meson-а, он решава компликације вишенитног рада уместо вас, ако додате `dependency('threads')`.

Вишенитни програм

Изворни код

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>
    #include "exampleworker.h"

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

      // Called from the worker thread.
      void notify();

    private:
      // Signal handlers.
      void on_start_button_clicked();
      void on_stop_button_clicked();
      void on_quit_button_clicked();

      void update_start_stop_buttons();
      void update_widgets();

      // Dispatcher handler.
      void on_notification_from_worker_thread();

      // Member data.
      Gtk::Box m_VBox;
      Gtk::Box m_ButtonBox;
      Gtk::Button m_ButtonStart;
      Gtk::Button m_ButtonStop;
      Gtk::Button m_ButtonQuit;
      Gtk::ProgressBar m_ProgressBar;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::TextView m_TextView;

      Glib::Dispatcher m_Dispatcher;
      ExampleWorker m_Worker;
      std::thread* m_WorkerThread;
    };

    #endif // GTKMM_EXAMPLEWINDOW_H

File: `exampleworker.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWORKER_H
    #define GTKMM_EXAMPLEWORKER_H

    #include <gtkmm.h>
    #include <thread>
    #include <mutex>

    class ExampleWindow;

    class ExampleWorker
    {
    public:
      ExampleWorker();

      // Thread function.
      void do_work(ExampleWindow* caller);

      void get_data(double* fraction_done, Glib::ustring* message) const;
      void stop_work();
      bool has_stopped() const;

    private:
      // Synchronizes access to member data.
      mutable std::mutex m_Mutex;

      // Data used by both GUI thread and worker thread.
      bool m_shall_stop;
      bool m_has_stopped;
      double m_fraction_done;
      Glib::ustring m_message;
    };

    #endif // GTKMM_EXAMPLEWORKER_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow() :
      m_VBox(Gtk::Orientation::VERTICAL, 5),
      m_ButtonBox(Gtk::Orientation::HORIZONTAL),
      m_ButtonStart("Start work"),
      m_ButtonStop("Stop work"),
      m_ButtonQuit("_Quit", /* mnemonic= */ true),
      m_ProgressBar(),
      m_ScrolledWindow(),
      m_TextView(),
      m_Dispatcher(),
      m_Worker(),
      m_WorkerThread(nullptr)
    {
      set_title("Multi-threaded example");
      set_default_size(300, 300);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      // Add the ProgressBar.
      m_VBox.append(m_ProgressBar);

      m_ProgressBar.set_text("Fraction done");
      m_ProgressBar.set_show_text();

      // Add the TextView, inside a ScrolledWindow.
      m_ScrolledWindow.set_child(m_TextView);

      // Only show the scrollbars when they are necessary.
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);

      m_TextView.set_editable(false);

      // Add the buttons to the ButtonBox.
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.append(m_ButtonStart);
      m_ButtonBox.append(m_ButtonStop);
      m_ButtonBox.append(m_ButtonQuit);
      m_ButtonBox.set_margin(5);
      m_ButtonBox.set_spacing(5);
      m_ButtonStart.set_hexpand(true);
      m_ButtonStart.set_halign(Gtk::Align::END);

      // Connect the signal handlers to the buttons.
      m_ButtonStart.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_start_button_clicked));
      m_ButtonStop.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_stop_button_clicked));
      m_ButtonQuit.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_quit_button_clicked));

      // Connect the handler to the dispatcher.
      m_Dispatcher.connect(sigc::mem_fun(*this, &ExampleWindow::on_notification_from_worker_thread));

      // Create a text buffer mark for use in update_widgets().
      auto buffer = m_TextView.get_buffer();
      buffer->create_mark("last_line", buffer->end(), /* left_gravity= */ true);

      update_start_stop_buttons();
    }

    void ExampleWindow::on_start_button_clicked()
    {
      if (m_WorkerThread)
      {
        std::cout << "Can't start a worker thread while another one is running." << std::endl;
      }
      else
      {
        // Start a new worker thread.
        m_WorkerThread = new std::thread(
          [this]
          {
            m_Worker.do_work(this);
          });
      }
      update_start_stop_buttons();
    }

    void ExampleWindow::on_stop_button_clicked()
    {
      if (!m_WorkerThread)
      {
        std::cout << "Can't stop a worker thread. None is running." << std::endl;
      }
      else
      {
       // Order the worker thread to stop.
        m_Worker.stop_work();
        m_ButtonStop.set_sensitive(false);
      }
    }

    void ExampleWindow::update_start_stop_buttons()
    {
      const bool thread_is_running = m_WorkerThread != nullptr;

      m_ButtonStart.set_sensitive(!thread_is_running);
      m_ButtonStop.set_sensitive(thread_is_running);
    }

    void ExampleWindow::update_widgets()
    {
      double fraction_done;
      Glib::ustring message_from_worker_thread;
      m_Worker.get_data(&fraction_done, &message_from_worker_thread);

      m_ProgressBar.set_fraction(fraction_done);

      if (message_from_worker_thread != m_TextView.get_buffer()->get_text())
      {
        auto buffer = m_TextView.get_buffer();
        buffer->set_text(message_from_worker_thread);

        // Scroll the last inserted line into view. That's somewhat complicated.
        auto iter = buffer->end();
        iter.set_line_offset(0); // Beginning of last line
        auto mark = buffer->get_mark("last_line");
        buffer->move_mark(mark, iter);
        m_TextView.scroll_to(mark);
        // TextView::scroll_to(iter) is not perfect.
        // We do need a TextMark to always get the last line into view.
      }
    }

    void ExampleWindow::on_quit_button_clicked()
    {
      if (m_WorkerThread)
      {
        // Order the worker thread to stop and wait for it to stop.
        m_Worker.stop_work();
        if (m_WorkerThread->joinable())
          m_WorkerThread->join();
      }
      close();
    }

    // notify() is called from ExampleWorker::do_work(). It is executed in the worker
    // thread. It triggers a call to on_notification_from_worker_thread(), which is
    // executed in the GUI thread.
    void ExampleWindow::notify()
    {
      m_Dispatcher.emit();
    }

    void ExampleWindow::on_notification_from_worker_thread()
    {
      if (m_WorkerThread && m_Worker.has_stopped())
      {
        // Work is done.
        if (m_WorkerThread->joinable())
          m_WorkerThread->join();
        delete m_WorkerThread;
        m_WorkerThread = nullptr;
        update_start_stop_buttons();
      }
      update_widgets();
    }

File: `exampleworker.cc` (For use with gtkmm 4)

    #include "exampleworker.h"
    #include "examplewindow.h"
    #include <sstream>
    #include <chrono>

    ExampleWorker::ExampleWorker() :
      m_Mutex(),
      m_shall_stop(false),
      m_has_stopped(false),
      m_fraction_done(0.0),
      m_message()
    {
    }

    // Accesses to these data are synchronized by a mutex.
    // Some microseconds can be saved by getting all data at once, instead of having
    // separate get_fraction_done() and get_message() methods.
    void ExampleWorker::get_data(double* fraction_done, Glib::ustring* message) const
    {
      std::lock_guard<std::mutex> lock(m_Mutex);

      if (fraction_done)
        *fraction_done = m_fraction_done;

      if (message)
        *message = m_message;
    }

    void ExampleWorker::stop_work()
    {
      std::lock_guard<std::mutex> lock(m_Mutex);
      m_shall_stop = true;
    }

    bool ExampleWorker::has_stopped() const
    {
      std::lock_guard<std::mutex> lock(m_Mutex);
      return m_has_stopped;
    }

    void ExampleWorker::do_work(ExampleWindow* caller)
    {
      {
        std::lock_guard<std::mutex> lock(m_Mutex);
        m_has_stopped = false;
        m_fraction_done = 0.0;
        m_message = "";
      } // The mutex is unlocked here by lock's destructor.

      // Simulate a long calculation.
      for (int i = 0; ; ++i) // do until break
      {
        std::this_thread::sleep_for(std::chrono::milliseconds(250));

        {
          std::lock_guard<std::mutex> lock(m_Mutex);

          m_fraction_done += 0.01;

          if (i % 4 == 3)
          {
            std::ostringstream ostr;
            ostr << (m_fraction_done * 100.0) << "% done\n";
            m_message += ostr.str();
          }

          if (m_fraction_done >= 1.0)
          {
            m_message += "Finished";
            break;
          }
          if (m_shall_stop)
          {
            m_message += "Stopped";
            break;
          }
        }

        caller->notify();
      }

      {
        std::lock_guard<std::mutex> lock(m_Mutex);
        m_shall_stop = false;
        m_has_stopped = true;
      }

      caller->notify();
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

## Препоручене технике

Овај одељак је једноставно скуп мудрости, општих смерница за стил и савета за прављење gtkmm апликација.

Користите Meson! Он вам је пријатељ :) Он испитује C и C++ датотеке, одређује како оне зависе једна од друге и генерише `build.ninja` или еквивалентну датотеку како би се датотеке могле превести исправним редоследом. Meson омогућава аутоматско подешавање инсталације софтвера, решавајући велики број системских специфичности ради повећања преносивости.

Изводите класе елемената (widgets) како бисте боље организовали свој код. Вероватно би требало да изведете барем свој главни Window. Тада можете учинити своје подређене елементе и руковаоце сигналима члановима те класе.

Правите сопствене сигнале уместо да прослеђујете показиваче унаоколо. Објекти могу међусобно комуницирати путем сигнала и руковаоца сигналима. Ово је много једноставније него да објекти држе показиваче једни на друге и позивају методе једни других. Класе gtkmm-а користе посебне верзије sigc::signal, али би требало да користите обичне sigc::signal, као што је описано у документацији за libsigc++.

### Животни век апликације

Већина апликација ће имати само један Window, или само један главни прозор. Ове апликације могу користити Gtk::Application::make_window_and_run(int argc, char** argv, T_Args&&... args). Она прави и приказује прозор. Када се прозор затвори, make_window_and_run() брише прозор и враћа се позиваоцу. Ово се може догодити када корисник затвори прозор, или када ваш код одлучи да затвори прозор помоћу Gtk::Window::close(). Можете спречити корисника да затвори прозор (на пример, ако постоје несачуване измене) преклапањем Gtk::Window::on_close_request().

Већина наших примера користи ову технику.

### Коришћење gtkmm елемента

Сви наши примери теже да имају исту структуру. Они прате ове кораке за коришћење Widget-а:

1. Декларишите променљиву типа Widget који желите да користите, углавном као члана променљиве изведене класе контејнера. Такође можете декларисати показивач на тип елемента, а затим га направити помоћу `new` или `Gtk::make_managed()` у свом коду.

2. Поставите атрибуте елемента. Ако елемент нема подразумевани конструктор, тада ћете морати да иницијализујете елемент у листи иницијализатора конструктора ваше класе контејнера.

3. Повежите све сигнале које желите да користите са одговарајућим руковаоцима.

4. Спакујте елемент у контејнер користећи одговарајући позив, нпр. Gtk::Box::append().

Ако не желите да сви елементи буду приказани, позовите Gtk::Widget::set_visible(false) на елементима које не желите да прикажете. Ако је контејнер сакривен, сви његови садржани елементи су такође сакривени, чак и ако set_visible(false) није позвано на њима.

## Изградња програма

Ово поглавље је слично поглављу Изградња програма и следећим одељцима у поглављу Први кораци у ГТК документацији. Гради се исти програм, али се користи gtkmm уместо GTK-а.

Програм се састоји од низа датотека:

Бинарна датотека

  Она се инсталира у `/usr/bin`.

Датотека радне површине

  Датотека радне површине пружа важне податке о програму шкољки радне површине, као што су његов назив, иконица, Д-бас назив, линија наредби за покретање итд. Инсталира се у `/usr/share/applications`.

Иконица

  Иконица се инсталира у `/usr/share/icons/hicolor/48x48/apps`, где ће бити пронађена без обзира на тренутну тему.

Шема подешавања

  Ако програм користи Gio::Settings, инсталираће своју шему у `/usr/share/glib-2.0/schemas`, тако да алати као што је dconf-editor могу да је пронађу.

Други ресурси

  Друге датотеке, као што су Gtk::Builder датотеке корисничког прочеља, најбоље је учитавати из ресурса сачуваних у самој бинарној датотеци програма. Ово елиминише потребу за већином датотека које би се традиционално инсталирале на место специфично за програм у `/usr/share`.

gtkmm укључује подршку за програме која је изграђена на врху Gio::Application. У овом поглављу ћемо изградити једноставан програм почевши од нуле, додајући све више и више делова током времена. Уз пут ћемо научити о Gtk::Application, Gtk::Builder, ресурсима, изборницима, подешавањима, Gtk::HeaderBar, Gtk::Stack, Gtk::SearchBar, Gtk::ListBox и још много тога.

Потпуни изворни ко̂дови за ове примере који се могу изградити могу се наћи у директоријуму `examples/book/buildapp` изворне дистрибуције gtkmm-documentation, или на мрежи у гит ризници gtkmm-documentation. Сваки пример можете изградити засебно користећи `meson` и `ninja` са `meson.build` датотелом или користећи `make` са `Makefile.example` датотелом. За више података, погледајте `README` који се налази у директоријуму `buildapp`.

### Безначајан програм

Када се користи Gtk::Application, функција `main()` може бити врло једноставна. Само позивамо Gtk::Application::run() на примерку наше класе програма.

Сва логика програма је у класи програма, која је подкласа Gtk::Application. Наш пример још увек нема никакву занимљиву функционалност. Све што ради је да отвара прозор када се активира без аргумената, и отвара датотеке које су му дате, ако се покрене са аргументима. (Тачније, наша класа програма покушава да отвори датотеке, али наш прозор програма изведен у подкласи још увек не ради оно што му је речено.)

Да бисмо обрадили ова два случаја, преписујемо подразумевани руковалац за signal_activate(), који се позива када се програм покрене без аргумената линије наредби, и подразумевани руковалац за signal_open(), који се позива када се програм покрене са аргументима линије наредби.

Gio::Application референца

Gtk::Application референца

Још једна важна класа која је део подршке за програме у gtkmm-у је Gtk::ApplicationWindow. Обично се и она изводи у подкласу. Наша подкласа још увек не ради ништа, тако да ћемо само добити празан прозор.

Као део почетног подешавања нашег програма, такође правимо иконицу и датотеку радне површине. Имајте на уму да @bindir@ у датотеци радне површине треба да буде замењен стварном путањом до извршне датотеке пре него што се ова датотека радне површине може користити.

Ево шта смо до сада постигли:

Безначајан програм

Ово још увек не изгледа врло импресивно, али се наш програм већ представља на магистрали сесије, има семантику једног примерка и прихвата датотеке као аргументе линије наредби.

Изворни код

File: `exampleapplication.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPLICATION_H
    #define GTKMM_EXAMPLEAPPLICATION_H

    #include <gtkmm.h>

    class ExampleAppWindow;

    class ExampleApplication: public Gtk::Application
    {
    protected:
      ExampleApplication();

    public:
      static Glib::RefPtr<ExampleApplication> create();

    protected:
      // Override default signal handlers:
      void on_activate() override;
      void on_open(const Gio::Application::type_vec_files& files,
        const Glib::ustring& hint) override;

    private:
      ExampleAppWindow* create_appwindow();
    };

    #endif /* GTKMM_EXAMPLEAPPLICATION_H */

File: `exampleappwindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPWINDOW_H_
    #define GTKMM_EXAMPLEAPPWINDOW_H_

    #include <gtkmm.h>

    class ExampleAppWindow : public Gtk::ApplicationWindow
    {
    public:
      ExampleAppWindow();

      void open_file_view(const Glib::RefPtr<Gio::File>& file);
    };

    #endif /* GTKMM_EXAMPLEAPPWINDOW_H */

File: `exampleapplication.cc` (For use with gtkmm 4)

    #include "exampleapplication.h"
    #include "exampleappwindow.h"

    ExampleApplication::ExampleApplication()
    : Gtk::Application("org.gtkmm.examples.application", Gio::Application::Flags::HANDLES_OPEN)
    {
      // Delete a window when it is removed from the application.
      signal_window_removed().connect([](Gtk::Window* window) { delete window; });
    }

    Glib::RefPtr<ExampleApplication> ExampleApplication::create()
    {
      return Glib::make_refptr_for_instance<ExampleApplication>(new ExampleApplication());
    }

    ExampleAppWindow* ExampleApplication::create_appwindow()
    {
      auto appwindow = new ExampleAppWindow();

      // Make sure that the application runs for as long this window is still open.
      add_window(*appwindow);

      // A window can be added to an application with Gtk::Application::add_window()
      // or Gtk::Window::set_application(). When all added windows have been removed,
      // the application stops running (Gtk::Application::run() returns),
      // unless Gio::Application::hold() has been called.

      return appwindow;
    }

    void ExampleApplication::on_activate()
    {
      // The application has been started, so let's show a window.
      auto appwindow = create_appwindow();
      appwindow->present();
    }

    void ExampleApplication::on_open(const Gio::Application::type_vec_files& files,
      const Glib::ustring& /* hint */)
    {
      // The application has been asked to open some files,
      // so let's open a new view for each one.
      ExampleAppWindow* appwindow = nullptr;
      auto windows = get_windows();
      if (windows.size() > 0)
        appwindow = dynamic_cast<ExampleAppWindow*>(windows[0]);

      if (!appwindow)
        appwindow = create_appwindow();

      for (const auto& file : files)
        appwindow->open_file_view(file);

      appwindow->present();
    }

File: `exampleappwindow.cc` (For use with gtkmm 4)

    #include "exampleappwindow.h"

    ExampleAppWindow::ExampleAppWindow()
    : Gtk::ApplicationWindow()
    {
    }

    void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& /* file */)
    {
    }

File: `main.cc` (For use with gtkmm 4)

    #include "exampleapplication.h"

    int main(int argc, char* argv[])
    {
      auto application = ExampleApplication::create();

      // Start the application, showing the initial window,
      // and opening extra views for any files that it is asked to open,
      // for instance as a command-line parameter.
      // run() will return when the last window has been closed.
      return application->run(argc, argv);
    }

File: `exampleapp.desktop.in` (For use with gtkmm 4)

    [Desktop Entry]
    Type=Application
    Name=Gtkmm example
    GenericName=Example
    Comment=From the "Programming with gtkmm 4" tutorial
    Icon=exampleapp
    StartupNotify=true
    Exec=@bindir@/exampleapp %U
    Categories=GNOME;GTK;Utility

### Попуњавање прозора

У овом кораку користимо примерак Gtk::Builder-а да бисмо повезали Gtk::Builder корисничку датотеку са нашом класом прозора програма.

Наша једноставна корисничка датотека даје прозору наслов и поставља елемент Gtk::Stack као главни садржај.

Да бисмо искористили ову датотеку у нашем програму, поново посећујемо нашу подкласу Gtk::ApplicationWindow и позивамо Gtk::Builder::create_from_resource() и Gtk::Builder::get_widget_derived() из методе ExampleAppWindow::create() да бисмо добили примерак наше подкласе Gtk::ApplicationWindow. Погледајте одељак Коришћење изведених елемената за више информација о get_widget_derived().

Можда сте приметили да користимо варијанту методе _from_resource() која чита корисничку датотеку. Сада треба да користимо GLib-ову функционалност ресурса да бисмо укључили корисничку датотеку у извршну датотеку. То се обично ради навођењем свих ресурса у .gresource.xml датотеци. Ова датотека мора бити претворена у C изворну датотеку која ће бити компајлирана и повезана у програм заједно са осталим изворним датотекама. Да бисмо то урадили, користимо алатку glib-compile-resources:

    $ glib-compile-resources --target=resources.c --generate-source exampleapp.gresource.xml

Одељак Gio::Resource и glib-compile-resources садржи више информација о датотекама ресурса. Ако градите помоћу Мезона, користите функцију `compile_resources()` у Мезоновом ГНОМ модулу.

Наш програм сада изгледа овако:

Попуњавање прозора

Изворни код

File: `exampleapplication.h` (For use with gtkmm 4)

    #include "../step1/exampleapplication.h"
    // Equal to the corresponding file in step1

File: `exampleappwindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPWINDOW_H_
    #define GTKMM_EXAMPLEAPPWINDOW_H_

    #include <gtkmm.h>

    class ExampleAppWindow : public Gtk::ApplicationWindow
    {
    public:
      ExampleAppWindow(BaseObjectType* cobject,
        const Glib::RefPtr<Gtk::Builder>& refBuilder);

      static ExampleAppWindow* create();

      void open_file_view(const Glib::RefPtr<Gio::File>& file);

    protected:
      Glib::RefPtr<Gtk::Builder> m_refBuilder;
    };

    #endif /* GTKMM_EXAMPLEAPPWINDOW_H */

File: `exampleapplication.cc` (For use with gtkmm 4)

    #include "exampleapplication.h"
    #include "exampleappwindow.h"
    #include <iostream>
    #include <exception>

    ExampleApplication::ExampleApplication()
    : Gtk::Application("org.gtkmm.examples.application", Gio::Application::Flags::HANDLES_OPEN)
    {
      // Delete a window when it is removed from the application.
      signal_window_removed().connect([](Gtk::Window* window) { delete window; });
    }

    Glib::RefPtr<ExampleApplication> ExampleApplication::create()
    {
      return Glib::make_refptr_for_instance<ExampleApplication>(new ExampleApplication());
    }

    ExampleAppWindow* ExampleApplication::create_appwindow()
    {
      auto appwindow = ExampleAppWindow::create();

      // Make sure that the application runs for as long this window is still open.
      add_window(*appwindow);

      // A window can be added to an application with Gtk::Application::add_window()
      // or Gtk::Window::set_application(). When all added windows have been removed,
      // the application stops running (Gtk::Application::run() returns),
      // unless Gio::Application::hold() has been called.

      return appwindow;
    }

    void ExampleApplication::on_activate()
    {
      try
      {
        // The application has been started, so let's show a window.
        auto appwindow = create_appwindow();
        appwindow->present();
      }
      // If create_appwindow() throws an exception (perhaps from Gtk::Builder),
      // no window has been created, no window has been added to the application,
      // and therefore the application will stop running.
      catch (const Glib::Error& ex)
      {
        std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl;
      }
      catch (const std::exception& ex)
      {
        std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl;
      }
    }

    void ExampleApplication::on_open(const Gio::Application::type_vec_files& files,
      const Glib::ustring& /* hint */)
    {
      // The application has been asked to open some files,
      // so let's open a new view for each one.
      ExampleAppWindow* appwindow = nullptr;
      auto windows = get_windows();
      if (windows.size() > 0)
        appwindow = dynamic_cast<ExampleAppWindow*>(windows[0]);

      try
      {
        if (!appwindow)
          appwindow = create_appwindow();

        for (const auto& file : files)
          appwindow->open_file_view(file);

        appwindow->present();
      }
      catch (const Glib::Error& ex)
      {
        std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl;
      }
      catch (const std::exception& ex)
      {
        std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl;
      }
    }

File: `exampleappwindow.cc` (For use with gtkmm 4)

    #include "exampleappwindow.h"
    #include <stdexcept>

    ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
      const Glib::RefPtr<Gtk::Builder>& refBuilder)
    : Gtk::ApplicationWindow(cobject),
      m_refBuilder(refBuilder)
    {
    }

    //static
    ExampleAppWindow* ExampleAppWindow::create()
    {
      // Load the Builder file and instantiate its widgets.
      auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");

      auto window = Gtk::Builder::get_widget_derived<ExampleAppWindow>(refBuilder, "app_window");
      if (!window)
        throw std::runtime_error("No \"app_window\" object in window.ui");

      return window;
    }

    void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& /* file */)
    {
    }

File: `main.cc` (For use with gtkmm 4)

    #include "../step1/main.cc"
    // Equal to the corresponding file in step1

File: `exampleapp.gresource.xml` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <gresources>
      <gresource prefix="/org/gtkmm/exampleapp">
        <file preprocess="xml-stripblanks">window.ui</file>
      </gresource>
    </gresources>

File: `window.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <object class="GtkApplicationWindow" id="app_window">
        <property name="title" translatable="yes">Example Application</property>
        <property name="default-width">600</property>
        <property name="default-height">400</property>
        <child>
          <object class="GtkBox" id="content_box">
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkStack" id="stack"/>
            </child>
          </object>
        </child>
      </object>
    </interface>

### Отварање датотека

У овом кораку ћемо учинити да наш програм прикаже садржај свих датотека које му се проследе преко линије наредби.

У ту сврху, додајемо члан података у прозор нашег програма и тамо чувамо показивач на Gtk::Stack. Показивач добијамо позивом Gtk::Builder::get_widget() у конструктору прозора програма.

Сада се враћамо на метод ExampleAppWindow::open_file_view() који се позива за сваки аргумент линије наредби, и правимо Gtk::TextView који затим додајемо као страницу у спремник (стек).

На крају, додајемо Gtk::StackSwitcher у област траке наслова у КС датотеци, и кажемо му да прикаже податке о нашем спремнику.

Пребацивач спремника добија све податке који су му потребни за приказ језичака из спремника којем припада. Овде прослеђујемо натпис који ће се приказати за сваку датотеку као последњи аргумент методу Gtk::Stack::add().

Наш програм почиње да добија облик:

Отварање датотека

Изворни код

File: `exampleapplication.h` (For use with gtkmm 4)

    #include "../step1/exampleapplication.h"
    // Equal to the corresponding file in step1

File: `exampleappwindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPWINDOW_H_
    #define GTKMM_EXAMPLEAPPWINDOW_H_

    #include <gtkmm.h>

    class ExampleAppWindow : public Gtk::ApplicationWindow
    {
    public:
      ExampleAppWindow(BaseObjectType* cobject,
        const Glib::RefPtr<Gtk::Builder>& refBuilder);

      static ExampleAppWindow* create();

      void open_file_view(const Glib::RefPtr<Gio::File>& file);

    protected:
      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      Gtk::Stack* m_stack {nullptr};
    };

    #endif /* GTKMM_EXAMPLEAPPWINDOW_H */

File: `exampleapplication.cc` (For use with gtkmm 4)

    #include "../step2/exampleapplication.cc"
    // Equal to the corresponding file in step2

File: `exampleappwindow.cc` (For use with gtkmm 4)

    #include "exampleappwindow.h"
    #include <iostream>
    #include <stdexcept>

    ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
      const Glib::RefPtr<Gtk::Builder>& refBuilder)
    : Gtk::ApplicationWindow(cobject),
      m_refBuilder(refBuilder)
    {
      m_stack = m_refBuilder->get_widget<Gtk::Stack>("stack");
      if (!m_stack)
        throw std::runtime_error("No \"stack\" object in window.ui");
    }

    //static
    ExampleAppWindow* ExampleAppWindow::create()
    {
      // Load the Builder file and instantiate its widgets.
      auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");

      auto window = Gtk::Builder::get_widget_derived<ExampleAppWindow>(refBuilder, "app_window");
      if (!window)
        throw std::runtime_error("No \"app_window\" object in window.ui");

      return window;
    }

    void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file)
    {
      const Glib::ustring basename = file->get_basename();

      auto scrolled = Gtk::make_managed<Gtk::ScrolledWindow>();
      scrolled->set_expand(true);
      auto view = Gtk::make_managed<Gtk::TextView>();
      view->set_editable(false);
      view->set_cursor_visible(false);
      scrolled->set_child(*view);
      m_stack->add(*scrolled, basename, basename);

      try
      {
        char* contents = nullptr;
        gsize length = 0;

        file->load_contents(contents, length);
        view->get_buffer()->set_text(contents, contents+length);
        g_free(contents);
      }
      catch (const Glib::Error& ex)
      {
        std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name()
          << "\"):\n  " << ex.what() << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "../step1/main.cc"
    // Equal to the corresponding file in step1

File: `window.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <object class="GtkApplicationWindow" id="app_window">
        <property name="title" translatable="yes">Example Application</property>
        <property name="default-width">600</property>
        <property name="default-height">400</property>
        <child type="titlebar">
          <object class="GtkHeaderBar" id="header">
            <child type="title">
              <object class="GtkStackSwitcher" id="tabs">
                <property name="stack">stack</property>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox" id="content_box">
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkStack" id="stack"/>
            </child>
          </object>
        </child>
      </object>
    </interface>

### Мени

Мени је приказан на десној страни траке заглавља. Намењен је за прикупљање ретко коришћених радњи које утичу на цео програм.

Баш као и прозор програма, наш мени наводимо у КС датотеци и додајемо га као ресурс у нашу извршну датотеку.

Да би се мени појавио, морамо да учитамо КС датотеку и повежемо резултујући модел менија са дугметом менија које смо додали у траку заглавља. Пошто менији раде активирањем Gio::Action радњи, такође морамо да додамо одговарајући скуп радњи у наш програм.

Додавање радњи је најбоље урадити у подразумеваном руковаоцу сигнала on_startup(), за који је загарантовано да ће бити позван једном за сваку примарну појаву програма.

Наша ставка менија за поставке још увек не ради ништа, али је ставка менија „Изађи“ потпуно функционална. Такође се може активирати уобичајеном пречицом Ctrl-Q. Пречица се додаје помоћу Gtk::Application::set_accel_for_action().

Мени изгледа овако:

Мени

Изворни код

File: `exampleapplication.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPLICATION_H
    #define GTKMM_EXAMPLEAPPLICATION_H

    #include <gtkmm.h>

    class ExampleAppWindow;

    class ExampleApplication: public Gtk::Application
    {
    protected:
      ExampleApplication();

    public:
      static Glib::RefPtr<ExampleApplication> create();

    protected:
      // Override default signal handlers:
      void on_startup() override;
      void on_activate() override;
      void on_open(const Gio::Application::type_vec_files& files,
        const Glib::ustring& hint) override;

    private:
      ExampleAppWindow* create_appwindow();
      void on_action_preferences();
      void on_action_quit();
    };

    #endif /* GTKMM_EXAMPLEAPPLICATION_H */

File: `exampleappwindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPWINDOW_H_
    #define GTKMM_EXAMPLEAPPWINDOW_H_

    #include <gtkmm.h>

    class ExampleAppWindow : public Gtk::ApplicationWindow
    {
    public:
      ExampleAppWindow(BaseObjectType* cobject,
        const Glib::RefPtr<Gtk::Builder>& refBuilder);

      static ExampleAppWindow* create();

      void open_file_view(const Glib::RefPtr<Gio::File>& file);

    protected:
      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      Gtk::Stack* m_stack {nullptr};
      Gtk::MenuButton* m_gears {nullptr};
    };

    #endif /* GTKMM_EXAMPLEAPPWINDOW_H */

File: `exampleapplication.cc` (For use with gtkmm 4)

    #include "exampleapplication.h"
    #include "exampleappwindow.h"
    #include <iostream>
    #include <exception>

    ExampleApplication::ExampleApplication()
    : Gtk::Application("org.gtkmm.examples.application", Gio::Application::Flags::HANDLES_OPEN)
    {
      // Delete a window when it is removed from the application.
      signal_window_removed().connect([](Gtk::Window* window) { delete window; });
    }

    Glib::RefPtr<ExampleApplication> ExampleApplication::create()
    {
      return Glib::make_refptr_for_instance<ExampleApplication>(new ExampleApplication());
    }

    ExampleAppWindow* ExampleApplication::create_appwindow()
    {
      auto appwindow = ExampleAppWindow::create();

      // Make sure that the application runs for as long this window is still open.
      add_window(*appwindow);

      // A window can be added to an application with Gtk::Application::add_window()
      // or Gtk::Window::set_application(). When all added windows have been removed,
      // the application stops running (Gtk::Application::run() returns),
      // unless Gio::Application::hold() has been called.

      return appwindow;
    }

    void ExampleApplication::on_startup()
    {
      // Call the base class's implementation.
      Gtk::Application::on_startup();

      // Add actions and keyboard accelerators for the menu.
      add_action("preferences", sigc::mem_fun(*this, &ExampleApplication::on_action_preferences));
      add_action("quit", sigc::mem_fun(*this, &ExampleApplication::on_action_quit));
      set_accel_for_action("app.quit", "<Ctrl>Q");
    }

    void ExampleApplication::on_activate()
    {
      try
      {
        // The application has been started, so let's show a window.
        auto appwindow = create_appwindow();
        appwindow->present();
      }
      // If create_appwindow() throws an exception (perhaps from Gtk::Builder),
      // no window has been created, no window has been added to the application,
      // and therefore the application will stop running.
      catch (const Glib::Error& ex)
      {
        std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl;
      }
      catch (const std::exception& ex)
      {
        std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl;
      }
    }

    void ExampleApplication::on_open(const Gio::Application::type_vec_files& files,
      const Glib::ustring& /* hint */)
    {
      // The application has been asked to open some files,
      // so let's open a new view for each one.
      ExampleAppWindow* appwindow = nullptr;
      auto windows = get_windows();
      if (windows.size() > 0)
        appwindow = dynamic_cast<ExampleAppWindow*>(windows[0]);

      try
      {
        if (!appwindow)
          appwindow = create_appwindow();

        for (const auto& file : files)
          appwindow->open_file_view(file);

        appwindow->present();
      }
      catch (const Glib::Error& ex)
      {
        std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl;
      }
      catch (const std::exception& ex)
      {
        std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl;
      }
    }

    void ExampleApplication::on_action_preferences()
    {

    }

    void ExampleApplication::on_action_quit()
    {
      // Gio::Application::quit() will make Gio::Application::run() return,
      // but it's a crude way of ending the program. The window is not removed
      // from the application. Neither the window's nor the application's
      // destructors will be called, because there will be remaining reference
      // counts in both of them. If we want the destructors to be called, we
      // must remove the window from the application. See ExampleApplication's constructor.
      // A window can be removed from an application with Gtk::Application::remove_window()
      // or Gtk::Window::unset_application().
      auto windows = get_windows();
      for (auto window : windows)
        remove_window(*window);

      // Not really necessary, when Gtk::Application::remove_window() is called,
      // unless Gio::Application::hold() has been called without a corresponding
      // call to Gio::Application::release().
      quit();
    }

File: `exampleappwindow.cc` (For use with gtkmm 4)

    #include "exampleappwindow.h"
    #include <iostream>
    #include <stdexcept>

    ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
      const Glib::RefPtr<Gtk::Builder>& refBuilder)
    : Gtk::ApplicationWindow(cobject),
      m_refBuilder(refBuilder)
    {
      // Get widgets from the Gtk::Builder file.
      m_stack = m_refBuilder->get_widget<Gtk::Stack>("stack");
      if (!m_stack)
        throw std::runtime_error("No \"stack\" object in window.ui");

      m_gears = m_refBuilder->get_widget<Gtk::MenuButton>("gears");
      if (!m_gears)
        throw std::runtime_error("No \"gears\" object in window.ui");

      // Connect the menu to the MenuButton m_gears.
      // (The connection between action and menu item is specified in gears_menu.ui.)
      auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
      auto menu = menu_builder->get_object<Gio::MenuModel>("menu");
      if (!menu)
        throw std::runtime_error("No \"menu\" object in gears_menu.ui");

      m_gears->set_menu_model(menu);
    }

    //static
    ExampleAppWindow* ExampleAppWindow::create()
    {
      // Load the Builder file and instantiate its widgets.
      auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");

      auto window = Gtk::Builder::get_widget_derived<ExampleAppWindow>(refBuilder, "app_window");
      if (!window)
        throw std::runtime_error("No \"app_window\" object in window.ui");

      return window;
    }

    void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file)
    {
      const Glib::ustring basename = file->get_basename();

      auto scrolled = Gtk::make_managed<Gtk::ScrolledWindow>();
      scrolled->set_expand(true);
      auto view = Gtk::make_managed<Gtk::TextView>();
      view->set_editable(false);
      view->set_cursor_visible(false);
      scrolled->set_child(*view);
      m_stack->add(*scrolled, basename, basename);

      try
      {
        char* contents = nullptr;
        gsize length = 0;

        file->load_contents(contents, length);
        view->get_buffer()->set_text(contents, contents+length);
        g_free(contents);
      }
      catch (const Glib::Error& ex)
      {
        std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name()
          << "\"):\n  " << ex.what() << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "../step1/main.cc"
    // Equal to the corresponding file in step1

File: `exampleapp.gresource.xml` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <gresources>
      <gresource prefix="/org/gtkmm/exampleapp">
        <file preprocess="xml-stripblanks">window.ui</file>
        <file preprocess="xml-stripblanks">gears_menu.ui</file>
      </gresource>
    </gresources>

File: `gears_menu.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <menu id="menu">
        <section>
          <item>
            <attribute name="label" translatable="yes">_Preferences</attribute>
            <attribute name="action">app.preferences</attribute>
          </item>
        </section>
        <section>
          <item>
            <attribute name="label" translatable="yes">_Quit</attribute>
            <attribute name="action">app.quit</attribute>
          </item>
        </section>
      </menu>
    </interface>

File: `window.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <object class="GtkApplicationWindow" id="app_window">
        <property name="title" translatable="yes">Example Application</property>
        <property name="default-width">600</property>
        <property name="default-height">400</property>
        <child type="titlebar">
          <object class="GtkHeaderBar" id="header">
            <child type="title">
              <object class="GtkStackSwitcher" id="tabs">
                <property name="stack">stack</property>
              </object>
            </child>
            <child type="end">
              <object class="GtkMenuButton" id="gears">
                <property name="direction">none</property>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox" id="content_box">
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkStack" id="stack"/>
            </child>
          </object>
        </child>
      </object>
    </interface>

### Прозорче за поставке

Уобичајени програм ће имати нека подешавања која треба запамтити од једног покретања до следећег. Чак и за наш једноставан пример програма, можда ћемо желети да променимо фонт који се користи за садржај.

Користићемо Gio::Settings за чување наших подешавања. Gio::Settings захтева шему која описује наша подешавања, у нашем случају датотеку `org.gtkmm.exampleapp.gschema.xml`.

Пре него што будемо могли да користимо ову шему у нашем програму, морамо је превести у бинарни облик који Gio::Settings очекује. ГИО обезбеђује макрое за ово у пројектима заснованим на ауто-алатима. Погледајте опис за GSettings. Мезон обезбеђује функцију `compile_schemas()` у Гномовом модулу.

Затим, морамо да повежемо наша подешавања са елементима којима би требало да управљају. Један згодан начин да то урадите је коришћење Gio::Settings::bind() за повезивање кључева подешавања са својствима објекта, као што то радимо за подешавање прелаза у конструктору класе ExampleAppWindow.

    m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
    m_settings->bind("transition", m_stack->property_transition_type());

Код за повезивање подешавања фонта је мало сложенији, пошто одговара својству објекта у Gtk::TextTag који прво морамо направити. Код се налази у ExampleAppWindow::open_file_view().

    auto tag = buffer->create_tag();
    m_settings->bind("font", tag->property_font());
    buffer->apply_tag(tag, buffer->begin(), buffer->end());

У овом тренутку, програм ће већ реаговати ако промените неко од подешавања, нпр. коришћењем алатке командне линије `gsettings`. Наравно, очекујемо да програм обезбеди прозорче за подешавања за ово. Па урадимо то сада. Наше прозорче за подешавања ће бити подкласа Gtk::Window, и користићемо исте технике које смо већ видели у ExampleAppWindow: Gtk::Builder корисничку датотеку и повезивање подешавања. У овом случају, повезивања су ипак сложенија. Користимо Gtk::FontDialogButton и Gtk::DropDown у прозорчету за подешавања. Врсте својстава у овим класама не могу се аутоматски претворити у ниску коју Gio::Settings захтева.

Након што направимо датотеку `prefs.ui` и класу ExampleAppPrefs, поново ћемо погледати метод ExampleApplication::on_action_preferences() у нашој класи програма, и учинити да отвара ново прозорче за подешавања.

    auto prefs_dialog = ExampleAppPrefs::create(*get_active_window());
    prefs_dialog->present();

Након свег овог рада, наш програм сада може да прикаже прозорче за подешавања овако:

Прозорче за поставке

Изворни код

File: `exampleapplication.h` (For use with gtkmm 4)

    #include "../step4/exampleapplication.h"
    // Equal to the corresponding file in step4

File: `exampleappprefs.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPPREFS_H_
    #define GTKMM_EXAMPLEAPPPREFS_H_

    #include <gtkmm.h>

    #ifdef GLIBMM_CHECK_VERSION
    #define HAS_GIO_SETTINGS_BIND_WITH_MAPPING GLIBMM_CHECK_VERSION(2,75,0)
    #else
    #define HAS_GIO_SETTINGS_BIND_WITH_MAPPING 0
    #endif

    class ExampleAppPrefs : public Gtk::Window
    {
    public:
      ExampleAppPrefs(BaseObjectType* cobject,
        const Glib::RefPtr<Gtk::Builder>& refBuilder);

      static ExampleAppPrefs* create(Gtk::Window& parent);

    protected:
    #if HAS_GIO_SETTINGS_BIND_WITH_MAPPING
      // Mappings from Gio::Settings to properties
      static std::optional<unsigned int> map_from_ustring_to_int(const Glib::ustring& transition);
      static std::optional<Glib::ustring> map_from_int_to_ustring(const unsigned int& pos);
    #else
      // Signal handlers
      void on_font_setting_changed(const Glib::ustring& key);
      void on_font_selection_changed();
      void on_transition_setting_changed(const Glib::ustring& key);
      void on_transition_selection_changed();
    #endif
      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      Glib::RefPtr<Gio::Settings> m_settings;
      Gtk::FontDialogButton* m_font {nullptr};
      Gtk::DropDown* m_transition {nullptr};
    };

    #endif /* GTKMM_EXAMPLEAPPPREFS_H_ */

File: `exampleappwindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPWINDOW_H_
    #define GTKMM_EXAMPLEAPPWINDOW_H_

    #include <gtkmm.h>

    class ExampleAppWindow : public Gtk::ApplicationWindow
    {
    public:
      ExampleAppWindow(BaseObjectType* cobject,
        const Glib::RefPtr<Gtk::Builder>& refBuilder);

      static ExampleAppWindow* create();

      void open_file_view(const Glib::RefPtr<Gio::File>& file);

    protected:
      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      Glib::RefPtr<Gio::Settings> m_settings;
      Gtk::Stack* m_stack {nullptr};
      Gtk::MenuButton* m_gears {nullptr};
    };

    #endif /* GTKMM_EXAMPLEAPPWINDOW_H */

File: `exampleapplication.cc` (For use with gtkmm 4)

    #include "exampleapplication.h"
    #include "exampleappwindow.h"
    #include "exampleappprefs.h"
    #include <iostream>
    #include <exception>

    ExampleApplication::ExampleApplication()
    : Gtk::Application("org.gtkmm.examples.application", Gio::Application::Flags::HANDLES_OPEN)
    {
      // Delete a window when it is removed from the application.
      signal_window_removed().connect([](Gtk::Window* window) { delete window; });
    }

    Glib::RefPtr<ExampleApplication> ExampleApplication::create()
    {
      return Glib::make_refptr_for_instance<ExampleApplication>(new ExampleApplication());
    }

    ExampleAppWindow* ExampleApplication::create_appwindow()
    {
      auto appwindow = ExampleAppWindow::create();

      // Make sure that the application runs for as long this window is still open.
      add_window(*appwindow);

      // A window can be added to an application with Gtk::Application::add_window()
      // or Gtk::Window::set_application(). When all added windows have been removed,
      // the application stops running (Gtk::Application::run() returns),
      // unless Gio::Application::hold() has been called.

      return appwindow;
    }

    void ExampleApplication::on_startup()
    {
      // Call the base class's implementation.
      Gtk::Application::on_startup();

      // Add actions and keyboard accelerators for the menu.
      add_action("preferences", sigc::mem_fun(*this, &ExampleApplication::on_action_preferences));
      add_action("quit", sigc::mem_fun(*this, &ExampleApplication::on_action_quit));
      set_accel_for_action("app.quit", "<Ctrl>Q");
    }

    void ExampleApplication::on_activate()
    {
      try
      {
        // The application has been started, so let's show a window.
        auto appwindow = create_appwindow();
        appwindow->present();
      }
      // If create_appwindow() throws an exception (perhaps from Gtk::Builder),
      // no window has been created, no window has been added to the application,
      // and therefore the application will stop running.
      catch (const Glib::Error& ex)
      {
        std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl;
      }
      catch (const std::exception& ex)
      {
        std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl;
      }
    }

    void ExampleApplication::on_open(const Gio::Application::type_vec_files& files,
      const Glib::ustring& /* hint */)
    {
      // The application has been asked to open some files,
      // so let's open a new view for each one.
      ExampleAppWindow* appwindow = nullptr;
      auto windows = get_windows();
      if (windows.size() > 0)
        appwindow = dynamic_cast<ExampleAppWindow*>(windows[0]);

      try
      {
        if (!appwindow)
          appwindow = create_appwindow();

        for (const auto& file : files)
          appwindow->open_file_view(file);

        appwindow->present();
      }
      catch (const Glib::Error& ex)
      {
        std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl;
      }
      catch (const std::exception& ex)
      {
        std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl;
      }
    }

    void ExampleApplication::on_action_preferences()
    {
      try
      {
        auto prefs_dialog = ExampleAppPrefs::create(*get_active_window());
        prefs_dialog->present();

        // Delete the dialog when it is hidden.
        prefs_dialog->signal_hide().connect([prefs_dialog](){ delete prefs_dialog; });
      }
      catch (const Glib::Error& ex)
      {
        std::cerr << "ExampleApplication::on_action_preferences(): " << ex.what() << std::endl;
      }
      catch (const std::exception& ex)
      {
        std::cerr << "ExampleApplication::on_action_preferences(): " << ex.what() << std::endl;
      }
    }

    void ExampleApplication::on_action_quit()
    {
      // Gio::Application::quit() will make Gio::Application::run() return,
      // but it's a crude way of ending the program. The window is not removed
      // from the application. Neither the window's nor the application's
      // destructors will be called, because there will be remaining reference
      // counts in both of them. If we want the destructors to be called, we
      // must remove the window from the application. See ExampleApplication's constructor.
      // A window can be removed from an application with Gtk::Application::remove_window()
      // or Gtk::Window::unset_application().
      auto windows = get_windows();
      for (auto window : windows)
        remove_window(*window);

      // Not really necessary, when Gtk::Application::remove_window() is called,
      // unless Gio::Application::hold() has been called without a corresponding
      // call to Gio::Application::release().
      quit();
    }

File: `exampleappprefs.cc` (For use with gtkmm 4)

    #include "exampleappprefs.h"
    #include "exampleappwindow.h"
    #include <array>
    #include <stdexcept>

    namespace
    {
    struct TransitionTypeStruct
    {
      Glib::ustring id;   // Value of "transition" key in Gio::Settings
      Glib::ustring text; // Text in the DropDown list
    };

    const std::array<TransitionTypeStruct, 3> transitionTypes =
    {
      TransitionTypeStruct{"none",             "None"},
      TransitionTypeStruct{"crossfade",        "Fade"},
      TransitionTypeStruct{"slide-left-right", "Slide"}
    };

    } // anonymous namespace

    ExampleAppPrefs::ExampleAppPrefs(BaseObjectType* cobject,
      const Glib::RefPtr<Gtk::Builder>& refBuilder)
    : Gtk::Window(cobject),
      m_refBuilder(refBuilder)
    {
      m_font = m_refBuilder->get_widget<Gtk::FontDialogButton>("font");
      if (!m_font)
        throw std::runtime_error("No \"font\" object in prefs.ui");

      m_transition = m_refBuilder->get_widget<Gtk::DropDown>("transition");
      if (!m_transition)
        throw std::runtime_error("No \"transition\" object in prefs.ui");

      // DropDown for transition type.
      auto string_list = Gtk::StringList::create();
      for (const auto& transitionType : transitionTypes)
        string_list->append(transitionType.text);

      m_transition->set_model(string_list);

      m_settings = Gio::Settings::create("org.gtkmm.exampleapp");

      // Connect preference properties to the Gio::Settings.
    #if HAS_GIO_SETTINGS_BIND_WITH_MAPPING
      m_settings->bind<Glib::ustring, Pango::FontDescription>("font",
        m_font->property_font_desc(), Gio::Settings::BindFlags::DEFAULT,
        [](const auto& font) { return Pango::FontDescription(font); },
        [](const auto& fontdesc) { return fontdesc.to_string(); }
      );
      m_settings->bind<Glib::ustring, unsigned int>("transition",
        m_transition->property_selected(), Gio::Settings::BindFlags::DEFAULT,
        [](const auto& transition) { return map_from_ustring_to_int(transition); },
        [](const auto& pos) { return map_from_int_to_ustring(pos); }
      );
    #else
      // This is easier when g_settings_bind_with_mapping() is
      // wrapped in a Gio::Settings method.
      m_settings->signal_changed("font").connect(
        sigc::mem_fun(*this, &ExampleAppPrefs::on_font_setting_changed));
      m_font->property_font_desc().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleAppPrefs::on_font_selection_changed));

      m_settings->signal_changed("transition").connect(
        sigc::mem_fun(*this, &ExampleAppPrefs::on_transition_setting_changed));
      m_transition->property_selected().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleAppPrefs::on_transition_selection_changed));

      // Synchronize the preferences dialog with m_settings.
      on_font_setting_changed("font");
      on_transition_setting_changed("transition");
    #endif
    }

    //static
    ExampleAppPrefs* ExampleAppPrefs::create(Gtk::Window& parent)
    {
      // Load the Builder file and instantiate its widgets.
      auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/prefs.ui");

      auto dialog = Gtk::Builder::get_widget_derived<ExampleAppPrefs>(refBuilder, "prefs_dialog");
      if (!dialog)
        throw std::runtime_error("No \"prefs_dialog\" object in prefs.ui");

      dialog->set_transient_for(parent);

      return dialog;
    }

    #if HAS_GIO_SETTINGS_BIND_WITH_MAPPING
    std::optional<unsigned int> ExampleAppPrefs::map_from_ustring_to_int(const Glib::ustring& transition)
    {
      for (std::size_t i = 0; i < transitionTypes.size(); ++i)
      {
        if (transitionTypes[i].id == transition)
          return i;
      }
      return std::nullopt;
    }

    std::optional<Glib::ustring> ExampleAppPrefs::map_from_int_to_ustring(const unsigned int& pos)
    {
      if (pos >= transitionTypes.size())
        return std::nullopt;
      return transitionTypes[pos].id;
    }
    #else
    void ExampleAppPrefs::on_font_setting_changed(const Glib::ustring& /* key */)
    {
      const auto font_setting = m_settings->get_string("font");
      const auto font_button = m_font->get_font_desc().to_string();
      if (font_setting != font_button)
        m_font->set_font_desc(Pango::FontDescription(font_setting));
    }

    void ExampleAppPrefs::on_font_selection_changed()
    {
      const auto font_setting = m_settings->get_string("font");
      const auto font_button = m_font->get_font_desc().to_string();
      if (font_setting != font_button)
        m_settings->set_string("font", font_button);
    }

    void ExampleAppPrefs::on_transition_setting_changed(const Glib::ustring& /* key */)
    {
      const auto transition_setting = m_settings->get_string("transition");
      const auto transition_button = transitionTypes[m_transition->get_selected()].id;
      if (transition_setting != transition_button)
      {
        for (std::size_t i = 0; i < transitionTypes.size(); ++i)
        {
          if (transitionTypes[i].id == transition_setting)
          {
            m_transition->set_selected(i);
            break;
          }
        }
      }
    }

    void ExampleAppPrefs::on_transition_selection_changed()
    {
      const auto pos = m_transition->get_selected();
      if (pos >= transitionTypes.size())
        return;
      const auto transition_setting = m_settings->get_string("transition");
      const auto transition_button = transitionTypes[pos].id;
      if (transition_setting != transition_button)
        m_settings->set_string("transition", transition_button);
    }
    #endif

File: `exampleappwindow.cc` (For use with gtkmm 4)

    #include "exampleappwindow.h"
    #include <iostream>
    #include <stdexcept>

    ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
      const Glib::RefPtr<Gtk::Builder>& refBuilder)
    : Gtk::ApplicationWindow(cobject),
      m_refBuilder(refBuilder)
    {
      // Get widgets from the Gtk::Builder file.
      m_stack = m_refBuilder->get_widget<Gtk::Stack>("stack");
      if (!m_stack)
        throw std::runtime_error("No \"stack\" object in window.ui");

      m_gears = m_refBuilder->get_widget<Gtk::MenuButton>("gears");
      if (!m_gears)
        throw std::runtime_error("No \"gears\" object in window.ui");

      // Bind settings.
      m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
      m_settings->bind("transition", m_stack->property_transition_type());

      // Connect the menu to the MenuButton m_gears.
      // (The connection between action and menu item is specified in gears_menu.ui.)
      auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
      auto menu = menu_builder->get_object<Gio::MenuModel>("menu");
      if (!menu)
        throw std::runtime_error("No \"menu\" object in gears_menu.ui");

      m_gears->set_menu_model(menu);
    }

    //static
    ExampleAppWindow* ExampleAppWindow::create()
    {
      // Load the Builder file and instantiate its widgets.
      auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");

      auto window = Gtk::Builder::get_widget_derived<ExampleAppWindow>(refBuilder, "app_window");
      if (!window)
        throw std::runtime_error("No \"app_window\" object in window.ui");

      return window;
    }

    void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file)
    {
      const Glib::ustring basename = file->get_basename();

      auto scrolled = Gtk::make_managed<Gtk::ScrolledWindow>();
      scrolled->set_expand(true);
      auto view = Gtk::make_managed<Gtk::TextView>();
      view->set_editable(false);
      view->set_cursor_visible(false);
      scrolled->set_child(*view);
      m_stack->add(*scrolled, basename, basename);

      auto buffer = view->get_buffer();
      try
      {
        char* contents = nullptr;
        gsize length = 0;

        file->load_contents(contents, length);
        buffer->set_text(contents, contents+length);
        g_free(contents);
      }
      catch (const Glib::Error& ex)
      {
        std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name()
          << "\"):\n  " << ex.what() << std::endl;
      }

      auto tag = buffer->create_tag();
      m_settings->bind("font", tag->property_font());
      buffer->apply_tag(tag, buffer->begin(), buffer->end());
    }

File: `main.cc` (For use with gtkmm 4)

    #include "exampleapplication.h"

    int main(int argc, char* argv[])
    {
      // Since this example is running uninstalled, we have to help it find its
      // schema. This is *not* necessary in a properly installed application.
      Glib::setenv ("GSETTINGS_SCHEMA_DIR", ".", false);

      auto application = ExampleApplication::create();

      // Start the application, showing the initial window,
      // and opening extra views for any files that it is asked to open,
      // for instance as a command-line parameter.
      // run() will return when the last window has been closed.
      return application->run(argc, argv);
    }

File: `exampleapp.gresource.xml` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <gresources>
      <gresource prefix="/org/gtkmm/exampleapp">
        <file preprocess="xml-stripblanks">window.ui</file>
        <file preprocess="xml-stripblanks">gears_menu.ui</file>
        <file preprocess="xml-stripblanks">prefs.ui</file>
      </gresource>
    </gresources>

File: `prefs.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <object class="GtkWindow" id="prefs_dialog">
        <property name="title" translatable="yes">Preferences</property>
        <property name="resizable">False</property>
        <property name="modal">True</property>
        <property name="hide_on_close">True</property>
        <child>
          <object class="GtkGrid" id="grid">
            <property name="margin-start">12</property>
            <property name="margin-end">12</property>
            <property name="margin-top">12</property>
            <property name="margin-bottom">12</property>
            <property name="row-spacing">12</property>
            <property name="column-spacing">12</property>
            <child>
              <object class="GtkLabel" id="fontlabel">
                <property name="label">_Font:</property>
                <property name="use-underline">True</property>
                <property name="mnemonic-widget">font</property>
                <property name="xalign">1</property>
                <layout>
                  <property name="column">0</property>
                  <property name="row">0</property>
                </layout>
              </object>
            </child>
            <child>
              <object class="GtkFontDialogButton" id="font">
                <property name="dialog">
                  <object class="GtkFontDialog"/>
                </property>
                <layout>
                  <property name="column">1</property>
                  <property name="row">0</property>
                </layout>
              </object>
            </child>
            <child>
              <object class="GtkLabel" id="transitionlabel">
                <property name="label">_Transition:</property>
                <property name="use-underline">True</property>
                <property name="mnemonic-widget">transition</property>
                <property name="xalign">1</property>
                <layout>
                  <property name="column">0</property>
                  <property name="row">1</property>
                </layout>
              </object>
            </child>
            <child>
              <object class="GtkDropDown" id="transition">
                <layout>
                  <property name="column">1</property>
                  <property name="row">1</property>
                </layout>
              </object>
            </child>
          </object>
        </child>
      </object>
    </interface>

File: `org.gtkmm.exampleapp.gschema.xml` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <schemalist>
      <schema path="/org/gtkmm/exampleapp/" id="org.gtkmm.exampleapp">
        <key name="font" type="s">
          <default>'Monospace 12'</default>
          <summary>Font</summary>
          <description>The font to be used for content.</description>
        </key>
        <key name="transition" type="s">
          <choices>
            <choice value='none'/>
            <choice value='crossfade'/>
            <choice value='slide-left-right'/>
          </choices>
          <default>'none'</default>
          <summary>Transition</summary>
          <description>The transition to use when switching tabs.</description>
        </key>
      </schema>
    </schemalist>

### Додавање траке претраге

Настављамо да допуњавамо функционалност нашег програма. За сада, додајемо претрагу. gtkmm ово подржава помоћу Gtk::SearchEntry и Gtk::SearchBar. Трака претраге је елемент који може да склизне са врха како би приказао поље за унос претраге.

Додајемо дугме за пребацивање у траку заглавља, које се може користити за извлачење траке претраге испод траке заглавља. Нови елементи су додати у датотеку `window.ui`.

Примена претраге захтева доста измена у коду које овде нећемо у потпуности проћи. Централни део примене претраге је руковалац сигналом који ослушкује промене текста у пољу за унос претраге, приказан овде без обраде грешака.

    void ExampleAppWindow::on_search_text_changed()
    {
      const auto text = m_searchentry->get_text();
      auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
      auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());

      // Very simple-minded search implementation.
      auto buffer = view->get_buffer();
      Gtk::TextIter match_start;
      Gtk::TextIter match_end;
      if (buffer->begin().forward_search(text, Gtk::TextSearchFlags::CASE_INSENSITIVE,
          match_start, match_end))
      {
        buffer->select_range(match_start, match_end);
        view->scroll_to(match_start);
      }
    }

Са траком претраге, наш програм сада изгледа овако:

Додавање траке претраге

Изворни код

File: `exampleapplication.h` (For use with gtkmm 4)

    #include "../step4/exampleapplication.h"
    // Equal to the corresponding file in step4

File: `exampleappprefs.h` (For use with gtkmm 4)

    #include "../step5/exampleappprefs.h"
    // Equal to the corresponding file in step5

File: `exampleappwindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPWINDOW_H_
    #define GTKMM_EXAMPLEAPPWINDOW_H_

    #include <gtkmm.h>

    #define HAS_SEARCH_ENTRY2 GTKMM_CHECK_VERSION(4,13,2)

    class ExampleAppWindow : public Gtk::ApplicationWindow
    {
    public:
      ExampleAppWindow(BaseObjectType* cobject,
        const Glib::RefPtr<Gtk::Builder>& refBuilder);

      static ExampleAppWindow* create();

      void open_file_view(const Glib::RefPtr<Gio::File>& file);

    protected:
      // Signal handlers
      void on_search_text_changed();
      void on_visible_child_changed();

      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      Glib::RefPtr<Gio::Settings> m_settings;
      Gtk::Stack* m_stack {nullptr};
      Gtk::ToggleButton* m_search {nullptr};
      Gtk::SearchBar* m_searchbar {nullptr};
    #if HAS_SEARCH_ENTRY2
      Gtk::SearchEntry2* m_searchentry {nullptr};
    #else
      Gtk::SearchEntry* m_searchentry {nullptr};
    #endif
      Gtk::MenuButton* m_gears {nullptr};
      Glib::RefPtr<Glib::Binding> m_prop_binding;
    };

    #endif /* GTKMM_EXAMPLEAPPWINDOW_H */

File: `exampleapplication.cc` (For use with gtkmm 4)

    #include "../step5/exampleapplication.cc"
    // Equal to the corresponding file in step5

File: `exampleappprefs.cc` (For use with gtkmm 4)

    #include "../step5/exampleappprefs.cc"
    // Equal to the corresponding file in step5

File: `exampleappwindow.cc` (For use with gtkmm 4)

    #include "exampleappwindow.h"
    #include <iostream>
    #include <stdexcept>

    ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
      const Glib::RefPtr<Gtk::Builder>& refBuilder)
    : Gtk::ApplicationWindow(cobject),
      m_refBuilder(refBuilder)
    {
      // Get widgets from the Gtk::Builder file.
      m_stack = m_refBuilder->get_widget<Gtk::Stack>("stack");
      if (!m_stack)
        throw std::runtime_error("No \"stack\" object in window.ui");

      m_search = m_refBuilder->get_widget<Gtk::ToggleButton>("search");
      if (!m_search)
        throw std::runtime_error("No \"search\" object in window.ui");

      m_searchbar = m_refBuilder->get_widget<Gtk::SearchBar>("searchbar");
      if (!m_searchbar)
        throw std::runtime_error("No \"searchbar\" object in window.ui");

    #if HAS_SEARCH_ENTRY2
      m_searchentry = m_refBuilder->get_widget<Gtk::SearchEntry2>("searchentry");
    #else
      m_searchentry = m_refBuilder->get_widget<Gtk::SearchEntry>("searchentry");
    #endif
      if (!m_searchentry)
        throw std::runtime_error("No \"searchentry\" object in window.ui");

      m_gears = m_refBuilder->get_widget<Gtk::MenuButton>("gears");
      if (!m_gears)
        throw std::runtime_error("No \"gears\" object in window.ui");

      // Bind settings.
      m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
      m_settings->bind("transition", m_stack->property_transition_type());

      // Bind properties.
      m_prop_binding = Glib::Binding::bind_property(m_search->property_active(),
        m_searchbar->property_search_mode_enabled(), Glib::Binding::Flags::BIDIRECTIONAL);

      // Connect signal handlers.
      m_searchentry->signal_search_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_search_text_changed));
      m_stack->property_visible_child().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_visible_child_changed));

      // Connect the menu to the MenuButton m_gears.
      // (The connection between action and menu item is specified in gears_menu.ui.)
      auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
      auto menu = menu_builder->get_object<Gio::MenuModel>("menu");
      if (!menu)
        throw std::runtime_error("No \"menu\" object in gears_menu.ui");

      m_gears->set_menu_model(menu);
    }

    //static
    ExampleAppWindow* ExampleAppWindow::create()
    {
      // Load the Builder file and instantiate its widgets.
      auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");

      auto window = Gtk::Builder::get_widget_derived<ExampleAppWindow>(refBuilder, "app_window");
      if (!window)
        throw std::runtime_error("No \"app_window\" object in window.ui");

      return window;
    }

    void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file)
    {
      const Glib::ustring basename = file->get_basename();

      auto scrolled = Gtk::make_managed<Gtk::ScrolledWindow>();
      scrolled->set_expand(true);
      auto view = Gtk::make_managed<Gtk::TextView>();
      view->set_editable(false);
      view->set_cursor_visible(false);
      scrolled->set_child(*view);
      m_stack->add(*scrolled, basename, basename);

      auto buffer = view->get_buffer();
      try
      {
        char* contents = nullptr;
        gsize length = 0;

        file->load_contents(contents, length);
        buffer->set_text(contents, contents+length);
        g_free(contents);
      }
      catch (const Glib::Error& ex)
      {
        std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name()
          << "\"):\n  " << ex.what() << std::endl;
        return;
      }

      auto tag = buffer->create_tag();
      m_settings->bind("font", tag->property_font());
      buffer->apply_tag(tag, buffer->begin(), buffer->end());

      m_search->set_sensitive(true);
    }

    void ExampleAppWindow::on_search_text_changed()
    {
      const auto text = m_searchentry->get_text();
      if (text.empty())
        return;

      auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
      if (!tab)
      {
        std::cout << "ExampleAppWindow::on_search_text_changed(): No visible child." << std::endl;
        return;
      }

      auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
      if (!view)
      {
        std::cout << "ExampleAppWindow::on_search_text_changed(): No visible text view." << std::endl;
        return;
      }

      // Very simple-minded search implementation.
      auto buffer = view->get_buffer();
      Gtk::TextIter match_start;
      Gtk::TextIter match_end;
      if (buffer->begin().forward_search(text, Gtk::TextSearchFlags::CASE_INSENSITIVE,
          match_start, match_end))
      {
        buffer->select_range(match_start, match_end);
        view->scroll_to(match_start);
      }
    }

    void ExampleAppWindow::on_visible_child_changed()
    {
      m_searchbar->set_search_mode(false);
    }

File: `main.cc` (For use with gtkmm 4)

    #include "exampleapplication.h"
    #include "exampleappwindow.h"

    int main(int argc, char* argv[])
    {
    #if HAS_SEARCH_ENTRY2
      // A GtkSearchEntry (e.g. from window.ui) shall be wrapped in
      // a Gtk::SearhcEntry2 and not in a deprecated Gtk::SearchEntry.
      Gtk::Application::wrap_in_search_entry2(true);
    #endif

      // Since this example is running uninstalled, we have to help it find its
      // schema. This is *not* necessary in a properly installed application.
      Glib::setenv ("GSETTINGS_SCHEMA_DIR", ".", false);

      auto application = ExampleApplication::create();

      // Start the application, showing the initial window,
      // and opening extra views for any files that it is asked to open,
      // for instance as a command-line parameter.
      // run() will return when the last window has been closed.
      return application->run(argc, argv);
    }

File: `window.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <object class="GtkApplicationWindow" id="app_window">
        <property name="title" translatable="yes">Example Application</property>
        <property name="default-width">600</property>
        <property name="default-height">400</property>
        <child type="titlebar">
          <object class="GtkHeaderBar" id="header">
            <child type="title">
              <object class="GtkStackSwitcher" id="tabs">
                <property name="stack">stack</property>
              </object>
            </child>
            <child type="end">
              <object class="GtkMenuButton" id="gears">
                <property name="direction">none</property>
              </object>
            </child>
            <child type="end">
              <object class="GtkToggleButton" id="search">
                <property name="sensitive">False</property>
                <property name="icon-name">edit-find-symbolic</property>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox" id="content_box">
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkSearchBar" id="searchbar">
                <child>
                  <object class="GtkSearchEntry" id="searchentry">
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkStack" id="stack">
                <property name="transition-duration">500</property>
              </object>
            </child>
          </object>
        </child>
      </object>
    </interface>

### Додавање бочне површи

Као још један део функционалности, додајемо бочну површ, која демонстрира Gtk::Revealer и Gtk::ListBox. Нови елементи су додати у датотеку `window.ui`.

Код за попуњавање бочне површи дугмадима за речи пронађене у свакој датотеци је мало превише сложен да бисмо овде улазили у њега. Али погледаћемо код за додавање дугмета за штиклирање за нову функцију у изборник. Ставка изборника је додата у КС датотеку `gears_menu.ui`.

Да бисмо повезали ставку изборника са новим `show-words` подешавањем, користимо Gio::Action који одговара датом Gio::Settings кључу. У конструктору ExampleAppWindow-а:

    // Повежите изборник са MenuButton m_gears и вежите подешавање show-words
    // на радњу win.show-words и ставку изборника „Words“.
    // (Веза између радње и ставке изборника је наведена у gears_menu.ui.)
    auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
    auto menu = menu_builder->get_object<Gio::MenuModel>("menu");
    m_gears->set_menu_model(menu);
    add_action(m_settings->create_action("show-words"));

Како наш програм сада изгледа:

Додавање бочне површи

Изворни код

File: `exampleapplication.h` (For use with gtkmm 4)

    #include "../step4/exampleapplication.h"
    // Equal to the corresponding file in step4

File: `exampleappprefs.h` (For use with gtkmm 4)

    #include "../step5/exampleappprefs.h"
    // Equal to the corresponding file in step5

File: `exampleappwindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPWINDOW_H_
    #define GTKMM_EXAMPLEAPPWINDOW_H_

    #include <gtkmm.h>

    #define HAS_SEARCH_ENTRY2 GTKMM_CHECK_VERSION(4,13,2)

    class ExampleAppWindow : public Gtk::ApplicationWindow
    {
    public:
      ExampleAppWindow(BaseObjectType* cobject,
        const Glib::RefPtr<Gtk::Builder>& refBuilder);

      static ExampleAppWindow* create();

      void open_file_view(const Glib::RefPtr<Gio::File>& file);

    protected:
      // Signal handlers
      void on_search_text_changed();
      void on_visible_child_changed();
      void on_find_word(const Gtk::Button* button);
      void on_reveal_child_changed();

      void update_words();

      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      Glib::RefPtr<Gio::Settings> m_settings;
      Gtk::Stack* m_stack {nullptr};
      Gtk::ToggleButton* m_search {nullptr};
      Gtk::SearchBar* m_searchbar {nullptr};
    #if HAS_SEARCH_ENTRY2
      Gtk::SearchEntry2* m_searchentry {nullptr};
    #else
      Gtk::SearchEntry* m_searchentry {nullptr};
    #endif
      Gtk::MenuButton* m_gears {nullptr};
      Gtk::Revealer* m_sidebar {nullptr};
      Gtk::ListBox* m_words {nullptr};
      Glib::RefPtr<Glib::Binding> m_prop_binding;
    };

    #endif /* GTKMM_EXAMPLEAPPWINDOW_H */

File: `exampleapplication.cc` (For use with gtkmm 4)

    #include "../step5/exampleapplication.cc"
    // Equal to the corresponding file in step5

File: `exampleappprefs.cc` (For use with gtkmm 4)

    #include "../step5/exampleappprefs.cc"
    // Equal to the corresponding file in step5

File: `exampleappwindow.cc` (For use with gtkmm 4)

    #include "exampleappwindow.h"
    #include <iostream>
    #include <stdexcept>
    #include <set>

    ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
      const Glib::RefPtr<Gtk::Builder>& refBuilder)
    : Gtk::ApplicationWindow(cobject),
      m_refBuilder(refBuilder)
    {
      // Get widgets from the Gtk::Builder file.
      m_stack = m_refBuilder->get_widget<Gtk::Stack>("stack");
      if (!m_stack)
        throw std::runtime_error("No \"stack\" object in window.ui");

      m_search = m_refBuilder->get_widget<Gtk::ToggleButton>("search");
      if (!m_search)
        throw std::runtime_error("No \"search\" object in window.ui");

      m_searchbar = m_refBuilder->get_widget<Gtk::SearchBar>("searchbar");
      if (!m_searchbar)
        throw std::runtime_error("No \"searchbar\" object in window.ui");

    #if HAS_SEARCH_ENTRY2
      m_searchentry = m_refBuilder->get_widget<Gtk::SearchEntry2>("searchentry");
    #else
      m_searchentry = m_refBuilder->get_widget<Gtk::SearchEntry>("searchentry");
    #endif
      if (!m_searchentry)
        throw std::runtime_error("No \"searchentry\" object in window.ui");

      m_gears = m_refBuilder->get_widget<Gtk::MenuButton>("gears");
      if (!m_gears)
        throw std::runtime_error("No \"gears\" object in window.ui");

      m_sidebar = m_refBuilder->get_widget<Gtk::Revealer>("sidebar");
      if (!m_sidebar)
        throw std::runtime_error("No \"sidebar\" object in window.ui");

      m_words = m_refBuilder->get_widget<Gtk::ListBox>("words");
      if (!m_words)
        throw std::runtime_error("No \"words\" object in window.ui");

      // Bind settings.
      m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
      m_settings->bind("transition", m_stack->property_transition_type());
      m_settings->bind("show-words", m_sidebar->property_reveal_child());

      // Bind properties.
      m_prop_binding = Glib::Binding::bind_property(m_search->property_active(),
        m_searchbar->property_search_mode_enabled(), Glib::Binding::Flags::BIDIRECTIONAL);

      // Connect signal handlers.
      m_searchentry->signal_search_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_search_text_changed));
      m_stack->property_visible_child().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_visible_child_changed));
      m_sidebar->property_reveal_child().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_reveal_child_changed));

      // Connect the menu to the MenuButton m_gears, and bind the show-words setting
      // to the win.show-words action and the "Words" menu item.
      // (The connection between action and menu item is specified in gears_menu.ui.)
      auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
      auto menu = menu_builder->get_object<Gio::MenuModel>("menu");
      if (!menu)
        throw std::runtime_error("No \"menu\" object in gears_menu.ui");

      m_gears->set_menu_model(menu);
      add_action(m_settings->create_action("show-words"));
    }

    //static
    ExampleAppWindow* ExampleAppWindow::create()
    {
      // Load the Builder file and instantiate its widgets.
      auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");

      auto window = Gtk::Builder::get_widget_derived<ExampleAppWindow>(refBuilder, "app_window");
      if (!window)
        throw std::runtime_error("No \"app_window\" object in window.ui");

      return window;
    }

    void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file)
    {
      const Glib::ustring basename = file->get_basename();

      auto scrolled = Gtk::make_managed<Gtk::ScrolledWindow>();
      scrolled->set_expand(true);
      auto view = Gtk::make_managed<Gtk::TextView>();
      view->set_editable(false);
      view->set_cursor_visible(false);
      scrolled->set_child(*view);
      m_stack->add(*scrolled, basename, basename);

      auto buffer = view->get_buffer();
      try
      {
        char* contents = nullptr;
        gsize length = 0;

        file->load_contents(contents, length);
        buffer->set_text(contents, contents+length);
        g_free(contents);
      }
      catch (const Glib::Error& ex)
      {
        std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name()
          << "\"):\n  " << ex.what() << std::endl;
        return;
      }

      auto tag = buffer->create_tag();
      m_settings->bind("font", tag->property_font());
      buffer->apply_tag(tag, buffer->begin(), buffer->end());

      m_search->set_sensitive(true);
      update_words();
    }

    void ExampleAppWindow::on_search_text_changed()
    {
      const auto text = m_searchentry->get_text();
      if (text.empty())
        return;

      auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
      if (!tab)
      {
        std::cout << "ExampleAppWindow::on_search_text_changed(): No visible child." << std::endl;
        return;
      }

      auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
      if (!view)
      {
        std::cout << "ExampleAppWindow::on_search_text_changed(): No visible text view." << std::endl;
        return;
      }

      // Very simple-minded search implementation.
      auto buffer = view->get_buffer();
      Gtk::TextIter match_start;
      Gtk::TextIter match_end;
      if (buffer->begin().forward_search(text, Gtk::TextSearchFlags::CASE_INSENSITIVE,
          match_start, match_end))
      {
        buffer->select_range(match_start, match_end);
        view->scroll_to(match_start);
      }
    }

    void ExampleAppWindow::on_visible_child_changed()
    {
      m_searchbar->set_search_mode(false);
      update_words();
    }

    void ExampleAppWindow::on_find_word(const Gtk::Button* button)
    {
      m_searchentry->set_text(button->get_label());
    }

    void ExampleAppWindow::on_reveal_child_changed()
    {
      update_words();
    }

    void ExampleAppWindow::update_words()
    {
      auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
      if (!tab)
        return;

      auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
      if (!view)
      {
        std::cout << "ExampleAppWindow::update_words(): No visible text view." << std::endl;
        return;
      }
      auto buffer = view->get_buffer();

      // Find all words in the text buffer.
      std::set<Glib::ustring> words;
      auto start = buffer->begin();
      Gtk::TextIter end;
      while (start)
      {
        while (start && !start.starts_word())
          ++start;

        if (!start)
          break;

        end = start;
        end.forward_word_end();
        if (start == end)
          break;

        auto word = buffer->get_text(start, end, false);
        words.insert(word.lowercase());
        start = end;
      }

      // Remove old children from the ListBox.
      while (auto child = m_words->get_first_child())
        m_words->remove(*child);

      // Add new child buttons, one per unique word.
      for (const auto& word : words)
      {
        auto row = Gtk::make_managed<Gtk::Button>(word);
        row->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,
          &ExampleAppWindow::on_find_word), row));
        m_words->append(*row);
      }
    }

File: `main.cc` (For use with gtkmm 4)

    #include "../step6/main.cc"
    // Equal to the corresponding file in step6

File: `gears_menu.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <menu id="menu">
        <section>
          <item>
            <attribute name="label" translatable="yes">_Words</attribute>
            <attribute name="action">win.show-words</attribute>
          </item>
          <item>
            <attribute name="label" translatable="yes">_Preferences</attribute>
            <attribute name="action">app.preferences</attribute>
          </item>
        </section>
        <section>
          <item>
            <attribute name="label" translatable="yes">_Quit</attribute>
            <attribute name="action">app.quit</attribute>
          </item>
        </section>
      </menu>
    </interface>

File: `window.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <object class="GtkApplicationWindow" id="app_window">
        <property name="title" translatable="yes">Example Application</property>
        <property name="default-width">600</property>
        <property name="default-height">400</property>
        <child type="titlebar">
          <object class="GtkHeaderBar" id="header">
            <child type="title">
              <object class="GtkStackSwitcher" id="tabs">
                <property name="stack">stack</property>
              </object>
            </child>
            <child type="end">
              <object class="GtkToggleButton" id="search">
                <property name="sensitive">False</property>
                <property name="icon-name">edit-find-symbolic</property>
              </object>
            </child>
            <child type="end">
              <object class="GtkMenuButton" id="gears">
                <property name="direction">none</property>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox" id="content_box">
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkSearchBar" id="searchbar">
                <child>
                  <object class="GtkSearchEntry" id="searchentry">
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkBox" id="hbox">
                <child>
                  <object class="GtkRevealer" id="sidebar">
                    <property name="transition-type">slide-right</property>
                    <child>
                     <object class="GtkScrolledWindow" id="sidebar-sw">
                       <property name="hscrollbar-policy">never</property>
                       <child>
                         <object class="GtkListBox" id="words">
                           <property name="selection-mode">none</property>
                         </object>
                       </child>
                     </object>
                    </child>
                  </object>
                </child>
                <child>
                  <object class="GtkStack" id="stack">
                    <property name="transition-duration">500</property>
                  </object>
                </child>
              </object>
            </child>
          </object>
        </child>
      </object>
    </interface>

File: `org.gtkmm.exampleapp.gschema.xml` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <schemalist>
      <schema path="/org/gtkmm/exampleapp/" id="org.gtkmm.exampleapp">
        <key name="font" type="s">
          <default>'Monospace 12'</default>
          <summary>Font</summary>
          <description>The font to be used for content.</description>
        </key>
        <key name="transition" type="s">
          <choices>
            <choice value='none'/>
            <choice value='crossfade'/>
            <choice value='slide-left-right'/>
          </choices>
          <default>'none'</default>
          <summary>Transition</summary>
          <description>The transition to use when switching tabs.</description>
        </key>
        <key name="show-words" type="b">
          <default>false</default>
          <summary>Show words</summary>
          <description>Whether to show a word list in the sidebar.</description>
        </key>
      </schema>
    </schemalist>

### Својства

Елементи и други објекти имају многа корисна својства. Овде приказујемо неке начине за њихово коришћење на нове и флексибилне начине, умотавањем у радње помоћу Gio::PropertyAction или њиховим везивањем помоћу Glib::Binding.

Да бисмо ово подесили, додајемо два натписа у траку заглавља у нашој `window.ui` датотеци, именоване `lines_label` и `lines`, и добијамо показиваче на њих у конструктору прозора програма, као што смо већ видели неколико пута. Додајемо нову ставку „Lines“ у изборник зупчаника, која покреће радњу `show-lines`.

Да би ова ставка изборника нешто радила, правимо радњу својства за својство `visible` натписа `lines` и додајемо је у радње прозора. Ефекат овога је да се видљивост натписа мења сваки пут када се радња активира. Будући да желимо да се оба натписа појављују и нестају заједно, везујемо својство `visible` елемента `lines_label` за исто својство елемента `lines`. У конструктору ExampleAppWindow-а:

    add_action(Gio::PropertyAction::create("show-lines", m_lines->property_visible()));
    m_binding_lines_visible = Glib::Binding::bind_property(m_lines->property_visible(),
      m_lines_label->property_visible());

Такође нам је потребна функција која броји редове тренутно активног језичка и ажурира натпис `lines`. Погледајте цео изворни код ако сте заинтересовани за детаље.

Ово доводи наш пример програма до овог изгледа:

Својства

Изворни код

File: `exampleapplication.h` (For use with gtkmm 4)

    #include "../step4/exampleapplication.h"
    // Equal to the corresponding file in step4

File: `exampleappprefs.h` (For use with gtkmm 4)

    #include "../step5/exampleappprefs.h"
    // Equal to the corresponding file in step5

File: `exampleappwindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEAPPWINDOW_H_
    #define GTKMM_EXAMPLEAPPWINDOW_H_

    #include <gtkmm.h>

    #define HAS_SEARCH_ENTRY2 GTKMM_CHECK_VERSION(4,13,2)

    class ExampleAppWindow : public Gtk::ApplicationWindow
    {
    public:
      ExampleAppWindow(BaseObjectType* cobject,
        const Glib::RefPtr<Gtk::Builder>& refBuilder);

      static ExampleAppWindow* create();

      void open_file_view(const Glib::RefPtr<Gio::File>& file);

    protected:
      // Signal handlers
      void on_search_text_changed();
      void on_visible_child_changed();
      void on_find_word(const Gtk::Button* button);
      void on_reveal_child_changed();

      void update_words();
      void update_lines();

      Glib::RefPtr<Gtk::Builder> m_refBuilder;
      Glib::RefPtr<Gio::Settings> m_settings;
      Gtk::Stack* m_stack {nullptr};
      Gtk::ToggleButton* m_search {nullptr};
      Gtk::SearchBar* m_searchbar {nullptr};
    #if HAS_SEARCH_ENTRY2
      Gtk::SearchEntry2* m_searchentry {nullptr};
    #else
      Gtk::SearchEntry* m_searchentry {nullptr};
    #endif
      Gtk::MenuButton* m_gears {nullptr};
      Gtk::Revealer* m_sidebar {nullptr};
      Gtk::ListBox* m_words {nullptr};
      Gtk::Label* m_lines {nullptr};
      Gtk::Label* m_lines_label {nullptr};
      Glib::RefPtr<Glib::Binding> m_binding_search_enabled;
      Glib::RefPtr<Glib::Binding> m_binding_lines_visible;
    };

    #endif /* GTKMM_EXAMPLEAPPWINDOW_H */

File: `exampleapplication.cc` (For use with gtkmm 4)

    #include "../step5/exampleapplication.cc"
    // Equal to the corresponding file in step5

File: `exampleappprefs.cc` (For use with gtkmm 4)

    #include "../step5/exampleappprefs.cc"
    // Equal to the corresponding file in step5

File: `exampleappwindow.cc` (For use with gtkmm 4)

    #include "exampleappwindow.h"
    #include <iostream>
    #include <stdexcept>
    #include <set>

    ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
      const Glib::RefPtr<Gtk::Builder>& refBuilder)
    : Gtk::ApplicationWindow(cobject),
      m_refBuilder(refBuilder)
    {
      // Get widgets from the Gtk::Builder file.
      m_stack = m_refBuilder->get_widget<Gtk::Stack>("stack");
      if (!m_stack)
        throw std::runtime_error("No \"stack\" object in window.ui");

      m_search = m_refBuilder->get_widget<Gtk::ToggleButton>("search");
      if (!m_search)
        throw std::runtime_error("No \"search\" object in window.ui");

      m_searchbar = m_refBuilder->get_widget<Gtk::SearchBar>("searchbar");
      if (!m_searchbar)
        throw std::runtime_error("No \"searchbar\" object in window.ui");

    #if HAS_SEARCH_ENTRY2
      m_searchentry = m_refBuilder->get_widget<Gtk::SearchEntry2>("searchentry");
    #else
      m_searchentry = m_refBuilder->get_widget<Gtk::SearchEntry>("searchentry");
    #endif
      if (!m_searchentry)
        throw std::runtime_error("No \"searchentry\" object in window.ui");

      m_gears = m_refBuilder->get_widget<Gtk::MenuButton>("gears");
      if (!m_gears)
        throw std::runtime_error("No \"gears\" object in window.ui");

      m_sidebar = m_refBuilder->get_widget<Gtk::Revealer>("sidebar");
      if (!m_sidebar)
        throw std::runtime_error("No \"sidebar\" object in window.ui");

      m_words = m_refBuilder->get_widget<Gtk::ListBox>("words");
      if (!m_words)
        throw std::runtime_error("No \"words\" object in window.ui");

      m_lines = m_refBuilder->get_widget<Gtk::Label>("lines");
      if (!m_lines)
        throw std::runtime_error("No \"lines\" object in window.ui");

      m_lines_label = m_refBuilder->get_widget<Gtk::Label>("lines_label");
      if (!m_lines_label)
        throw std::runtime_error("No \"lines_label\" object in window.ui");

      // Bind settings.
      m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
      m_settings->bind("transition", m_stack->property_transition_type());
      m_settings->bind("show-words", m_sidebar->property_reveal_child());

      // Bind properties of the search button to the search bar.
      m_binding_search_enabled = Glib::Binding::bind_property(m_search->property_active(),
        m_searchbar->property_search_mode_enabled(), Glib::Binding::Flags::BIDIRECTIONAL);

      // Connect signal handlers.
      m_searchentry->signal_search_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_search_text_changed));
      m_stack->property_visible_child().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_visible_child_changed));
      m_sidebar->property_reveal_child().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_reveal_child_changed));

      // Connect the menu to the MenuButton m_gears, and bind the show-words setting
      // to the win.show-words action and the "Words" menu item.
      // (The connection between action and menu item is specified in gears_menu.ui.)
      auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
      auto menu = menu_builder->get_object<Gio::MenuModel>("menu");
      if (!menu)
        throw std::runtime_error("No \"menu\" object in gears_menu.ui");

      m_gears->set_menu_model(menu);
      add_action(m_settings->create_action("show-words"));

      // Bind the "visible" property of m_lines to the win.show-lines action, to
      // the "Lines" menu item and to the "visible" property of m_lines_label.
      add_action(Gio::PropertyAction::create("show-lines", m_lines->property_visible()));
      m_binding_lines_visible = Glib::Binding::bind_property(m_lines->property_visible(),
        m_lines_label->property_visible());
    }

    //static
    ExampleAppWindow* ExampleAppWindow::create()
    {
      // Load the Builder file and instantiate its widgets.
      auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");

      auto window = Gtk::Builder::get_widget_derived<ExampleAppWindow>(refBuilder, "app_window");
      if (!window)
        throw std::runtime_error("No \"app_window\" object in window.ui");

      return window;
    }

    void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file)
    {
      const Glib::ustring basename = file->get_basename();

      auto scrolled = Gtk::make_managed<Gtk::ScrolledWindow>();
      scrolled->set_expand(true);
      auto view = Gtk::make_managed<Gtk::TextView>();
      view->set_editable(false);
      view->set_cursor_visible(false);
      scrolled->set_child(*view);
      m_stack->add(*scrolled, basename, basename);

      auto buffer = view->get_buffer();
      try
      {
        char* contents = nullptr;
        gsize length = 0;

        file->load_contents(contents, length);
        buffer->set_text(contents, contents+length);
        g_free(contents);
      }
      catch (const Glib::Error& ex)
      {
        std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name()
          << "\"):\n  " << ex.what() << std::endl;
        return;
      }

      auto tag = buffer->create_tag();
      m_settings->bind("font", tag->property_font());
      buffer->apply_tag(tag, buffer->begin(), buffer->end());

      m_search->set_sensitive(true);
      update_words();
      update_lines();
    }

    void ExampleAppWindow::on_search_text_changed()
    {
      const auto text = m_searchentry->get_text();
      if (text.empty())
        return;

      auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
      if (!tab)
      {
        std::cout << "ExampleAppWindow::on_search_text_changed(): No visible child." << std::endl;
        return;
      }

      auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
      if (!view)
      {
        std::cout << "ExampleAppWindow::on_search_text_changed(): No visible text view." << std::endl;
        return;
      }

      // Very simple-minded search implementation.
      auto buffer = view->get_buffer();
      Gtk::TextIter match_start;
      Gtk::TextIter match_end;
      if (buffer->begin().forward_search(text, Gtk::TextSearchFlags::CASE_INSENSITIVE,
          match_start, match_end))
      {
        buffer->select_range(match_start, match_end);
        view->scroll_to(match_start);
      }
    }

    void ExampleAppWindow::on_visible_child_changed()
    {
      m_searchbar->set_search_mode(false);
      update_words();
      update_lines();
    }

    void ExampleAppWindow::on_find_word(const Gtk::Button* button)
    {
      m_searchentry->set_text(button->get_label());
    }

    void ExampleAppWindow::on_reveal_child_changed()
    {
      update_words();
    }

    void ExampleAppWindow::update_words()
    {
      auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
      if (!tab)
        return;

      auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
      if (!view)
      {
        std::cout << "ExampleAppWindow::update_words(): No visible text view." << std::endl;
        return;
      }
      auto buffer = view->get_buffer();

      // Find all words in the text buffer.
      std::set<Glib::ustring> words;
      auto start = buffer->begin();
      Gtk::TextIter end;
      while (start)
      {
        while (start && !start.starts_word())
          ++start;

        if (!start)
          break;

        end = start;
        end.forward_word_end();
        if (start == end)
          break;

        auto word = buffer->get_text(start, end, false);
        words.insert(word.lowercase());
        start = end;
      }

      // Remove old children from the ListBox.
      while (auto child = m_words->get_first_child())
        m_words->remove(*child);

      // Add new child buttons, one per unique word.
      for (const auto& word : words)
      {
        auto row = Gtk::make_managed<Gtk::Button>(word);
        row->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,
          &ExampleAppWindow::on_find_word), row));
        m_words->append(*row);
      }
    }

    void ExampleAppWindow::update_lines()
    {
      auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
      if (!tab)
        return;

      auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
      if (!view)
      {
        std::cout << "ExampleAppWindow::update_lines(): No visible text view." << std::endl;
        return;
      }
      auto buffer = view->get_buffer();

      int count = 0;
      auto iter = buffer->begin();
      while (iter)
      {
        ++count;
        if (!iter.forward_line())
          break;
      }
      m_lines->set_text(Glib::ustring::format(count));
    }

File: `main.cc` (For use with gtkmm 4)

    #include "../step6/main.cc"
    // Equal to the corresponding file in step6

File: `gears_menu.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <menu id="menu">
        <section>
          <item>
            <attribute name="label" translatable="yes">_Words</attribute>
            <attribute name="action">win.show-words</attribute>
          </item>
          <item>
            <attribute name="label" translatable="yes">_Lines</attribute>
            <attribute name="action">win.show-lines</attribute>
          </item>
          <item>
            <attribute name="label" translatable="yes">_Preferences</attribute>
            <attribute name="action">app.preferences</attribute>
          </item>
        </section>
        <section>
          <item>
            <attribute name="label" translatable="yes">_Quit</attribute>
            <attribute name="action">app.quit</attribute>
          </item>
        </section>
      </menu>
    </interface>

File: `window.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <object class="GtkApplicationWindow" id="app_window">
        <property name="title" translatable="yes">Example Application</property>
        <property name="default-width">600</property>
        <property name="default-height">400</property>
        <child type="titlebar">
          <object class="GtkHeaderBar" id="header">
            <child>
              <object class="GtkLabel" id="lines_label">
                <property name="visible">False</property>
                <property name="label" translatable="yes">Lines:</property>
              </object>
            </child>
            <child>
              <object class="GtkLabel" id="lines">
                <property name="visible">False</property>
              </object>
            </child>
            <child type="title">
              <object class="GtkStackSwitcher" id="tabs">
                <property name="stack">stack</property>
              </object>
            </child>
            <child type="end">
              <object class="GtkToggleButton" id="search">
                <property name="sensitive">False</property>
                <property name="icon-name">edit-find-symbolic</property>
              </object>
            </child>
            <child type="end">
              <object class="GtkMenuButton" id="gears">
                <property name="direction">none</property>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox" id="content_box">
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkSearchBar" id="searchbar">
                <child>
                  <object class="GtkSearchEntry" id="searchentry">
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkBox" id="hbox">
                <child>
                  <object class="GtkRevealer" id="sidebar">
                    <property name="transition-type">slide-right</property>
                    <child>
                      <object class="GtkScrolledWindow" id="sidebar-sw">
                        <property name="hscrollbar-policy">never</property>
                        <child>
                          <object class="GtkListBox" id="words">
                            <property name="selection-mode">none</property>
                          </object>
                        </child>
                      </object>
                    </child>
                  </object>
                </child>
                <child>
                  <object class="GtkStack" id="stack">
                    <property name="transition-duration">500</property>
                  </object>
                </child>
              </object>
            </child>
          </object>
        </child>
      </object>
    </interface>

### Трака заглавља

Наш програм већ користи Gtk::HeaderBar уместо „обичне“ насловне траке прозора. Трака заглавља је директни садржани елемент прозора, а њена врста је `titlebar`. Ово је постављено у датотеци `window.ui`.

Овде ћемо само направити две мале измене на траци заглавља. Својство `decoration-layout` је постављено у датотеци `window.ui` да приказује само дугме за затварање и сакрива дугмад за умањивање и увећавање. Такође укључујемо иконицу у датотеку ресурса и постављамо ову иконицу као иконицу прозора. У конструктору ExampleAppWindow-а:

    Gtk::IconTheme::get_for_display(get_display())->add_resource_path("/org/gtkmm/exampleapp");
    set_icon_name("exampleapp");

Ево како апликација сада изгледа:

Трака заглавља

Датотека `window.ui` поставља наслов траке заглавља, али овај наслов није приказан. То је зато што је пребацивач стека дете врсте `title`. Пребацивач стека постаје произвољни наслов који скрива натпис наслова.

Изворни код

File: `exampleapplication.h` (For use with gtkmm 4)

    #include "../step4/exampleapplication.h"
    // Equal to the corresponding file in step4

File: `exampleappprefs.h` (For use with gtkmm 4)

    #include "../step5/exampleappprefs.h"
    // Equal to the corresponding file in step5

File: `exampleappwindow.h` (For use with gtkmm 4)

    #include "../step8/exampleappwindow.h"
    // Equal to the corresponding file in step8

File: `exampleapplication.cc` (For use with gtkmm 4)

    #include "../step5/exampleapplication.cc"
    // Equal to the corresponding file in step5

File: `exampleappprefs.cc` (For use with gtkmm 4)

    #include "../step5/exampleappprefs.cc"
    // Equal to the corresponding file in step5

File: `exampleappwindow.cc` (For use with gtkmm 4)

    #include "exampleappwindow.h"
    #include <iostream>
    #include <stdexcept>
    #include <set>

    ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
      const Glib::RefPtr<Gtk::Builder>& refBuilder)
    : Gtk::ApplicationWindow(cobject),
      m_refBuilder(refBuilder)
    {
      // Get widgets from the Gtk::Builder file.
      m_stack = m_refBuilder->get_widget<Gtk::Stack>("stack");
      if (!m_stack)
        throw std::runtime_error("No \"stack\" object in window.ui");

      m_search = m_refBuilder->get_widget<Gtk::ToggleButton>("search");
      if (!m_search)
        throw std::runtime_error("No \"search\" object in window.ui");

      m_searchbar = m_refBuilder->get_widget<Gtk::SearchBar>("searchbar");
      if (!m_searchbar)
        throw std::runtime_error("No \"searchbar\" object in window.ui");

    #if HAS_SEARCH_ENTRY2
      m_searchentry = m_refBuilder->get_widget<Gtk::SearchEntry2>("searchentry");
    #else
      m_searchentry = m_refBuilder->get_widget<Gtk::SearchEntry>("searchentry");
    #endif
      if (!m_searchentry)
        throw std::runtime_error("No \"searchentry\" object in window.ui");

      m_gears = m_refBuilder->get_widget<Gtk::MenuButton>("gears");
      if (!m_gears)
        throw std::runtime_error("No \"gears\" object in window.ui");

      m_sidebar = m_refBuilder->get_widget<Gtk::Revealer>("sidebar");
      if (!m_sidebar)
        throw std::runtime_error("No \"sidebar\" object in window.ui");

      m_words = m_refBuilder->get_widget<Gtk::ListBox>("words");
      if (!m_words)
        throw std::runtime_error("No \"words\" object in window.ui");

      m_lines = m_refBuilder->get_widget<Gtk::Label>("lines");
      if (!m_lines)
        throw std::runtime_error("No \"lines\" object in window.ui");

      m_lines_label = m_refBuilder->get_widget<Gtk::Label>("lines_label");
      if (!m_lines_label)
        throw std::runtime_error("No \"lines_label\" object in window.ui");

      // Bind settings.
      m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
      m_settings->bind("transition", m_stack->property_transition_type());
      m_settings->bind("show-words", m_sidebar->property_reveal_child());

      // Bind properties of the search button to the search bar.
      m_binding_search_enabled = Glib::Binding::bind_property(m_search->property_active(),
        m_searchbar->property_search_mode_enabled(), Glib::Binding::Flags::BIDIRECTIONAL);

      // Connect signal handlers.
      m_searchentry->signal_search_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_search_text_changed));
      m_stack->property_visible_child().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_visible_child_changed));
      m_sidebar->property_reveal_child().signal_changed().connect(
        sigc::mem_fun(*this, &ExampleAppWindow::on_reveal_child_changed));

      // Connect the menu to the MenuButton m_gears, and bind the show-words setting
      // to the win.show-words action and the "Words" menu item.
      // (The connection between action and menu item is specified in gears_menu.ui.)
      auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
      auto menu = menu_builder->get_object<Gio::MenuModel>("menu");
      if (!menu)
        throw std::runtime_error("No \"menu\" object in gears_menu.ui");

      m_gears->set_menu_model(menu);
      add_action(m_settings->create_action("show-words"));

      // Bind the "visible" property of m_lines to the win.show-lines action, to
      // the "Lines" menu item and to the "visible" property of m_lines_label.
      add_action(Gio::PropertyAction::create("show-lines", m_lines->property_visible()));
      m_binding_lines_visible = Glib::Binding::bind_property(m_lines->property_visible(),
        m_lines_label->property_visible());

      // Set the window icon.
      Gtk::IconTheme::get_for_display(get_display())->add_resource_path("/org/gtkmm/exampleapp");
      set_icon_name("exampleapp");
    }

    //static
    ExampleAppWindow* ExampleAppWindow::create()
    {
      // Load the Builder file and instantiate its widgets.
      auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");

      auto window = Gtk::Builder::get_widget_derived<ExampleAppWindow>(refBuilder, "app_window");
      if (!window)
        throw std::runtime_error("No \"app_window\" object in window.ui");

      return window;
    }

    void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file)
    {
      const Glib::ustring basename = file->get_basename();

      auto scrolled = Gtk::make_managed<Gtk::ScrolledWindow>();
      scrolled->set_expand(true);
      auto view = Gtk::make_managed<Gtk::TextView>();
      view->set_editable(false);
      view->set_cursor_visible(false);
      scrolled->set_child(*view);
      m_stack->add(*scrolled, basename, basename);

      auto buffer = view->get_buffer();
      try
      {
        char* contents = nullptr;
        gsize length = 0;

        file->load_contents(contents, length);
        buffer->set_text(contents, contents+length);
        g_free(contents);
      }
      catch (const Glib::Error& ex)
      {
        std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name()
          << "\"):\n  " << ex.what() << std::endl;
        return;
      }

      auto tag = buffer->create_tag();
      m_settings->bind("font", tag->property_font());
      buffer->apply_tag(tag, buffer->begin(), buffer->end());

      m_search->set_sensitive(true);
      update_words();
      update_lines();
    }

    void ExampleAppWindow::on_search_text_changed()
    {
      const auto text = m_searchentry->get_text();
      if (text.empty())
        return;

      auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
      if (!tab)
      {
        std::cout << "ExampleAppWindow::on_search_text_changed(): No visible child." << std::endl;
        return;
      }

      auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
      if (!view)
      {
        std::cout << "ExampleAppWindow::on_search_text_changed(): No visible text view." << std::endl;
        return;
      }

      // Very simple-minded search implementation.
      auto buffer = view->get_buffer();
      Gtk::TextIter match_start;
      Gtk::TextIter match_end;
      if (buffer->begin().forward_search(text, Gtk::TextSearchFlags::CASE_INSENSITIVE,
          match_start, match_end))
      {
        buffer->select_range(match_start, match_end);
        view->scroll_to(match_start);
      }
    }

    void ExampleAppWindow::on_visible_child_changed()
    {
      m_searchbar->set_search_mode(false);
      update_words();
      update_lines();
    }

    void ExampleAppWindow::on_find_word(const Gtk::Button* button)
    {
      m_searchentry->set_text(button->get_label());
    }

    void ExampleAppWindow::on_reveal_child_changed()
    {
      update_words();
    }

    void ExampleAppWindow::update_words()
    {
      auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
      if (!tab)
        return;

      auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
      if (!view)
      {
        std::cout << "ExampleAppWindow::update_words(): No visible text view." << std::endl;
        return;
      }
      auto buffer = view->get_buffer();

      // Find all words in the text buffer.
      std::set<Glib::ustring> words;
      auto start = buffer->begin();
      Gtk::TextIter end;
      while (start)
      {
        while (start && !start.starts_word())
          ++start;

        if (!start)
          break;

        end = start;
        end.forward_word_end();
        if (start == end)
          break;

        auto word = buffer->get_text(start, end, false);
        words.insert(word.lowercase());
        start = end;
      }

      // Remove old children from the ListBox.
      while (auto child = m_words->get_first_child())
        m_words->remove(*child);

      // Add new child buttons, one per unique word.
      for (const auto& word : words)
      {
        auto row = Gtk::make_managed<Gtk::Button>(word);
        row->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,
          &ExampleAppWindow::on_find_word), row));
        m_words->append(*row);
      }
    }

    void ExampleAppWindow::update_lines()
    {
      auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
      if (!tab)
        return;

      auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
      if (!view)
      {
        std::cout << "ExampleAppWindow::update_lines(): No visible text view." << std::endl;
        return;
      }
      auto buffer = view->get_buffer();

      int count = 0;
      auto iter = buffer->begin();
      while (iter)
      {
        ++count;
        if (!iter.forward_line())
          break;
      }
      m_lines->set_text(Glib::ustring::format(count));
    }

File: `main.cc` (For use with gtkmm 4)

    #include "../step6/main.cc"
    // Equal to the corresponding file in step6

File: `exampleapp.gresource.xml` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <gresources>
      <gresource prefix="/org/gtkmm/exampleapp">
        <file preprocess="xml-stripblanks">window.ui</file>
        <file preprocess="xml-stripblanks">gears_menu.ui</file>
        <file preprocess="xml-stripblanks">prefs.ui</file>
        <file>exampleapp.png</file>
      </gresource>
    </gresources>

File: `window.ui` (For use with gtkmm 4)

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <object class="GtkApplicationWindow" id="app_window">
        <property name="title" translatable="yes">Example Application</property>
        <property name="default-width">600</property>
        <property name="default-height">400</property>
        <child type="titlebar">
          <object class="GtkHeaderBar" id="header">
            <property name="decoration-layout">icon:close</property>
            <child>
              <object class="GtkLabel" id="lines_label">
                <property name="visible">False</property>
                <property name="label" translatable="yes">Lines:</property>
              </object>
            </child>
            <child>
              <object class="GtkLabel" id="lines">
                <property name="visible">False</property>
              </object>
            </child>
            <child type="title">
              <object class="GtkStackSwitcher" id="tabs">
                <property name="stack">stack</property>
              </object>
            </child>
            <child type="end">
              <object class="GtkToggleButton" id="search">
                <property name="sensitive">False</property>
                <property name="icon-name">edit-find-symbolic</property>
              </object>
            </child>
            <child type="end">
              <object class="GtkMenuButton" id="gears">
                <property name="direction">none</property>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox" id="content_box">
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkSearchBar" id="searchbar">
                <child>
                  <object class="GtkSearchEntry" id="searchentry">
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkBox" id="hbox">
                <child>
                  <object class="GtkRevealer" id="sidebar">
                    <property name="transition-type">slide-right</property>
                    <child>
                      <object class="GtkScrolledWindow" id="sidebar-sw">
                        <property name="hscrollbar-policy">never</property>
                        <child>
                          <object class="GtkListBox" id="words">
                            <property name="selection-mode">none</property>
                          </object>
                        </child>
                      </object>
                    </child>
                  </object>
                </child>
                <child>
                  <object class="GtkStack" id="stack">
                    <property name="transition-duration">500</property>
                  </object>
                </child>
              </object>
            </child>
          </object>
        </child>
      </object>
    </interface>

## Допринеси

Овај документ, као и толико другог сјајног софтвера, направили су бесплатно волонтери. Ако сте уопште упућени у било који аспект gtkmm-а који већ нема документацију, размислите о доприносу овом документу.

Идеално би било да пошаљете захтев за спајање за датотеку `docs/tutorial/C/index-in.docbook`. Ова датотека се налази у модулу `gtkmm-documentation` у Гномовом гиту.

Ако одлучите да допринесете, пошаљите свој допринос као проблем или захтев за спајање на Гитлабу. Такође можете разговарати о својим идејама на Гномовом Discourse примерку, у категорији Platform/Language bindings са ознаком `cplusplus`. Такође, имајте на уму да је цео овај документ слободан, и сваки додатак који пружите мора такође бити слободан. То значи да људи морају бити у могућности да користе било који део ваших примера у својим програмима, а примерци овог документа (укључујући ваш допринос) се могу слободно дистрибуирати.

## Паметни показивач RefPtr

Glib::RefPtr је паметни показивач. Тачније, то је паметни показивач са бројањем референци. Можда сте упознати са std::unique_ptr<> и std::shared_ptr<>, који су такође паметни показивачи. У gtkmm-у 4.0 Glib::RefPtr<> је алијас за std::shared_ptr<>, који користи бројање референци. Glib::RefPtr<> је уведен много пре него што је постојао паметни показивач са бројањем референци у C++ стандардној библиотеци.

Ако правите сопствене класе изведене из Glib::ObjectBase са методама create() које враћају Glib::RefPtr, морате користити `Glib::make_refptr_for_instance()` у својим методама create(). Ова функција прави std::shared_ptr са посебним брисачем, који управља бројањем референци за умотани C објекат.

Glib::RefPtr се не користи за чување елемената изведених из Gtk::Widget. Елементи имају јавне конструкторе уместо заштићених конструктора и метода create().

Референца

Паметни показивач се понаша слично као обичан показивач. Ево неколико примера.

### Умножавање

Можете копирати RefPtr-ове, баш као и обичне показиваче. Али за разлику од обичних показивача, не морате да бринете о брисању основног примерка.

    auto refPixbuf = Gdk::Pixbuf::create_from_file(filename);
    auto refPixbuf2 = refPixbuf;

Наравно, ово значи да можете чувати RefPtr-ове у стандардним контејнерима, као што су std::vector или std::list.

    std::list<Glib::RefPtr<Gdk::Pixbuf>> listPixbufs;
    auto refPixbuf = Gdk::Pixbuf::create_from_file(filename);
    listPixbufs.push_back(refPixbuf);

### Дереференцирање

Можете дереференцирати паметни показивач помоћу оператора ->, да бисте позвали методе основног примерка, баш као и код обичног показивача.

    auto refPixbuf = Gdk::Pixbuf::create_from_file(filename);
    auto width = refPixbuf->get_width();

Такође можете користити оператор * и метод get() да приступите основној појави, али је то обично лоша идеја. Осим ако нисте пажљиви, можете завршити са показивачем или референцом која није укључена у број референци.

    auto refPixbuf = Gdk::Pixbuf::create_from_file(filename);
    auto& underlying = *refPixbuf; // Могуће, али се не препоручује

### Промена врсте (Casting)

Можете променити врсту RefPtr-а у основне врсте, баш као и код обичних показивача.

    auto refStore = Gtk::TreeStore::create(columns);
    Glib::RefPtr<Gtk::TreeModel> refModel = refStore;

Ово значи да било који метод који прима аргумент const Glib::RefPtr<BaseType>& такође може примити и const Glib::RefPtr<DerivedType>&. Промена врсте је имплицитна, баш као што би била за обичан показивач.

Такође можете променити врсту у изведену врсту, али је синтакса мало другачија него код обичног показивача.

    auto refStore = std::dynamic_pointer_cast<Gtk::TreeStore>(refModel);
    auto refStore2 = std::static_pointer_cast<Gtk::TreeStore>(refModel);

### Проверавање за nullptr

Баш као и код обичних показивача, можете проверити да ли RefPtr показује на било шта.

    auto refModel = m_TreeView.get_model();
    if (refModel)
    {
      auto cols_count = refModel->get_n_columns();
      ...
    }

Али за разлику од обичних показивача, RefPtr-ови се аутоматски покрећу на `nullptr` тако да не морате сами да се сећате да то урадите.

### Константност

Употреба кључне речи `const` у C++-у није увек јасна. Можда нећете схватити да const Something* објављује показивач на const Something. Показивач се може изменити, али не и Something на који он показује.

Стога је RefPtr еквивалент за Something* за параметар метода const Glib::RefPtr<Something>&, а еквивалент за const Something* је const Glib::RefPtr<const Something>&.

Окружење `const ... &` код оба је само због ефикасности, попут коришћења const std::string& уместо std::string за параметар метода како би се избегло непотребно умножавање.

## Сигнали

### Повезивање руковалаца сигналима

Разреди gtkmm елемената имају методе за приступ сигналима, као што је Gtk::Button::signal_clicked(), који вам омогућавају да повежете ваш руковалац сигналом. Захваљујући флексибилности библиотеке libsigc++, библиотеке повратних позива коју користи gtkmm, руковалац сигналом може бити скоро било која врста функције, али ћете вероватно желети да користите метод разреда. Међу GTK C програмерима, ови руковаоци сигналима се често називају повратни позиви (callbacks).

Ево примера руковаоца сигналом који се повезује на сигнал:

    #include <gtkmm/button.h>

    void on_button_clicked()
    {
      std::cout << "Hello World" << std::endl;
    }

    class some_class
    {
    public:
      some_class
      {
        button.signal_clicked().connect(sigc::ptr_fun(&on_button_clicked));
      }
    private:
      Gtk::Button button {"Hello World"};
    };

У овом (нефункционалном) коду има доста тога за размишљање. Прво идентификујмо укључене стране:

- Руковалац сигналом је on_button_clicked().

- Повезујемо га са објектом Gtk::Button под називом `button`.

- Када дугме емитује свој сигнал `clicked`, биће позвана функција on_button_clicked().

Погледајмо сада поново везу:

        ...
        button.signal_clicked().connect(sigc::ptr_fun(&on_button_clicked));
        ...

Имајте на уму да показивач на on_button_clicked() не прослеђујемо директно методу connect() сигнала. Уместо тога, позивамо `sigc::ptr_fun()` и прослеђујемо резултат методу connect().

`sigc::ptr_fun()` генерише sigc::slot. Слот је објекат који изгледа и понаша се као функција, али је заправо објекат. Они су такође познати као функцијски објекти или функтори. `sigc::ptr_fun()` генерише слот за самосталну функцију или статички метод. `sigc::mem_fun()` генерише слот за метод члана одређеног примерка.

C++ ламбда израз је функтор који се може имплицитно конвертовати у sigc::slot у позиву connect(). Ламбда израз се може користити уместо `sigc::ptr_fun()`. Такође је могуће користити ламбда израз уместо `sigc::mem_fun()`, али тада нећете добити аутоматско раскидање везе руковаоца сигналом када објекат изведен из sigc::trackable изађе из опсега.

Ево нешто већег примера слотова на делу:

    #include <gtkmm/button.h>

    void on_button_clicked()
    {
      std::cout << "Hello World" << std::endl;
    }

    class some_class
    {
    public:
      some_class
      {
        button.signal_clicked().connect(sigc::ptr_fun(&on_button_clicked));
        button.signal_clicked().connect(sigc::mem_fun(*this, &some_class::on_button_clicked));
      }
      void on_button_clicked();
    private:
      Gtk::Button button {"Hello World"};
    };

Први позив connect() је баш као онај који смо видели прошли пут; овде нема ничег новог.

Следећи је занимљивији. `sigc::mem_fun()` се позива са два аргумента. Први аргумент је *this, што је објекат на који ће наш нови слот показивати. Други аргумент је показивач на један од његових метода. Ова конкретна верзија `sigc::mem_fun()` прави слот који ће, када буде „позван“, позвати наведени метод наведеног објекта.

Још једна ствар коју треба приметити у вези са овим примером је да смо два пута позвали connect() за исти објекат сигнала. Ово је сасвим у реду — када се кликне на дугме, биће позвана оба руковаоца сигналом.

Управо смо вам рекли да сигнал дугмета `clicked` очекује позив метода без аргумената. Сви сигнали имају овакве захтеве — не можете повезати функцију са два аргумента на сигнал који не очекује ниједан (осим ако наравно не користите адаптер, као што је `sigc::bind()`). Стога је важно знати који тип руковаоца сигналом ће се очекивати да повежете са датим сигналом.

### Писање руковалаца сигналима

Да бисте сазнали који тип руковаоца сигналом можете да повежете са сигналом, можете га потражити у референтној документацији или у датотеци заглавља. Ево примера декларације сигнала коју бисте могли видети у gtkmm заглављима:

    Glib::SignalProxy<bool(Gtk::DirectionType)> signal_focus()

Осим имена сигнала (`focus`), овде је важно приметити аргументе шаблона. Први аргумент, bool, је тип који руковалац сигналом треба да врати; а тип унутар заграда, Gtk::DirectionType, је тип првог и јединог аргумента овог сигнала. Гледајући референтну документацију, можете видети и имена аргумената.

Исти принципи важе и за сигнале који имају више аргумената. Ево једног са три (преузето из `<gtkmm/textbuffer.h>`):

    Glib::SignalProxy<void(TextBuffer::iterator&, const Glib::ustrin&, int)> signal_insert();

Прати исти облик. Први тип је void, тако да би то требало да буде повратни тип нашег руковаоца сигналом. Следећа три типа су типови аргумената, по реду. Прототип нашег руковаоца сигналом би могао изгледати овако:

    void on_insert(TextBuffer::iterator& pos, const Glib::ustring& text, int bytes)

### Прекидање везе руковалаца сигналима

Погледајмо још једном метод `connect` сигнала:

    sigc::connection signal<void(int)>::connect(const sigc::slot<void(int)>&);

Враћени sigc::connection се може користити за управљање везом. Чувањем објекта везе можете прекинути везу његовог придруженог руковаоца сигналом коришћењем метода sigc::connection::disconnect().

### Преклапање подразумеваних руковалаца сигналима

До сада смо вам говорили да извршавате радње као одговор на притиске дугмади и слично руковањем сигналима. То је свакако добар начин за обављање ствари, али није и једини.

Уместо мукотрпног повезивања руковалаца сигналима са сигналима, можете једноставно направити нову класу која наслеђује елемент — рецимо, дугме (Button) — а затим преклопити подразумевани руковалац сигналом, као што је Button::on_clicked(). Ово може бити много једноставније од повезивања руковалаца сигналима за све.

Прављење подкласа није увек најбољи начин за постизање ствари. Корисно је само када желите да елемент сам рукује својим сигналом. Ако желите да нека друга класа рукује сигналом, мораћете да повежете посебан руковалац. Ово је још тачније ако желите да неколико објеката рукује истим сигналом, или ако желите да један руковалац сигналом одговара на исти сигнал из различитих објеката.

Класе gtkmm-а су дизајниране имајући на уму преклапање; оне садрже виртуелне методе чланице посебно намењене преклапању.

Погледајмо пример преклапања:

    #include <gtkmm/button.h>

    class OverriddenButton : public Gtk::Button
    {
    protected:
      void on_clicked() override;
    }

    void OverriddenButton::on_clicked()
    {
      std::cout << "Hello World" << std::endl;

      // call the base class's version of the method:
      Gtk::Button::on_clicked();
    }

Овде дефинишемо нову класу под називом OverriddenButton, која наслеђује Gtk::Button. Једина ствар коју мењамо је метод on_clicked(), који се позива кад год Gtk::Button емитује сигнал `clicked`. Овај метод исписује „Hello World“ на `stdout`, а затим позива оригинални, преклопљени метод, како би омогућио да Gtk::Button уради оно што би урадио да нисмо извршили преклапање.

Не морате увек да позивате родитељски метод; постоје тренуци када то можда нећете желети. Имајте на уму да смо позвали родитељски метод након исписивања „Hello World“, али смо могли да га позовемо и пре. У овом једноставном примеру то једва да је битно, али постоје тренуци када ће бити. Са повезаним руковаоцима сигналима није баш тако лако променити овакве детаље, а овде можете урадити нешто што уопште не можете са повезаним руковаоцима сигналима: можете позвати родитељски метод у средини свог прилагођеног кода.

### Везивање додатних аргумената

Ако користите један руковалац сигналом за хватање истог сигнала из неколико елемената, можда ћете желети да тај руковалац сигналом прими неке додатне информације. На пример, можда ћете желети да знате које дугме је кликнуто. То можете урадити помоћу `sigc::bind()`. Ево неког кода из примера helloworld2.

    m_button1.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &HelloWorld::on_button_clicked), "button 1"));

Ово каже да желимо да сигнал пошаље додатни Glib::ustring аргумент руковаоцу сигнала, и да вредност тог аргумента треба да буде „button 1“. Наравно, мораћемо да додамо тај додатни аргумент у декларацију нашег руковаоца сигнала:

    void on_button_clicked(const Glib::ustring& data);

Наравно, обичан руковалац сигнала „clicked“ не би имао аргументе.

`sigc::bind()` се не користи често, али вам понекад може бити од користи. Ако сте упознати са GTK програмирањем, вероватно сте приметили да је ово слично додатним `gpointer data` аргументима које имају сви GTK повратни позиви. Ово се углавном превише користи у GTK-у за пренос информација које би требало да буду сачуване као подаци чланови у изведеном елементу, али је извођење елемената веома тешко у C-у. У gtkmm-у имамо далеко мању потребу за овим триком.

### Сигнали догађаја

Сигнали догађаја се емитују као резултат неког корисничког уноса, на пример притиска тастера или померања миша. Обично не рукујете овим догађајима директно. Уместо тога, користите подразред Gtk::EventController-а, као што су Gtk::EventControllerKey или Gtk::GestureClick. Управљачи догађајима се могу додати елементу помоћу Gtk::Widget::add_controller().

Повремено вам може бити корисно да рукујете догађајима када постоји нешто што не можете постићи обичним сигналима. Gtk::Button, на пример, не шаље координате показивача миша са својим сигналом `clicked`, али бисте могли да рукујете са Gtk::GestureClick::signal_pressed() ако су вам потребни ови подаци. Gtk::EventControllerKey::signal_key_pressed() се често користи за руковање притисцима тастера.

Неки сигнали управљача догађајима се понашају мало другачије. Вредност коју враћа руковалац сигнала указује на то да ли је он у потпуности „обрадио“ догађај. Ако је вредност `false`, тада ће gtkmm проследити догађај следећем руковаоцу сигнала. Ако је вредност `true`, тада неће бити потребно позивати друге руковаоце сигнала.

Руковање догађајем не утиче на остале сигнале елемента. Ако рукујете са Gtk::GestureClick::signal_pressed() за Gtk::Button, и даље ћете моћи да добијете сигнал `clicked`. Они се емитују у (скоро) исто време.

Ево једноставног примера:

    void on_button_press(int n_press, double x, double y);
    Gtk::Button button("label");
    auto controller = Gtk::GestureClick::create();
    controller->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
    controller->signal_pressed().connect(sigc::ptr_fun(&on_button_press));
    button.add_controller(controller);

Када је миш изнад дугмета и притисне се тастер миша, биће позвано on_button_press().

Позив set_propagation_phase() је неопходан у овом случају јер C класа GtkButton додаје управљач догађајима, рукујући кликовима дугмета у фази хватања (capture phase). GtkButton заузима догађај, што значи да се догађај не шири у фази мехура (bubble phase), где управљачи догађајима подразумевано рукују догађајима.

#### Редослед руковаоца сигнала

Подразумевано, руковаоци сигнала који враћају void се позивају након свих претходно повезаних руковаоца сигнала. Међутим, ово може бити проблем са сигналима догађаја који могу зауставити ширење догађаја враћањем вредности `true`. На пример, постојећи руковаоци сигнала, или подразумевани руковалац сигнала, могу вратити `true` како би спречили позивање других руковаоца сигнала. Да бисте навели да ваш руковалац сигнала треба да буде позван пре осталих руковаоца сигнала, можете навести `false` за параметар `after`. Овај параметар функције connect() је опционалан ако руковалац сигнала враћа void. На пример,

    key_controller->signal_key_pressed().connect(sigc::ptr_fun(&on_mywindow_key_pressed), false);

Догађај се шири између елемената у 3 фазе.

1. Фаза хватања – извршава се од највишег нивоа наниже до елемента догађаја.

2. Циљна фаза – извршава се само на елементу догађаја.

3. Фаза мехура – извршава се од елемента догађаја навише до највишег нивоа.

Поглавље Руковање улазом у GTK документацији детаљније описује руковање корисничким уносом.

### Изузеци у руковаоцима сигнала

Када се програм прекине због необрађеног C++ изузетка, понекад је могуће користити исправљач грешака (debugger) за проналажење места где је изузетак избачен. Ово је теже него обично ако је изузетак избачен из руковаоца сигнала.

Овај одељак првенствено описује шта можете очекивати на Линукс систему када користите gdb исправљач грешака.

Прво, погледајмо једноставан пример где је изузетак избачен из обичне функције (без руковаоца сигналом).

    refs: C/index-in.docbook:9330 | flags: no-wrap | notes: (itstool) path: section/programlisting

Ево исечка из gdb сесије. Приказани су само најзанимљивији делови излаза.

    > gdb without_signal
    (gdb) run
    terminate called after throwing an instance of 'char const*'

    Program received signal SIGABRT, Aborted.
    (gdb) backtrace
    #7  0x08048864 in throwSomething () at without_signal.cc:6
    #8  0x0804887d in main (argc=1, argv=0xbfffecd4) at without_signal.cc:12

Можете видети да је изузетак избачен из `without_signal.cc`, линија 6 (`throw "Something";`).

Сада да видимо шта се дешава када се изузетак избаци из руковаоца сигналом. Ево изворног кода.

    // with_signal.cc
    #include <gtkmm.h>

    bool throwSomething()
    {
      throw "Something";
      return true;
    }

    int main(int argc, char** argv)
    {
      Glib::signal_timeout().connect(sigc::ptr_fun(throwSomething), 500);
      auto app = Gtk::Application::create("org.gtkmm.with_signal");
      app->hold();
      return app->run();
    }

А ево исечка из gdb сесије.

    > gdb with_signal
    (gdb) run
    (with_signal:2703): glibmm-ERROR **:
    unhandled exception (type unknown) in signal handler

    Program received signal SIGTRAP, Trace/breakpoint trap.
    (gdb) backtrace
    #2  0x0063c6ab in glibmm_unexpected_exception () at exceptionhandler.cc:77
    #3  Glib::exception_handlers_invoke () at exceptionhandler.cc:150
    #4  0x0063d370 in glibmm_source_callback (data=0x804d620) at main.cc:212
    #13 0x002e1b31 in Gtk::Application::run (this=0x804f300) at application.cc:178
    #14 0x08048ccc in main (argc=1, argv=0xbfffecd4) at with_signal.cc:16

Изузетак је ухваћен у glibmm-у, а програм се завршава позивом функције `g_error()`. Остали изузеци могу резултирати другачијим понашањем, али у сваком случају изузетак из руковаоца сигналом је ухваћен у glibmm-у или gtkmm-у, а gdb не може да види где је избачен.

Да бисте видели где је изузетак избачен, можете користити gdb наредбу `catch throw`.

    > gdb with_signal
    (gdb) catch throw
    Catchpoint 1 (throw)
    (gdb) run
    Catchpoint 1 (exception thrown), 0x00714ff0 in __cxa_throw ()
    (gdb) backtrace
    #0  0x00714ff0 in __cxa_throw () from /usr/lib/i386-linux-gnu/libstdc++.so.6
    #1  0x08048bd4 in throwSomething () at with_signal.cc:6
    (gdb) continue
    Continuing.
    (with_signal:2375): glibmm-ERROR **
    unhandled exception (type unknown) in signal handler

    Program received signal SIGTRAP, Trace/breakpoint trap.

Ако има много ухваћених изузетака пре занимљивог неухваћеног, овај метод може бити заморан. Може се аутоматизовати следећим gdb наредбама.

    refs: C/index-in.docbook:9429 | flags: no-wrap | notes: (itstool) path: section/programlisting

Ове наредбе ће исписати бектрејс из сваког `throw` и наставити. Бектрејс из последњег (или можда претпоследњег) `throw` пре него што се програм заустави је онај занимљив.

## Прављење сопствених сигнала

Сада када сте видели сигнале и руковаоце сигналима у gtkmm-у, можда ћете желети да користите исту технику да омогућите интеракцију између сопствених класа. То је заправо врло једноставно коришћењем библиотеке libsigc++ директно.

Ово није чисто gtkmm или GUI питање. gtkmm користи libsigc++ за имплементацију својих прокси омотача за GTK систем сигнала, али за нове, не-GTK сигнале, можете направити чисте C++ сигнале, користећи sigc::signal<> шаблон.

На пример, да бисте направили сигнал који шаље 2 параметра, bool и int, само објавите sigc::signal, овако:

    refs: C/index-in.docbook:9466 | flags: no-wrap | notes: (itstool) path: appendix/programlisting

Могли бисте само декларисати тај сигнал као јавну променљиву чланицу, али неки људи то сматрају неукусним и више воле да га учине доступним преко приступне методе, овако:

    class Server
    {
    public:
      //signal accessor:
      using type_signal_something = sigc::signal<void(bool, int)>;
      type_signal_something signal_something();

    protected:
      type_signal_something m_signal_something;
    };

    Server::type_signal_something Server::signal_something()
    {
      return m_signal_something;
    }

Затим се можете повезати на сигнал користећи исту синтаксу која се користи при повезивању на gtkmm сигнале. На пример,

    refs: C/index-in.docbook:9494 | flags: no-wrap | notes: (itstool) path: appendix/programlisting

### Пример

Ово је потпуно функционалан пример који дефинише и користи прилагођене сигнале.

Изворни код

File: `client.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_CLIENT_H
    #define GTKMM_EXAMPLE_CLIENT_H

    #include <sigc++/sigc++.h>

    //Client must inherit from sigc::trackable.
    //because libsigc++ needs to keep track of the lifetime of signal handlers.
    class Client : public sigc::trackable
    {
    public:
      Client();
      virtual ~Client();

      //Signal handler:
      void on_server_something(bool a, int b);
    };

    #endif //GTKMM_EXAMPLE_CLIENT_H

File: `server.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLE_SERVER_H
    #define GTKMM_EXAMPLE_SERVER_H

    #include <sigc++/sigc++.h>

    class Server
    {
    public:
      Server();
      virtual ~Server();

      void do_something();

      //signal accessor:
      using type_signal_something = sigc::signal<void(bool, int)>;
      type_signal_something signal_something();

    protected:
      type_signal_something m_signal_something;
    };

    #endif //GTKMM_EXAMPLE_SERVER_H

File: `client.cc` (For use with gtkmm 4)

    #include "client.h"
    #include <iostream>

    Client::Client()
    {
    }

    Client::~Client()
    {
    }

    void Client::on_server_something(bool a, int b)
    {
      std::cout << "Client::on_server_something() called with these parameters: "
          << a << ", " << b << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

    #include "server.h"
    #include "client.h"
    #include <iostream>

    int main(int, char**)
    {
      Server server;
      Client client;

      //Connect a Server signal to the signal handler in Client.
      server.signal_something().connect(sigc::mem_fun(client,
                  &Client::on_server_something) );

      std::cout << "Before Server::do_something()" << std::endl;

      //Tell the server to do something that will eventually cause it to emit the
      //"something" signal.
      server.do_something();    // Client::on_server_something() will run before
                                // Server::do_something() has completed.

      std::cout << "After Server::do_something()" << std::endl;

      return 0;
    }

File: `server.cc` (For use with gtkmm 4)

    #include "server.h"
    #include <iostream>

    Server::Server()
    {
    }

    Server::~Server()
    {
    }

    Server::type_signal_something Server::signal_something()
    {
      return m_signal_something;
    }

    void Server::do_something()
    {
      m_signal_something.emit(false, 5);
    }

## Поређење са другим системима сигнализације

(Напомена: GTK назива ову шему „сигнализацијом“; пажљиви читалац са искуством у GUI скуповима алата приметиће да се овај исти дизајн често среће под називом „емитер-слушалац“ (нпр. у Metrowerks-овом PowerPlant оквиру за Мекинтош). Ради на веома сличан начин: постављају се `емитери`, а затим се на њих повезују `слушаоци`; емитер чува списак објеката који га слушају, и када неко пошаље поруку емитеру, он позива све своје објекте на списку са том поруком. У gtkmm-у, објекти сигнала играју улогу емитера, а слотови играју улогу слушалаца — донекле. Више о овоме касније.)

Руковаоци сигналима у gtkmm-у су строго типизирани, док GTK C код дозвољава да повежете повратни позив са погрешним бројем и врстом аргумената, што доводи до сегфаулта у време извршавања. И, за разлику од Qt-а, gtkmm то постиже без мењања C++ језика.

О заобилажењу руковалаца сигналима: Ово можете урадити и у чистом C свету GTK-а; томе и служи GTK-ов систем објеката. Али у GTK-у морате проћи кроз неке компликоване процедуре да бисте добили објектно-оријентисане функције као што су наслеђивање и преклапање. У C++-у је то једноставно, пошто су те функције подржане у самом језику; можете пустити компајлер да обави прљав посао.

Ово је једно од места где лепота C++-а заиста долази до изражаја. Човек не би помислио на прављење подкласе ГТК елемента само да би преписао његов метод радње; то је једноставно превише посла. У ГТК-у скоро увек користите сигнале да бисте нешто урадили, осим ако не пишете нови елемент. Али пошто је преписивање метода тако лако у C++-у, потпуно је практично — и разумно — направити подкласу дугмета за ту сврху.

## gtkmm и Win32

Једна од главних предности gtkmm-а је то што је вишеплатформски. gtkmm програми написани на другим платформама као што је ГНУ/Линукс могу се углавном пренети на Виндоуз (и обрнуто) са мало измена у изворном коду.

gtkmm тренутно ради са MinGW/GCC преводиоцем верзије која подржава C++17, као што је gcc 7 или новији. Такође ради са Microsoft Visual C++ 2017 15.7.x или новијим (укључујући слободно доступна express/community издања) на Виндоуз платформи. Постоји инсталатер доступан за gtkmm на Мајкрософт Виндоузу, али у време писања овог текста (децембар 2024.) он садржи само инсталатер за gtkmm2. Имајте на уму да иако је обично у реду мешати градње урађене са Visual Studio 2017 и 2019, немојте то радити када градите gtkmm са његовим -mm зависностима.

Постоје README датотеке које описују како да изградите gtkmm и његове зависности на Виндоузу:

- libsigc++: MSVC_NMake/MSVC-Builds.md

- glibmm: README.win32.md

- cairomm: MSVC_NMake/MSVC-Builds.md

- pangomm: README.win32.md

- gtkmm: README.win32.md, MSVC_NMake/README

## Рад са изворним кодом gtkmm-а

Ако сте заинтересовани да помогнете у развоју gtkmm-а или да исправите грешку у gtkmm-у, вероватно ћете морати да изградите развојну верзију gtkmm-а. Међутим, не би требало да инсталирате развојну верзију преко ваше стабилне верзије. Уместо тога, требало би да је инсталирате поред ваше постојеће gtkmm инсталације, у посебну путању.

Најлакши начин да то урадите је коришћењем jhbuild-а. jhbuild је програм који знатно олакшава изградњу Гном софтвера израчунавањем зависности и изградњом ствари исправним редоследом. Овај одељак ће дати кратко објашњење како да подесите jhbuild за изградњу и инсталацију gtkmm-а из ризнице изворног кода (git). За најновије информације о jhbuild-у, погледајте jhbuild упутство.

Имајте на уму да ћете за изградњу gtkmm-а из гита често морати да изградите и многе његове зависности из гита. jhbuild то чини лакшим него што би иначе било, али ће бити потребно доста времена да се све изграде и инсталирају. Вероватно ћете наићи на проблеме при градњи, иако ће они обично бити брзо исправљени ако их пријавите.

gnome-build-meta је алтернатива за jhbuild. Описан је на вики страници Building system components, али овде се концентришемо на jhbuild.

### Подешавање jhbuild-а

Да бисте подесили jhbuild, пратите основна упутства за инсталацију из jhbuild упутства. Након што инсталирате jhbuild, требало би да копирате пример jhbuild датотеке подешавања у ваш лични директоријум извршавањем следеће наредбе из jhbuild директоријума:

    $ cp examples/sample.jhbuildrc ~/.config/jhbuildrc

Модул gtkmm је дефинисан у `gnome-suites-core-deps-latest.modules` комплету модула. Зато уредите вашу `jhbuildrc` датотеку и поставите подешавање moduleset овако:

    moduleset = 'gnome-suites-core-deps-latest'

Након што подесите исправан скуп модула, треба да кажете jhbuild-у који модул или модуле да изгради. Да бисте изградили gtkmm и све његове зависности, подесите `modules` овако:

    modules = [ 'gtkmm' ]

Можете изградити неколико модула постављањем променљиве `modules` на мета-пакет, нпр. `meta-gnome-core`, или навођењем више од једног назива модула. Променљива `modules` одређује који ће модули бити изграђени када ништа изричито не наведете у линији наредби. Касније увек можете изградити другачији скуп модула тако што ћете га навести у линији наредби (нпр. `jhbuild build gtkmm`).

Постављање префикса

По подразумеваним поставкама, jhbuild је подешен да инсталира сав софтвер изграђен помоћу jhbuild-а под префиксом `~/jhbuild/install`. Можете изабрати другачији префикс, али се препоручује да овај префикс буде другачији од осталог софтвера који сте инсталирали (немојте га поставити на `/usr`!). Ако сте пратили упутства за jhbuild, онда овај префикс припада вашем кориснику, тако да не морате да покрећете jhbuild као `root`.

Такође би требало да подесите `buildroot` у `jhbuildrc`. jhbuild изграђује gtkmm и многе његове зависности помоћу Мезона. Мезон не дозвољава изградњу унутар самог изворног стабла. Подразумевана радња jhbuild-а је изградња у директоријуму `build` који се налази непосредно испод корена изворног кода. Неки модули имају директоријум `build` са датотекама које се користе приликом изградње помоћу Autotools-а. Те датоке могу бити уништене ако дозволите jhbuild-у да гради у том директоријуму.

Када сте преузели jhbuild из гит ризнице, добили сте одређени број `.modules` датотека које одређују зависности између модула. По подразумеваним поставкама jhbuild не користи преузете верзије ових датотека, већ чита најновије верзије из гит ризнице. Ово је обично оно што желите. Ако то не желите, користите променљиву `use_local_modulesets` у `jhbuildrc`.

### Инсталирање и коришћење гит верзије gtkmm-а

Након што подесите jhbuild као што је горе описано, изградња gtkmm-а би требало да буде релативно једноставна. Када први пут покренете jhbuild, требало би да извршите следећи низ наредби како бисте се уверили да jhbuild има потребне алате и потврдили да је исправно подешен:

    $ jhbuild bootstrap
    $ jhbuild sanitycheck

#### Инсталирање gtkmm-а помоћу jhbuild-а

Ако је све прошло како треба, требало би да можете да изградите gtkmm и све његове зависности из гита извршавањем наредбе `jhbuild build` (или, ако нисте навели gtkmm у променљивој `modules`, наредбом `jhbuild build gtkmm`).

Ова наредба ће изградити и инсталирати низ модула и вероватно ће потрајати доста времена при првом покретању. Након првог пута, међутим, требало би да иде знатно брже јер треба само поново изградити датотеке које су се измениле од последње изградње. Алтернативно, након што први пут изградите и инсталирате gtkmm, можете поново изградити само gtkmm (без поновне изградње свих његових зависности) наредбом `jhbuild buildone gtkmm`.

#### Коришћење гит верзије gtkmm-а

Након што инсталирате гит верзију gtkmm-а, спремни сте да почнете да је користите и експериментишете са њом. Да бисте користили нову верзију gtkmm-а коју сте управо инсталирали, морате да подесите неке променљиве окружења како би ваша `configure` или `meson.build` скрипта знала где да пронађе нове библиотеке. Срећом, jhbuild нуди лако решење за овај проблем. Извршавање наредбе `jhbuild shell` ће покренути нову шкољку са свим исправно подешеним променљивама окружења. Сада, ако поново конфигуришете и изградите свој пројекат као што то обично радите, он би требало да се повеже са новоинсталираним библиотекама. Да бисте се вратили у претходно окружење, једноставно изађите из jhbuild шкољке.

Након што изградите свој софтвер, мораћете да покренете свој програм и унутар jhbuild окружења. Да бисте то урадили, можете поново користити наредбу `jhbuild shell` за покретање нове шкољку са подешеним jhbuild окружењем. Алтернативно, можете извршити једнократну наредбу у jhbuild окружењу користећи следећу наредбу: `jhbuild run назив-наредбе`. У овом случају, наредба ће бити покренута са постављеним исправним променљивама окружења, али ће се вратити у ваше претходно окружење након што програм изађе.

## Обмотавање C библиотека помоћу gmmproc-а

gtkmm користи алат `gmmproc` за генерисање већине свог изворног кода, користећи .defs датотеке које дефинишу АПИ-је библиотека заснованих на GObject-у. Тако је прилично лако направити додатне омотаче у gtkmm стилу за друге библиотеке засноване на glib/GObject-у.

Ово укључује разне алате, од којих су неки застарели, али барем раде, и успешно су коришћени у неколико пројеката.

### Структура изградње

Стварање изворног кода за gtkmm-ов омотачки API захтева коришћење алата као што су `gmmproc` и `generate_wrap_init.pl`, који су укључени у glibmm. У теорији бисте могли сами да напишете своје датотеке изградње како бисте их користили на одговарајући начин, али много боља опција је да искористите инфраструктуру изградње коју обезбеђује модул mm-common. За почетак, много помаже ако изаберете постојећи модул увезивања као пример који ћете погледати.

На пример, замислимо да правимо омотач за C библиотеку под називом libsomething. Она обезбеђује API заснован на GObject-у са типовима названим, на пример, SomeWidget и SomeStuff.

#### Копирање скелетног пројекта

Обично би се наша библиотека омотача звала libsomethingmm. Можемо почети тако што ћемо копирати изворно стабло скелета из модула mm-common. Почевши од верзије mm-common 1.0.0, ова скелетна апликација се гради помоћу Мезоновог система изградње.

      $ git clone https://gitlab.gnome.org/GNOME/mm-common.git
      $ cp -a mm-common/skeletonmm libsomethingmm

Ово обезбеђује структуру директоријума за изворне .hg и .ccg датотеке и ручно писане .h и .cc датотеке, са `meson.build` датотекама које могу да наведу различите датотеке у употреби, изражене Мезоновим променљивама. Структура директоријума обично изгледа овако, након што одговарајуће преименујемо директоријуме:

- `libsomethingmm`: Директоријум највишег нивоа.

  - `libsomething`: Садржи главну датотеку укључивања и pkg-config .pc датотеку.

    - `src`: Садржи .hg и .ccg изворне датотеке.

    - `libsomethingmm`: Садржи ручно писане .h и .cc датотеке.

Осим преименовања директоријума, требало би да преименујемо и неке изворне датотеке. На пример:

    $ for f in $(find libsomethingmm -depth -name '*skeleton*'); do \
        d="${f%/*}"; b="${f##*/}"; mv "$f" "$d/${b//skeleton/libsomething}"; \
      done

Одређени број скелетних датотека мора се касније попунити садржајем специфичним за пројекат.

Имајте на уму да ће се датотеке које се завршавају на `.in` користити за генерисање датотека са истим именом, али без суфикса `.in`, заменом неких променљивих стварним вредностима током фазе конфигурисања.

Генерисане датотеке се чувају у стаблу изградње, које је одвојено од изворног стабла када се користе `meson` и `ninja`.

#### Мењање датотека изградње

Сада уређујемо датотеке како бисмо их прилагодили нашим потребама. Можда бисте радије користили алатку за претрагу и замену у више датотека, као што је `regexxer`. Имајте на уму да скоро све датотеке обезбеђене у изворном стаблу скелета садрже текст чувара места. Стога, замене треба извршити глобално, а не ограничавати их само на Мезонове датотеке.

Сва помињања `skeleton` треба заменити исправним именом C библиотеке за коју правите омотач, као што је „something“ или „libsomething“. На исти начин, све појаве `SKELETON` треба заменити са „SOMETHING“ или „LIBSOMETHING“, а све појаве `Skeleton` променити у „Something“.

Исто тако, замените све појаве `Joe Hacker` именом предвиђеног носиоца ауторских права, што сте вероватно ви. Урадите исто и за адресу е-поште `joe@example.com`.

##### meson.build у директоријуму највишег нивоа

- Уобичајено је да везни модули прате број издања библиотеке коју омотавају. Тако би, на пример, ако је Ц библиотека на издању 1.23.4, почетно издање везног модула било 1.23.0. Међутим, избегавајте почетак са парним бројем споредног издања јер то обично указује на стабилно издање.

- У функцији `project()`, измените лиценцу и Ц++ издање, ако је потребно.

- Вероватно ћете морати да додате више потребних модула осим glibmm и skeleton (libsomething).

##### Остале meson.build датотеке

Затим морамо прилагодити остале `meson.build` датотеке:

- `skeleton/meson.build`: Можда овде нема много тога за измену осим општих замена назива.

- `skeleton/skeletonmm/meson.build`

  `defs_basefiles`

    Ако имамо више .defs и docs.xml датотека, додајемо их овде.

  `hg_ccg_basenames`

    Овде морамо навести све наше `.hg` и `.ccg` датотеке.

  `extra_cc_files, extra_h_files`

    Било које додатне ручно писане `.h` и `.cc` изворне датотеке иду овде.

##### Стварање .hg и .ccg датотека

Сада треба да направимо наше прве `.hg` и `.ccg` датотеке, како бисмо омотали један од објеката у Ц библиотеци. Један пар примерних изворних датотека већ постоји: `skeleton.ccg` и `skeleton.hg`. Направите копије ових датотека по потреби.

У одељку .hg и .ccg датотеке можете сазнати више о синтакси која се користи у овим датотекама.

### Generating the .defs files

Датотеке `.defs` су текстуалне датотеке, у формату лиспа, које описују АПИ Ц библиотеке, укључујући њене

- објекти (GObjects, елементи, прочеља, „boxed“ типови и обичне структуре)

- функције

- набројиви типови

- сигнали

- својства

- в-функције

We split these `.defs` files into separate files. For instance, in the `gtk/src` directory of the gtkmm sources, you will find these files:

`gtk.defs`

  Includes the other files.

`gtk_methods.defs`

  Objects and functions.

`gtk_enums.defs`

  Enumerations.

`gtk_signals.defs`

  Signals and properties.

`gtk_vfuncs.defs`

  vfuncs (function pointer member fields in structs), written by hand.

Скрипта `skeletonmm/tools/generate_defs_and_docs.sh` ствара све `.defs` датотеке и `*_docs.xml` датотеку, описану у одељку Документација.

If the wrapped C library uses GObject-introspection and generates a GIR file, you can alternatively copy and adapt the `skeletonmm/tools/generate_from_gir.sh` script, which also generates all `.defs` files and the `*_docs.xml` file.

#### Generating .defs without a GIR file

##### Стварање .defs датотека метода

Ова `.defs` датотека описује објекте и њихове функције. Ствара је скрипта `h2def.py` коју можете наћи у glibmm-овом директоријуму `tools/defs_gen`. На пример,

    $ ./h2def.py /usr/include/gtk-4.0/gtk/*.h > gtk_methods.defs

##### Стварање .defs датотека набрајања

Ова `.defs` датотека описује набројиве типове и њихове могуће вредности. Генерише је скрипта `enumextract.py` коју такође можете пронаћи у glibmm-овом директоријуму `tools/defs_gen`. На пример,

    $ ./enumextract.py /usr/include/gtk-4.0/gtk/*.h > gtk_enums.defs

##### Стварање .defs датотека за сигнале и својства

Ова `.defs` датотека описује сигнале и својства. Генерише је посебно помагало `generate_extra_defs` које се налази у сваком пројекту за премотавање, као што је `gtkmm/tools/extra_defs_gen/`. На пример

    $ cd tools/extra_defs_gen
    $ ./generate_extra_defs > gtk_signals.defs

Морате уредити изворни код сопствене алатке `generate_extra_defs` како бисте генерисали `.defs` за GObject C типове које желите да премотате. У скелетном изворном стаблу, изворна датотека се зове `tools/extra_defs_gen/generate_defs_skeleton.cc`. Ако то већ нисте урадили, датотеку треба преименовати, замењујући чувар места `skeleton` основним називом вашег новог увезивања. Датотека `tools/extra_defs_gen/meson.build` такође треба да помиње назив нове изворне датотеке.

Затим уредите `.cc` датотеку да бисте навели исправне типове. На пример, ваша `main()` функција може изгледати овако:

    #include <glibmm_generate_extra_defs/generate_extra_defs.h>
    #include <libsomething.h>
    #include <iostream>

    int main(int, char**)
    {
      something_init();

      std::cout << get_defs(SOME_TYPE_WIDGET)
                << get_defs(SOME_TYPE_STUFF);
      return 0;
    }

##### Писање .defs за виртуелне функције (vfuncs)

Ова `.defs` датотека описује виртуелне функције (vfuncs). Мора се написати ручно. Постоји скелетна датотека `skeleton/src/skeleton_vfunc.defs` од које можете почети. Такође можете погледати gtkmm-ову датотеку `gtk/src/gtk_vfuncs.defs`.

#### Generating .defs from a GIR file

Glibmm since version 2.88.1 contains the `mmgir` tool. This is a C++ program. To use it, build glibmm with `meson` with the `-Dbuild-mmgir=true` option.

`mmgir` can generate all the `.defs` files. For instance,

    $ ./mmgir --gir /usr/share/gir-1.0/Gtk-4.0.gir \
        --gir-search-dir /usr/share/gir-1.0 \
        --enum-defs gtk_enums.defs \
        --function-defs gtk_methods.defs \
        --signal-defs gtk_signals.defs \
        --vfunc-defs gtk_vfuncs.defs

### .hg и .ccg датотеке

.hg и .ccg изворне датотеке су веома сличне .h и .cc C++ изворним датотекама, али садрже додатне макрое, као што су `_CLASS_GOBJECT()` и `_WRAP_METHOD()`, из којих `gmmproc` генерише одговарајући C++ изворни код, обично на истом месту у заглављу. Сав додатни C++ изворни код ће бити дословно прекопиран у одговарајућу .h или .cc датотеку.

.hg датотека ће обично укључивати нека заглавља и затим декларисати класу, користећи неке макрое за додавање API-ја или понашања тој класи. На пример, gtkmm-ова `button.hg` изгледа отприлике овако:

    #include <gtkmm/widget.h>
    #include <gtkmm/actionable.h>
    _DEFS(gtkmm,gtk)
    _PINCLUDE(gtkmm/private/widget_p.h)

    namespace Gtk
    {

    class GTKMM_API Button
      : public Widget,
        public Actionable
    {
      _CLASS_GTKOBJECT(Button, GtkButton, GTK_BUTTON, Gtk::Widget, GtkWidget, , , GTKMM_API)
      _IMPLEMENTS_INTERFACE(Actionable)
    public:

      _CTOR_DEFAULT
      explicit Button(const Glib::ustring& label, bool mnemonic = false);

      _WRAP_METHOD(void set_label(const Glib::ustring& label), gtk_button_set_label)

      ...

      _WRAP_SIGNAL(void clicked(), "clicked")

      ...

      _WRAP_PROPERTY("label", Glib::ustring)
    };

    } // namespace Gtk

Макрои у овом примеру раде следеће:

`_DEFS()`

  Наводи одредишни директоријум за генерисане изворе и назив главне .defs датотеке коју `gmmproc` треба да обради.

`_PINCLUDE()`

  Говори `gmmproc`-у да укључи заглавље у генерисану `private/button_p.h` датотеку.

`_CLASS_GTKOBJECT()`

  Говори `gmmproc`-у да дода неке typedef-ове, конструкторе и стандардне методе овој класи, што је прикладно при премотавању виџета.

`_IMPLEMENTS_INTERFACE()`

  Говори наредби `gmmproc` да дода код за покретање за прочеље.

`_CTOR_DEFAULT`

  Додаје подразумевани конструктор.

`_WRAP_METHOD()`, `_WRAP_SIGNAL()` и `_WRAP_PROPERTY()`

  Додаје начине за премошћавање делова Ц API-ја.

Датотеке .h и .cc ће бити створене из датотека .hg и .ccg њиховом обрадом помоћу наредбе `gmmproc` на овај начин, иако се то дешава аутоматски када се користи горе наведена структура изградње:

    $ cd gtk/src
    $ /usr/lib/glibmm-2.68/proc/gmmproc -I ../../tools/m4 --defs . button . ./../gtkmm

Приметите да смо доставили наредби `gmmproc` путању до .m4 датотека претварања, путању до .defs датотеке, назив .hg датотеке, изворни директоријум и одредишни директоријум.

Требало би да избегавате укључивање Ц заглавља из вашег Ц++ заглавља, како бисте избегли загађивање општег именског простора и извожење непотребног јавног API-ја. Али ћете морати да укључите неопходна Ц заглавља из ваше .ccg датотеке.

Макрои су објашњени детаљније у следећим одељцима.

#### М4 претварања

Макрои које користите у .hg и .ccg датотекама често морају да знају како да претворе Ц++ врсту у Ц врсту, или обрнуто. `gmmproc` узима ове податке из .m4 датотеке у вашем `tools/m4/` или `codegen/m4/` директоријуму. Ово му омогућава да позове Ц функцију у изведби вашег Ц++ начина, прослеђујући одговарајуће параметре тој Ц функцији. На пример, ово говори наредби `gmmproc` како да претвори GtkTreeView показивач у Gtk::TreeView показивач:

    _CONVERSION(`GtkTreeView*',`TreeView*',`Glib::wrap($3)')

`$3` ће бити замењен називом параметра када ово претварање користи наредба `gmmproc`.

Неки додатни макрои чине ово лакшим и доследнијим. Погледајте у gtkmm .m4 датотекама за примере. На пример:

    _CONVERSION(`PrintSettings&',`GtkPrintSettings*',__FR2P)
    _CONVERSION(`const PrintSettings&',`GtkPrintSettings*',__FCR2P)
    _CONVERSION(`const Glib::RefPtr<Printer>&',`GtkPrinter*',__CONVERT_REFPTR_TO_P($3))

#### М4 покретања

Често при премошћавању начина, пожељно је сачувати повратну вредност Ц функције у оно што се назива излазни параметар. У овом случају, Ц++ начин враћа void, али је излазни параметар у којем се чува вредност Ц функције укључен у списак аргумената Ц++ начина. `gmmproc` омогућава такву функционалност, али морају бити укључени одговарајући макрои покретања како би рекли наредби `gmmproc` како да покрене Ц++ параметар из повратне вредности Ц функције.

На пример, ако постоји Ц функција која враћа GtkWidget* и из неког разлога, уместо да Ц++ метод такође враћа елемент, пожељно је да Ц++ метод постави елемент у наведени излазни параметар, макро за покретање као што је следећи би био неопходан:

    _INITIALIZATION(`Gtk::Widget&',`GtkWidget*',`$3 = Glib::wrap($4)')

`$3` ће бити замењено именом излазног параметра Ц++ метода, а `$4` ће бити замењено повратном вредношћу Ц функције када ово покретање користи `gmmproc`. Ради практичности, `$1` ће такође бити замењено Ц++ врстом без амперсанда (&), а `$2` ће бити замењено Ц врстом.

#### Макрои класа

Макро класе декларише саму класу и њен однос са основним Ц типом. Он генерише неке унутрашње конструкторе, члан `gobject_`, дефиниције типова (typedefs), `gobj()` приступнике, регистрацију типа и метод `Glib::wrap()`, између осталог.

Остали макрои, као што су `_WRAP_METHOD()` и `_WRAP_SIGNAL()`, могу се користити само након позива `_CLASS_*` макроа.

Већина макроа класа узима необавезни аргумент, префикс декларације. Обично се користи за макро Ц препроцесора дефинисан као `__declspec(dllexport)` или `__declspec(dllimport)` приликом изградње са MSVC на Windows-у, а у супротном је дефинисан као празна ниска.

##### _CLASS_GOBJECT

Овај макро декларише омотач за тип који је изведен из GObject, али чији омотач није изведен из Gtk::Object.

`_CLASS_GOBJECT( Ц++ класа, Ц класа, Ц макро за кастовање, Ц++ основна класа, Ц основна класа, права Ц класа, права Ц основна класа, префикс декларације )`

права Ц класа и права Ц основна класа су необавезни. Ретко се користе. На пример, из `adjustment.hg`:

    _CLASS_GOBJECT(Adjustment, GtkAdjustment, GTK_ADJUSTMENT, Glib::Object, GObject, , , GTKMM_API)

##### _CLASS_GTKOBJECT

Овај макро декларише омотач за тип чији је омотач изведен из Gtk::Object, као што је елемент (widget) или прозорче (dialog).

`_CLASS_GTKOBJECT( Ц++ класа, Ц класа, Ц макро за кастовање, Ц++ основна класа, Ц основна класа, права Ц класа, права Ц основна класа, префикс декларације )`

права Ц класа и права Ц основна класа су необавезни. Ретко се користе. На пример, из `button.hg`:

    _CLASS_GTKOBJECT(Button, GtkButton, GTK_BUTTON, Gtk::Widget, GtkWidget, , , GTKMM_API)

Обично ћете користити овај макро када је класа већ изведена из Gtk::Object. На пример, користићете га када омотавате ГТК елемент, јер је Gtk::Widget изведен из Gtk::Object.

Такође можете извести класе које нису елементи из Gtk::Object како би се могле користити без Glib::RefPtr. На пример, тада би се могле инстанцирати помоћу `Gtk::make_managed()` или на стеку као члан променљива. Ово је практично, али ово треба да користите само када сте сигурни да право бројање референци није потребно. Сматрамо га корисним за елементе.

##### _CLASS_BOXEDTYPE

Овај макро декларише омотач за структуру која није GObject, регистровану помоћу `g_boxed_type_register_static()`.

`_CLASS_BOXEDTYPE( C++ class, C class, new function, copy function, free function, declaration prefix )`

На пример, из Gdk::RGBA:

    _CLASS_BOXEDTYPE(RGBA, GdkRGBA, NONE, gdk_rgba_copy, gdk_rgba_free, GDKMM_API)

##### _CLASS_BOXEDTYPE_STATIC

Овај макро декларише омотач за једноставну доделиву структуру као што је GdkRectangle. Сличан је макроу `_CLASS_BOXEDTYPE`, али се C структура не додељује динамички.

`_CLASS_BOXEDTYPE_STATIC( C++ class, C class, declaration prefix )`

На пример, за Gdk::Rectangle:

    _CLASS_BOXEDTYPE_STATIC(Rectangle, GdkRectangle, GDKMM_API)

##### _CLASS_OPAQUE_COPYABLE

Овај макро декларише омотач за непровидну структуру која има функције за копирање и ослобађање. Нове функције, функције за копирање и ослобађање ће се користити за инстанцирање подразумеваног конструктора, конструктора копије и деструктора.

`_CLASS_OPAQUE_COPYABLE( C++ class, C class, new function, copy function, free function, declaration prefix )`

На пример, из Glib::VariantType:

    _CLASS_OPAQUE_COPYABLE(VariantType, GVariantType, NONE, g_variant_type_copy, g_variant_type_free, GLIBMM_API)

##### _CLASS_OPAQUE_REFCOUNTED

Овај макро декларише омотач за непровидну структуру са бројањем референци. C++ омотач се не може директно инстанцирати и може се користити само са Glib::RefPtr.

`_CLASS_OPAQUE_REFCOUNTED( C++ class, C class, new function, ref function, unref function, declaration prefix )`

На пример, за Gtk::CssSection:

    _CLASS_OPAQUE_REFCOUNTED(CssSection, GtkCssSection, NONE, gtk_css_section_ref, gtk_css_section_unref, GTKMM_API)

##### _CLASS_GENERIC

Овај макро се може користити за омотавање структура које се не уклапају ни у једну специјализовану категорију.

`_CLASS_GENERIC( C++ class, C class )`

Напомена: Без аргумента declaration prefix. На пример, за Gdk::TimeCoord:

    _CLASS_GENERIC(TimeCoord, GdkTimeCoord)

##### _CLASS_INTERFACE

Овај макро дефинише омотач за тип који је изведен из GTypeInterface.

`_CLASS_INTERFACE( C++ class, C class, C casting macro, C interface struct, Base C++ class (optional), Base C class (optional), declaration prefix (optional) )`

На пример, из `celleditable.hg`:

    _CLASS_INTERFACE(CellEditable, GtkCellEditable, GTK_CELL_EDITABLE, GtkCellEditableIface, , , GTKMM_API)

Два додатна опциона параметра су својевремено додата за случај када је интерфејс изведен из другог интерфејса, за шта се веровало да је случај када GInterface има други GInterface као предуслов. Ово је међутим неспоразум. Када GInterface A има GInterface B као предуслов, то значи да свака класа која имплементира A мора такође имплементирати и B. На пример, из `loadableicon.hg` у glibmm-2.4:

    _CLASS_INTERFACE(LoadableIcon, GLoadableIcon, G_LOADABLE_ICON, GLoadableIconIface, Icon, GIcon, GIOMM_API)

#### Макрои конструктора

Макрои `_CTOR_DEFAULT()` и `_WRAP_CTOR()` додају конструкторе, омотавајући наведене `*_new()` C функције. Ови макрои претпостављају да C објекат има својства са истим именима као и параметри функције, као што је обично случај, тако да може проследити параметре директно позиву `g_object_new()`. Ови конструктори никада заправо не позивају `*_new()` C функције, јер gtkmm мора заправо инстанцирати изведене GType-ове, а `*_new()` C функције су замишљене само као помоћне функције за C програмере.

Када се користи `_CLASS_GOBJECT()`, конструктори треба да буду заштићени (protected, а не јавни) и сваки конструктор треба да има одговарајући `_WRAP_CREATE()` у јавном одељку. Ово спречава да класа буде инстанцирана без коришћења RefPtr-а. На пример:

    class GTKMM_API TextMark : public Glib::Object
    {
      _CLASS_GOBJECT(TextMark, GtkTextMark, GTK_TEXT_MARK, Glib::Object, GObject, , , GTKMM_API)

    protected:
      _WRAP_CTOR(TextMark(const Glib::ustring& name, bool left_gravity = true), gtk_text_mark_new)

    public:
      _WRAP_CREATE(const Glib::ustring& name, bool left_gravity = true)

##### _CTOR_DEFAULT

Овај макро креира подразумевани конструктор без аргумената.

##### _WRAP_CTOR

Овај макро креира конструктор са аргументима, еквивалентан `*_new()` C функцији. Он заправо неће позвати `*_new()` функцију, већ ће једноставно креирати еквивалентан конструктор са истим типовима аргумената. Прихвата C++ потпис конструктора и име C функције.

Такође прихвата необавезан додатни аргумент:

errthrow

  Ово саопштава `gmmproc`-у да Ц `*_new()` има завршни GError** параметар који треба занемарити.

##### Ручно кодирање конструктора

Када конструктор мора бити делимично ручно написан јер, на пример, параметри Ц функције `*_new()` не одговарају директно својствима објекта, или зато што Ц функција `*_new()` ради више од позивања `g_object_new()`, макро `_CONSTRUCT()` се може користити у .ccg датотеци како би се уштедело нешто труда. Макро `_CONSTRUCT` прихвата низ назива својстава и вредности. На пример, из `button.ccg`:

    Button::Button(const Glib::ustring& label, bool mnemonic)
    :
      _CONSTRUCT("label", label.c_str(), "use_underline", gboolean(mnemonic))
    {}

#### Макрои који потискују стварање неког кода

Неки макрои потискују стварање неког кода када се користе након `_CLASS_*` макроа. Неки потискују дефиницију у створеној .cc датотеци, други потискују и декларацију у .h датотеци и дефиницију у .cc датотеци.

##### _CUSTOM_DEFAULT_CTOR

Потискује декларацију и дефиницију подразумеваног конструктора у `_CLASS_BOXEDTYPE`, `_CLASS_BOXEDTYPE_STATIC` и `_CLASS_OPAQUE_COPYABLE`.

##### _CUSTOM_CTOR_CAST

Потискује декларацију и дефиницију конструктора који прихвата показивач на умотани Ц објекат у `_CLASS_BOXEDTYPE` и `_CLASS_BOXEDTYPE_STATIC`.

Потискује дефиницију конструктора који прихвата показивач на умотани Ц објекат у `_CLASS_INTERFACE` и `_CLASS_OPAQUE_COPYABLE`.

Потискује дефиницију конструктора који прихвата показивач на умотани Ц објекат и конструктора који прихвата construct_params у `_CLASS_GOBJECT` и `_CLASS_GTKOBJECT`.

##### _CUSTOM_DTOR

Потискује дефиницију деструктора у `_CLASS_GOBJECT` и `_CLASS_GTKOBJECT`.

##### _CUSTOM_MOVE_OPERATIONS

Потискује декларацију и дефиницију конструктора премештања и оператора доделе премештања у `_CLASS_GOBJECT` и `_CLASS_GTKOBJECT`.

На пример:

    class Derived : public Glib::Object
    {
      _CLASS_GOBJECT(Derived, GDerived, G_DERIVED, Glib::Object, GObject)

      _CUSTOM_MOVE_OPERATIONS

    public:
      Derived(Derived&& src) noexcept;
      Derived& operator=(Derived&& src) noexcept;
      // ...
    };

##### _CUSTOM_WRAP_NEW

Потискује дефиницију `Glib::wrap_new()` функције у `_CLASS_GOBJECT`.

##### _CUSTOM_WRAP_FUNCTION

Потискује дефиницију `Glib::wrap()` функције у `_CLASS_GOBJECT` и `_CLASS_GTKOBJECT`.

##### _NO_WRAP_FUNCTION

Потискује декларацију и дефиницију `Glib::wrap()` функције у `_CLASS_GOBJECT`, `_CLASS_BOXEDTYPE`, `_CLASS_BOXEDTYPE_STATIC`, `_CLASS_OPAQUE_COPYABLE`, `_CLASS_INTERFACE` и `_CLASS_GTKOBJECT`.

#### Макрои метода

##### _WRAP_METHOD

Овај макро ствара Ц++ методу за обавијање Ц функције.

`_WRAP_METHOD( потпис Ц++ методе, назив Ц функције)`

На пример, из `entry.hg`:

    _WRAP_METHOD(void set_text(const Glib::ustring& text), gtk_entry_set_text)

Ц функција (нпр. `gtk_entry_set_text`) је потпуније описана у .defs датотеци, а `convert*.m4` датотеке садрже неопходну конверзију из Ц++ врсте параметра у Ц врсту параметра. Овај макро такође ствара doxygen коментаре документације на основу `*_docs.xml` и `*_docs_override.xml` датотека.

Постоје неки необавезни додатни аргументи:

refreturn

  Извршите додатно `reference()` над повратном вредношћу, у случају да Ц функција не пружа референцу.

errthrow ["<exceptions>"]

  Користите последњи GError** параметар Ц функције за избацивање изузетка. Опционо „<exceptions>“ је списком зарезима раздвојених изузетака који могу бити избачени. То одређује које @throws Doxygen наредбе се додају у документацију. Подразумевана вредност је Glib::Error. Ако желите зарез у опису изузетка, ставите контра косу црту испред њега. Пример: `errthrow "Glib::OptionError Hello\, world, Glib::ConvertError"`

deprecated ["<text>"]

  Смешта створени код у #ifdef блокове. Текст о застарелости може бити наведен као опциони параметар.

ignore_deprecations

  Смешта створени код у .cc датотеку унутар G_GNUC_BEGIN_IGNORE_DEPRECATIONS / G_GNUC_END_IGNORE_DEPRECATIONS блока. (Само у glibmm >= 2.70.1)

constversion

  Само позовите неконстантну верзију исте функције, уместо да стварате скоро истоветан код.

newin "<издање>"

  Додаје @newin Doxygen наредбу у документацију, или замењује @newin наредбу створену из Ц документације.

ifdef <одредник>

  Смешта створени код у #ifdef блокове.

slot_name <назив_параметра>

  Наводи назив параметра места (slot) за метод, ако га има. Ово омогућава програму `gmmproc` да створи код за умножавање места и прослеђивање умношка Ц функцији у њеном завршном `gpointer user_data` параметру. Опција `slot_callback` се такође мора користити за навођење назива везивне функције повратног позива која се такође прослеђује Ц функцији.

slot_callback <назив_функције>

  Користи се заједно са опцијом `slot_name` за навођење назива везивне функције повратног позива која управља извлачењем места и његовим позивањем. Адреса овог повратног позива се такође прослеђује Ц функцији коју метод обавија.

no_slot_copy

  Говори програму `gmmproc` да не прослеђује умножак места Ц функцији, ако га метод има. Уместо тога се прослеђује само место. Назив параметра места и везивна функција повратног позива морају бити наведени опцијама `slot_name` и `slot_callback`.

Бирање који C++ типови треба да се користе је такође важно приликом обавијања Ц АПИ-ја. Иако је обично очигледно који C++ типови треба да се користе у C++ методу, ево неких савета:

- Објекти који се користе преко RefPtr-а: Проследите RefPtr као константну референцу. На пример, `const Glib::RefPtr<Gtk::FileFilter>& filter`.

- Константни објекти који се користе преко RefPtr-а: Ако функција не треба да мења објекат, побрините се да објекат буде константан, чак и ако је RefPtr већ константан. На пример, `const Glib::RefPtr<const Gtk::FileFilter>& filter`.

- Обавијање GList* и GSList* параметара: Прво треба да откријете који се објекти налазе у пољу података списка за сваку ставку, обично читањем документације за Ц функцију. Списак се затим може обавити типом std::vector. На пример, `std::vector<Glib::RefPtr<Gdk::Pixbuf>>`. Можда ћете морати да дефинишете Traits тип како бисте навели како Ц и C++ типови треба да се претворе.

- Обавијање GList* и GSList* повратних типова: Морате открити да ли позивалац треба да ослободи списак и да ли треба да отпусти ставке у списку, поново читањем документације Ц функције. Помоћу ових података можете изабрати власништво (никакво, површно или дубоко) за m4 правило конверзије, које вероватно треба да ставите директно у .hg датотеку јер власништво зависи од функције пре него од типа. На пример:

      #m4 _CONVERSION(`GSList*',`std::vector<Widget*>',`Glib::SListHandler<Widget*>::slist_to_vector($3, Glib::OWNERSHIP_SHALLOW)')

##### _WRAP_METHOD_DOCS_ONLY

Овај макро је сличан `_WRAP_METHOD()`, али генерише само документацију за C++ метод који обавија C функцију. Користите ово када морате ручно да напишете метод, али желите да користите документацију која би била генерисана да је метод генерисан.

`_WRAP_METHOD_DOCS_ONLY(назив C функције)`

На пример, из `recentinfo.hg`:

    _WRAP_METHOD_DOCS_ONLY(gtk_recent_info_get_applications)

Постоје неки необавезни додатни аргументи:

errthrow ["<exceptions>"]

  Искључује документацију последњег GError** параметра C функције. Опционо „<exceptions>“ је листа изузетака раздвојених зарезом који могу бити избачени. То одређује које @throws Doxygen наредбе се додају у документацију. Подразумевана вредност је Glib::Error. Ако желите зарез у опису изузетка, ставите обрнуту косу црту испред њега. Пример: `errthrow "Glib::OptionError Hello\, world, Glib::ConvertError"`

newin "<издање>"

  Додаје @newin Doxygen наредбу у документацију, или замењује @newin наредбу створену из Ц документације.

voidreturn

  Не укључујте @return Doxygen наредбу у документацију. Корисно ако обавијена C функција враћа вредност, али одговарајући C++ метод враћа void.

##### _IGNORE, _IGNORE_SIGNAL, _IGNORE_PROPERTY

`gmmproc` ће вас упозорити на стандардном излазу о функцијама, сигналима, својствима и својствима деце које сте заборавили да обавијете, помажући да осигурате да обавијате комплетан API. Али ако не желите да обавијете неке функције, сигнале, својства или својства деце, или ако сте изабрали да ручно напишете неке методе, онда можете користити _IGNORE(), _IGNORE_SIGNAL() или _IGNORE_PROPERTY() макро да зауставите `gmmproc` да се жали.

    _IGNORE(назив C функције 1, назив C функције 2, итд)
    _IGNORE_SIGNAL(назив C сигнала 1, назив C сигнала 2, итд)
    _IGNORE_PROPERTY(назив C својства 1, назив C својства 2, итд)

На пример, из `flowbox.hg`:

    _IGNORE(gtk_flow_box_set_filter_func, gtk_flow_box_set_sort_func)
    _IGNORE_SIGNAL(activate-cursor-child, toggle-cursor-child, move-cursor)

##### _WRAP_SIGNAL

Овај макро генерише C++ сигнал у libsigc++ стилу за обавијање C GObject сигнала. Он заправо генерише јавни метод приступа, као што је `signal_clicked()`, који враћа прокси објекат. `gmmproc` користи .defs датотеку да открије врсте C параметара и .m4 датотеке конверзије да открије одговарајуће конверзије врста.

`_WRAP_SIGNAL(потпис C++ руковаоца сигналом, назив C сигнала)`

На пример, из `button.hg`:

    _WRAP_SIGNAL(void clicked(),"clicked")

Сигнали обично имају показиваче функција у GTK структури, са одговарајућом вредношћу набрајања и `g_signal_new()` у .c датотеци.

Постоје неки необавезни додатни аргументи:

no_default_handler

  Немојте генерисати `on_something()` виртуелни метод како бисте омогућили лако преклапање подразумеваног руковаоца сигналом. Користите ово када би додавање сигнала са подразумеваним руковаоцем сигналом нарушило ABI повећањем величине табеле виртуелних функција класе, и када додајете сигнал без јавног C подразумеваног руковаоца.

custom_default_handler

  Генеришите декларацију виртуелног метода `on_something()` у `.h` датотеци, али немојте генерисати дефиницију у `.cc` датотеци. Користите ово када дефиницију морате генерисати ручно.

custom_c_callback

  Немојте генерисати C функцију повратног позива за сигнал. Користите ово када функцију повратног позива морате генерисати ручно.

refreturn

  Извршите додатно `reference()` над повратном вредношћу виртуелног метода `on_something()`, у случају да C функција не обезбеђује референцу.

deprecated ["<text>"]

  Смешта створени код у #ifdef блокове. Текст о застарелости може бити наведен као опциони параметар.

newin "<издање>"

  Додаје @newin Doxygen наредбу у документацију, или замењује @newin наредбу створену из Ц документације.

ifdef <одредник>

  Смешта створени код у #ifdef блокове.

exception_handler <method_name>

  Омогућава коришћење произвољног руковаоца изузецима уместо подразумеваног. Кориснички дефинисан руковалац може поново избацити изузетак, а тада ће га ухватити подразумевани руковалац.

detail_name <parameter_name>

  Додаје параметар const Glib::ustring& методу signal_something(). Користите га ако сигнал прихвата детаљан назив сигнала, тј. ако основни C код региструје сигнал са заставицом `G_SIGNAL_DETAILED`.

two_signal_methods

  Користи се заједно са опцијом `detail_name` за генерисање два signal_something() метода, једног без параметра и једног са параметром без подразумеване вредности. Само са опцијом `detail_name` генерише се један метод, са параметром са подразумеваном вредношћу. Користите опцију `two_signal_methods` ако је то неопходно ради очувања ABI-ја.

##### _WRAP_PROPERTY

Овај макро генерише C++ метод за премошћавање својства C GObject-а. Морате навести назив својства и жељену C++ врсту за то својство. `gmmproc` користи .defs датотеку за откривање C врсте и .m4 датотеке конверзије за откривање одговарајућих претварања врста.

`_WRAP_PROPERTY(C property name, C++ type)`

На пример, из `button.hg`:

    _WRAP_PROPERTY("label", Glib::ustring)

Постоје неки необавезни додатни аргументи:

deprecated ["<text>"]

  Смешта створени код у #ifdef блокове. Текст о застарелости може бити наведен као опциони параметар.

newin "<издање>"

  Додаје @newin Doxygen наредбу у документацију, или замењује @newin наредбу створену из Ц документације.

##### _WRAP_VFUNC

Овај макро генерише C++ метод за премошћавање виртуелне C функције.

`_WRAP_VFUNC( C++ method signature, C function name)`

На пример, из `widget.hg`:

    _WRAP_VFUNC(SizeRequestMode get_request_mode() const, get_request_mode)

Ц функција (нпр. `get_request_mode`) је детаљније описана у датотеци `*_vfuncs.defs`, а датотеке `convert*.m4` садрже неопходну конверзију из C++ типа параметра у Ц тип параметра. Конверзије се такође могу писати у .hg датотеци. Виртуелне функције често захтевају посебне конверзије које је најбоље држати локално у .hg датотеци где се користе.

Постоје неки необавезни додатни аргументи:

refreturn

  Извршите додатно `reference()` на повратну вредност функције `something_vfunc()`, у случају да виртуелна Ц функција не обезбеђује референцу.

refreturn_ctype

  Извршите додатно `reference()` на повратну вредност преклопљене функције `something_vfunc()` у Ц повратном позиву, у случају да Ц функција која позива очекује да она обезбеди референцу.

keep_return

  Задржите копију повратне вредности у Ц повратној функцији, у случају да Ц функција која позива не очекује да добије сопствену референцу.

errthrow

  Користите последњи GError** параметар Ц виртуелне функције (ако постоји) за избацивање изузетка.

custom_vfunc

  Немојте генерисати дефиницију виртуелне функције (vfunc) у `.cc` датотеци. Користите ово када морате ручно генерисати виртуелну функцију.

custom_vfunc_callback

  Немојте генерисати Ц функцију повратног позива за виртуелну функцију. Користите ово када морате ручно генерисати функцију повратног позива.

ifdef <одредник>

  Смешта створени код у #ifdef блокове.

slot_name <назив_параметра>

  Наводи назив параметра места (slot) за метод, ако га има. Ово омогућава програму `gmmproc` да створи код за умножавање места и прослеђивање умношка Ц функцији у њеном завршном `gpointer user_data` параметру. Опција `slot_callback` се такође мора користити за навођење назива везивне функције повратног позива која се такође прослеђује Ц функцији.

slot_callback <назив_функције>

  Користи се заједно са опцијом `slot_name` за навођење назива везивне функције повратног позива која управља извлачењем места и његовим позивањем. Адреса овог повратног позива се такође прослеђује Ц функцији коју метод обавија.

no_slot_copy

  Говори програму `gmmproc` да не прослеђује умножак места Ц функцији, ако га метод има. Уместо тога се прослеђује само место. Назив параметра места и везивна функција повратног позива морају бити наведени опцијама `slot_name` и `slot_callback`.

return_value <value>

  Дефинише неподразумевану повратну вредност.

err_return_value <value>

  Дефинише неподразумевану повратну вредност, која се користи само ако C++ функција `something_vfunc()` избаци изузетак који се прослеђује до Ц функције повратног позива. Ако је наведено return_value, али није err_return_value, тада се return_value користи и када се изузетак прослеђује.

exception_handler <method_name>

  Омогућава коришћење произвољног руковаоца изузецима уместо подразумеваног. Кориснички дефинисан руковалац може поново избацити изузетак, а тада ће га ухватити подразумевани руковалац.

Правило за које могу постојати изузеци: Ако виртуелна Ц функција враћа показивач на објекат изведен из GObject, тј. објекат са бројањем референци, тада виртуелна C++ функција треба да врати Glib::RefPtr<> објекат. Један од додатних аргумената refreturn или refreturn_ctype је обавезан.

#### Остали макрои

##### _IMPLEMENTS_INTERFACE

Овај макро генерише код за покретање интерфејса.

`_IMPLEMENTS_INTERFACE(C++ interface name)`

На пример, из `grid.hg`:

    _IMPLEMENTS_INTERFACE(Orientable)

Постоји један опциони додатни аргумент:

ifdef <одредник>

  Смешта створени код у #ifdef блокове.

##### _WRAP_ENUM

Овај макро генерише C++ набрајање (enum) за омотавање Ц набрајања. Морате навести жељено C++ име и име основног Ц набрајања.

На пример, из `enums.hg`:

    _WRAP_ENUM(Orientation, GtkOrientation, decl_prefix GTKMM_API)

Постоје неки необавезни додатни аргументи:

NO_GTYPE

  Користите ову опцију ако набрајање није GType. Ово је случај када не постоји функција `*_get_type()` за Ц набрајање, али пазите да вам можда не треба само додатна датотека заглавља за ту функцију. Такође би требало да пошаљете извештај о грешци за Ц АПИ, јер би сва набрајања требало да буду регистрована као Г-врсте (GTypes).

  Ако наведете `NO_GTYPE`, немојте користити то набрајање као врсту у _WRAP_PROPERTY. То би изазвало грешку у време извршавања када се позове генерисани метод property_*().

  На пример, из `icontheme.hg`:

      _WRAP_ENUM(IconLookupFlags, GtkIconLookupFlags, NO_GTYPE, decl_prefix GTKMM_API)

gtype_func <назив_функције>

  Одређује назив функције `*_get_type()` за Ц набрајање. Користите овај параметар ако `gmmproc` не може да изведе исправан назив функције из назива врсте Ц набрајања.

  На пример, из `dbusproxy.hg` у glibmm-у:

      _WRAP_ENUM(ProxyFlags, GDBusProxyFlags, gtype_func g_dbus_proxy_flags_get_type, decl_prefix GIOMM_API)

CONV_TO_INT

  „Претвориво у цео број.“ Генерише обично набрајање (не класу набрајања) унутар класе. Такво набрајање има опсег као класа набрајања, али за разлику од класе набрајања, може се имплицитно претворити у int.

  На пример, из `dialog.hg`:

      _WRAP_ENUM(ResponseType, GtkResponseType, CONV_TO_INT, decl_prefix GTKMM_API)

s#<од>#<у>#

  Замењује (део) назива једне или више константи набрајања. Можете додати било који број замена.

  На пример, из `toplevel.hg`:

      _WRAP_ENUM(FullscreenMode, GdkFullscreenMode,
        s#^CURRENT_MONITOR$#ON_CURRENT_MONITOR#, s#^ALL_MONITORS$#ON_ALL_MONITORS#,
        decl_prefix GDKMM_API)

decl_prefix <префикс_декларације>

  Обично се користи за макро Ц препроцесора дефинисан као `__declspec(dllexport)` или `__declspec(dllimport)` приликом изградње помоћу MSVC-а на Виндоузу, а у супротном је дефинисан као празна ниска.

deprecated ["<text>"]

  Смешта створени код у #ifdef блокове. Текст о застарелости може бити наведен као опциони параметар.

newin "<издање>"

  Додаје @newin Doxygen наредбу у документацију, или замењује @newin наредбу створену из Ц документације.

##### _WRAP_ENUM_DOCS_ONLY

Овај макро само генерише блок Доксиген документације за набрајање. Ово је корисно за набрајања која се не могу преломити помоћу `_WRAP_ENUM()` јер су сложено дефинисана (можда коришћењем Ц макроа), али је и даље пожељно укључивање генерисане документације набрајања. Користи се са истом синтаксом као и `_WRAP_ENUM()` и такође обрађује исте опције (иако се NO_GTYPE, gtype_func <назив_функције> и CONV_TO_INT занемарују јер не праве разлику када се само генерише документација набрајања).

##### _WRAP_GERROR

Овај макро ствара Ц++ класу изузетка, изведену из Glib::Error, са Code набрајањем и code() методом. Морате навести жељени Ц++ назив, назив одговарајућег Ц набрајања, и префикс за вредности Ц набрајања.

Овај изузетак затим могу избацити методе које су створене из _WRAP_METHOD() са опцијом „errthrow“.

На пример, из `pixbuf.hg`:

    _WRAP_GERROR(PixbufError, GdkPixbufError, GDK_PIXBUF_ERROR, decl_prefix GDKMM_API)

_WRAP_GERROR() прихвата исте необавезне аргументе као и _WRAP_ENUM() (иако се CONV_TO_INT занемарује јер су сва набрајања класе изузетка обична набрајања унутар класе).

##### _MEMBER_GET / _MEMBER_SET

Користите ове макрое ако обавијате једноставну структуру или уоквирену врсту која пружа директан приступ својим члановима података, како бисте направили добављаче и постављаче за чланове података.

`_MEMBER_GET(Ц++ назив, Ц назив, Ц++ врста, Ц врста)`

`_MEMBER_SET(Ц++ назив, Ц назив, Ц++ врста, Ц врста)`

На пример, у `rectangle.hg`:

    _MEMBER_GET(x, x, int, int)

##### _MEMBER_GET_PTR / _MEMBER_SET_PTR

Користите ове макрое да аутоматски обезбедите добављаче и постављаче за члана података који је врста показивача. За функцију добављача, направиће две методе, једну константну и једну неконстантну.

`_MEMBER_GET_PTR(Ц++ назив, Ц назив, Ц++ врста, Ц врста)`

`_MEMBER_SET_PTR(Ц++ назив, Ц назив, Ц++ врста, Ц врста)`

На пример, за Pango::Analysis у `item.hg`:

    // _MEMBER_GET_PTR(engine_lang, lang_engine, EngineLang*, PangoEngineLang*)
    // То је само коментар. Тешко је пронаћи пример из стварног света.

##### _MEMBER_GET_GOBJECT / _MEMBER_SET_GOBJECT

Користите ове макрое да обезбедите добављаче и постављаче за члана података који је GObject врста која мора бити упућена пре него што буде враћена.

`_MEMBER_GET_GOBJECT(C++ name, C name, C++ type, C type)`

`_MEMBER_SET_GOBJECT(C++ name, C name, C++ type, C type)`

На пример, у Pangomm-у, `layoutline.hg`:

    _MEMBER_GET_GOBJECT(layout, layout, Pango::Layout, PangoLayout*)

#### gmmproc обрада параметара

`gmmproc` омогућава обраду параметара у потпису методе за макрое који обрађују потписе метода (као што су `_WRAP_METHOD()`, `_WRAP_CTOR()` и `_WRAP_CREATE()`) на различите начине:

##### Промена редоследа параметара

За све макрое који обрађују потписе метода, могуће је навести другачији редослед за C++ параметре од постојећег редоследа у C функцији, виртуелној функцији или сигналу. На пример, рецимо да је следећа C функција преломљена као C++ метода за класу Gtk::Widget:

    void gtk_widget_set_device_events(GtkWidget* widget, GdkDevice* device,
      GdkEventMask events);

Међутим, промена редоследа два параметра C++ методе је неопходна. Нешто попут следећег би преломило функцију као C++ методу са другачијим редоследом за два параметра:

    _WRAP_METHOD(void set_device_events(Gdk::EventMask events{events},
      const Glib::RefPtr<const Gdk::Device>& device{device}),
      gtk_widget_set_device_events)

`{c_param_name}` који следи након назива параметара методе говори програму `gmmproc` да мапира C++ параметар на наведени C параметар унутар `{}`. Пошто називи C++ параметара одговарају C параметрима, горенаведено се може поново написати као:

    _WRAP_METHOD(void set_device_events(Gdk::EventMask events{.},
      const Glib::RefPtr<const Gdk::Device>& device{.}),
      gtk_widget_set_device_events)

Имајте на уму да приликом промене редоследа параметара за потпис методе `_WRAP_SIGNAL()`, називи C параметара ће увек бити `p0`, `p1`, итд. јер алатка `generate_extra_defs` користи те називе параметара без обзира на то који су називи параметара у C API-ју. Тако је алатка тренутно написана.

##### Обрада опционих параметара

За све макрое који обрађују потписе метода осим `_WRAP_SIGNAL()` и `_WRAP_VFUNC()` такође је могуће учинити параметре опционим тако да се генеришу додатне C++ методе без наведеног опционог параметра. На пример, рецимо да је следећа `*_new()` функција преломљена као конструктор у класи Gtk::ToolButton:

    GtkToolItem* gtk_tool_button_new(GtkWidget* icon_widget, const gchar* label);

Такође, рецимо да C API дозвољава NULL за параметар label функције тако да је тај параметар опциони. Било би могуће да `gmmproc` генерише изворни конструктор (са свим параметрима) заједно са додатним конструктором без тог опционог параметра додавањем `{?}` на назив параметра овако:

    _WRAP_CTOR(ToolButton(Widget& icon_widget, const Glib::ustring& label{?}),
      gtk_tool_button_new)

У овом случају, биће генерисана два конструктора: један са опционим параметром и један без њега.

##### Обрада излазних параметара

Помоћу `_WRAP_METHOD()` је такође могуће да се повратна вредност умотане C функције (ако је има) постави у излазни параметар C++ методе уместо да C++ метода такође враћа вредност као што то чини C функција. Да бисте то урадили, једноставно укључите излазни параметар у списак параметара C++ методе додајући `{OUT}` на назив излазног параметра. На пример, ако је `gtk_widget_get_request_mode()` декларисана на следећи начин:

    GtkSizeRequestMode gtk_widget_get_request_mode(GtkWidget* widget);

А ако се жели да C++ метода постави излазни параметар уместо да враћа SizeRequestMode, могло би се употребити нешто попут следећег:

    _WRAP_METHOD(void get_request_mode(SizeRequestMode& mode{OUT}) const,
      gtk_widget_get_request_mode)

Ознака `{OUT}` додата на назив излазног параметра mode говори програму `gmmproc` да постави повратну вредност C функције у тај излазни параметар. У овом случају, међутим, такође би морао бити наведен неопходан макро за покретање попут следећег:

    _INITIALIZATION(`SizeRequestMode&',`GtkSizeRequestMode',`$3 = (SizeRequestMode)($4)')

Што би се такође могло написати као:

    _INITIALIZATION(`SizeRequestMode&',`GtkSizeRequestMode',`$3 = ($1)($4)')

`_WRAP_METHOD()` такође подржава постављање C++ излазних параметара из C излазних параметара ако их C функција која се умотава има. Претпоставимо, на пример, да желимо да умотамо следећу C функцију која враћа вредност у свом C излазном параметру out_mime_type:

    GInputStream* gdk_clipboard_read_finish(GdkClipboard* clipboard,
      GAsyncResult* result, const char** out_mime_type, GError** error)

Да би `gmmproc` поставио вредност враћену у C++ излазни параметар out_mime_type, могао би се употребити макро `_WRAP_METHOD()` попут следећег:

    _WRAP_METHOD(Glib::RefPtr<Gio::InputStream> read_finish(
      const Glib::RefPtr<Gio::AsyncResult>& result,
      Glib::ustring& out_mime_type{>>}), gdk_clipboard_read_finish, errthrow)

Ознака `{>>}` након назива параметра out_mime_type указује на то да C++ излазни параметар треба да буде постављен из вредности враћене у C параметру из C функције. `gmmproc` ће генерисати декларацију привремене променљиве у којој ће се чувати вредност C излазног параметра и наредбу која поставља C++ излазни параметар из привремене променљиве. У овом случају може бити неопходно имати `_INITIALIZATION()` који описује како да се постави Glib::ustring& из const char** као што је следеће:

    _INITIALIZATION(`Glib::ustring&',`const char*',`$3 = Glib::convert_const_gchar_ptr_to_ustring($4)')

##### Обрада ниски као параметара

Улазни параметар вредности ниске у C++ методи је обично const Glib::ustring& или const std::string&. У C коду то је const gchar*. Када се празна ниска претвори у const gchar*, она се може претворити или у `nullptr` или у показивач на празну ниску (помоћу c_str()). Неки параметри у неким C функцијама прихватају `nullptr` и тумаче га на посебан начин. Други параметри не смеју бити `nullptr`.

Подразумевана конверзија у `_WRAP_METHOD()` и сличним макроима је

- за обавезне параметре (са или без подразумеваних вредности): празна ниска у празну ниску,

- за опционе параметре (са додатим `{?}`): празна ниска у `nullptr`.

Ако подразумевана конверзија није најбоља конверзија, додајте `{NULL}` на обавезни параметар или `{?!NULL}` на опциони параметар (`!NULL` = није `NULL`). Ако додате и назив C параметра и `NULL`, раздвојте их размаком: `{c_param_name NULL}`.

#### Основни типови

Неки од основних типова који се користе у C АПИ-јима имају боље алтернативе у C++-у. На пример, нема потребе за типом gboolean пошто C++ има bool. Следећи списак приказује неке често коришћене типове у C АПИ-јима и у шта бисте их могли претворити у C++ омотачкој библиотеци.

C тип C++ тип
- gboolean bool
- gint int
- guint guint
- gdouble double
- gunichar gunichar
- gchar*Glib::ustring (или std::string за називе датотека)

### Ручно кодиране изворне датотеке

Можда ћете желети да укључите додатне изворне датотеке које неће бити генерисане помоћу `gmmproc` из `.hg` и `.ccg` датотека. Њих можете једноставно поставити у ваш `libsomething/libsomethingmm` директоријум и навести их у `meson.build` у променљивама `extra_h_files` и `extra_cc_files`.

### Покретање

Ваша библиотека мора бити покренута пре него што се може користити, како би се регистровали нови типови које она чини доступним. Такође, C библиотека коју омотавате може имати сопствену функцију покретања коју треба да позовете. То можете учинити у функцији `init()` коју можете поставити у ручно кодиране датотеке `init.h` и `init.cc`. Ова функција треба да покрене ваше зависности (као што су C функција и gtkmm) и позове вашу генерисану функцију `wrap_init()`. На пример:

    void init()
    {
      Gtk::init_gtkmm_internals(); //Поставља g систем типова и Glib::wrap() табелу.
      wrap_init(); //Обавештава Glib::wrap() табелу о libsomethingmm класама.
    }

Имплементација методе `wrap_init()` у `wrap_init.cc` је генерисана помоћу `generate_wrap_init.pl`, али је декларација у `wrap_init.h` ручно кодирана, тако да ћете морати да прилагодите `wrap_init.h` како би се функција `wrap_init()` појавила у исправном C++ именском простору.

### Problems in the C API

Вероватно ћете наићи на неке проблеме у библиотеци коју омотавате, нарочито ако је реч о новом пројекту. Ево неких уобичајених проблема са решењима.

#### Немогућност унапредне декларације структура

По конвенцији, структуре се деклиришу у заглављима glib/GTK стила на следећи начин:

    typedef struct _ExampleWidget ExampleWidget;

    struct _ExampleWidget
    {
      ...
    };

Додатни typedef омогућава да се структура користи у заглављу без укључивања њене пуне дефиниције, једноставно њеним унапредним деклирисањем, понављањем тог typedef-а. То значи да не морате да укључујете заглавље C библиотеке у ваше C++ заглавље, чиме га држите изван вашег јавног АПИ-ја. `gmmproc` претпоставља да је ова техника коришћена, па ћете видети грешке преводиоца ако то није случај.

Ова грешка преводиоца може изгледати овако:

    example-widget.h:56: error: using typedef-name 'ExampleWidget' after 'struct'
    ../../libexample/libexamplemm/example-widget.h:34: error: 'ExampleWidget' has a previous declaration here
    make[4]: *** [example-widget.lo] Error 1

или овако:

    example-widget.h:60: error: '_ExampleWidget ExampleWidget' redeclared as different kind of symbol
    ../../libexample/libexamplemm/example-widget.h:34: error: previous declaration of 'typedef struct _ExampleWidget ExampleWidget'

Ово је лако исправити у C библиотеци, па пошаљите закрпу релевантном одржаваоцу.

#### Недостатак својстава

По конвенцији, објекти glib/GTK стила имају `*_new()` функције, као што је `example_widget_new()` које не раде ништа више осим што позивају `g_object_new()` и враћају резултат. Улазни параметри се достављају функцији `g_object_new()` заједно са називима својстава за које су они вредности. На пример,

    GtkWidget* example_widget_new(int something, const char* thing)
    {
            return g_object_new (EXAMPLE_TYPE_WIDGET, "something", something, "thing", thing, NULL);
    }

Ово омогућава језичким везама да примене сопствене еквиваленте (као што су Ц++ конструктори), без коришћења функције `*_new()`. Ово је често неопходно како би заправо могли да инстанцирају изведени Г-тип (GType), да додају сопствене кукице за руковаоце сигналима и виртуелне функције (vfuncs).

У најмању руку, функција `_new()` не би требало да користи никакав приватни АПИ (функције које се налазе само у .c датотеци). Чак и када нема функција, понекад можемо поново применити 2 или 3 линије кода у функцији `_new()` све док те линије кода користе АПИ који нам је доступан.

Друго решење је додавање функције `*_construct()` коју Ц++ конструктор може позвати након инстанцирања сопственог типа. На пример,

    GtkWidget* example_widget_new(int something, const char* thing)
    {
            ExampleWidget* widget;
            widget = g_object_new (EXAMPLE_TYPE_WIDGET, NULL);
            example_widget_construct(widget, "something", something, "thing", thing);
    }

    void example_widget_construct(ExampleWidget* widget, int something, const char* thing)
    {
            //Do stuff that uses private API:
            widget->priv->thing = thing;
            do_something(something);
    }

Додавање својстава и осигуравање да она исправно међусобно делују је релативно тешко исправити у Ц библиотеци, али је могуће, па пријавите грешку и покушајте да пошаљете закрпу релевантном одржаваоцу.

### Документација

Уопштено говорећи, пројекти у gtkmm стилу користе Doxygen, који чита посебно обликоване Ц++ коментаре и генерише ХТМЛ документацију. Ове doxygen коментаре можете писати директно у заглављима датотека.

#### Поновно коришћење Ц документације

Можда ћете желети да поново искористите документацију која постоји за Ц библиотеку коју обавијате. Ц библиотеке у ГТК стилу обично користе gtk-doc или gi-docgen и стога имају коментаре изворног кода обликоване за gtk-doc или gi-docgen и неку додатну документацију у .sgml и .xml датотекама. Скрипта docextract_to_xml.py, из glibmm-овог директоријума `tools/defs_gen`, може да прочита ове датотеке и генерише .xml датотеку коју `gmmproc` може да користи за генерисање doxygen коментара. `gmmproc` ће чак покушати да преобрази документацију како би је учинио прикладнијом за Ц++ АПИ.

На пример,

    ./docextract_to_xml.py -s ~/checkout/gnome/gtk/gtk/ > gtk_docs.xml

Будући да овај аутоматски преображај није увек прикладан, можда ћете желети да обезбедите ручно писани текст за одређени метод. То можете учинити тако што ћете копирати ИксМЛ чвор за функцију из ваше `something_docs.xml` датотеке у `something_docs_override.xml` датотеку и променити садржај. Алтернативно, можете написати сопствену документацију у `.hg` датотеци.

#### Структура изградње документације

Ако сте копирали скелетно изворно стабло у mm-common и заменили текст чувара места, већ ћете имати одговарајуће `meson.build` и `Doxyfile.in` датотеке у директоријуму `doc/reference/`. Вероватно ћете морати да измените променљиву `tag_file_modules` у `meson.build`. Са mm-common поставком градње, листа Doxygen улазних датотека није дефинисана у Doxygen датотеци подешавања, већ се прослеђује из `meson/ninja` на стандардни улаз за `doxygen`.

## ГНУ-ова лиценца за слободну документацију

Ауторска права (C) 2000, 2001, 2002 Free Software Foundation, https://fsf.org/. Свакоме је дозвољено да умножава и расподељује верне примерке овог документа лиценце, али његово мењање није дозвољено.

#### 0. УВОД

Сврха ове Лиценце је да приручник, уџбеник или други функционалан и користан документ учини „слободним“ у смислу слободе: да свима осигура стварну слободу умножавања и расподеле, са или без измена, било комерцијално или некомерцијално. Секундарно, ова Лиценца чува аутору и издавачу начин да добију признање за свој рад, док се не сматрају одговорним за измене које су направили други.

Ова Лиценца је врста „copyleft-а“, што значи да изведени радови документа и сами морају бити слободни у истом смислу. Она допуњује ГНУ-ову општу јавну лиценцу, која је copyleft лиценца дизајнирана за слободан софтвер.

Ову Лиценцу смо осмислили како бисмо је користили за приручнике за слободан софтвер, јер слободан софтвер захтева слободну документацију: слободан програм треба да долази са приручницима који пружају исте слободе као и сам софтвер. Али ова Лиценца није ограничена на софтверске приручнике; може се користити за било који текстуални рад, без обзира на предметну област или на то да ли је објављен као штампана књига. Препоручујемо ову Лиценцу првенствено за радове чија је сврха подучавање или референца.

#### 1. ПРИМЕЊИВОСТ И ДЕФИНИЦИЈЕ

Ова лиценца се примењује на било које корисничко упутство или други рад, на било ком медијуму, који садржи обавештење постављено од стране носиоца ауторских права у коме се наводи да се може дистрибуирати под условима ове Лиценце. Такво обавештење даје светску, бесплатну лиценцу, неограниченог трајања, за коришћење тог рада под овде наведеним условима. „Документ“ се у даљем тексту односи на било које такво упутство или рад. Сваки члан јавности је носилац лиценце и њему се обраћа са „ви“. Прихватате лиценцу ако умножавате, мењате или дистрибуирате рад на начин који захтева дозволу према закону о ауторским правима.

„Измењено издање“ Документа означава било који рад који садржи Документ или његов део, било да је копиран дословно, или са изменама и/или преведен на други језик.

„Секундарни одељак“ је именовани додатак или уводни део Документа који се искључиво бави односом издавача или аутора Документа према општој теми Документа (или сродним питањима) и не садржи ништа што би могло потпасти директно под ту општу тему. (Дакле, ако је Документ делом уџбеник математике, Секундарни одељак не сме објашњавати математику.) Однос може бити питање историјске повезаности са темом или сродним питањима, или правног, комерцијалног, филозофског, етичког или политичког става у вези са њима.

„Непроменљиви одељци“ су одређени Секундарни одељци чији су наслови одређени као наслови Непроменљивих одељака у обавештењу које каже да је Документ издат под овом Лиценцом. Ако се одељак не уклапа у горњу дефиницију Секундарног, онда није дозвољено да буде означен као Непроменљив. Документ може садржати нула Непроменљивих одељака. Ако Документ не идентификује ниједан Непроменљиви одељак, онда их нема.

„Текстови на корицама“ су одређени кратки одломци текста који су наведени као Текстови на предњој корици или Текстови на задњој корици, у обавештењу које каже да је Документ издат под овом Лиценцом. Текст на предњој корици може имати највише 5 речи, а текст на задњој корици највише 25 речи.

„Провидна“ копија Документа означава машински читљиву копију, представљену у формату чија је спецификација доступна широј јавности, која је погодна за једноставно ревидирање документа генеричким уређивачима текста или (за слике састављене од пиксела) генеричким програмима за сликање или (за цртеже) неким широко доступним уређивачем цртежа, и која је погодна за унос у форматизере текста или за аутоматско превођење у различите формате погодне за унос у форматизере текста. Копија направљена у иначе Провидном формату датотеке чије је означавање, или одсуство означавања, уређено тако да омета или обесхрабрује накнадне измене од стране читалаца није Провидна. Формат слике није Провидан ако се користи за било коју значајну количину текста. Копија која није „Провидна“ назива се „Непровидна“.

Примери погодних формата за Провидне копије укључују обичан АСКИ без означавања, ТеКсинфо улазни формат, ЛаТеКс улазни формат, СГМЛ или ИксМЛ користећи јавно доступан ДТД, и стандардно усклађен једноставан ХТМЛ, Постскрипт или ПДФ дизајниран за људске измене. Примери провидних формата слика укључују ПНГ, ИксЦФ и ЈПГ. Непровидни формати укључују власничке формате који се могу читати и уређивати само власничким процесорима текста, СГМЛ или ИксМЛ за које ДТД и/или алати за обраду нису опште доступни, и машински генерисан ХТМЛ, Постскрипт или ПДФ који производе неки процесори текста само за излазне сврхе.

„Насловна страна“ означава, за штампану књигу, саму насловну страну, плус следеће странице које су потребне да би се читљиво држао материјал који ова Лиценца захтева да се појави на насловној страни. За радове у форматима који немају насловну страну као такву, „Насловна страна“ означава текст близу најистакнутијег појављивања наслова рада, који претходи почетку тела текста.

Одељак „Под насловом ИксИпсилонЗет“ означава именовану подјединицу Документа чији је наслов или прецизно ИксИпсилонЗет или садржи ИксИпсилонЗет в заградама након текста који преводи ИксИпсилонЗет на други језик. (Овде ИксИпсилонЗет стоји за специфичан назив одељка поменут испод, као што су „Захвалнице“, „Посвете“, „Подршке“ или „Историјат“.) „Очувати наслов“ таквог одељка када мењате Документ значи да он остаје одељак „Под насловом ИксИпсилонЗет“ према овој дефиницији.

Документ може укључивати Одрицања од гаранције поред обавештења које наводи да се ова Лиценца примењује на Документ. Ова Одрицања од гаранције се сматрају укљученим референцом у ову Лиценцу, али само у погледу одрицања од гаранција: било која друга импликација коју ова Одрицања од гаранције могу имати је ништавна и нема ефекта на значење ове Лиценце.

#### 2. ДОСЛОВНО УМНОЖАВАЊЕ

Можете умножавати и дистрибуирати Документ на било ком медијуму, комерцијално или некомерцијално, под условом да су ова Лиценца, обавештења о ауторским правима и обавештење о лиценци које каже да се ова Лиценца примењује на Документ репродуковани у свим копијама, и да не додајете никакве друге услове онима из ове Лиценце. Не смете користити техничке мере за ометање или контролу читања или даљег умножавања копија које правите или дистрибуирате. Међутим, можете прихватити накнаду у замену за копије. Ако дистрибуирате довољно велики број копија, морате такође поштовати услове из одељка 3.

Такође можете позајмљивати копије, под истим горе наведеним условима, и можете јавно приказивати копије.

#### 3. УМНОЖАВАЊЕ У ВЕЛИКИМ КОЛИЧИНАМА

Ако објављујете штампане копије (или копије у медијима који обично имају штампане корице) Документа, у броју већем од 100, а обавештење о лиценци Документа захтева Текстове на корицама, морате приложити копије у корицама које јасно и читљиво носе све ове Текстове на корицама: Текстове на предњој корици на предњој корици, и Текстове на задњој корици на задњој корици. Обе корице морају такође јасно и читљиво идентификовати вас као издавача ових копија. Предња корица мора представити пун наслов са свим речима наслова подједнако истакнутим и видљивим. Можете додати и други материјал на корице. Умножавање са изменама ограниченим на корице, све док оне чувају наслов Документа и задовољавају ове услове, може се сматрати дословним умножавањем у другим аспектима.

Ако су захтевани текстови за било коју корицу превише обимни да би се читљиво уклопили, требало би да поставите прве наведене (колико год разумно стане) на саму корицу, а остатак наставите на суседним страницама.

Ако објављујете или дистрибуирате Непровидне копије Документа в броју већем од 100, морате или укључити машински читљиву Провидну копију уз сваку Непровидну копију, или навести у или уз сваку Непровидну копију локацију на рачунарској мрежи са које општа јавност која користи мрежу има приступ за преузимање користећи стандардне мрежне протоколе потпуне Провидне копије Документа, без додатног материјала. Ако користите ову другу опцију, морате предузети разумно опрезне кораке, када започнете дистрибуцију Непровидних копија у великој количини, како бисте осигурали да ова Провидна копија остане тако доступна на наведеној локацији најмање годину дана након последњег пута када сте дистрибуирали Непровидну копију (директно или преко својих агената или продаваца) тог издања јавности.

Тражи се, али се не захтева, да контактирате ауторе Документа знатно пре редистрибуције било ког великог броја копија, како бисте им дали прилику да вам доставе ажурирану верзију Документа.

#### 4. ИЗМЕНЕ

Можете умножавати и дистрибуирати Измењено издање Документа под условима из одељака 2 и 3 изнад, под условом да издате Измењено издање под управо овом Лиценцом, при чему Измењено издање преузима улогу Документа, чиме се лиценцира дистрибуција и измена Измењеног издања свакоме ко поседује његову копију. Поред тога, у Измењеном издању морате урадити следеће:

1. На насловној страни (и на корицама, ако их има) користите наслов који се разликује од наслова Документа и од наслова претходних верзија (које би, ако их је било, требало да буду наведене у одељку Историјат Документа). Можете користити исти наслов као и у претходној верзији ако првобитни издавач те верзије да дозволу.

2. Наведите на насловној страни, као ауторе, једну или више особа или ентитета одговорних за ауторство измена у Измењеној верзији, заједно са најмање пет главних аутора Документа (свим његовим главним ауторима, ако их има мање од пет), осим ако вас они не ослободе овог захтева.

3. Наведите на насловној страни назив издавача Измењене верзије, као издавача.

4. Сачувајте сва обавештења о ауторским правима Документа.

5. Додајте одговарајуће обавештење о ауторским правима за ваше измене поред осталих обавештења о ауторским правима.

6. Укључите, одмах након обавештења о ауторским правима, обавештење о лиценци којим се јавности даје дозвола за коришћење Измењене верзије под условима ове Лиценце, у облику приказаном у Додатку испод.

7. Сачувајте у том обавештењу о лиценци комплетне спискове Непроменљивих одељака и обавезних Текстова на корицама наведених у обавештењу о лиценци Документа.

8. Укључите неизмењену копију ове Лиценце.

9. Сачувајте одељак под насловом „Историјат“, сачувајте његов наслов и додајте му ставку која наводи барем наслов, годину, нове ауторе и издавача Измењене верзије као што је наведено на насловној страни. Ако у Документу не постоји одељак под насловом „Историјат“, направите га наводећи наслов, годину, ауторе и издавача Документа као што је наведено на његовој насловној страни, а затим додајте ставку која описује Измењену верзију као што је наведено у претходној реченици.

10. Сачувајте мрежну локацију, ако постоји, наведену у Документу за јавни приступ Транспарентној копији Документа, као и мрежне локације наведене у Документу за претходне верзије на којима је заснован. Оне се могу поставити у одељак „Историјат“. Можете изоставити мрежну локацију за дело које је објављено најмање четири године пре самог Документа, или ако првобитни издавач верзије на коју се односи да дозволу.

11. За било који одељак под насловом „Захвалнице“ или „Посвете“, сачувајте наслов одељка и сачувајте у одељку сву суштину и тон сваке од захвалница и/или посвета сарадника које су тамо наведене.

12. Сачувајте све Непроменљиве одељке Документа, неизмењене у њиховом тексту и у њиховим насловима. Бројеви одељака или еквиваленти се не сматрају делом наслова одељака.

13. Обришите сваки одељак под насловом „Потврде“. Такав одељак не сме бити укључен у Измењену верзију.

14. Немојте мењати наслов ниједном постојећем одељку тако да се зове „Потврде“ или да буде у сукобу са насловом било ког Непроменљивог одељка.

15. Сачувајте сва Одрицања од гаранције.

Ако Измењена верзија укључује нове уводне одељке или додатке који се квалификују као Секундарни одељци и не садрже материјал копиран из Документа, можете по свом избору означити неке или све ове одељке као непроменљиве. Да бисте то урадили, додајте њихове наслове на листу Непроменљивих одељака у обавештењу о лиценци Измењене верзије. Ови наслови морају бити различити од свих осталих наслова одељака.

Можете додати одељак под насловом „Потврде“, под условом да не садржи ништа осим потврда ваше Измењене верзије од стране различитих страна – на пример, изјаве о стручној ревизији или да је текст одобрен од стране неке организације као меродавна дефиниција стандарда.

Можете додати пасус од највише пет речи као Текст на предњој корици, и пасус од највише 25 речи као Текст на задњој корици, на крај листе Текстова на корицама у Измењеној верзији. Било који ентитет може додати (или путем договора) само један пасус Текста на предњој корици и један на задњој корици. Ако Документ већ садржи текст на корици за исту корицу, који сте претходно додали ви или је додат путем договора који је склопио исти ентитет у чије име наступате, не можете додати други; али можете заменити стари, уз изричиту дозволу претходног издавача који је додао стари.

Аутор(и) и издавач(и) Документа овом Лиценцом не дају дозволу за коришћење њихових имена ради рекламирања или ради потврђивања или имплицирања подршке било којој Измењеној верзији.

#### 5. КОМБИНОВАЊЕ ДОКУМЕНАТА

Можете комбиновати овај Документ са другим документима објављеним под овом Лиценцом, под условима дефинисаним у одељку 4 изнад за измењене верзије, под условом да у комбинацију укључите све Непроменљиве одељке свих изворних докумената, неизмењене, и наведете их све као Непроменљиве одељке вашег комбинованог рада у његовом обавештењу о лиценци, и да сачувате сва њихова Одрицања од гаранције.

Комбиновани рад треба да садржи само један примерак ове Лиценце, а више идентичних Непроменљивих одељака може бити замењено једним примерком. Ако постоји више Непроменљивих одељака са истим називом али различитим садржајем, учините наслов сваког таквог одељка јединственим тако што ћете на његов крај додати, у заградама, име изворног аутора или издавача тог одељка ако је познато, или у супротном јединствени број. Направите исто прилагођавање насловима одељака у списку Непроменљивих одељака у обавештењу о лиценци комбинованог рада.

У комбинацији, морате комбиновати све одељке под насловом „Историјат“ у различитим изворним документима, формирајући један одељак под насловом „Историјат“; исто тако комбинујте све одељке под насловом „Захвалнице“, и све одељке под насловом „Посвете“. Морате обрисати све одељке под насловом „Подршке“.

#### 6. ЗБИРКЕ ДОКУМЕНАТА

Можете направити збирку која се састоји од Документа и других докумената објављених под овом Лиценцом, и заменити појединачне примерке ове Лиценце у различитим документима једним примерком који је укључен у збирку, под условом да пратите правила ове Лиценце за дословно умножавање сваког од докумената у свим осталим аспектима.

Можете извући појединачни документ из такве збирке, и дистрибуирати га појединачно под овом Лиценцом, под условом да уметнете примерак ове Лиценце у извучени документ, и пратите ову Лиценцу у свим осталим аспектима који се тичу дословног умножавања тог документа.

#### 7. ПРИКУПЉАЊЕ СА НЕЗАВИСНИМ РАДОВИМА

Збирка Документа или његових деривата са другим засебним и независним документима или радовима, у или на волумену медијума за складиштење или дистрибуцију, назива се „збирним“ (aggregate) ако се ауторска права која произилазе из збирке не користе за ограничавање законских права корисника збирке изван онога што појединачни радови дозвољавају. Када је Документ укључен у збирку, ова Лиценца се не примењује на друге радове у збирци који сами по себи нису деривативни радови Документа.

Ако је захтев за Текстом на корицама из одељка 3 применљив на ове примерке Документа, онда ако Документ чини мање од једне половине целе збирке, Текстови на корицама Документа могу се поставити на корице које окружују Документ унутар збирке, или на електронски еквивалент корица ако је Документ у електронском облику. У супротном, морају се појавити на штампаним корицама које окружују целу збирку.

#### 8. ПРЕВОД

Превод се сматра врстом измене, тако да можете дистрибуирати преводе Документа под условима из одељка 4. Замена Непроменљивих одељака преводима захтева посебну дозволу њихових носилаца ауторских права, али можете укључити преводе неких или свих Непроменљивих одељака поред изворних верзија тих Непроменљивих одељака. Можете укључити превод ове Лиценце, и свих обавештења о лиценци у Документу, и сва Одрицања од гаранције, под условом да такође укључите изворну енглеску верзију ове Лиценце и изворне верзије тих обавештења и одрицања. У случају неслагања између превода и изворне верзије ове Лиценце или обавештења или одрицања, изворна верзија ће превладати.

Ако је одељак у Документу под насловом „Захвалнице“, „Посвете“ или „Историјат“, захтев (одељак 4) за очувањем његовог наслова (одељак 1) обично ће захтевати измену стварног наслова.

#### 9. ОКОНЧАВАЊЕ

Не смете умножавати, мењати, подлиценцирати или дистрибуирати Документ осим како је изричиво предвиђено овом Лиценцом. Сваки други покушај умножавања, измене, подлиценцирања или дистрибуције Документа је ништаван, и аутоматски ће окончати ваша права под овом Лиценцом. Међутим, странама које су од вас примиле примерке, или права, под овом Лиценцом, лиценце неће бити окончане све док те стране остану у потпуној усклађености.

#### 10. БУДУЋЕ РЕВИЗИЈЕ ОВЕ ЛИЦЕНЦЕ

Задужбина за слободан софтвер може с времена на време објављивати нове, измењене верзије ГНУ-ове лиценце за слободну документацију. Такве нове верзије ће бити сличне по духу садашњој верзији, али се могу разликовати у детаљима како би се решили нови проблеми или недоумице. Погледајте https://www.gnu.org/licenses/.

Свакој верзији Лиценце даје се препознатљив број верзије. Ако Документ прецизира да се на њега примењује одређена нумерисана верзија ове Лиценце „или било која каснија верзија“, имате могућност да пратите одредбе и услове било те наведене верзије или било које касније верзије коју је објавила (не као нацрт) Задужбина за слободан софтвер. Ако Документ не наводи број верзије ове Лиценце, можете изабрати било коју верзију коју је икада објавила (не као нацрт) Задужбина за слободан софтвер.

#### ДОДАТАК: Како користити ову Лиценцу за ваше документе

Да бисте користили ову Лиценцу у документу који сте написали, укључите примерак Лиценце у документ и ставите следећа обавештења о ауторским правима и лиценци одмах након насловне стране:

> Copyright (C) ГОДИНА ВАШЕ ИМЕ.

> Дозвољено је умножавање, дељење и/или мењање овог документа под условима ГНУ-ове Слободне документационе лиценце, верзије 1.2 или било које новије верзије коју објави Задужбина слободног софтвера; без непоменљивих одељака, без текста на предњој страни и без текста на задњој страни. Примерак лиценце је укључен у одељку под насловом „GNU Free Documentation License“.

Ако имате непоменљиве одељке, текст на предњој страни и текст на задњој страни, замените ред „with...Texts.“ овим:

> са непоменљивим одељцима који су НАВЕДИТЕ ЊИХОВЕ НАСЛОВЕ, са текстом на предњој страни који је СПИСАК, и са текстом на задњој страни који је СПИСАК.

Ако имате непоменљиве одељке без текста на корицама, или неку другу комбинацију ова три, спојите те две могућности како би одговарале ситуацији.

Ако ваш документ садржи нетривијалне примере програмског кода, препоручујемо да ове примере објавите упоредо под слободном софтверском лиценцом по вашем избору, као што је ГНУ-ова општа јавна лиценца, како бисте омогућили њихово коришћење у слободном софтверу.