niedziela, 30 marca 2008

NetBeans RoadShow



W dniach 11-13 kwietnia 2008 w Krakowie, Warszawie oraz Wrocławiu odbędzie się NetBeans RoadShow, ogólnopolskie tournee będące częścią NetBeans WorldTour 2008 promującego środowisko developerskie NetBeans.

Każdego dnia, podczas wykładów prowadzonych przez specjalistów z Sun Microsystems oraz rodzimych developerów, przedstawiane zostaną nowinki techniczne oraz możliwości tego wspaniałego narzędzia.

I ja tam będę ;). 11 kwietnia w Krakowie i 13 kwietnia we Wrocławiu będę chciał przedstawić zalety NetBeans Platform, wyjaśnić podstawowe pojęcia związane z platformą oraz zaprezentować kilka prostych przykładów aplikacji tworzonych przy jej użyciu.

Serdecznie zapraszam

sobota, 29 marca 2008

Własna zakładka w oknie opcji

Dzisiaj będzie o tym jak do okna opcji dodać własną zakładkę.



Możemy założyć nowy projekt albo skorzystać już z jakiegoś istniejącego. Ja założyłem nowy projekt OptionsModule z pakietem org.myorg.option. Dodajemy do pakietu nowy plik typu Options Panel z kategorii Module Development.



W następnym oknie kreatora wybieramy typ panelu z opcjami jako główny, podajmy nazwy oraz ikonkę.



Jako ikonkę podałem



Następnie kreator prosi nas o podanie prefiksu i pakietu dla nowo tworzonych plików.



Po zakończeniu kreatora przejdźmy do edycji pliku OptionPanel.java. Panel będzie formatką, na której użytkownik będzie mógł edytować stworzone przez nas opcje. Dodajmy więc do panelu JCheckBox, JTextField oraz JSlider. Będą to przykłady zmiennych odpowiednio typów boolean, String i int.



Przejdźmy teraz do edycji kodu OptionPanel.java. Interesować nas będą dwie metody load() i store(). Pierwsza z nich jest odpowiedzialna za odczytanie zapisanych opcji przy otwarciu panelu a druga za zapisanie opcji przy zamknięciu. Standardowo kreator w tych metodach podpowiada nam w komentarzu jak możemy zapisać czy odczytać nasze zmienne.
void load(){
  jCheckBox1.setSelected(NbPreferences.forModule(OptionPanel.class).
   getBoolean("jCheckBox1", false));
  jTextField1.setText(NbPreferences.forModule(OptionPanel.class).
   get("jTextField1", "tekst"));
  jSlider1.setValue(NbPreferences.forModule(OptionPanel.class).
   getInt("jSlider1", 50));
}
void store() {
  NbPreferences.forModule(OptionPanel.class).
   putBoolean("jCheckBox1", jCheckBox1.isSelected());
  NbPreferences.forModule(OptionPanel.class).
   put("jTextField1", jTextField1.getText());
  NbPreferences.forModule(OptionPanel.class).
   putInt("jSlider1", jSlider1.getValue());
}


Do zapamiętywania wartości użyłem klasy NbPreferences. Klasa ta tworzy plik o rozszerzeniu properties dla każdego modułu, dla którego zostanie wywołana. W naszym przypadku będzie to plik option.properies w katalogu config\Preferences\org\myorg w katalogu roboczym dla aplikacji. Przykładowo dla NetBeans IDE jest to $HOME\.netbeans\6.1beta. Metody put, putBoolean, putInt zapisują odpowiednio wartość typu String, boolean, int. Pierwszy argument to klucz a drugi to wartość dla tego klucza. Jak łatwo się domyśleć metody get, getBoolean i getInt służą do odczytywania wartości. Pierwszy argument to klucz a drugi to wartość na wypadek gdyby klucz nie został znaleziony lub wartość jest innego typu. Uruchommy więc nasz przykład (Install\Reload in Target Platform). Wybieramy z menu Tools -> Options.



Po ustawieniu jakiś wartości w polach i zamknięciu (za pomocą przycisku OK) i ponownym otwarciu wartości w polach powinny być ustawione tak jak w momencie zamykania okna. Jeśli teraz gdziekolwiek w naszym programie chcemy skorzystać z jakiejś opcji wystarczy pobrać ją w ten sam sposób jak to było wyżej pokazane w metodzie load().

piątek, 28 marca 2008

Nowy typ plików w Netbeans

Opiszę dzisiaj w jaki sposób dodać obsługę własnego typu plików. Załóżmy ze mamy jakiś plik z rozszerzeniem *.mtyp i chcemy aby pliki tego typu były w jakiś sposób wyróżnione.
Stwórzmy w tym celu nowy moduł o nazwie MyTypeModule:



W nowym module wybieramy z kreatora nowy plik typu File Type.



W następnym oknie kreatora podajemy nowy typ MIME i rozszerzenia dla naszego pliku.



W następnym oknie podajemy prefiks dla plików tworzonych przez kreatora dla obsługi naszego pliku oraz ikonę jaką będą miały pliki *.mtyp.



