Okná a toolbary programu DynaSim: Rozdiel medzi revíziami

Z Kiwiki
Skočit na navigaci Skočit na vyhledávání
d
 
Riadok 2: Riadok 2:
 
[[Kategória:Diplomové práce]]
 
[[Kategória:Diplomové práce]]
 
[[Kategória:Informatika]]
 
[[Kategória:Informatika]]
{{Hlavička_FM|{{PAGENAME}}|Bc. Michal Janíček|
 
Ing. Juraj Ďuďák|
 
2009/2010
 
|Diplomový projekt
 
|Mechatronika
 
}}
 
 
{{Praca_uvod|4|Interaktívny simulátor DynaSim|Platforma NetBeans|Vizuálna časť programu DynaSim|Dátová časť simulačných komponentov|Okná a toolbary programu DynaSim|Funkčná časť programu DynaSim}}
 
{{Praca_uvod|4|Interaktívny simulátor DynaSim|Platforma NetBeans|Vizuálna časť programu DynaSim|Dátová časť simulačných komponentov|Okná a toolbary programu DynaSim|Funkčná časť programu DynaSim}}
 
__TOC__
 
__TOC__

Aktuálna revízia z 18:56, 29. júl 2010

Okná a toolbary programu DynaSim

Paleta

Paleta slúži ako knižnica komponentov, ktoré sa z nej dajú preniesť na editačnú plochu DynaSimu. Je aj súčasťou NetBeans IDE, kde napríklad Matisse GUI Builder ukladá AWT a Swing komponenty na paletu, a užívateľ ich potom môže pomocou drag&drop premiestniť na NetBeans editor. Aby sme mohli paletu v našom module používať, je potrebné pridať závislosť na Common Palette. Obsah palety je spravovaný PaletteController-om. PaletteController je prístupný DynaSimTopComponentu cez Lookup. Opäť si treba uvedomiť, že sa jedná o 2 okná, kde editačná plocha a paleta majú svoj vlastný TopComponent. PaletteController sa vytvára pomocou triedy PaletteFactory. Ako paleta môže vypadať je znázornené na nasledujúcom obrázku.

Obr.11 Paleta komponentov v programe DynaSim

Pre tvorbu palety je možné zvoliť jeden z dvoch prístupov. Prvý spôsob je vytvoriť pre každý komponent .xml súbor, v ktorom je zapísané meno a popis komponentu. Takto vytvorené .xml súbory je potom potrebné zaregistrovať v layer.xml súbore DynaSim modulu. Druhý spôsob, ktorý som si ja pre tvorbu palety zvolil, je vytvoriť hierarchiu uzlov a z nej následne vygenerovať paletu. Je potrebné si uvedomiť, že takto vytvorená hierarchia uzlov sa skladá z troch vrstiev. Prvá vrstva predstavuje root uzol, ktorý predáme metóde createPallete() na vytvorenie jej komponentov. Druhá vrstva pozostáva z detských uzlov root uzla a definuje kategórie palety. Posledná vrstva definuje jednotlivé komponenty v kategóriách. Triedy pre tieto komponenty v programe DynaSim nesú meno Shape (tvar), preto aj na obr.12 sú označené ako Shape.

Obr.12 Hierarchia uzlov palety

Všetky triedy pre vytvorenie palety a jej hierarchie uzlov sa nachádzajú v balíčku sk.tnuni.dynasim.palette. Trieda Category slúži len ako kontajner, ktorý v sebe drží dáta. Pri kategóriách nám postačuje, že poznáme ich meno, preto táto trieda obsahuje len getter a setter na nastavenie mena danej kategórie. Trieda CategoryChildren je už zaujímavejšia. Jej deklarácia je nasledovná:

 public class CategoryChildren extends Children.Keys

Trieda Children vytvára a manažuje detské uzly a správa sa ako ich kontajner. Tu je táto trieda použitá ako manažér kategórií, ktoré sa zobrazujú na palete. Kategórie palety slúžia na rozdelenie jednotlivých komponentov do skupín (kategórií). Akým spôsobom budú komponenty rozdelené, už zostáva na užívateľovi. Children.Keys obsahuje metódu na tvorbu uzlov createNodes(). Na triedu CategoryChildren sa môžeme pozerať ako na root uzol, lebo v sebe jedna jej inštancia bude obsahovať všetky existujúce kategórie.

Trieda CategoryNode je potomkom triedy AbstractNode a jej konštruktor dostane ako parameter inštanciu triedy Category, ktorá je následne predaná triede ShapeChildren, čím sa dostávame do ďalšej vrstvy hierarchie.

