Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/flexstor/common/awt/tree/Tree.java


1   /*
2    * Tree.java
3    *
4    * Copyright $Date: 2003/08/11 02:22:36 $ FLEXSTOR.net Inc.
5    *
6    * This work is licensed for use and distribution under license terms found at
7    * http://www.flexstor.org/license.html
8    *
9    */
10  
11  package com.flexstor.common.awt.tree;
12  
13  import java.awt.Color;
14  import java.awt.Dimension;
15  import java.awt.Graphics;
16  import java.awt.Image;
17  import java.awt.Panel;
18  import java.awt.Rectangle;
19  import java.awt.Scrollbar;
20  import java.awt.SystemColor;
21  import java.awt.event.ActionListener;
22  import java.awt.event.AdjustmentEvent;
23  import java.awt.event.AdjustmentListener;
24  import java.awt.event.InputEvent;
25  import java.awt.event.KeyEvent;
26  import java.awt.event.KeyListener;
27  import java.awt.event.MouseEvent;
28  import java.awt.event.MouseListener;
29  import java.util.ArrayList;
30  
31  import com.flexstor.common.awt.ActionMulticaster;
32  
33  
34  /**
35   * The ProtoView Tree control is designed to display and navigate through a hierarchical
36   * list of items.  Items in the tree are referred to as nodes.  The FlexTreeNode class which
37   * accompanies the Tree is used to refer to individual nodes of the tree.
38   *
39   * @version 3.0
40   * @author Don Preuninger (Proto View)
41   * @author Viktor Snezhko (Proto View)
42   * @author Dan Schroeder (Rorke Data/FLEXSTOR.net)
43   */
44  public class Tree
45     extends Panel
46     implements KeyListener, AdjustmentListener, MouseListener
47  {
48     /**
49      * HitTest constant returned by getHitTestFlags when the x,y coordinates to
50      * the HitTest method were not over a populated area of the tree.
51      */
52     public static final int HITTEST_NONE = 1;
53  
54     /**
55      * HitTest constant returned by getHitTestFlags when the x,y coordinates to
56      * the HitTest method were over the margin area of a node.
57      */
58     public static final int HITTEST_MARGIN = 2;
59  
60     /**
61      * HitTest constant returned by getHitTestFlags when the x,y coordinates to
62      * the HitTest method were over a plus/minus button of a node.
63      */
64     public static final int HITTEST_BUTTON = 4;
65  
66     /**
67      * HitTest constant returned by getHitTestFlags when the x,y coordinates to
68      * the HitTest method were over the image area of a node.
69      */
70     public static final int HITTEST_IMAGE = 8;
71  
72     /**
73      * HitTest constant returned by getHitTestFlags when the x,y coordinates to
74      * the HitTest method were over the text area of a node.
75      */
76     public static final int HITTEST_TEXT = 16;
77  
78     /**
79      * HitTest constant returned by getHitTestFlags when the x,y coordinates to
80      * the HitTest method were over the area to the right of the node text.
81      */
82     public static final int HITTEST_TEXT_RIGHT = 32;
83     
84     /**
85      * Filler Panel to server as RightBottom corner of tree panel when both 
86      * scrollbars are visible
87      */
88     private Panel pCorner = new Panel();
89     
90     private Scrollbar    m_vscroll;
91     private Scrollbar    m_hscroll;
92     protected TreeSelectionI selection;
93     protected ActionMulticaster actionMulticaster;
94  
95     protected Image      m_imPlus = null;
96     protected Image      m_imMinus = null;
97     private   Image      m_im = null;
98     private   FlexTreeNode rootNode = null;
99     protected FlexTreeNode firstVisNode = null;
100    private   FlexTreeNode lastVisNode = null;
101 
102    private   int     nTreeId = -1;
103    private   int     m_iVisCount = 0;
104    protected int     m_iLinesPerPage = 0;
105    protected int     m_iHScroll = 0;
106    private   int     m_iVScroll = 0;
107    private   int     m_iMaxLineLen = 0;
108    private   int     nHitTest = 0;
109    protected int     m_iIndent = 17;   // The horizontal shift of a child node from its parent.
110    protected int     m_iLineHeight = 18;
111    private   long    nLastScrollTime = -1;
112    private   boolean bShowLines = true;
113    private   boolean bAllowPaint = true;
114    private   boolean bLayoutInProgress = false;
115    private   boolean bPaintInProgress  = false;
116 
117    private   Color   bkgndClr = null;
118 
119    /**
120     * Constuctor.
121     */
122    public Tree()
123    {
124       setLayout ( null );
125       setMultiSelection ( true );
126 
127       m_vscroll = new Scrollbar ( Scrollbar.VERTICAL );
128       m_vscroll.setUnitIncrement ( 1 );
129       m_vscroll.setVisible ( false );
130       m_vscroll.addAdjustmentListener ( this );
131       add ( m_vscroll );
132 
133       m_hscroll = new Scrollbar ( Scrollbar.HORIZONTAL );
134       m_hscroll.setUnitIncrement ( 5 );
135       m_hscroll.setBlockIncrement ( 70 );
136       m_hscroll.setVisible ( false );
137       m_hscroll.addAdjustmentListener ( this );
138       add ( m_hscroll );
139       add(pCorner);
140 
141       super.addKeyListener ( this );
142       m_vscroll.addKeyListener ( this );
143       m_hscroll.addKeyListener ( this );
144 
145       addMouseListener ( this );
146 
147       nTreeId = TreeNotifier.registerTree ( this );
148       actionMulticaster = new ActionMulticaster();
149 
150       rootNode = new FlexTreeNode ( null, null, null );
151       rootNode.setAsTrueRoot ( nTreeId );
152       firstVisNode = null;
153 
154       setBackgroundColor ( SystemColor.control );
155       setForeground ( Color.black );
156       setPlusImage ( null );
157       setMinusImage ( null );
158    }
159 
160    protected int getTreeId ( )
161    {
162       return nTreeId;
163    }
164 
165    public void setBackgroundColor(Color bkgnd)
166    {
167       bkgndClr = bkgnd;
168    }
169    
170    /**
171     * Ensures that the first visible node does not have its removed flag set to true.
172     * This can happen when the first visible item in the tree is delted (Bug #6565)
173     */
174    public void checkFirstVisibleNode ( )
175    {
176       if ( (firstVisNode != null) && (firstVisNode.getRemoved()) )
177       {
178          if ( rootNode == null )
179             firstVisNode = null;
180          else
181             firstVisNode = rootNode.getFirstChild();
182       }
183    }
184 
185    public void removeNotify()
186    {
187       super.removeNotify();
188       TreeNotifier.unregisterTree ( this );
189 
190       if ( actionMulticaster != null )
191       {
192          actionMulticaster.removeAll();
193          actionMulticaster = null;
194       }
195    }
196 
197    public void setMultiSelection ( boolean bState )
198    {
199       if ( bState )
200          selection = new TreeMultiSelection(this);
201       else
202          selection = new TreeSingleSelection();
203    }
204 
205    /**
206     * Get the first visible node of the tree.  This is the first
207     * node that is visible if the tree is scrolled all the way to the top.
208     * This method will return null if there are no nodes in the tree. Visible nodes
209     * are nodes which are at the root level, or child nodes whose anscestors are all
210     * expanded.
211     * @return First visible node.
212     * @see #getNextVisibleNode
213     */
214    public FlexTreeNode getFirstVisibleNode()
215    {
216       return rootNode.nFirstChild;
217    }
218 
219    /**
220     * Set the first node that is visible at the top of the tree window to the
221     * specified node.  Here, visible is meant literally as the first line that the
222     * user can see at the top of the component.
223     * @param Valid node.
224     * @see #getSelectedNode
225     * @see #getRootNode
226     */
227    public void setFirstVisible(FlexTreeNode node)
228    {
229       if(node == null)
230          return;
231       firstVisNode = node;
232       repaint ( false );
233    }
234 
235    /**
236     * Get the root node of the tree.  There may be multiple nodes at the root level,
237     * in which case this method returns the first one.  The method will return null if
238     * the tree is empty.
239     * @return Root node.
240     * @see #getSelectedNode
241     */
242    public FlexTreeNode getRootNode()
243    {
244       return rootNode.nFirstChild;
245    }
246 
247    protected FlexTreeNode getTrueRootNode()
248    {
249       return rootNode;
250    }
251 
252    /**
253     * Return true if node is visible at the time.
254     */
255    public boolean isNodeVisible(FlexTreeNode node)
256    {
257       if(node == null)
258          return false;
259       FlexTreeNode n = firstVisNode;
260       for(int i = 0; i < m_iLinesPerPage; i++)
261       {
262          if(n == node)
263             return true;
264          if(n == null)
265             return false;
266          n = n.getNextVisible();
267       }
268       return false;
269    }
270 
271    /**
272     * Make the specified node to be visible in the window.
273     * @param node instance of FlexTreeNode.
274     * @return The visible index of a node starting from the very top of tree.
275     */
276    public int ensureVisible(FlexTreeNode node)
277    {
278       if(node == null)
279          return -1;
280       FlexTreeNode n = rootNode;
281       //-------------------
282       // visibility of selected node
283       int i;
284       int iFirstVis = -1;
285       //-------------------
286       // find matching node
287       for(i = 0; i < m_iVisCount; i++)
288       {
289          if((n = n.getNextVisible()) == null)
290             break;
291          if(n == firstVisNode)
292             iFirstVis = 0;
293          if(iFirstVis >= 0)
294             iFirstVis++;
295          //-------------------
296          // find match
297          if(n == node)
298             break;
299       }
300       i--;
301       //-------------------
302       // check and adjust visibility of node
303       if(iFirstVis >= 0 && iFirstVis <= m_iLinesPerPage)
304          return i;
305       if(iFirstVis < 0)
306       {
307          while(firstVisNode != node)
308          {
309             if(firstVisNode.getPrevVisible() == null)
310                break;
311             firstVisNode = firstVisNode.getPrevVisible();
312             if(m_iVScroll > 0)
313                m_vscroll.setValue(--m_iVScroll);
314             if(lastVisNode.getPrevVisible() != null)
315                lastVisNode = lastVisNode.getPrevVisible();
316          }
317       }
318       else
319       {
320          iFirstVis -= m_iLinesPerPage;
321          while(iFirstVis-- > 0)
322          {
323             if(lastVisNode.getNextVisible() == null)
324                break;
325             lastVisNode = lastVisNode.getNextVisible();
326             if(firstVisNode.getNextVisible() == null)
327                break;
328             firstVisNode = firstVisNode.getNextVisible();
329             if(m_iVScroll + m_iLinesPerPage < m_iVisCount)
330                m_vscroll.setValue(++m_iVScroll);
331          }
332       }
333 
334       repaint ( false );
335       return i;
336    }
337 
338    /**
339     * Perform mouse position testing to determine where the mouse pointer
340     * is located with respect to the nodes in the tree.
341     * @param x the horizontal position to test for
342     * @param y the veritcal position to test for
343     * @return The node that is at the x,y coordinates. The null is returned if the coordinates are not
344     * contained on a line with any node.
345     */
346    public FlexTreeNode hitTest(int x, int y)
347    {
348       nHitTest = 0;
349       int line = y / m_iLineHeight + 1;
350 
351       if ( firstVisNode == null )
352       {
353          nHitTest = HITTEST_NONE;
354          return null;
355       }
356       
357       FlexTreeNode node = firstVisNode.nodeAtLine( line );
358       if ( node != null )
359       {
360          int nodeline = node.lineFromNode( this );
361          if ( line != nodeline )
362          {
363             nHitTest = HITTEST_NONE;
364             return null;
365          }
366          
367          int margin = m_iIndent;
368          margin *= node.getLevel();
369          margin -= m_iHScroll;
370          
371          if ( x <= margin )
372          {
373              nHitTest = HITTEST_MARGIN;
374              return node;
375          }
376          
377          if ( node.getAlwaysShowPlusMinus() || node.nFirstChild != null )
378          {
379             if( x < ( margin + 16 ) )
380             {
381                nHitTest = HITTEST_BUTTON;
382                return node;
383             }
384          }
385          else if ( x < ( margin + 16 ) )
386          {
387             nHitTest = HITTEST_MARGIN;
388             return node;
389          }
390 
391          margin += 11 + m_imPlus.getWidth( null );
392 
393          Image image = node.isExpanded() ? node.getExpandedImage() : node.getCollapsedImage();
394          int nImageWidth = image.getWidth( this );
395          if ( x <= ( margin + nImageWidth ) )
396          {
397             nHitTest = HITTEST_IMAGE;
398             return node;
399          }
400 
401          margin += nImageWidth;
402 
403          if ( x > margin )
404          {
405             if ( x > ( margin + 5 + getFontMetrics( getFont() ).stringWidth( node.getLabel() ) ) )
406                 nHitTest = HITTEST_TEXT_RIGHT;
407             else
408                 nHitTest = HITTEST_TEXT;
409             
410             return node;
411          }
412       }
413       
414       return null;
415    }
416 
417    /**
418     * Set an option to display lines between nodes and images.
419     * @param enable indicates whether node connecting lines should be displayed or not.
420     */
421    public void setLines ( boolean enable )
422    {
423       bShowLines = enable;
424    }
425 
426    /**
427     * Get an option to display lines between nodes and images.
428     * @return An option.
429     */
430    public boolean getLines()
431    {
432       return bShowLines;
433    }
434 
435    /**
436     * Get an option to redraw tree on any modification of tree structure.
437     * @return An option.
438     */
439    public boolean getRedraw()
440    {
441       return bAllowPaint;
442    }
443 
444    /**
445     * Set an option to redraw tree on any modification of tree structure.
446     * This property can be used when performing a large number of
447     * insertions or deletions of nodes by setting this property to false.
448     * @param enable an option.
449     */
450    public void setRedraw(boolean enable)
451    {
452       bAllowPaint = enable;
453    }
454 
455    /**
456     * Get the number of items that are expanded and visible when scrolling
457     * through tree.  It is not just the number of nodes visible on the screen
458     * at the current moment.
459     * @return Number of visible nodes.
460     * @see #getCount
461     */
462    public int getVisibleCount()
463    {
464       int count = 0;
465       FlexTreeNode n = rootNode;
466       if(n.nFirstChild != null)
467       {
468          n = n.nFirstChild;
469          while(n != null)
470          {
471             n = n.getNextVisible();
472             count++;
473          }
474       }
475       return count;
476    }
477 
478    private int nodeWidth(FlexTreeNode node)
479    {
480       Image image = node.isExpanded() ? node.getExpandedImage() : node.getCollapsedImage();
481       int length = 16 + image.getWidth(this) +(node.getLevel() * m_iIndent) + m_imPlus.getWidth(this);
482 
483       if(node.getLabel() != null)
484          length += getFontMetrics(getFont()).stringWidth(node.getLabel());
485       return length;
486    }
487 
488    /**
489     * Set the image of the "plus" button.
490     * @param image new image.
491     * @see #setMinusImage
492     */
493    public void setPlusImage ( Image image )
494    {
495       if ( image == null )
496          m_imPlus = (new DefaultTreeImages()).getPlusImage();
497       else
498          m_imPlus = image;
499    }
500 
501    /**
502     * Set the image of the "minus" button.
503     * @param image new image
504     * @see #setPlusImage
505     */
506    public void setMinusImage ( Image image )
507    {
508       if ( image == null )
509          m_imMinus = (new DefaultTreeImages()).getMinusImage();
510       else
511          m_imMinus = image;
512    }
513 
514    /**
515     * Add new node to level 0
516     * It is a wrapper for addNode(null, FlexTreeNode.LAST_SIBLING, text, iImage, iSelImage)
517     * @param text text of the node
518     * @param iImage id of image in the indexed image list that will be displayed when
519     * node is not selected. In case of -1, the image is calculated automatically: leaf node
520     * if node has no children or image at the index "0" if node has a child.
521     * @param iSelImage id of image in the indexed image list that will be displayed when
522     * node is selected. In case of -1, the image is calculated automatically: leaf node
523     * if node has no children or image at the index "1" if node has a child.
524     * @see #setDefaultImages
525     * @see #setDefaultImages
526     * @see #addNode(FlexTreeNode,int,String,int,int)
527     */
528    public void addRootNode ( FlexTreeNode root )
529    {
530       root.setAsRoot();
531       reset();
532 
533       rootNode.addChild ( root, FlexTreeNode.LAST );
534 
535       firstVisNode = root;
536       repaint ( true );
537    }
538 
539    /**
540     * Remove all nodes from the tree.
541     * @see #removeNode
542     */
543    public void reset()
544    {
545       rootNode.remove();
546       rootNode = new FlexTreeNode(null, null, null);
547       rootNode.setAsTrueRoot ( nTreeId );
548       rootNode.setExpanded ( true, true );
549       firstVisNode = null;
550       lastVisNode = null;
551       selection.clearSelected();
552       repaint ( true );
553    }
554 
555    /**
556     * Return the preferred size of the Tree control.
557     * If size was never set, then returned value is new Dimension(200,250).
558     */
559    public Dimension getPreferredSize()
560    {
561       Dimension d = getSize();
562       if(d.width == 0)
563          d.width = 200;
564       if(d.height == 0)
565          d.height = 250;
566       return d;
567    }
568 
569    public ArrayList getSelectedItems ( boolean bSubItems )
570    {
571       ArrayList l = selection.getSelectedNodes();
572 
573       if ( bSubItems )
574       {
575          FlexTreeNode node;
576          ArrayList resultList = new ArrayList();
577          resultList.addAll(l);
578 
579          for ( int i = 0; i < l.size(); i++ )
580          {
581             node = (FlexTreeNode)l.get(i);
582             resultList.addAll ( node.getAllChildren() );
583          }
584          return resultList;
585       }
586       else
587          return l;
588 
589    }
590 
591    public void setCurrentNode ( FlexTreeNode n )
592    {
593       selection.setCurrentNode ( n );
594    }
595 
596    public FlexTreeNode getCurrentNode ( )
597    {
598       return selection.getCurrentNode();
599    }
600 
601    public void unselectAllNodes ( )
602    {
603       selection.clearSelected();
604    }
605 
606    public int getSelectedCount ( )
607    {
608       return selection.getSelectedCount();
609    }
610 
611    public boolean isSelected ( FlexTreeNode n )
612    {
613       return selection.isSelected(n);
614    }
615 
616    public boolean isFocusTraversable()
617    {
618       return true;
619    }
620 
621 
622 
623 
624 
625 // Event Processing code
626 
627    public void addActionListener(ActionListener l)
628    {
629       if ( actionMulticaster != null )
630          actionMulticaster.add(l);
631    }
632 
633    public void removeActionListener(ActionListener l)
634    {
635       if ( actionMulticaster != null )
636         actionMulticaster.remove(l);
637    }
638 
639    private void fireActionEvent ( TreeActionEvent e )
640    {
641       if ( actionMulticaster != null )
642         actionMulticaster.dispatch ( e );
643    }
644 
645    public void keyTyped ( KeyEvent e )
646    {
647    }
648 
649    public void keyReleased ( KeyEvent e )
650    {
651    }
652 
653    public void keyPressed ( KeyEvent e )
654    {
655       switch ( e.getKeyCode() )
656       {
657          case KeyEvent.VK_UP:
658          {
659             FlexTreeNode node = selection.getCurrentNode();
660             if ( node != null )
661             {
662                node = node.getPrevVisible();
663                {
664                   if ( node != null )
665                   {
666                      TreeActionEvent ae = new TreeActionEvent ( this, TreeActionEvent.BEGIN_NODE_SELECT, e.getModifiers(), node);
667                      fireActionEvent ( ae );
668 
669                      if ( ae.getCancel() )
670                         return;
671 
672                      selection.clearSelected();
673                      selection.setCurrentNode ( node );
674                      ensureVisible ( node );
675                      repaint ( false );
676 
677                      m_vscroll.setValue ( m_iVScroll );
678 
679                      ae = new TreeActionEvent(this, TreeActionEvent.END_NODE_SELECT, e.getModifiers(), node);
680                      fireActionEvent ( ae );
681                   }
682                }
683             }
684 
685             break;
686          }
687 
688          case KeyEvent.VK_DOWN:
689          {
690             FlexTreeNode node = selection.getCurrentNode();
691             if ( node != null )
692             {
693                node = node.getNextVisible();
694                {
695                   if ( node != null )
696                   {
697                      TreeActionEvent ae = new TreeActionEvent ( this, TreeActionEvent.BEGIN_NODE_SELECT, e.getModifiers(), node);
698                      fireActionEvent ( ae );
699 
700                      if ( ae.getCancel() )
701                         return;
702 
703                      selection.clearSelected();
704                      selection.setCurrentNode ( node );
705                      ensureVisible ( node );
706                      repaint ( false );
707 
708                      m_vscroll.setValue ( m_iVScroll );
709 
710                      ae = new TreeActionEvent(this, TreeActionEvent.END_NODE_SELECT, e.getModifiers(), node);
711                      fireActionEvent ( ae );
712                   }
713                }
714             }
715 
716             break;
717          }
718 
719 
720          case KeyEvent.VK_PAGE_UP:
721          {
722             FlexTreeNode node = selection.getCurrentNode();
723 
724             if ( firstVisNode != node )
725             {
726                node = firstVisNode;
727             }
728             else
729             {
730                FlexTreeNode prev;
731                for ( int i = 0; i < m_iLinesPerPage; i++ )
732                {
733                   prev = node.getPrevVisible();
734                   if ( prev == null )
735                      break;
736                   else
737                      node = prev;
738                }
739             }
740 
741             if ( node != null )
742             {
743                TreeActionEvent ae = new TreeActionEvent ( this, TreeActionEvent.BEGIN_NODE_SELECT, e.getModifiers(), node);
744                fireActionEvent ( ae );
745 
746                if ( ae.getCancel() )
747                   return;
748 
749                selection.clearSelected();
750                selection.setCurrentNode ( node );
751                ensureVisible ( node );
752                repaint ( false );
753 
754                m_vscroll.setValue ( m_iVScroll );
755 
756                ae = new TreeActionEvent(this, TreeActionEvent.END_NODE_SELECT, e.getModifiers(), node);
757                fireActionEvent ( ae );
758             }
759 
760             break;
761          }
762 
763          case KeyEvent.VK_PAGE_DOWN:
764          {
765             FlexTreeNode node = selection.getCurrentNode();
766 
767             if ( lastVisNode != node )
768             {
769                node = lastVisNode;
770             }
771             else
772             {
773                FlexTreeNode next;
774                for ( int i = 0; i < m_iLinesPerPage; i++ )
775                {
776                   next = node.getNextVisible();
777                   if ( next == null )
778                      break;
779                   else
780                      node = next;
781                }
782             }
783 
784             if ( node != null )
785             {
786                TreeActionEvent ae = new TreeActionEvent ( this, TreeActionEvent.BEGIN_NODE_SELECT, e.getModifiers(), node);
787                fireActionEvent ( ae );
788 
789                if ( ae.getCancel() )
790                   return;
791 
792                selection.clearSelected();
793                selection.setCurrentNode ( node );
794                ensureVisible ( node );
795                repaint ( false );
796 
797                m_vscroll.setValue ( m_iVScroll );
798 
799                ae = new TreeActionEvent(this, TreeActionEvent.END_NODE_SELECT, e.getModifiers(), node);
800                fireActionEvent ( ae );
801             }
802 
803             break;
804          }
805 
806          case KeyEvent.VK_HOME:
807          {
808             FlexTreeNode node = rootNode.getFirstChild();
809 
810             TreeActionEvent ae = new TreeActionEvent ( this, TreeActionEvent.BEGIN_NODE_SELECT, e.getModifiers(), node);
811             fireActionEvent ( ae );
812 
813             if ( ae.getCancel() )
814                return;
815 
816             selection.clearSelected();
817             selection.setCurrentNode ( node );
818             firstVisNode = node;
819             repaint ( false );
820 
821             m_iVScroll = 0;;
822             m_vscroll.setValue ( m_iVScroll );
823 
824             ae = new TreeActionEvent(this, TreeActionEvent.END_NODE_SELECT, e.getModifiers(), node);
825             fireActionEvent ( ae );
826 
827             break;
828          }
829 
830          case KeyEvent.VK_END:
831          {
832             FlexTreeNode node = rootNode.getLastVisible();
833 
834             TreeActionEvent ae = new TreeActionEvent ( this, TreeActionEvent.BEGIN_NODE_SELECT, e.getModifiers(), node);
835             fireActionEvent ( ae );
836 
837             if ( ae.getCancel() )
838                return;
839 
840             selection.clearSelected();
841             selection.setCurrentNode ( node );
842             ensureVisible ( node );
843             repaint ( false );
844 
845             m_vscroll.setValue ( m_iVScroll );
846 
847             ae = new TreeActionEvent(this, TreeActionEvent.END_NODE_SELECT, e.getModifiers(), node);
848             fireActionEvent ( ae );
849 
850             break;
851          }
852       }
853    }
854 
855    public void mouseClicked ( MouseEvent e )
856    {
857    }
858 
859    public void mousePressed ( MouseEvent e )
860    {
861       // Process only the left mouse button.
862       if( (e.getModifiers() & InputEvent.BUTTON3_MASK) != 0 )
863          return;
864 
865       requestFocus();
866 
867       // Do not process events if there is no visible first node.
868       if ( firstVisNode == null )
869          return;
870 
871       int        x             = e.getX();
872       int        y             = e.getY();
873       FlexTreeNode node          = hitTest(x, y);
874       int        flags         = nHitTest;
875 
876       // Do not continue if user clicked on the background.
877       if ( (node == null) || (flags == HITTEST_MARGIN) || (flags == HITTEST_TEXT_RIGHT) )
878          return;
879 
880       int margin = node.getLevel() * m_iIndent - m_iHScroll;
881 
882       // If the node is not fully visible, then scroll down 1 row.
883       if ( !isNodeVisible(node) )
884          adjustmentValueChanged ( new AdjustmentEvent ( m_vscroll, AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED, AdjustmentEvent.UNIT_INCREMENT, 0 ) );
885 
886       // User clicked on - or + image.
887       if ( flags == HITTEST_BUTTON )
888       {
889          if ( node.getAlwaysShowPlusMinus() || node.nFirstChild != null )
890          {
891             int nBeginEvent = node.isExpanded() ? TreeActionEvent.BEGIN_NODE_COLLAPSE : TreeActionEvent.BEGIN_NODE_EXPAND;
892             int nEndEvent   = node.isExpanded() ? TreeActionEvent.END_NODE_COLLAPSE   : TreeActionEvent.END_NODE_EXPAND;
893 
894             TreeActionEvent ae = new TreeActionEvent(this, nBeginEvent, e.getModifiers(), node);
895             fireActionEvent ( ae );
896 
897             if ( ae.getCancel() )
898                return;
899 
900             node.setExpanded(!node.isExpanded(), false);
901             repaint ( true );
902 
903             ae = new TreeActionEvent(this, nEndEvent, e.getModifiers(), node);
904             fireActionEvent ( ae );
905          }
906       }
907 
908       // Double click.
909       else if ( e.getClickCount() == 2 )
910       {
911          int nBeginEvent = node.isExpanded() ? TreeActionEvent.BEGIN_NODE_COLLAPSE : TreeActionEvent.BEGIN_NODE_EXPAND;
912          int nEndEvent   = node.isExpanded() ? TreeActionEvent.END_NODE_COLLAPSE   : TreeActionEvent.END_NODE_EXPAND;
913 
914          TreeActionEvent ae = new TreeActionEvent(this, nBeginEvent, e.getModifiers(), node);
915          fireActionEvent ( ae );
916 
917          if ( ae.getCancel() )
918             return;
919 
920          node.setExpanded(!node.isExpanded(), false);
921          repaint ( true );
922 
923          ae = new TreeActionEvent(this, nEndEvent, e.getModifiers(), node);
924          fireActionEvent ( ae );
925 
926          ae = new TreeActionEvent(this, TreeActionEvent.NODE_DOUBLE_CLICKED, e.getModifiers(), node);
927          fireActionEvent ( ae );
928 
929          return;
930       }
931 
932       // Single click.
933       else if ( e.getClickCount() == 1 )
934       {
935          TreeActionEvent ae = new TreeActionEvent(this, TreeActionEvent.BEGIN_NODE_SELECT, e.getModifiers(), node);
936          fireActionEvent ( ae );
937 
938          if ( ae.getCancel() )
939             return;
940 
941          setRedraw ( false );
942          selection.handleMouseSelection ( node, e.isShiftDown(), e.isControlDown() );
943          setRedraw ( true );
944          repaint ( false );
945 
946          ae = new TreeActionEvent(this, TreeActionEvent.END_NODE_SELECT, e.getModifiers(), node);
947          fireActionEvent ( ae );
948       }
949    }
950 
951    public void mouseReleased ( MouseEvent e )
952    {
953    }
954 
955    public void mouseEntered ( MouseEvent e )
956    {
957    }
958 
959    public void mouseExited ( MouseEvent e )
960    {
961    }
962 
963    public void adjustmentValueChanged ( AdjustmentEvent e )
964    {
965       if ( firstVisNode == null )
966          return;
967 
968       if(e.getSource() == m_hscroll)
969       {
970          m_iHScroll = m_hscroll.getValue();
971          repaint ( false );
972          return;
973       }
974 
975       switch(e.getAdjustmentType())
976       {
977          case AdjustmentEvent.UNIT_DECREMENT:   // Scroll up one line.
978          {
979             if ( firstVisNode.getPrevVisible() != null )
980             {
981                firstVisNode = firstVisNode.getPrevVisible();
982                if(m_iVScroll > 0)
983                   m_vscroll.setValue(--m_iVScroll);
984                if(lastVisNode.getPrevVisible() != null)
985                   lastVisNode = lastVisNode.getPrevVisible();
986                repaint ( false );
987             }
988             break;
989         }
990 
991         case AdjustmentEvent.UNIT_INCREMENT:    // Scroll down one line.
992         {
993             if(lastVisNode.getNextVisible() != null)
994             {
995                 lastVisNode = lastVisNode.getNextVisible();
996                if(firstVisNode.getNextVisible() != null)
997                   firstVisNode = firstVisNode.getNextVisible();
998                repaint ( false );
999             }
1000            if(m_iVScroll + m_iLinesPerPage < m_iVisCount)
1001               m_vscroll.setValue(++m_iVScroll);
1002            break;
1003         }
1004
1005         case AdjustmentEvent.BLOCK_DECREMENT:
1006         {
1007            int i = m_iVScroll - m_iLinesPerPage + 1;
1008            if(i < 0)
1009               i = 0;
1010
1011            FlexTreeNode n = firstVisNode;
1012            while(m_iVScroll > i && firstVisNode != null)
1013            {
1014               m_iVScroll--;
1015               if(firstVisNode.getPrevVisible() != null)
1016                  firstVisNode = firstVisNode.getPrevVisible();
1017               if(lastVisNode.getPrevVisible() != null)
1018                  lastVisNode = lastVisNode.getPrevVisible();
1019            }
1020            repaint ( false );
1021            break;
1022         }
1023
1024         case AdjustmentEvent.BLOCK_INCREMENT :
1025         {
1026            int i = m_iVScroll + m_iLinesPerPage - 1;
1027            if(i + m_iLinesPerPage > m_iVisCount)
1028               i = m_iVisCount - m_iLinesPerPage;
1029            if(i < 0) i = 0;
1030               FlexTreeNode n = lastVisNode;
1031
1032            while(m_iVScroll < i && lastVisNode != null)
1033            {
1034               m_iVScroll++;
1035               if(firstVisNode.getNextVisible() != null)
1036                  firstVisNode = firstVisNode.getNextVisible();
1037               if(lastVisNode.getNextVisible() != null)
1038                  lastVisNode = lastVisNode.getNextVisible();
1039            }
1040            repaint ( false );
1041            break;
1042         }
1043
1044         case AdjustmentEvent.TRACK :
1045         {
1046            m_iVScroll = m_vscroll.getValue();
1047            int count = 0;
1048            FlexTreeNode n = rootNode.nFirstChild;
1049            while((n != null) && (count < m_iVScroll))
1050            {
1051               count++;
1052               n = n.getNextVisible();
1053            }
1054            if(n != null)
1055            {
1056               firstVisNode = n;
1057               repaint ( false );
1058            }
1059
1060            break;
1061         }
1062      }
1063   }
1064
1065
1066
1067
1068
1069// Paint code
1070
1071   public void invalidate()
1072   {
1073      super.invalidate();
1074      repaint ( true );
1075   }
1076
1077   public void repaint ( boolean bRecalculateLayout )
1078   {
1079      if ( bRecalculateLayout )
1080         doFullLayout();
1081      else
1082         paintImage();
1083   }
1084
1085   private void doFullLayout ( )
1086   {
1087      if ( bLayoutInProgress )
1088         return;
1089
1090      bLayoutInProgress = true;
1091
1092      Rectangle r = new Rectangle ( getSize() );
1093
1094      // Check client size
1095      if ( r.width < 16 || r.height < 16 )
1096      {
1097         m_vscroll.setVisible(false);
1098         m_hscroll.setVisible(false);
1099         bLayoutInProgress = false;
1100         return;
1101      }
1102
1103      // set vertical scroll bar
1104      m_iMaxLineLen = 10;
1105      FlexTreeNode n = rootNode;
1106      if(n != null)
1107      {
1108         n = n.nFirstChild;
1109         while(n != null)
1110         {
1111            m_iMaxLineLen = Math.max(m_iMaxLineLen, nodeWidth(n) + 3);
1112            n = n.getNextVisible();
1113         }
1114      }
1115      boolean bVisH = m_hscroll.isVisible();
1116      boolean bVisV = m_vscroll.isVisible();
1117      if((m_iMaxLineLen > (r.width - (bVisV ? 15 : 0))) != bVisH)
1118         m_hscroll.setVisible(bVisH = !bVisH);
1119
1120      m_iLinesPerPage = (r.height - (bVisH ? 15 : 0)) / m_iLineHeight;
1121
1122      // set vertical scroll bar
1123      m_iVisCount = getVisibleCount();
1124      if((m_iVisCount > m_iLinesPerPage) != bVisV)
1125      {
1126         if(bVisV)
1127            firstVisNode = rootNode.nFirstChild;
1128         m_vscroll.setVisible(bVisV = !bVisV);
1129         if((m_iMaxLineLen > (r.width - (bVisV ? 15 : 0))) != bVisH)
1130            m_hscroll.setVisible(bVisH = !bVisH);
1131         //--------------------------------
1132         // recalculate one more time
1133         //--------------------------------
1134         m_iLinesPerPage = (r.height - (bVisH ? 15 : 0)) / m_iLineHeight;
1135         if((m_iVisCount > m_iLinesPerPage) != bVisV)
1136         {
1137            if(bVisV)
1138               firstVisNode = rootNode.nFirstChild;
1139            m_vscroll.setVisible(bVisV = !bVisV);
1140         }
1141      }
1142
1143      if(firstVisNode != null)
1144         lastVisNode = firstVisNode.nodeAtLine(m_iLinesPerPage);
1145
1146      // adjust for scroll bars visibility
1147      if(bVisV)
1148      {
1149         m_vscroll.setValues(m_iVScroll, m_iLinesPerPage, 0, m_iVisCount);
1150         r.width -= 15;
1151      }
1152      else
1153         m_iVScroll = 0;
1154
1155      if(bVisH)
1156      {
1157         m_hscroll.setValues(m_iHScroll, r.width, 0, m_iMaxLineLen );
1158         r.height -= 15;
1159      }
1160      else
1161         m_iHScroll = 0;
1162
1163      if(bVisV)
1164      {
1165         Rectangle rr = m_vscroll.getBounds();
1166         if(r.x + r.width != rr.x || r.y != rr.y || 15 != rr.width || r.height != rr.height)
1167            m_vscroll.setBounds(r.x + r.width, r.y, 15, r.height);
1168      }
1169
1170      if(bVisH)
1171      {
1172         Rectangle rr = m_hscroll.getBounds();
1173         if(r.x != rr.x || r.y + r.height != rr.y || r.width != rr.width || 15 != rr.height)
1174            m_hscroll.setBounds(r.x, r.y + r.height, r.width, 15);
1175      }
1176
1177      
1178      pCorner.setBounds(0,0,0,0);
1179      // If Both the ScrollBars are visible then add a filler Panel at RighBottom Corner of Panel
1180      // To solve Bug254
1181      if (bVisH && bVisV)
1182      {
1183        pCorner.setBounds(r.x+r.width, r.y+r.height, 15, 15);
1184      } 
1185      repaint ( false );
1186      bLayoutInProgress = false;
1187   }
1188
1189   public void paint ( Graphics g )
1190   {
1191      Dimension d = getSize();
1192
1193      if ( m_im != null )
1194         if ( m_im.getHeight(this) < d.height || m_im.getWidth(this) < d.width )
1195            m_im = null;
1196
1197      try
1198      {
1199         if ( m_im == null )
1200            m_im = createImage ( d.width, d.height );
1201      }
1202      catch ( Exception e )  { }
1203
1204      paintImage();
1205   }
1206
1207   private void paintImage ( )
1208   {
1209      if ( bPaintInProgress || m_im == null || !bAllowPaint )
1210         return;
1211
1212      bPaintInProgress = true;
1213
1214      FlexTreeNode node  = firstVisNode;
1215      Graphics   ig    = m_im.getGraphics();
1216      int        total = m_iLinesPerPage + 1 ;
1217      int        count = 1;
1218
1219      // Clear the background.
1220      ig.setColor ( bkgndClr );
1221      ig.fillRect ( 0, 0, getSize().width, getSize().height );
1222
1223      // Paint the nodes.
1224      while ( node != null && count <= total )
1225      {
1226         try
1227         {
1228          node._paint ( this, ig, count );
1229         }
1230         catch(NullPointerException npe)
1231         {
1232           AdjustmentEvent ae = new AdjustmentEvent(m_vscroll,AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,AdjustmentEvent.TRACK,m_vscroll.getValue() );
1233           adjustmentValueChanged(ae);
1234           setSize(getSize().width+1,getSize().height+1);
1235           setSize(getSize().width-1,getSize().height-1);
1236         }
1237         if ( count == total-1 )
1238            lastVisNode = node;
1239         node = node.getNextVisible();
1240         count++;
1241      }
1242
1243      ig.dispose();
1244
1245      Graphics g = getGraphics();
1246
1247      if(g != null)
1248      {
1249         g.drawImage(m_im, 0, 0, this);
1250         g.dispose();
1251      }
1252
1253      bPaintInProgress = false;
1254   }
1255
1256   public void drawSelectedBorder ( Graphics g, int l, int t, int w, int h )
1257   {
1258      int d = w & 1;
1259      w += l;
1260      int i = t - 1;
1261
1262      g.setColor ( Color.yellow );
1263
1264      // left/right lines
1265      while((i += 2) <= h + t)
1266      {
1267         g.drawLine(l, i, l, i);
1268         g.drawLine(w, i + d, w, i + d);
1269      }
1270
1271      // top/bottom lines
1272      d = h & 1;
1273      h += t;
1274      i = l - 1;
1275      while((i += 2) <= w)
1276      {
1277         g.drawLine(i, t, i, t);
1278         g.drawLine(i + d, h, i + d, h);
1279      }
1280   }
1281
1282
1283}
1284