Jako ikonkę podałem ten oto plik .

Po zakończeniu kreatora otrzymujemy kilka nowych plików. W zasadzie po tych kilku kliknięciach myszką mamy już gotowy moduł wyróżniający pliki z rozszerzeniem mtyp.
Plik MyTypeTemplate.mtyp jest szablonem dla nowo tworzonych plików, w nim możemy wpisać co mają zawierać nowo tworzone pliki. W pliku Bundles.properties w pakiecie, w którym znajdują się pliki MyType* możemy zmienić linijkę Templates/Other/MyTypeTemplate.mtyp=Nowy plik mojego typu (mtyp) co spowoduje wyświetlanie nowej nazwy w kreatorze dodawania nowego pliku. Jeśli wszystkie zmiany już gotowe pora wypróbować nasz moduł. Możemy go zainstalować np. w środowisku IDE lub stworzyć plik modułu nbm i zainstalować.



Po wybraniu Install/Reload in Target Platform uruchomi się nam Netbeans jeszcze raz. W nowo uruchomionym środowisku utwórzmy jakiś projekt (np. JavaApplication1) a następnie spróbujmy dodać do niego nowy plik.



Jak widzimy w kategorii Other pojawiła się nowa pozycja. Po wybraniu nazwy w drzewku projektu pojawił się nowo utworzonym plik z treścią, która zdefiniowaliśmy w pliku Bundles.properties.



W najbliższym czasie spróbuje opisać jak do edytora plików mtyp dodać własne kolorowanie składni oraz podpowiedzi.r

czwartek, 6 marca 2008

Tworzenie aplikacji z wykorzystaniem NetBeans Platform

Na prostym przykładzie spróbuję pokazać jak można wykorzystać Netbeans Platform do tworzenia aplikacji desktopowej.
A więc zaczynamy. Będzie nam potrzebny Netbeans (6.0) ;)
Zaczynamy od utworzenia nowego projektu (typu NetBeans Module Suite).



Nazywamy nasz projekt SampleSuite i zapisujemy.




W ustawieniach dla nowo utworzonego projektu z sekcji Libraries wybieramy tylko moduły z klastra Platform7 (inne nie będą nam potrzebne). W sekcji Build wybieramy Create standalone application. Możemy wybrać jaki tytuł będzie nosiła nasza aplikacja, jaką będzie miała ikonę oraz jakim obrazkiem się z nami przywita (sekcja Splash Screen).



W tym momencie mamy już gotowy szkielet aplikacji, który można uruchomić.



Utworzymy teraz nowy moduł, który dodamy do aplikacji. W skład modułu wejdzie drzewko. Po zaznaczeniu jakiegoś liścia w drzewie, po prawej stronie w oknie Properites pojawią się parametry zaznaczonego obiektu.



Wprowadzamy nazwę projektu i nazwę pakietu głównego i dodajemy go do projektu aplikacji.





Wszystkie nowe klasy będziemy tworzyć w nowo utworzonym projekcie modułu. Utworzymy teraz okno z drzewkiem nawigatora. Aby nasze drzewko miało co pokazywać utwórzmy klasę MyDataObject, która będzie reprezentowała jakieś dane. W praktyce klasa ta może reprezentować np. dane z pliku (dla plików mamy gotową klasę FileObject) lub z bazy danych.



public class MyDataObject {
private static int count = 0;
private final int index;
private final String name;

public MyDataObject(){
index = count++;
name = "Nasz " + index + ". obiekt";
}

public String getName(){
return name;
}

public int getIndex(){
return index;
}
}


Każda nowo powołana instancja obiektu MyDataObject będzie posiadała kolejny numer.
Aby można było zbudować drzewo na podstawie MyDataObject potrzebujemy jeszcze klasy typu Node. Node jest warstwą prezentacji dla danych (MyDataObject).


public class MyDataNode extends AbstractNode{
public MyDataNode(DataObject obj) {
super(Children.LEAF, Lookups.singleton(obj));
setDisplayName(obj.getName());
}

public MyDataNode() {
super(Children.create(new MyDataChildFactory(), true));
setDisplayName("Korzeń");
}
}


Jak widzimy klasa MyDataNode rozszerza klasę AbstractNode. Po wklejeniu (lub wpisaniu) powyższego kodu klasy NetBeans najprawdopodobniej nie rozpozna klasy AbstractNode. Musimy do naszego projektu modułu dodać odpowiednie zależności. W tym celu we właściwościach projektu, w kategorii Libraries wybieramy Add dependencies i szukamy modułu zawierającego klasę AbstractNode.




Dodajmy od razu wszystkie moduły, które będą nam później potrzebne



Dalej nierozpoznana pozostaje klasa MyDataChildFactory. Jest to fabryka liści-potomków, którą właśnie utworzymy.


public class MyDataChildFactory extends ChildFactory{
@Override
protected boolean createKeys(List list) {
for (int i = 0; i < 10; i++) {
list.add(new MyDataObject());
}
return true;
}

@Override
protected Node createNodeForKey(MyDataObject obj) {
return new MyDataNode(obj);
}
}