Trieda Shape je trieda, ktorá je ekvivalent triedy Category, ale na rozdiel od Category v sebe drží dáta pre jednotlivé komponenty a to je number – poradové číslo, v súčasnej verzii nepoužívané, category – meno kategórie do ktorej tento komponent patrí, title - meno komponentu získané z XML súboru, image – cesta k súboru s ikonou, ktorá sa zobrazí na palete vedľa mena komponentu, name – meno XML súboru bez prípony, description – tooltip pre daný komponent.

Trieda ShapeChildren je ekvivalnetom CategoryChildren, na rozdiel od CategoryChildren v sebe drží všetky uzly typu Shape danej kategórie. Nakoniec trieda ShapeNode je zodpovedná za zobrazovanie jednotlivých komponentov v palete. Keďže samotné uzly komponentov už nemajú ďalšie detské uzly, supertriede predáme prázdny kontajner detských uzlov Children.LEAF. Cez metódy setDisplayName a setIconWithExtention a ďalšie podobné metódy nastavíme komponentu ako sa má v palete zobrazovať, t.j aké má mať na palete komponent meno, akú bude mať ikonu, tooltip atď. Tieto parametre sú uložené v triede Shape. Tie sa do nej dostanú za pomoci triedy BrowseFolders.

BrowseFolders je jednoduchá trieda, ktorá prechádza priečinkami na zvolenej ceste. V rámci projektu DynaSim sa nachádza priečinok s názvom library. Ak sa pozrieme na obr.12, library sa nachádza na úrovni root. Library má svoje podpriečinky, tých môže byť ľubovoľné množstvo a môžu mať ľubovoľné pomenovanie. Tieto podpriečinky predstavujú jednotlivé kategórie a platí rovnica: meno podpriečinku = meno kategórie. Nakoniec sa prehľadá obsah týchto podpriečinkov. Ak obsahujú SVG súbory, začne sa hľadať či existuje XML súbor s rovnakým názvom. Ak áno, začne sa s ich parsovaním a z ich root elementov sa získa hodnota atribútu name. Tým sa získa meno komponentu. Ďalej sa hľadá, či existuje PNG alebo GIF súbor s rovnakým menom ako parsovaný XML súbor. V prípade že existuje, použije sa ako ikona komponentu v palete. Uloží sa aj meno súboru bez prípon a z XML súboru sa získa popis pre tooltip. Trieda BrowsFolders toto všetko v konštruktore vykoná pri jej vytvorení na zadanej ceste. Tá musí byť presne určená a ukazovať na miesto s adresárom library. Na nájdenie tohto priečinku sa použije FolderSearcher, vysvetlený v kap. 3.1. Tieto získané hodnoty sa zapíšu do dvojrozmerného poľa typu String menom itemList a cez ten sa potom pristupuje k jednotlivým položkám pri nastavovaní členských premenných inštancie Shape. Prvý rozmer poľa itemList určuje poradové číslo SVG súboru z ktorého je daný komponent. Druhý rozmer má uložené hodnoty v takomto poradí: 0 – poradové čislo, 1 – názov kategórie, 2 – názov SVG súboru bez prípony, 3 – cesta k ikone komponentu, 4 – meno komponentu z XML, 5 – tooltip z XML súboru. BrowseFolders obsahuje aj statickú premennú folderList. Je to jednorozmerné pole Stringov a obsahuje zoznam adresárov v library.

Doteraz bola vysvetlená len hierarchia uzlov palety a spôsob akým získavajú potrebné dáta. Teraz treba vysvetliť, ako sa vytvorí paleta samotná za pomoci týchto uzlov. Na tvorbu palety sa používa trieda PaletteController. Na tieto účely som si vytvoril triedu PaletteSupport, ktorá má na starosti manažovanie všetkých vlastností palety ako sú tvorba inštancie PaletteController, akcie paletz, či drag&drop. Trieda obsahuje metódu na vytvorenie palety createPallete. Jej funckia je nasledovná.

 public static PaletteController createPalette() {
     AbstractNode paletteRoot = new AbstractNode(new CategoryChildren());
     paletteRoot.setName("Palette Root");
     return PaletteFactory.createPalette( paletteRoot, new MyActions(), null, new MyDnDHandler() );
 }

Ako možno z kódu vidieť, najskôr sa vytvorí AbstractNode menom paletteRoot z nášho CategoryChildren, čo je trieda ktorá v sebe drží všetky kategórie palety. Tá si následne vytvorí jednotlivé CategoryNode a tie následne volajú ShapeChildren. Inými slovami vytvorením paletteRoot sa nám automaticky vytvorí celá hierarchia uzlov potrebná na tvorbu palety. Funkcia potom vráti PaletteController, ktorý sa vytvorí cez PaletteFactory. Aby sme však pochopili, aká paleta sa vytvorí treba preskúmať premenné createPalette.

 public static PaletteController createPalette(Node node, PaletteActions pa, PaletteFilter pf, DragAndDropHandler dadh) throws IOException

