package elte.java2_utikalauz5.swing;
/**
Fastruktúrák használata Swingben.
A felhasználói felület tetején a naplózó komponens látható, ahol a fastruktúra adatmodelljének, valamit a kiválasztás változását követhetjük nyomon.
Alatta egy végtelen, teljes 10-áris fastruktúra látható, amely kezdetben az adott fapont útvonalát reprezentáló szöveget tartalmaz minden fapontjában.
A fastruktúra fapontjai szerkeszthetőek (jobb egérgombbal kattintás hatására), elrejthetőek, egy adott fapont gyermekei pedig növekvő ABC-rendben jelennek meg.
Legalul a vezérlő panel látható, melynek gombjai lehetővé teszik a fastruktúra jellemzőinek szabályozását, valamint a megjelenítési stílus váltását.
A fastruktúra megjelenítése nyomkövethető.
@link.forrásfájl {@docRoot}/../data/swing/src JTreeTeszt.java
@link.letöltés {@docRoot}/../data/swing JTreeTeszt.jar
@since Java 2 Útikalauz programozóknak
*/
public class JTreeTeszt extends SwingTeszt implements javax.swing.event.
TreeModelListener,javax.swing.event.TreeExpansionListener, javax.swing.
event.TreeWillExpandListener,javax.swing.event.TreeSelectionListener {
/** Verziószám. */
private final static long serialVersionUID = 15L;
/**
Indítás alkalmazásként.
@param argumentumok Az indításkor megadott paraméterek.
*/
public static void main(String argumentumok[]) {
new JTreeTeszt().init(argumentumok);
}
/**
Applet leírása.
@return A példaprogram leírása
*/
@Override
public String getAppletInfo() {
return "Fastruktúrák használatát bemutató tesztprogram";
}
/** Fastruktúra adatmodellje */
private Modell modell;
/** Fastruktúra komponens */
private javax.swing.JTree fa;
/** Levélikon használati kapcsolója */
private javax.swing.JCheckBox levélikon;
/** Ágikon használati kapcsolója */
private javax.swing.JCheckBox ágikon;
/** GUI felépítése */
@Override
protected void felépít() {
modell = new Modell(10);
modell.addTreeModelListener(this); //adatmodell figyelése
fa = new javax.swing.JTree(modell);
levélikon = new javax.swing.JCheckBox("levélikon", true);
ágikon = new javax.swing.JCheckBox("ágikon", true);
add(new javax.swing.JScrollPane(fa));
fa.putClientProperty("JTree.lineStyle", "Angled");
fa.setSelectionModel(new javax.swing.tree.DefaultTreeSelectionModel() {
/** Verziószám. */
private final static long serialVersionUID = 15L;
/** Kiválasztás folyamatban van */
private boolean kiválasztás = false;
/**
Kiválasztások beállítása.
@param utak A kiválasztandó fapontok.
*/
@Override
public void setSelectionPaths(javax.swing.tree.TreePath[] utak) {
if (kiválasztás) super.setSelectionPaths(utak);
else {
clearSelection();
addSelectionPaths(utak);
}
}
/**
Kiválasztások hozzáadása.
@param utak A kiválasztandó fapontok.
*/
@Override
public void addSelectionPaths(javax.swing.tree.TreePath[] utak) {
if (utak==null || utak.length==0) return;
java.util.Vector újutak = new
java.util.Vector(utak.length);
javax.swing.tree.TreePath szülő = getLeadSelectionPath();
if (szülő==null) szülő=utak[0];
if (szülő!=null) szülő = szülő.getParentPath(); //csak ezen
for (javax.swing.tree.TreePath út : utak) //szülő gyermekei
if (út!=null && út.getParentPath()==szülő) újutak.add(út);
kiválasztás = true;
if (újutak.size()>0) super.addSelectionPaths(újutak.toArray(
new javax.swing.tree.TreePath[újutak.size()]));
kiválasztás = false;
}
});
fa.addTreeSelectionListener(this); //kiválasztás figyelése
fa.addTreeExpansionListener(this); //fapont kinyitásának figyelése
fa.addTreeWillExpandListener(this);
javax.swing.tree.DefaultTreeCellRenderer dtcr =
new javax.swing.tree.DefaultTreeCellRenderer() {
/** Verziószám. */
private final static long serialVersionUID = 15L;
/** Az érték nélküli fapontok betűtípusa */
private java.awt.Font alapbetűtípus;
/**
Megjelenítés felkonfigurálása.
@param fa A megjelenítendő fastruktúra.
@param érték A megjelenítendő érték.
@param kiválasztva Adott fapont kiválasztott állapota.
@param kinyitva Adott fapont kinyitott állapota.
@param levél Megadja hogy a fapont levél-e.
@param sor Megjelenítendő fapont sorszáma.
@param fókuszált Adott fapont fókuszált állapota.
@param return A megjelenítést végző grafikus komponens.
*/
@Override
public java.awt.Component getTreeCellRendererComponent(javax.swing.
JTree fa, Object érték, boolean kiválasztva, boolean kinyitva,
boolean levél, int sor, boolean fókuszált) {
Modell.Fapont pont = (Modell.Fapont)érték;
if (alapbetűtípus==null) alapbetűtípus=fa.getFont().deriveFont(
java.awt.Font.ITALIC, fa.getFont().getSize()*0.8f);
if (pont.tartalom == null) { //érték nélküli fapont
setBackground(fa.getBackground());
setBackgroundNonSelectionColor(fa.getBackground());
setFont(alapbetűtípus);
} else { //értékkel rendelkező fapont
setBackground(java.awt.Color.yellow); //színes háttérrel
setBackgroundNonSelectionColor(java.awt.Color.yellow);
setFont(fa.getFont());
} //megjelenítés elvégeztetése
return super.getTreeCellRendererComponent(fa,érték, kiválasztva,
kinyitva, levél, sor, fókuszált);
}
/**
Nyitott belső fapont ikonja.
@return Alapértelmezett ikon, csak ha megjelenítése engedélyezett.
*/
@Override
public javax.swing.Icon getOpenIcon() {
if (ágikon.isSelected()) return getDefaultOpenIcon();
return null; //nincs a belső pontokhoz ikon
}
/**
Zárt belső fapont ikonja.
@return Alapértelmezett ikon, csak ha megjelenítése engedélyezett.
*/
@Override
public javax.swing.Icon getClosedIcon() {
if (ágikon.isSelected()) return getDefaultClosedIcon();
return null; //nincs a belső pontokhoz ikon
}
/**
Levélikon.
@return Alapértelmezett ikon, csak ha megjelenítése engedélyezett.
*/
@Override
public javax.swing.Icon getLeafIcon() {
if (levélikon.isSelected()) return getDefaultLeafIcon();
return null;
}
};
fa.setCellRenderer( dtcr );
fa.setCellEditor(new Editor( fa, dtcr ));
add(new javax.swing.JScrollPane(napló), java.awt.BorderLayout.NORTH);
javax.swing.JPanel panel = new javax.swing.JPanel(); //vezérlő panel
add(panel, java.awt.BorderLayout.SOUTH);
panel.setPreferredSize(new java.awt.Dimension(450, 200));
javax.swing.AbstractButton gomb = new javax.swing.JButton("elrejt");
panel.add(gomb);
gomb.setActionCommand("elrejt");
gomb.addActionListener(this); //nyomógomb figyelése
gomb = new javax.swing.JButton("megmutat"); //megmutat
panel.add(gomb);
gomb.setActionCommand("megmutat");
gomb.addActionListener(this); //nyomógomb figyelése
gomb = new javax.swing.JButton("visszaállít"); //visszaállít
panel.add(gomb);
gomb.setActionCommand("visszaállít");
gomb.addActionListener(this); //nyomógomb figyelése
javax.swing.JLabel címke = new javax.swing.JLabel("kiválasztási mód:");
panel.add(címke);
címke.setToolTipText("selectionMode");
javax.swing.JComboBox legördülőlista = new javax.swing.JComboBox( new
Object[] {"DISCONTIGUOUS", "CONTIGUOUS", "SINGLE"});
panel.add(legördülőlista);
legördülőlista.setToolTipText("_TREE_SELECTION");
legördülőlista.setActionCommand("kiválaszt");
legördülőlista.addActionListener(this); //kiválasztás figyelése
gomb = new javax.swing.JCheckBox("gyökér látható?", fa.isRootVisible());
panel.add(gomb);
gomb.setActionCommand("root");
gomb.setToolTipText("rootVisible");
gomb.addActionListener(this); //gomb kiválasztásának figyelése
panel.add(ágikon);
ágikon.setActionCommand("folder");
ágikon.addActionListener(this); //gomb kiválasztásának figyelése
panel.add(levélikon);
levélikon.setActionCommand("leaf");
levélikon.addActionListener(this); //gomb kiválasztásának figyelése
gomb = new javax.swing.JCheckBox("szerkeszthető?", fa.isEditable());
panel.add(gomb); //szerkeszthető
gomb.setActionCommand("editable");
gomb.setToolTipText("editable");
gomb.addActionListener(this); //gomb kiválasztásának figyelése
for (javax.swing.Action váltás : stílusváltások)
panel.add(new javax.swing.JButton(váltás)); //stílusváltásgombok
nyomkövetés(fa); //nyomkövethető a megjelenítés
}
/**
Adatváltozás naplózása.
@param tme Faesemény.
*/
public void treeNodesChanged(javax.swing.event.TreeModelEvent tme) {
naplóz("treeNodesChanged", tme);
}
/**
Beszúrás naplózása.
@param tme Faesemény.
*/
public void treeNodesInserted(javax.swing.event.TreeModelEvent tme) {
naplóz("treeNodesInserted", tme);
}
/**
Törlés naplózása.
@param tme Faesemény.
*/
public void treeNodesRemoved(javax.swing.event.TreeModelEvent tme) {
naplóz("treeNodesRemoved", tme);
}
/**
Struktúraváltozás naplózása.
@param tme Faesemény.
*/
public void treeStructureChanged(javax.swing.event.TreeModelEvent tme) {
naplóz("treeStructureChanged", tme);
}
/**
Faesemény naplózása.
@param tme Faesemény.
*/
private void naplóz(String esemény, javax.swing.event.TreeModelEvent tme) {
napló.append("TreeModelEvent: "+esemény+", fapont="+tme.getTreePath().
getLastPathComponent()+", indexek=");
int[] indexek = tme.getChildIndices(); //indexek listázása
if (indexek==null) napló.append("null");
else {
napló.append("[");
for (int index : indexek) napló.append(index+",");
napló.append("]");
}
napló.append(", pontok="); //fapontok listázása
Object[] pontok = tme.getChildren();
if (pontok==null) napló.append("null");
else {
napló.append("[");
for (Object pont : pontok) napló.append(pont+",");
napló.append("]");
}
naplóz("");
}
/**
Fapont kinyitásának naplózása.
@param tee Kinyitási esemény.
*/
public void treeExpanded(javax.swing.event.TreeExpansionEvent tee) {
naplóz("TreeExpansionEvent: treeExpanded, fapont="+ tee.getPath().
getLastPathComponent());
}
/**
Fapont bezárásának naplózása.
@param tee Bezárási esemény.
*/
public void treeCollapsed(javax.swing.event.TreeExpansionEvent tee) {
naplóz("TreeExpansionEvent: treeCollapsed, fapont="+ tee.getPath().
getLastPathComponent());
}
/**
Fapont kinyitási igényének naplózása.
@param tee Kinyitási esemény.
*/
public void treeWillExpand(javax.swing.event.TreeExpansionEvent tee) {
naplóz("TreeExpansionEvent: treeWillExpand, fapont="+ tee.getPath().
getLastPathComponent());
}
/**
Fapont bezárási igényének naplózása.
@param tee Bezárási esemény.
*/
public void treeWillCollapse(javax.swing.event.TreeExpansionEvent tee) {
naplóz("TreeExpansionEvent: treeWillCollapse, fapont="+ tee.getPath().
getLastPathComponent());
}
/**
Fapont kiválasztásának naplózása.
@param tse Kiválasztási esemény.
*/
public void valueChanged(javax.swing.event.TreeSelectionEvent tse) {
naplóz("TreeSelectionEvent: megváltozott kiválasztások száma="+ tse.
getPaths().length);
}
/**
Gombnyomások kezelése.
@param ae Gombnyomás esemény.
*/
public void actionPerformed(java.awt.event.ActionEvent ae) {
if (ae.getActionCommand().equals("kiválaszt")) {
String választás = (String)((javax.swing.JComboBox)ae.getSource()).
getSelectedItem();
int mód = 0;
if (választás.equals("DISCONTIGUOUS")) mód = javax.swing.tree.
TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION;
else if (választás.equals("CONTIGUOUS")) mód = javax.swing.tree.
TreeSelectionModel.CONTIGUOUS_TREE_SELECTION;
else if (választás.equals("SINGLE")) mód = javax.swing.tree.
TreeSelectionModel.SINGLE_TREE_SELECTION;
fa.getSelectionModel().setSelectionMode(mód);
return;
}
javax.swing.AbstractButton gomb = (javax.swing.AbstractButton)ae.
getSource();
if (ae.getActionCommand().equals("elrejt")) {
javax.swing.tree.TreePath[] pontok = fa.getSelectionPaths();
if (pontok==null) return; //kiválasztott fapontok elrejtése
for (int i=0; i pontok = new java.util.Hashtable
();
/**
Konstruktor.
@param ágakszáma Az elágazások száma.
*/
Modell(int ágakszáma) {
this.ágakszáma = ágakszáma;
initRoot(); //gyökérpont beállítása
}
/** Gyökérpont beállítása */
private void initRoot() {
Fapont root = new Fapont(ROOTID);
root.tartalom = ágakszáma+"-alapú teljes fa";
pontok.put(ROOTID, root);
}
/**
Gyökérelem lekérdezése.
@return Fa gyökéreleme.
*/
public Object getRoot() {
return pontok.get(ROOTID);
}
/**
Látható gyermekek számának lekérdezése.
@param szülő Adott fapont.
@return Adott fapont látható gyermekeinek száma.
*/
public int getChildCount(Object szülő) {
Fapont pont = (Fapont)szülő;
if (!pont.rendezett) return ágakszáma;
String azonosító = (pont).azonosító+JEL;
int szám = 0;
for (int i=ágakszáma; --i>=0;) if (pontok.get(azonosító+i).látható)
szám++;
return szám;
}
/** Rendezettséget megadó objektum */
private java.text.Collator collator = java.text.Collator.getInstance();
/**
Fapont gyermekeinek rendezése és realizálása.
@param pont A rendezendő fapont.
*/
private void rendez( Fapont pont ) {
pont.rendezett = true;
String azonosító = pont.azonosító+JEL;
Fapont[] rendezés = new Fapont[ágakszáma];
for (int i=0; i<ágakszáma; i++) { //beszúrásos rendezés
pont = pontok.remove(azonosító+i);
if (pont==null) pont = new Fapont(azonosító+i);
int kezdőindex = 0; //beszúrási pozíció
int végindex = i; //logaritmikus keresése
int újindex = (kezdőindex + végindex) >> 1;
while (végindex>kezdőindex) {
if (collator.compare(pont.toString(), rendezés[újindex].
toString())>0) kezdőindex = újindex+1;
else végindex = újindex;
újindex = (kezdőindex + végindex) >> 1;
}
for (int j=i; --j>=újindex;) rendezés[j+1]=rendezés[j];
rendezés[újindex]=pont; //beszúrás
}
for (int i=0; i<ágakszáma; i++) { //rendezett fapontok felvétele
rendezés[i].azonosító = azonosító + i;
pontok.put(rendezés[i].azonosító, rendezés[i]);
}
}
/**
Adott gyermek lekérdezése.
@param szülő A kérdéses fapont.
@param index A keresett gyermek indexe.
@return A keresett gyermek.
*/
public Object getChild(Object szülő, int index) {
Fapont pont = (Fapont)szülő;
if (!pont.rendezett) rendez(pont);
String azonosító = pont.azonosító+JEL;
for (int i=0; i<ágakszáma; i++) if (pontok.get(azonosító+i).látható) {
if (index == 0) return pontok.get(azonosító+i);
index--;
}
return null; //nincs ilyen indexű gyermek
}
/**
Adott gyermek indexének lekérdezése.
@param szülő A kérdéses fapont.
@param gyermek A keresett gyermek.
@return A keresett gyermek indexe.
*/
public int getIndexOfChild(Object szülő, Object gyermek) {
Fapont pont = (Fapont)gyermek;
if (!pont.látható) return -1; //nem látszik
String azonosító = ((Fapont)szülő).azonosító+JEL;
int index = 0;
try { //valóban gyermeke az adott fapontnak
index=Integer.parseInt(pont.azonosító.substring(azonosító.length()));
} catch (Exception e) {
return -1;
} //index korrigálása a nemlátható
for (int i=index;--i>=0;) if (!pontok.get(azonosító+i).látható) index--;
return index;
}
/**
Akkor levél, ha nincs látható gyermeke.
@param fapont A kérdéses fapont.
@return Adott fapont levél-e.
*/
public boolean isLeaf(Object fapont) {
return getChildCount(fapont)==0;
}
/**
Eredeti érték visszaállítása.
@param útvonal A visszaállítandó fapont.
*/
public void resetNode(javax.swing.tree.TreePath útvonal) {
removeNode((Fapont)útvonal.getLastPathComponent());
fireTreeStructureChanged(útvonal); //fastruktúra újraépítése
}
/**
Adott fapont és realizált gyermekeinek törlése.
@param pont A törlendő fapont.
*/
private void removeNode(Fapont pont) {
if (pontok.remove(pont.azonosító)!=null)
naplóz("Fapont törölve! azonosító="+pont.azonosító+
", tárolt fapontok száma="+pontok.size());
if (pont.rendezett) {
String azonosító = pont.azonosító+JEL;
for (int i=ágakszáma; --i>=0;) removeNode(pontok.get(azonosító+i));
}
if (getRoot()==null) initRoot(); //gyökér újrainicializálása
}
/**
Adott fapont elrejtése.
@param útvonal Az elrejtendő ág.
*/
public void hideNode(javax.swing.tree.TreePath útvonal) {
Fapont pont = (Fapont)útvonal.getLastPathComponent();
if (pont == getRoot()) return; //gyökér mindig látszik
pont.látható = false;
fireTreeStructureChanged(útvonal); //fastruktúra újraépítése
}
/**
Fapont elrejtett gyermekeinek újra megmutatása.
@param útvonal A megjelenítendő ág.
*/
public void showChildren(javax.swing.tree.TreePath útvonal) {
Fapont pont = (Fapont)útvonal.getLastPathComponent();
boolean változás = false; //fastruktúrát újra kell építeni?
if (pont.rendezett) {
String azonosító = pont.azonosító+JEL;
for (int i=ágakszáma; --i>=0;) if (!pontok.get(azonosító+i).látható)
pontok.get(azonosító+i).látható = változás = true;
}
if (változás) fireTreeStructureChanged(útvonal);
}
/**
Adott fapont tartalomváltozásának jelzése.
@param útvonal A megváltozott fapont.
@param újérték A megváltozott tartalom.
*/
public void valueForPathChanged(javax.swing.tree.TreePath útvonal, Object
újérték) {
Fapont pont = (Fapont)útvonal.getLastPathComponent();
if (!pont.toString().equals(újérték.toString())) {
pont.tartalom = újérték.toString(); //valóban megváltozott
fireTreeStructureChanged(útvonal); //fastruktúra újraépítése
}
}
/** Adatmodell változását figyelők listája */
private java.util.Vector figyelők = new
java.util.Vector();
/**
Modellfigyelők felvétele.
@param figyelő Adatmodell figyelő.
*/
public void addTreeModelListener(javax.swing.event.TreeModelListener figyelő
) {
figyelők.add(figyelő); //figyelő felvétele
}
/**
Modellfigyelők törlése.
@param figyelő Adatmodell figyelő.
*/
public void removeTreeModelListener(javax.swing.event.TreeModelListener
figyelő) {
figyelők.remove(figyelő); //figyelő törlése
}
/**
Fastruktúra újraépíttetése.
@param útvonal Az újraépíttetendő részfa.
*/
private void fireTreeStructureChanged(javax.swing.tree.TreePath útvonal) {
if (útvonal.getPathCount()>1) útvonal = útvonal.getParentPath();
Fapont pont = (Fapont)útvonal.getLastPathComponent();
rendez( pont) ; //újra kell rendezni
javax.swing.event.TreeModelEvent esemény = new javax.swing.event.
TreeModelEvent(this, útvonal);
for (javax.swing.event.TreeModelListener figyelő : figyelők)
figyelő.treeStructureChanged(esemény); //változás jelzése
}
}
/** Szerkesztést végző osztály */
class Editor extends javax.swing.tree.DefaultTreeCellEditor {
/** Verziószám. */
private final static long serialVersionUID = 15L;
/**
Konstruktor.
@param fa A szerkesztendő fa.
@param dtcr A használandó megjelenítő.
*/
Editor(javax.swing.JTree fa, javax.swing.tree.DefaultTreeCellRenderer dtcr){
super( fa, dtcr );
}
/**
Szerkesztés megkezdésének jelzése.
@param esemény A szerkesztést indító esemény.
@return Igaz ha az esemény hatására indítható a szerkesztés.
*/
@Override
public boolean isCellEditable(java.util.EventObject esemény) {
if (!fa.isEditable()) return false; //csak ha szerkeszthető a fa
if (esemény instanceof java.awt.event.MouseEvent &&
javax.swing.SwingUtilities.isRightMouseButton(//jobbegérgomb
(java.awt.event.MouseEvent)esemény)) {
prepareForEditing(); //esetén indulhat a szerkesztés
return true;
}
return super.isCellEditable(esemény);
}
}}