Klasa MyDataNode posiada dwa konstruktory. Domyślny konstruktor tworzy korzeń drzewka z MyDataChildFactory jako fabryką dzieci dla niego. Drugi konstruktor tworzy obiekt MyDataNode jako liść bez dzieci (Children.LEAF). Mechanizm Lookup jest tematem na osobny wykład. W wielkim skrócie można powiedzieć, że Lookup jest mapą gdzie kluczem jest typ obiektu a wartością instancje obiektów tego typu. Lookups.singleton(obj) "dba" aby w mapie była tylko jedna instancja obiektu.
MyDataChildFactory posiada dwie metody:
createKeys tworzy listę kluczy (obiektów MyDataObject), dla których będą utworzone nody (węzły) reprezentujące je w strukturze drzewa
createNodeForKey jest wywoływana dla każdego stworzonego klucza w pierwszej metodzie.

Mając już wszystkie potrzebne klasy możemy przejść do stworzenia drzewka.
Tworzymy nowy plik typu Window Component z kategorii Module development.



Window position ustawiamy jako explorer i zaznaczymy otwieranie przy starcie aplikacji.



W następnym oknie kreatora podajemy Class name prefix (dla naszej aplikacji będzie to Tree). Po tym kreator utworzy dla nas 4 pliki TreeAction.java, TreeTopComponent.java, TreeTopComponentSettings.xml, TreeTopComponentWstcref.xml. Pliki xml są plikami konfiguracyjnymi. TreeTopComponent to obiekt klasy TopComponent (póki co możemy przyjąć, że jest to panel, na którym możemy poukładać różne kontrolki Swing), TreeAction to klasa akcji pozwalająca otwierać ten panel jako nowe okno w naszej aplikacji. Jeśli uruchomimy aplikację zobaczymy, że pojawiło się nowe okno ("Tree Window") oraz nowa pozycja w menu Windows (Windows -> Tree). Nazwy dla naszego okna i akcji możemy zmienić w pliku Bundle.properties w pakiecie, w którym te klasy się znajdują (w naszym przypadku org.myorg.samplemodule.tree.Bundle.properties).

Przejdźmy do edycji pliku TreeTopComponent.java. W trybie Design ustawiamy BorderLayout jako layout głównego komponentu (TopComponent w oknie inspektora). W „środek” tego layouta wstawiamy z palety komponentów JScrollPane.



We właściwościach jScrollPane1 w sekcji Code ustawiamy Custom Creation Code.



Klasa BeanTreeView jest rozszerzeniem klasy JScrollPane. Wyświetla ona węzły (Node) w postaci drzewka. Musimy jeszcze uzupełnić import dla klasy BeanTreeView. Przechodzimy do trybu edycji Source i dodajemy brakujący import.



Musimy jeszcze do naszego drzewa dodać obsługę zdarzeń użytkownika (zaznaczanie liścia, itp). W tym celu dodajemy interfejs ExplorerManager.Provider.


final class TreeTopComponent extends TopComponent implements ExplorerManager.Provider{
private ExplorerManager manager = new ExplorerManager();


który wprowadza tylko jedną metodę:


public ExplorerManager getExplorerManager() {
return manager;
}


W konstruktorze okna wskazujemy menadżerowi, jaki obiekt ma być pokazany w drzewie.


private TreeTopComponent() {
...
manager.setRootContext(new MyDataNode());
}


Po uruchomieniu naszej aplikacji, pokaże się nam już drzewko, jednak aplikacja nie będzie na razie w żaden sposób reagowała na zaznaczenie obiektu. Ponieważ chcemy, aby po zaznaczeniu liścia w drzewie, w oknie Properties pokazały się parametry zaznaczonego obiektu, musimy do definicji naszego węzła dodać obsługę properties.


@Override
protected Sheet createSheet() {
Sheet sheet = Sheet.createDefault();
Sheet.Set set = Sheet.createPropertiesSet();
MyDataObject data = getLookup().lookup(MyDataObject.class);

try {
Property indexProp = new PropertySupport.Reflection(data, Integer.class, "getIndex", null);
Property nameProp = new PropertySupport.Reflection(data, String.class, "getName", null);
indexProp.setName("Index");
nameProp.setName("Name");
set.put(indexProp);
set.put(nameProp);
} catch (NoSuchMethodException ex) {
Exceptions.printStackTrace(ex);
}
sheet.put(set);
return sheet;
}


Musimy nadpisać metodę createSheet() w MyDataNode. Aby okno properties wiedziało, że zmieniono zaznaczenie musimy jeszcze dodać do konstruktora TreeTopComponent linijkę:


associateLookup(ExplorerUtils.createLookup(manager, getActionMap()));


Uruchamiamy aplikację:



Aby drzewko wyglądało tak jak na powyższym rysunku (wiele poziomów) wystarczy w konstruktorze MyDataNode(MyDataObject object)
zmienić dzieci liścia z Children.LEAF na Children.create(new MyChildFactory(), true) tak jak w domyślnym konstruktorze.