Node predstavuje root uzol, pa sú akcie palety. Pre súčasné potreby palety nie sú potrebné žiadne špeciálne akcie a tak vygenerované metódy v triede MyActions() vracajú null. Filter pf má hodnotu null, pretože nechceme použiť žiadne filtrovanie komponentov palety. Nakoniec sa vytvorí nový DragAndDropHandler, ktorého detská trieda je zahniezdená priamo v PaletteSupport triede. Tá bude bližsie rozobraná v nasledujúcej podkapitole.

Aby sme vytvorili paletu musíme vytvoriť inštanciu triedy PaletteController v DynaSimTopComponent cez PaletteSupport.createPalette(). To však nestačí na zobrazenie, pretože sa nachádzame v inom TopComponente. Paleta má vlastný TopComponent a aby medzi nimi vznikla určitá forma komunikácie a dokázali si medzi sebou predávať hodnoty, ju nutné vložiť kontrolér palety do Lookupu. Pridanie PaletteController s menom controler do Lookupu Topcomponentu je nasledovné:

 associateLookup(new AbstractLookup(content));
 content.add(controller);

InstanceContent content sa používa na zobrazovanie Properties okna a samotný kontrolér palety sa do neho pridáva ako položka. Podobne ako content aj controler sa prenáša medzi triedami ako premenná v ich konštruktore, z DynaSimTopComponent-u do DynaSimScene, z tej ďalej do Component_Widget. Tu sa musí v metóde notifyStateChanged opäť controller pridať do content, pretože po vytvorení nového Properties okna sa paleta zruší. Jej pridaním do content sa opäť vytvorí.

Implementácia Drag&Drop funkcionality

Drag&Drop z palety zabezpečuje trieda MyDnDHandler, ktorá je zahniezdená v PaletteSupport aj jej inštancia je vytvorená v konštruktore PaletteController-a. Táto trieda je detskou triedou triedy DragAndDropHandler. Preťažená je len 1 funkcia a to customize. Tá má na práci prenos dát určitého typu medzi paletou a TopComponentom, ktorý Drop z palety akceptuje. Keďže po dropnutí komponentu z palety na plochu editora chceme, aby sa na danom mieste vytvoril nový grafický komponent typu Component_Widget. Na jeho tvorbu ale potrebujeme najskôr inštanciu typu MyNode a z tej už dokážeme vytvoriť ďalej celú grafickú aj dátovú časť komponentu. Konštruktor pre MyNode má 4 premenné a na ich prenos z palety do inštancie DynaSimScene sa potrebujeme zamerať. Štvrtá premenná je lokácia bodu na ktorý sme položili komponent z palety na plochu editora takže nám stačí prenos troch premnných a to je: meno komponentu, kategória do ktorej patrí a meno SVG súboru bez prípony pre vykreslenie komponentu. Akým spôsobom sa teda tieto 3 hodnoty premenných prenesú z palety do DynaSimScene? Najskôr sa v metóde customize nájde uzol palety z lookup-u. Z neho cez rôzne metódy ako getDisplayName a pod. uložíme do poľa typu String meno, kategótiu a meno súboru. Potom do exTransferable typu ExTransferable, čo je premenná metódy customize uložíme pomocou metódy put toto pole Stringov. Tým sme si zabezpečili prenos týchto hodnôt z palety na iné miesto. Problém je, že miesto, na ktoré chceme tieto hodnoty preniesť musí byť na ne pripravené.

