001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry.contrib.tree.components;
016    
017    import org.apache.commons.logging.Log;
018    import org.apache.commons.logging.LogFactory;
019    import org.apache.tapestry.BaseComponent;
020    import org.apache.tapestry.IAsset;
021    import org.apache.tapestry.IMarkupWriter;
022    import org.apache.tapestry.IRender;
023    import org.apache.tapestry.IRequestCycle;
024    import org.apache.tapestry.contrib.tree.model.ITreeModelSource;
025    import org.apache.tapestry.contrib.tree.model.ITreeRowSource;
026    import org.apache.tapestry.contrib.tree.model.ITreeStateListener;
027    import org.apache.tapestry.contrib.tree.model.ITreeStateModel;
028    import org.apache.tapestry.contrib.tree.model.TreeRowObject;
029    import org.apache.tapestry.contrib.tree.model.TreeStateEvent;
030    import org.apache.tapestry.contrib.tree.simple.SimpleNodeRenderFactory;
031    import org.apache.tapestry.engine.IPageLoader;
032    import org.apache.tapestry.event.PageDetachListener;
033    import org.apache.tapestry.event.PageEvent;
034    import org.apache.tapestry.spec.ComponentSpecification;
035    import org.apache.tapestry.util.ComponentAddress;
036    
037    /**
038     * @author tsveltin?
039     */
040    public abstract class TreeNodeView extends BaseComponent implements
041            PageDetachListener
042    {
043    
044        private static final Log LOG = LogFactory.getLog(TreeNodeView.class);
045    
046        private Boolean m_objNodeState;
047    
048        private Boolean m_objShowNodeImages;
049    
050        private INodeRenderFactory m_objNodeRenderFactory;
051    
052        private IAsset m_objOpenNodeImage;
053    
054        private IAsset m_objCloseNodeImage;
055    
056        private int m_currentForeachConnectImageValue = TreeRowObject.LINE_CONN_IMG;
057    
058        /**
059         * Creates tree .
060         *
061         */
062        public TreeNodeView()
063        {
064            super();
065            initialize();
066        }
067        
068        public abstract INodeRenderFactory getNodeRenderFactoryParameter();
069    
070        public abstract Boolean getShowNodeImages();
071        
072        private void initialize()
073        {
074            m_objNodeState = null;
075            m_objShowNodeImages = null;
076            m_objNodeRenderFactory = null;
077            m_currentForeachConnectImageValue = TreeRowObject.LINE_CONN_IMG;
078        }
079    
080        public IRender getCurrentRenderer()
081        {
082            INodeRenderFactory objRenderFactory = getNodeRenderFactory();
083            ITreeRowSource objTreeRowSource = getTreeRowSource();
084            return objRenderFactory.getRender(objTreeRowSource.getTreeRow()
085                    .getTreeNode(), getTreeModelSource(), getPage()
086                    .getRequestCycle());
087        }
088    
089        public Object[] getNodeContext()
090        {
091            ITreeModelSource objModelSource = getTreeModelSource();
092            ComponentAddress objModelSourceAddress = new ComponentAddress(
093                    objModelSource);
094            ITreeRowSource objTreeRowSource = getTreeRowSource();
095            TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
096            Object objValueUID = objTreeRowObject.getTreeNodeUID();
097            if (LOG.isDebugEnabled())
098            {
099                LOG.debug("getNodeContext objValueUID = " + objValueUID);
100            }
101    
102            return new Object[] { objValueUID, new Boolean(isNodeOpen()),
103                    objModelSourceAddress };
104        }
105    
106        /**
107         * Called when a node in the tree is clicked by the user. If the node is
108         * expanded, it will be collapsed, and vice-versa, that is, the tree state
109         * model is retrieved, and it is told to collapse or expand the node.
110         * 
111         * @param cycle
112         *            The Tapestry request cycle object.
113         */
114        public void nodeExpandCollaps(IRequestCycle cycle)
115        {
116            Object[] context = cycle.getListenerParameters();
117            Object objValueUID = null;
118            if (context != null && context.length > 0)
119            {
120                objValueUID = context[0];
121            }
122            ComponentAddress objModelSourceAddress = (ComponentAddress) context[2];
123            ITreeModelSource objTreeModelSource = (ITreeModelSource) objModelSourceAddress
124                    .findComponent(cycle);
125            // ITreeModelSource objTreeModelSource = getTreeModelSource();
126            ITreeStateModel objStateModel = objTreeModelSource.getTreeModel()
127                    .getTreeStateModel();
128            boolean bState = objStateModel.isUniqueKeyExpanded(objValueUID);
129    
130            if (bState)
131            {
132                objStateModel.collapse(objValueUID);
133                fireNodeCollapsed(objValueUID, objTreeModelSource);
134            }
135            else
136            {
137                objStateModel.expandPath(objValueUID);
138                fireNodeExpanded(objValueUID, objTreeModelSource);
139            }
140        }
141    
142        /**
143         * Called when a node in the tree is selected by the user. the tree state
144         * model is retrieved, and it is told to select the node.
145         * 
146         * @param cycle
147         *            The Tapestry request cycle object.
148         */
149        public void nodeSelect(IRequestCycle cycle)
150        {
151            Object[] context = cycle.getListenerParameters();
152            Object objValueUID = null;
153            if (context != null && context.length > 0)
154            {
155                objValueUID = context[0];
156            }
157            ComponentAddress objModelSourceAddress = (ComponentAddress) context[2];
158            ITreeModelSource objTreeModelSource = (ITreeModelSource) objModelSourceAddress
159                    .findComponent(cycle);
160            // ITreeModelSource objTreeModelSource = getTreeModelSource();
161            ITreeStateModel objStateModel = objTreeModelSource.getTreeModel()
162                    .getTreeStateModel();
163            Object objSelectedNodeInState = objStateModel.getSelectedNode();
164    
165            if (objValueUID.equals(objSelectedNodeInState))
166            {
167                // do nothing, the selected node in UI is the same as the selected
168                // in
169                // state model. The user should use refresh of back button.
170                return;
171            }
172    
173            objStateModel.setSelectedNode(objValueUID);
174            fireNodeSelected(objValueUID, objTreeModelSource);
175        }
176    
177        private void fireNodeSelected(Object objValueUID,
178                ITreeModelSource objTreeModelSource)
179        {
180            deliverEvent(TreeStateEvent.SELECTED_NODE_CHANGED, objValueUID,
181                    objTreeModelSource);
182        }
183    
184        private void fireNodeCollapsed(Object objValueUID,
185                ITreeModelSource objTreeModelSource)
186        {
187            deliverEvent(TreeStateEvent.NODE_COLLAPSED, objValueUID,
188                    objTreeModelSource);
189        }
190    
191        private void fireNodeExpanded(Object objValueUID,
192                ITreeModelSource objTreeModelSource)
193        {
194            deliverEvent(TreeStateEvent.NODE_EXPANDED, objValueUID,
195                    objTreeModelSource);
196        }
197    
198        private void deliverEvent(int nEventUID, Object objValueUID,
199                ITreeModelSource objTreeModelSource)
200        {
201            ITreeStateListener objListener = objTreeModelSource
202                    .getTreeStateListener();
203            if (objListener != null)
204            {
205                TreeStateEvent objEvent = new TreeStateEvent(nEventUID,
206                        objValueUID, objTreeModelSource.getTreeModel()
207                                .getTreeStateModel());
208                objListener.treeStateChanged(objEvent);
209            }
210    
211        }
212    
213        public void pageDetached(PageEvent arg0)
214        {
215            initialize();
216        }
217    
218        public void finishLoad(IRequestCycle objCycle, IPageLoader arg0,
219                ComponentSpecification arg1)
220        {
221            super.finishLoad(objCycle, arg0, arg1);
222            getPage().addPageDetachListener(this);
223    
224            m_objOpenNodeImage = getAsset("_openNodeImage");
225            m_objCloseNodeImage = getAsset("_closeNodeImage");
226        }
227    
228        public boolean isNodeOpen()
229        {
230            if (m_objNodeState == null)
231            {
232                ITreeRowSource objTreeRowSource = getTreeRowSource();
233                TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
234                Object objValueUID = objTreeRowObject.getTreeNodeUID();
235                ITreeModelSource objTreeModelSource = getTreeModelSource();
236                ITreeStateModel objStateModel = objTreeModelSource.getTreeModel()
237                        .getTreeStateModel();
238                boolean bState = objStateModel.isUniqueKeyExpanded(objValueUID);
239                m_objNodeState = new Boolean(bState);
240            }
241            return m_objNodeState.booleanValue();
242        }
243    
244        /**
245         * Returns the openNodeImage.
246         * 
247         * @return IAsset
248         */
249        public IAsset getNodeImage()
250        {
251            IAsset objResult = null;
252            ITreeRowSource objRowSource = getTreeRowSource();
253            boolean bLeaf = objRowSource.getTreeRow().getLeaf();
254            int nRowType = objRowSource.getTreeRow().getTreeRowPossiotionType();
255            if (!bLeaf)
256            {
257                if (isNodeOpen())
258                {
259                    switch(nRowType)
260                    {
261                    case TreeRowObject.FIRST_LAST_ROW:
262                    {
263                        objResult = getAsset("_topLastOpenNodeImage");
264                        break;
265                    }
266    
267                    case TreeRowObject.FIRST_ROW:
268                    {
269                        objResult = getAsset("_topOpenNodeImage");
270                        break;
271                    }
272    
273                    case TreeRowObject.MIDDLE_ROW:
274                    {
275                        objResult = getAsset("_middleOpenNodeImage");
276                        break;
277                    }
278    
279                    case TreeRowObject.LAST_ROW:
280                    {
281                        objResult = getAsset("_bottomOpenNodeImage");
282                        break;
283                    }
284    
285                    default:
286                    {
287                        objResult = getAsset("_openNodeImage");
288                        break;
289                    }
290                    }
291                }
292                else
293                {
294                    switch(nRowType)
295                    {
296                    case TreeRowObject.FIRST_LAST_ROW:
297                    {
298                        objResult = getAsset("_topLastCloseNodeImage");
299                        break;
300                    }
301    
302                    case TreeRowObject.FIRST_ROW:
303                    {
304                        objResult = getAsset("_topCloseNodeImage");
305                        break;
306                    }
307    
308                    case TreeRowObject.MIDDLE_ROW:
309                    {
310                        objResult = getAsset("_middleCloseNodeImage");
311                        break;
312                    }
313    
314                    case TreeRowObject.LAST_ROW:
315                    {
316                        objResult = getAsset("_bottomCloseNodeImage");
317                        break;
318                    }
319    
320                    default:
321                    {
322                        objResult = getAsset("_closeNodeImage");
323                        break;
324                    }
325                    }
326                }
327            }
328            else
329            {
330                switch(nRowType)
331                {
332                case TreeRowObject.FIRST_LAST_ROW:
333                {
334                    objResult = getAsset("_topLineImage");
335                    break;
336                }
337    
338                case TreeRowObject.FIRST_ROW:
339                {
340                    objResult = getAsset("_topLineImage");
341                    break;
342                }
343    
344                case TreeRowObject.MIDDLE_ROW:
345                {
346                    objResult = getAsset("_middleCrossLineImage");
347                    break;
348                }
349    
350                case TreeRowObject.LAST_ROW:
351                {
352                    objResult = getAsset("_bottomLineImage");
353                    break;
354                }
355    
356                default:
357                {
358                    objResult = getAsset("_bottomLineImage");
359                    break;
360                }
361                }
362    
363            }
364            return objResult;
365        }
366    
367        public IAsset getNodeImageOld()
368        {
369            if (isNodeOpen())
370            {
371                if (m_objOpenNodeImage == null)
372                {
373                    m_objOpenNodeImage = getAsset("_openNodeImage");
374                }
375                return m_objOpenNodeImage;
376            }
377    
378            if (m_objCloseNodeImage == null)
379            {
380                m_objCloseNodeImage = getAsset("_closeNodeImage");
381            }
382            return m_objCloseNodeImage;
383        }
384    
385        /**
386         * Returns the closeNodeImage.
387         * 
388         * @return IAsset
389         */
390        public IAsset getCloseNodeImage()
391        {
392            return m_objCloseNodeImage;
393        }
394    
395        /**
396         * Returns the openNodeImage.
397         * 
398         * @return IAsset
399         */
400        public IAsset getOpenNodeImage()
401        {
402            return m_objOpenNodeImage;
403        }
404    
405        /**
406         * Sets the closeNodeImage.
407         * 
408         * @param closeNodeImage
409         *            The closeNodeImage to set
410         */
411        public void setCloseNodeImage(IAsset closeNodeImage)
412        {
413            m_objCloseNodeImage = closeNodeImage;
414        }
415    
416        /**
417         * Sets the openNodeImage.
418         * 
419         * @param openNodeImage
420         *            The openNodeImage to set
421         */
422        public void setOpenNodeImage(IAsset openNodeImage)
423        {
424            m_objOpenNodeImage = openNodeImage;
425        }
426    
427        /**
428         * @see org.apache.tapestry.AbstractComponent#renderComponent(IMarkupWriter,
429         *      IRequestCycle)
430         */
431        protected void renderComponent(IMarkupWriter arg0, IRequestCycle arg1)
432        {
433            super.renderComponent(arg0, arg1);
434            m_objNodeState = null;
435        }
436    
437        /**
438         * Returns the ShowNodeImages.
439         * 
440         * @return Boolean
441         */
442        public Boolean isShowNodeImages()
443        {
444            if (m_objShowNodeImages == null)
445            {
446                if (isParameterBound("nodeRenderFactory"))
447                {
448                    m_objShowNodeImages = getShowNodeImages();
449                }
450                else
451                {
452                    m_objShowNodeImages = Boolean.TRUE;
453                }
454    
455            }
456            return m_objShowNodeImages;
457        }
458    
459        public boolean getShowImages()
460        {
461            boolean bResult = isShowNodeImages().booleanValue();
462            return bResult;
463        }
464    
465        public boolean getShowWithoutImages()
466        {
467            boolean bResult = !isShowNodeImages().booleanValue();
468            return bResult;
469        }
470    
471        public String getOffsetStyle()
472        {
473            // return "width: " + getTreeDataView().getTreeDeep() * 15;
474            ITreeRowSource objTreeRowSource = getTreeRowSource();
475            TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
476            int nTreeRowDepth = 0;
477            if (objTreeRowObject != null)
478            {
479                nTreeRowDepth = objTreeRowObject.getTreeRowDepth();
480                if (nTreeRowDepth != 0) nTreeRowDepth = nTreeRowDepth - 1;
481            }
482            return "padding-left: " + nTreeRowDepth * 19 + "px";
483        }
484    
485        public INodeRenderFactory getNodeRenderFactory()
486        {
487            if (m_objNodeRenderFactory == null)
488            {
489                if (isParameterBound("nodeRenderFactory"))
490                    m_objNodeRenderFactory = getNodeRenderFactoryParameter();
491                else m_objNodeRenderFactory = new SimpleNodeRenderFactory();
492            }
493            return m_objNodeRenderFactory;
494        }
495    
496        public abstract boolean getMakeNodeDirect();
497    
498        public boolean getMakeNodeNoDirect()
499        {
500            return !getMakeNodeDirect();
501        }
502    
503        public String getCleanSelectedID()
504        {
505            return getSelectedNodeID();
506        }
507    
508        public String getSelectedID()
509        {
510            ITreeRowSource objTreeRowSource = getTreeRowSource();
511            ITreeModelSource objTreeModelSource = getTreeModelSource();
512            TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
513            Object objNodeValueUID = objTreeRowObject.getTreeNodeUID();
514            Object objSelectedNode = objTreeModelSource.getTreeModel()
515                    .getTreeStateModel().getSelectedNode();
516            if (objNodeValueUID.equals(objSelectedNode)) { return getSelectedNodeID(); }
517            return "";
518        }
519    
520        private String getSelectedNodeID()
521        {
522            // return getTreeDataView().getTreeView().getSelectedNodeID();
523            return "tree";
524        }
525    
526        public String getNodeStyleClass()
527        {
528            ITreeRowSource objTreeRowSource = getTreeRowSource();
529            ITreeModelSource objTreeModelSource = getTreeModelSource();
530            TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
531            boolean bResult = false;
532            if (objTreeRowObject != null)
533            {
534                Object objNodeValueUID = objTreeRowObject.getTreeNodeUID();
535                Object objSelectedNode = objTreeModelSource.getTreeModel()
536                        .getTreeStateModel().getSelectedNode();
537                bResult = objNodeValueUID.equals(objSelectedNode);
538            }
539            if (bResult) { return "selectedNodeViewClass"; }
540    
541            return "notSelectedNodeViewClass";
542        }
543    
544        public ITreeRowSource getTreeRowSource()
545        {
546            ITreeRowSource objSource = (ITreeRowSource) getPage().getRequestCycle()
547                    .getAttribute(ITreeRowSource.TREE_ROW_SOURCE_ATTRIBUTE);
548            return objSource;
549        }
550    
551        public ITreeModelSource getTreeModelSource()
552        {
553            ITreeModelSource objSource = (ITreeModelSource) getPage()
554                    .getRequestCycle().getAttribute(
555                            ITreeModelSource.TREE_MODEL_SOURCE_ATTRIBUTE);
556            return objSource;
557        }
558    
559        public boolean getShowConnectImage()
560        {
561            ITreeRowSource objRowSource = getTreeRowSource();
562            int nRowType = objRowSource.getTreeRow().getTreeRowPossiotionType();
563            if (TreeRowObject.MIDDLE_ROW == nRowType) return true;
564            return false;
565        }
566    
567        public int[] getForeachConnectImageList()
568        {
569            ITreeRowSource objTreeRowSource = getTreeRowSource();
570            TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
571            return objTreeRowObject.getLineConnImages();
572        }
573    
574        public boolean getDisableLink()
575        {
576            ITreeRowSource objRowSource = getTreeRowSource();
577            boolean bLeaf = objRowSource.getTreeRow().getLeaf();
578            return bLeaf;
579        }
580    
581        /**
582         * Returns the openNodeImage.
583         * 
584         * @return IAsset nevalidno neshto
585         */
586        public IAsset getConnectImage()
587        {
588            IAsset objResult = null;
589            int nConnectImageType = getCurrentForeachConnectImageValue();
590            switch(nConnectImageType)
591            {
592            case TreeRowObject.EMPTY_CONN_IMG:
593            {
594                objResult = getAsset("_whiteSpaceImage");
595                break;
596            }
597    
598            case TreeRowObject.LINE_CONN_IMG:
599            {
600                objResult = getAsset("_middleLineImage");
601                break;
602            }
603    
604            default:
605            {
606                objResult = getAsset("_whiteSpaceImage");
607                break;
608            }
609            }
610            return objResult;
611        }
612    
613        /**
614         * @return Returns the m_CurrentForeachConnectImageValue.
615         */
616        public int getCurrentForeachConnectImageValue()
617        {
618            return m_currentForeachConnectImageValue;
619        }
620    
621        /**
622         * @param currentForeachConnectImageValue
623         *            The m_CurrentForeachConnectImageValue to set.
624         */
625        public void setCurrentForeachConnectImageValue(
626                int currentForeachConnectImageValue)
627        {
628            m_currentForeachConnectImageValue = currentForeachConnectImageValue;
629        }
630    }