Miesto kam chceme tieto hodnoty preniesť a na ktorom chceme vytvoriť komponenty je inštancia typu DynaSimScene. Ako už bolo viac krát spomínané, DynaSimScene je tiež widget a tým pádom mu je možné pridať rôzne akcie ako iným widgetom. Preto môžeme do jeho konštruktora napísať:

 
getActions().addAction(ActionFactory.createAcceptAction(new AcceptProvider())

Tento riadok kódu nám zabezpečí, že DynaSimScene bude akceptovať veci dropnuté na jeho plochu, ale AcceptProvider má 2 metódy, ktoré sa automaticky vygenerujú a ich kód treba doplniť. Metóda isAcceptable vždy vracia ConnectorState.ACCEPT. To nám zabezpečí, že akýkoľvek komponent z palety dropnutý na editačnú plochu je akceptovaný editorom. Druhá metóda je oveľa dôležitejšia a jej deklarácia vypadá nasledovne:

 public void accept(Widget widget, Point point, Transferable transferable)

Z premennej point získame bod na ktorý chceme premiestniť komponent a v transferable je uložené pole Stringov, ktoré sme poslali z MyDnDHandler cez exTransferable premennú. K poľu Stringov sa dostaneme z transferable nasledovným spôsobom: String name[] = getNameFromTransferable(transferable);

Tým pádom máme k dispozícii všetky premenné na vytvorenie inštancie typu MyNode. V tejto metóde accept sa vykonajú všetky úkony na tvorbu komponentu na DynaSimScene. Vytvorí sa nový widget cez addNode a cez addPin sa mu pridajú vo for-slučke všetky piny. Nakoniec kontrolér palety cez metódu clearSelection() odznačí označenú položku na palete.

Tvorba vlastných okien – náhľad na schému

PropertySheet a paleta sú okná, ktoré sú prebraté z NetBeans IDE a vývojár ich môže zavolať a použiť vo vlastnom programe. Platforma Netbeans nám však dáva možnosť vytvoriť si aj svoje vlastné okná úplne od základu.

Na DynaSimScene je možné vytvoriť satelitný pohľad na túto scénu pomocou metódy createSatelliteView(). Problém však je, že ak takýmto spôsobom vytvoríme náhľad, stane sa neoddeliteľnou súčasťou scény. To znamená, že nám na jednej zo strán scény bude zaberať určité miesto a užívateľ ho nebude môcť vypnúť. Treba však myslieť na to, že niektorí užívatelia nebudú v určitých situáciách potrebovať tento náhľad a uvítali by zväčšenie pracovnej plochy odstránením náhľadu zo scény. Jedným z riešení je vytvorenie samostatného okna v rámci aplikácie, podobné ako je práve Property okno alebo okno palety, kde by bol umiestený náhľad na scénu. Spôsobov ako takéto okno vytvoriť je viacero. Je možné priamo v module DynaSim vytvoriť nový Window Component a podstupovať podobne ako pri tvorbe DynaSimTopComponent (kap.2.1.1). Lepším riešením je však tvorba nového modulu, ktorý bude v sebe obsahovať TopComponent starajúci sa o zobrazovanie satelitného pohľadu scény. Tým, že vytvoríme nový modul, sa podstatne zvýši znovupoužiteľnosť tejto časti programu. Modul môžeme volať aj v iných aplikáciách postavených na platforme NetBeans cez závislosti modulov.

Vytvoríme teda nový modul v rámci našej aplikácie DynaSim Suite. V ľavej časti NetBeans IDE v okne Projects rozbalíme našu aplikáciu. Tu pravým tlačítkom myši klikneme na pložku Modules a vyberieme položku Add New... Otvorí sa wizard pre tvorbu modulu. V Project Name zadáme meno pre náš modul, v Project Location jeho umiestnenie na disku. Klikneme na next. Do Code Name Base zadáme meno hlavného balíčku (package), napr. sk.tnuni.satellite. Je dobré nechať si vygenrovať aj layer.xml. Po stlačení tlačidla Finish sa vytvorí nový modul. V balíčku, ktorý sme zadali sa vytvorí konfiguračný súbor Bundle.properties. Do tohto balíčku si teraz pridáme nový TopComponent. Pravým tlačidlom myši klikneme na balíček a vyberieme položku New... V nej si nájdeme Window Componet, ak sa tu však nenachádza treba ho nájsť v Other → Module Development → Window Componet. Po jeho vábere by sa malo zobraziť okno ako je na obr.13.

Obr.13 Tvorba nového TopComponentu

Vo Windosws Position si vyberieme v ktorej časti aplikácie sa má naše okno zobrazovať. Pre satelitný pohľad som si zvolil ľavú časť aplikácie, t.j. tá, kde sa napríklad v NetBeans IDE nachádza okno Projects. Ak chceme aby sa okno zobrazilo so spustením aplikácie, treba zašktrtnúť Open on Application Start. Po stlačení next prejdeme do ďalšej časti wizarda. Tu stačí zvoliť Class Name Prefix. To je predpona, ktorú budú mať všetky súbory TopComponentu. Ak teda zvolíme ako predponu SatelliteView, wizard vygeneruje niekoľko súborov s menom začínajúcim sa touto predponou, napríklad SatelliteViewTopComponent.java a ďalšie.

Máme vytvorený modul, ktorý sa stará o zobrazovanie okna na pozícii explorer. Teraz treba oknu dodať funkcionalitu. Požadujeme, aby naše okno zobrazovalo satelitný pohľad na scénu DynaSimTopComponent. Na to stačí do triedy SatelliteViewTopComponent doplniť jednu metódu ako je znázornená na nasledujúcich riadokoch:

 public void installSatteliteView(Scene scene) {
     if(this.scene==scene) return;
     JComponent satview = scene.createSatelliteView();
         
     this.remove(satView);
     this.satView = null;
     add(satview, BorderLayout.CENTER);
     this.satView = satview;
     this.scene = scene;
 }

Ako vidno z kódu, jedná sa o veľmi jednoduchú metódu. Metóda je v tomto príklade zjednodušená na účely opisu jej princípu. Najskôr sa overí, či scéna ktorá volá túto metódu je tá istá, čo scéna zapísaná v tejto triede. Ak áno nič sa nestane, lebo satelitný pohľad už je funkčný. Ak je však volaná inou scénou (inou inštanciou Scene), vytvorí sa nový satelitný pohľad a odstráni sa starý, ktorý bol doteraz aktívny. Potom sa TopComponentu pridá novovytvorený satelitný pohľad. Aby táto metóda robila to, čo od nej očakávame, musí byť volaná z našej triedy DynaSimTopComponent. Ten je však umiestnený v inom module, preto musíme všetky packages v module pre satelitný pohľad spraviť public a následne modul pridať ako závislosť modulu DynaSim. Ako toho docieliť je popísane v kap. 1.5.

Teraz už len stačí vyriešiť metódy v rámci modulu DynaSim. V DynaSimScene treba vytvoriť metódu, ktorá bude volať installSatelliteView z druhého modulu. Najskôr si vytvoríme v tejto metóde inštanciu SatelliteViewTopComponent cez metódu findInstance(). Potom z tejto inštancie zavoláme našu metódu na vytvorenie satelitného pohľadu. Metóda triedy DynaSimScene vypadá nasledovne:

 public void installSatView(){
     SatelliteViewTopComponent tc = SatelliteViewTopComponent.findInstance();
     tc.installSatteliteView(this);
 }

Túto metódu ešte treba volať v TopComponente DynaSimu v jeho metóde componentActivated(), aby sa zavolala vždy keď sa aktivuje editačné okno DynaSimu.

Pridávanie tlačidiel do toolbaru

Aplikačné okno NetBeans platformy obsahuje oblasť pre toolbary. Do nej je možné pridávať vlastné toolbary a vlastné tlačidlá. Nasledujúci postup sa bude vzťahovať na tvorbu tlačidla v menu a toolbare, ktoré po jeho stlačení vytvorí nový projekt programu DynaSim. V podstate sa vytvorí nová inštancia DynaSimTopComponent s tým, že už existujúce inštancie DynaSimTopComponentov sa zachovajú. To znamená, že ak už má užívateľ spustený nejaký projekt na ktorom pracuje, stlačením tlačidla pre nový projekt sa vytvorí nová záložka na mieste pre projekty. Užívateľ kliknutím na záložky potom bude môcť prepínať medzi rozpracovanými projektmi. Pridanie položky do menu a príslušného tlačidla na toolbar sa dá vykonať veľmi jednoducho, opäť pomocou wizarda prístupného v NetBeans IDE. V projects okne si nájdeme modul, ktorému chceme pridať tlačidlo. V našom prípade je to modul DynaSim. Klikneme naň pravým tlačidlom myši a vyberieme v New položku Action. Ak sa tam táto položka nenachádza nájdeme ju v Other → Module Development → Action. Otvorí sa okno wizarda. V prvom kroku si musíme vybrať, či má byť daná akcia tlačidla prístupná vždy, alebo len za určitých podmienok. Vyberieme si položku Always Enabled, lebo chceme, aby sme mohli nový projekt vytvárať kedykoľvek. Pokračujeme tlačítkom Next.

Obr.14 Wizard pre tvorbu novej akcie

Ďalší krok vo wizardovi (obr.14) je určenie, či chceme pre našu akciu vytvoriť položku v hlavnom menu bare, tlačidlo na toolbare, alebo obidvoje. Prvá položka Category reprezentuje sémantické zgrupovanie akcií. Je možné si vybrať niektorú z už existujúcich alebo si vytvoriť vlastnú kategóriu. V položke Menu si vyberieme v ktorej kategórii v hlavnom menu sa má naša akcia zobraziť. Poradie voči ostatným akciám v danej kategórii sa určuje cez Position. HERE identifikuje lokáciu, kde sa naša akcia medzi ostatnými položkami zobrazí. Podobne je to aj s toolbar tlačítkom. Nakoniec si môžeme zvoliť klávesovú skratku, ktorou našu akciu zavoláme. Opäť klikneme na Next a v poslednej časti wizarda si vyberieme meno pre triedu v ktorej sa akcia tlačidla vykoná. V položke Display Name si určíme meno nášho tlačidla a nakoniec v Icon položke vyberieme ikonu pre naše tlačidlo. Ikony môžu byt formátu PNG alebo GIF. Každá akcia by mala mať k dispozícii 2 ikony. Jedna o rozmeroch 16x16 a ďalšia 24x24 pixelov. Menšia ikona by mala mať normálny názov, napríklad new.png. Táto sa použije na vytvorenie menu položky v prípade, že sme ju v predošlej časti wizarda nechali vytvoriť. Väčšia ikona by sa mala volať rovnako ako menšia, ale na koniec mena jej treba pridať číslicu 24, teda new24.png. Táto ikona sa použije na tvorbu toolbar tlačidla. Stlačením Finish sa nám vygeneruje nová trieda vo zvolenom balíčku. Do nej už stačí doplniť kód, ktorý sa má vykonať. V metóde actionPerformed vytvoríme nový DynaSimTopComponent a zavoláme jeho metódu open (). Týmto postupom sme vytvorili nové tlačítko v toolbare a príslušnú menu položku. Po kliknutí na ne sa vytvorí nový projekt programu DynaSim. Obdobným spôsobom fungujú aj ďalšie akcie ktoré sú volané z menu resp. z toolbar tlačidiel.

Úprava toolbaru a menu baru pomocou layer.xml

V predošlej kapitole bolo vysvetlené ako si vytvoriť vlastné položky v menu a nové tlačidlá v toolbaroch. Tým nie je problém vytvoriť si nové tlačidlá pre nový projekt, save, load a ďalšie akcie. Problém však je, že aplikácie postavené na platforme NetBeans dedia určité defaultné tlačítka, ktoré sú neaktívne. Vývojár ich môže samozrejme využiť, ale jednoduchše je vytvoriť si tlačítka vlastné. Preto chceme tieto neaktívne tlačítka odstrániť. To sa dá vykonať jednoducho editáciou konfiguračného súboru modulov layer.xml. V module DynaSim sa tento súbor nachádza v balíčku sk.tnuni.dynasim. Layer.xml je centrálny konfiguračný súbor, ktorý definuje prakticky všetko, čo modul pridáva do NetBeans platformy. V Menu v položke File sa defaultne nachádza Save, Save As, Save All. Tieto sú pre nás neaktívne a chceme odstrániť ich akcie a aj ich položky v menu. Na to je nutné editovať layer.xml súbor. Na odstránenie akcií si nájdeme element folder, ktorý má atribút s menom Actions. V ňom je zobrazená hierarchia akcií pre jednotlivé kategórie. Položky, čo chceme odstrániť sa nachádzajú v kategórii File. Do nej treba doplniť 3 detské elementy s atribútom name ako to vidno na príklade.

 <folder name="Actions">
     <folder name="File">
         <file name="org-openide-actions-SaveAction.shadow_hidden"/>
         <file name="org-openide-actions-SaveAllAction.shadow_hidden"/>
         <file name="org-openide-actions-SaveAsAction.shadow_hidden"/>
     </folder>
 </folder>

Tým sme odstránili akcie samotné. Teraz treba odstrániť ich menu položky podobným spôsobom.

 <folder name="Menu">
     <folder name="File">
         <file name="org-openide-actions-SaveAction.shadow_hidden"/>
         <file name="org-openide-actions-SaveAllAction.shadow_hidden"/>
         <file name="org-openide-actions-SaveAsAction.shadow_hidden"/>
     </folder>
 </folder>

Nakoniec sa odstráni aj toolbar tlačítko pre SaveAll.

 <folder name="Toolbars">
     <folder name="File">
         <file name="org-openide-actions-SaveAllAction.shadow_hidden"/>
     </folder>
 </folder>

K akciám sa v layer.xml pristupuje cez atribút name. Jeho hodnota musí byť zložená z balíčku v ktorom sa akcia nachádza. Miesto bodiek sa použijú pomlčky. Ďalej následuje meno akcie a za ním prípona shadow. Ak chceme akciu skryť použije sa shadow_hidden. Napríklad org-openide-actions-SaveAllAction.shadow_hidden znamená, že chceme akciu SaveAllAction z org.openide.actions skryť. Je možné skryť len tlačítko na toolbare s tým, že príslušné tlačítko v menu zostane viditeľné a naopak.

Layer.xml však neslúži len na zobrazovanie a skrývanie menu a toolbar položiek. Predstavme si situáciu, kde máme už vytvorený toolbar, ale zistíme, že sa nám nepáči rozloženie jednotlivých tlačítiek. Jeden zo spôsobov je zmazať všetky triedy pre akcie tlačítiek a odstrániť ich z layer.xml a properties.bundle. Následne cez wizarda vytvoriť tlačítka nanovo a v Position im nastaviť novú pozíciu voči ostatným tlačítkam. Toto je však veľmi napraktický a časovo náročný spôsob, obzvlášť ak chceme meniť polohu väčšieho množstva tlačítiek. Našťastie tu je však oveľa jednoduchšie riešenie. Každé tlačítko má v layer.xml detský element attr s atribútom name, ktorý má hodnotu position. Druhý atribút je intvalue. Jeho hodnota je nejaký integer. V závislosti na hodnote tohto čísla sa určí pozícia daného tlačítka voči ostatným tlačítkam. Čím je číslo menšie, tým sa tlačítko na toolbare umiestni viac vľavo, v prípade menu zase viac navrch. Tlačítko pre toolbar nejakej akcie má vlastnú int hodnotu a položka menu môže mať inú hodnotu. To znamená, že poloha tlačítka na toolbare a poloha položky v menu bare sú od seba nezávislé.

Príklad použitia pozičného atribútu v layer.xml:

 <file name="sk-tnuni-dynasim-ZoomDefaultAction.shadow">
     <attr name="position" intvalue="255"/>
 </file>
 <file name="sk-tnuni-dynasim-InitGridsAction.shadow">
     <attr name="position" intvalue="260"/>
 </file>

Príklad je vyňatý z elementu pre Menu bar v jeho View menu. Tu môžeme vidieť, že akcia ZoomDefaultAction má menšiu hodnotu pozície ako InitGridsAction. To znamená, že sa v menu ZoomDefaultAction zobrazí nad InitGridsAction. Prepísaním týchto hodnôt v layer.xml súbore môžeme upraviť pozície jednotlivých položiek menu, či tlačidiel na toolbare voči sebe. Po vytvorení vlastnej akcie sa obvykle v layer.xml element attr na zmenu pozície nevytvorí. Vývojár ho musí doplniť sám a odporúča sa to, lebo ak tak neučiní, pri kompilácii dostane varovné hlásenia o chýbajúcich pozíciách pre jednotlivé položky menu a toolbaru.

Na prvý pohľad sa môže layer.xml javiť veľmi zložito a mätúco, ale v skutočnosti je to silný nástroj NetBeans platformy, vďaka ktorému môžeme manažovať menu bar a tool bar samotnej platformy.

Akcie pre uloženie a nahratie simulačných schém

Kým akcie na vytvorenie nového pracovného okna DynaSimu, zoomovanie alebo vytvorenie mriežky na scéne sú relatívne jednoduché a zo zdrojového kódu ľahko pochopiteľné, akcie pre uloženie a nahratie schémy nie sú úplne triviálnou záležitosťou. To hlavne z dôvodu, že potrebujeme zistiť polohu a hodnoty všetkých widgetov na scéne, tie následne zapísať do nejakého súboru na disku. Pri nahrávaní scény budeme tento súbor parsovať a zo získaných hodnôt vybudujeme novú scénu, ktorá musí byť identická s tou, ktorá bola do súboru uložená.

Schéma zo scény sa ukladá do XML súboru, aby však nedochádzalo k nechcenej zámene s XML súbormi pre simulačné komponenty, prípona save súborov je zmenená na .dsp (DynaSim Project). Štruktúrovanie samotného dokumentu je však zhodné s XML špecifikáciou. Na ukladanie a nahrávanie schémy je v programe niekoľko tried a metód v už existujúcich triedach. Triedy pre akcie samotné sú SaveAsXMLAction a LoadFromXMLAction. Tieto boli vytvorené cez wizarda na tvorbu akcií a ich metóda ActionPerformed iba nájde aktívny DynaSimTopComponent. Z neho sa podľa zvolenej akcie zavolá príslušná metóda na uloženie scény saveAsXML alebo nahratie scény loadFromXML. DynaSimTopComponent tieto metódy priamo neobsluhuje, iba ich predá ďalej tým, že zavolá metódy DynaSimScene. Opäť sa podľa príslušnej akcie zavolá buď metóda saveWidgetsToXML alebo loadWidgetsFromXML. To sú metódy z triedy DynaSimScene. Teraz sa bližšie pozrieme na saveWidgetsToXML. Táto metóda najskôr vytvorí JFileChooser, čo je swing komponent, prevažne určený na ukladanie a nahrávanie súborov. Jeho dialógové okno sa nastaví ako .SAVE_DIALOG a vykonajú sa ďalšie potrebné natavenia ako napríklad povolenie pracovať len so súbormi (nie s priečinkami) a ktorý typ súborov má vytvárať a akceptovať. Potom sa zo súboru, ktorý užívateľ cez tento JFileChooser vytvorí inštancia triedy WidgetsXML, čo je posledná trieda potrebná pre implementáciu save a load funkcionality do programu.

Aby sme mohli pokračovať vo vysvetlovaní metódy saveWidgetsToXML, je potrebné si najskôr objasniť čo vlastne trieda WidgetsXML robí. Konštruktor má takýto tvar:

 public WidgetsXML(File file) {
     this.file = file;
 }

Jediný parameter ktorý mu predávame je súbor file. Ten sa potom uloží medzi členské premenné. Skutočnú funkcionalitu inštancii tejto triedy dodávajú jej metódy. Ich krátke zhrnutie je nasledovné:

public void prepareToSave() – Metóda do nášho súboru zapíše hlavičku XML a základnú prázdnu hierarchiu XML elementov pomocou PrintWriteru. Výsledný súbor sa uloží do členskej premennej Document document.

public void prepareToLoad() – Načíta sa obsah súboru do Document document. Táto metóda sa volá pri loadovaní.

public void addMyNode(MyNode node) – V elemente my-nodes sa vytvorí nový detský element MyNode. Vytvoria sa v ňom atribúty pre meno, kategóriu, bod, kde widget na scéne leží, jednotlivé hodnoty z property okna a ďalšie potrebné údaje o našom Component_widgete. Potom sa z našej premennej node do nich zapíšu hodnoty.

public ArrayList<MyNode> getMyNodes() – Táto metóda sa volá počas loadovania a v Document document sa postupne parsuje obsah elementu my-nodes z XML súboru. Zo získaných údajov sa vytvorí nová inštancia MyNode a potom sa do nej zapíšu hodnoty vnútorných premnných. Nakoniec sa pridá do ArrayListu.

public void setCounter(IdGenerator generator) – Metóda, ktorá uloží do elementu counter detský element s atribútom posledného vygenerovaného id čísla pre jednotlivé piny. Piny musia mať unikátne číslo, preto si musíme pamätať po loade, aké je posledné číslo pinu.

public void getCounter(IdGenerator generator) – Metóda na zistenie vygenerovaného čísla posledného pinu. public boolean save() – Táto metóda sa volá ako posledná pred uložením. Z Document document sa vytvorí nový XML súbor.

Ďalšie metódy sú addMyPin, getMyPins, addMyEdge a getMyEdges. Tieto metódy sú ekvivalentom addMyNodes a getMyNodes pre piny a spojovacie čiary a fungujú na rovnakom princípe.

Po ozrejmení si metód vo WidgetsXML sa môžeme vrátiť do metódy saveWidgetsToXML v triede DynaSimScene. Čo teda potrebujeme pre uloženie scény spraviť? Potrebujeme nájsť všetky Component_Widgety, všetky ich Pin_Widgety a všetky spojovacie čiary typu MyConnectionWidget. K ním treba nájsť ich dátové časti MyNode, MyPin, MyEdge. Tie cez príslušné metódy addMyNode, addMyPin, addMyEdge triedy WidgetsXML zapíšeme do XML. Najskôr si nájdeme zoznam všetkých Component_Widgetov. Ako už bolo na začiatku tejto práce spomenuté, widgety sa ukladajú na vrstvu mainLayer a spojovacie čiary na vrstvu connectionLayer. Takže ak chceme získať zoznam všetkých Component_Widgetov na scéne stačí jeden riadok kódu:

 List<Widget> list = mainLayer.getChildren();

Potom už stačí len vo for-cykle prechádzať zoznam, z jednotlivých widgetov získať ich dátovú časť MyNode a pridať tento uzol cez wxml.addMyNode inštancii triedy WidgetsXML. Treba si uvedomiť, že väčšina Component_Widgetov má obvykle aj niekoľko pinov, čiže v ďalšom forcykle nájdeme všetky ich Pin_Widgety a ich dátovú časť MyPin zapíšeme do wxml cez metódu addMyPin. Podobný postup volíme aj pri hľadaní spojovacích čiar. Spojovacie čiary sú detskými widgetami vrstvy connectionLayer a ich zoznam dostaneme cez connectionLayer.getChildren(). Cez addMyEdge sa v slučke pridajú MyEdge do wxml. Nakoniec sa zavolá z WidgetsXML metóda save(), ktorá vygeneruje zo získaných údajov náš XML súbor.

Pri nahrávaní schémy z tohto súboru sa volá metóda loadWidgetsFromXML(). Tu sa tiež podobným spôsobom vytvorí JFileChooser, tento krát ale s OPEN_DIALOG. Po výbere load súboru užívateľom sa opäť vytvorí z tohto súboru inštancia triedy WidgetsXML menom wxml a zavolá sa jej metóda prepareToLoad. Z wxml získame ArrayList pre MyNode a postupne ich vo for-cykle vytvárame po jednom. Z nich sa vytvoria nové Component_Widget. Opäť nemôžeme zabudnúť ani na piny jednotlivých Component_Widgetov a treba do tohto for-cyklu dať ďalší, ktorý ich bude pridávať ako detské widgety Component_Widgetu. Pridávanie spojovacích čiar je tiež podobné, ale tu treba myslieť na to, ktorý pin s ktorým treba prepojiť. Každý MyPin má svoje unikátne číslo, uložené v premennej id. Každý MyEdge má 2 hodnoty - source a target. V nich je zapísaná hodnota id zdrojového a cieľového pinu. Tu teda vo for-cykle prechádzame MyEdge po jednej a každá MyEdge musí porovnávať tieto hodnoty source a target s id všetkých pinov na scéne. Ak sa nájdu zhodné id pinov s týmito hodnotami, vytvorí sa medzi nimi spojenie cez metódu addEdge.

Po vytvorení všetkých Component_Widgetov a ich prepojení spojovacími čiarami podľa XML súboru sa loading ukončí a scéna by mala zodpovedať scéne v dobe jej uloženia do súboru.