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.IBinding;
022    import org.apache.tapestry.IMarkupWriter;
023    import org.apache.tapestry.IRender;
024    import org.apache.tapestry.IRequestCycle;
025    import org.apache.tapestry.contrib.tree.model.ITreeModelSource;
026    import org.apache.tapestry.contrib.tree.model.ITreeRowSource;
027    import org.apache.tapestry.contrib.tree.model.ITreeStateListener;
028    import org.apache.tapestry.contrib.tree.model.ITreeStateModel;
029    import org.apache.tapestry.contrib.tree.model.TreeRowObject;
030    import org.apache.tapestry.contrib.tree.model.TreeStateEvent;
031    import org.apache.tapestry.contrib.tree.simple.SimpleNodeRenderFactory;
032    import org.apache.tapestry.engine.IPageLoader;
033    import org.apache.tapestry.event.PageDetachListener;
034    import org.apache.tapestry.event.PageEvent;
035    import org.apache.tapestry.spec.ComponentSpecification;
036    import org.apache.tapestry.util.ComponentAddress;
037    
038    /**
039     * @author tsveltin?
040     */
041    public abstract class TreeNodeView extends BaseComponent implements PageDetachListener
042    {
043        private static final Log LOG = LogFactory.getLog(TreeNodeView.class);
044    
045        private IBinding m_objNodeRenderFactoryBinding;
046    
047        private IBinding m_objShowNodeImagesBinding;
048    
049        private IBinding m_objMakeNodeDirectBinding;
050    
051        private Boolean m_objNodeState;
052    
053        private Boolean m_objShowNodeImages;
054    
055        private Boolean m_objMakeNodeDirect;
056    
057        private INodeRenderFactory m_objNodeRenderFactory;
058    
059        private IAsset m_objOpenNodeImage;
060    
061        private IAsset m_objCloseNodeImage;
062    
063        private int m_CurrentForeachConnectImageValue = TreeRowObject.LINE_CONN_IMG;
064    
065        public TreeNodeView()
066        {
067            super();
068            initialize();
069        }
070    
071        private void initialize()
072        {
073            m_objNodeState = null;
074            m_objShowNodeImages = null;
075            m_objNodeRenderFactory = null;
076            m_objMakeNodeDirect = 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(
085                    objTreeRowSource.getTreeRow().getTreeNode(),
086                    getTreeModelSource(),
087                    getPage().getRequestCycle());
088        }
089    
090        public Object[] getNodeContext()
091        {
092            ITreeModelSource objModelSource = getTreeModelSource();
093            ComponentAddress objModelSourceAddress = new ComponentAddress(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[]
103            { objValueUID, new Boolean(isNodeOpen()), objModelSourceAddress };
104        }
105    
106        /**
107         * Called when a node in the tree is clicked by the user. If the node is expanded, it will be
108         * collapsed, and vice-versa, that is, the tree state model is retrieved, and it is told to
109         * 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().getTreeStateModel();
127            boolean bState = objStateModel.isUniqueKeyExpanded(objValueUID);
128    
129            if (bState)
130            {
131                objStateModel.collapse(objValueUID);
132                fireNodeCollapsed(objValueUID, objTreeModelSource);
133            }
134            else
135            {
136                objStateModel.expandPath(objValueUID);
137                fireNodeExpanded(objValueUID, objTreeModelSource);
138            }
139        }
140    
141        /**
142         * Called when a node in the tree is selected by the user. the tree state model is retrieved,
143         * and it is told to select the node.
144         * 
145         * @param cycle
146         *            The Tapestry request cycle object.
147         */
148        public void nodeSelect(IRequestCycle cycle)
149        {
150            Object context[] = cycle.getListenerParameters();
151            Object objValueUID = null;
152            if (context != null && context.length > 0)
153            {
154                objValueUID = context[0];
155            }
156            ComponentAddress objModelSourceAddress = (ComponentAddress) context[2];
157            ITreeModelSource objTreeModelSource = (ITreeModelSource) objModelSourceAddress
158                    .findComponent(cycle);
159            //ITreeModelSource objTreeModelSource = getTreeModelSource();
160            ITreeStateModel objStateModel = objTreeModelSource.getTreeModel().getTreeStateModel();
161            Object objSelectedNodeInState = objStateModel.getSelectedNode();
162    
163            if (objValueUID.equals(objSelectedNodeInState))
164            {
165                //do nothing, the selected node in UI is the same as the selected in
166                //state model. The user should use refresh of back button.
167                return;
168            }
169    
170            objStateModel.setSelectedNode(objValueUID);
171            fireNodeSelected(objValueUID, objTreeModelSource);
172        }
173    
174        private void fireNodeSelected(Object objValueUID, ITreeModelSource objTreeModelSource)
175        {
176            deliverEvent(TreeStateEvent.SELECTED_NODE_CHANGED, objValueUID, objTreeModelSource);
177        }
178    
179        private void fireNodeCollapsed(Object objValueUID, ITreeModelSource objTreeModelSource)
180        {
181            deliverEvent(TreeStateEvent.NODE_COLLAPSED, objValueUID, objTreeModelSource);
182        }
183    
184        private void fireNodeExpanded(Object objValueUID, ITreeModelSource objTreeModelSource)
185        {
186            deliverEvent(TreeStateEvent.NODE_EXPANDED, objValueUID, objTreeModelSource);
187        }
188    
189        private void deliverEvent(int nEventUID, Object objValueUID, ITreeModelSource objTreeModelSource)
190        {
191            ITreeStateListener objListener = objTreeModelSource.getTreeStateListener();
192            if (objListener != null)
193            {
194                TreeStateEvent objEvent = new TreeStateEvent(nEventUID, objValueUID, objTreeModelSource
195                        .getTreeModel().getTreeStateModel());
196                objListener.treeStateChanged(objEvent);
197            }
198    
199        }
200    
201        public void pageDetached(PageEvent arg0)
202        {
203            initialize();
204        }
205    
206        public void finishLoad(IRequestCycle objCycle, IPageLoader arg0, ComponentSpecification arg1)
207        {
208            super.finishLoad(objCycle, arg0, arg1);
209            getPage().addPageDetachListener(this);
210    
211            m_objOpenNodeImage = getAsset("_openNodeImage");
212            m_objCloseNodeImage = getAsset("_closeNodeImage");
213        }
214    
215        public boolean isNodeOpen()
216        {
217            if (m_objNodeState == null)
218            {
219                ITreeRowSource objTreeRowSource = getTreeRowSource();
220                TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
221                Object objValueUID = objTreeRowObject.getTreeNodeUID();
222                ITreeModelSource objTreeModelSource = getTreeModelSource();
223                ITreeStateModel objStateModel = objTreeModelSource.getTreeModel().getTreeStateModel();
224                boolean bState = objStateModel.isUniqueKeyExpanded(objValueUID);
225                m_objNodeState = new Boolean(bState);
226            }
227            return m_objNodeState.booleanValue();
228        }
229    
230        /**
231         * Returns the openNodeImage.
232         * 
233         * @return IAsset
234         */
235        public IAsset getNodeImage()
236        {
237            IAsset objResult = null;
238            ITreeRowSource objRowSource = getTreeRowSource();
239            boolean bLeaf = objRowSource.getTreeRow().getLeaf();
240            int nRowType = objRowSource.getTreeRow().getTreeRowPossiotionType();
241            if (!bLeaf)
242            {
243                if (isNodeOpen())
244                {
245                    switch (nRowType)
246                    {
247                        case TreeRowObject.FIRST_LAST_ROW:
248                        {
249                            objResult = getAsset("_topLastOpenNodeImage");
250                            break;
251                        }
252    
253                        case TreeRowObject.FIRST_ROW:
254                        {
255                            objResult = getAsset("_topOpenNodeImage");
256                            break;
257                        }
258    
259                        case TreeRowObject.MIDDLE_ROW:
260                        {
261                            objResult = getAsset("_middleOpenNodeImage");
262                            break;
263                        }
264    
265                        case TreeRowObject.LAST_ROW:
266                        {
267                            objResult = getAsset("_bottomOpenNodeImage");
268                            break;
269                        }
270    
271                        default:
272                        {
273                            objResult = getAsset("_openNodeImage");
274                            break;
275                        }
276                    }
277                }
278                else
279                {
280                    switch (nRowType)
281                    {
282                        case TreeRowObject.FIRST_LAST_ROW:
283                        {
284                            objResult = getAsset("_topLastCloseNodeImage");
285                            break;
286                        }
287    
288                        case TreeRowObject.FIRST_ROW:
289                        {
290                            objResult = getAsset("_topCloseNodeImage");
291                            break;
292                        }
293    
294                        case TreeRowObject.MIDDLE_ROW:
295                        {
296                            objResult = getAsset("_middleCloseNodeImage");
297                            break;
298                        }
299    
300                        case TreeRowObject.LAST_ROW:
301                        {
302                            objResult = getAsset("_bottomCloseNodeImage");
303                            break;
304                        }
305    
306                        default:
307                        {
308                            objResult = getAsset("_closeNodeImage");
309                            break;
310                        }
311                    }
312                }
313            }
314            else
315            {
316                switch (nRowType)
317                {
318                    case TreeRowObject.FIRST_LAST_ROW:
319                    {
320                        objResult = getAsset("_topLineImage");
321                        break;
322                    }
323    
324                    case TreeRowObject.FIRST_ROW:
325                    {
326                        objResult = getAsset("_topLineImage");
327                        break;
328                    }
329    
330                    case TreeRowObject.MIDDLE_ROW:
331                    {
332                        objResult = getAsset("_middleCrossLineImage");
333                        break;
334                    }
335    
336                    case TreeRowObject.LAST_ROW:
337                    {
338                        objResult = getAsset("_bottomLineImage");
339                        break;
340                    }
341    
342                    default:
343                    {
344                        objResult = getAsset("_bottomLineImage");
345                        break;
346                    }
347                }
348    
349            }
350            return objResult;
351        }
352    
353        public IAsset getNodeImageOld()
354        {
355            if (isNodeOpen())
356            {
357                if (m_objOpenNodeImage == null)
358                {
359                    m_objOpenNodeImage = getAsset("_openNodeImage");
360                }
361                return m_objOpenNodeImage;
362            }
363    
364            if (m_objCloseNodeImage == null)
365            {
366                m_objCloseNodeImage = getAsset("_closeNodeImage");
367            }
368            return m_objCloseNodeImage;
369        }
370    
371        /**
372         * Returns the closeNodeImage.
373         * 
374         * @return IAsset
375         */
376        public IAsset getCloseNodeImage()
377        {
378            return m_objCloseNodeImage;
379        }
380    
381        /**
382         * Returns the openNodeImage.
383         * 
384         * @return IAsset
385         */
386        public IAsset getOpenNodeImage()
387        {
388            return m_objOpenNodeImage;
389        }
390    
391        /**
392         * Sets the closeNodeImage.
393         * 
394         * @param closeNodeImage
395         *            The closeNodeImage to set
396         */
397        public void setCloseNodeImage(IAsset closeNodeImage)
398        {
399            m_objCloseNodeImage = closeNodeImage;
400        }
401    
402        /**
403         * Sets the openNodeImage.
404         * 
405         * @param openNodeImage
406         *            The openNodeImage to set
407         */
408        public void setOpenNodeImage(IAsset openNodeImage)
409        {
410            m_objOpenNodeImage = openNodeImage;
411        }
412    
413        /**
414         * @see org.apache.tapestry.AbstractComponent#renderComponent(IMarkupWriter, IRequestCycle)
415         */
416        protected void renderComponent(IMarkupWriter arg0, IRequestCycle arg1)
417        {
418            super.renderComponent(arg0, arg1);
419            m_objNodeState = null;
420        }
421    
422        /**
423         * Returns the ShowNodeImagesBinding.
424         * 
425         * @return IBinding
426         */
427        public IBinding getShowNodeImagesBinding()
428        {
429            return m_objShowNodeImagesBinding;
430        }
431    
432        /**
433         * Sets the ShowNodeImagesBinding.
434         * 
435         * @param ShowNodeImagesBinding
436         *            The ShowNodeImagesBinding to set
437         */
438        public void setShowNodeImagesBinding(IBinding ShowNodeImagesBinding)
439        {
440            m_objShowNodeImagesBinding = ShowNodeImagesBinding;
441        }
442    
443        /**
444         * Returns the ShowNodeImages.
445         * 
446         * @return Boolean
447         */
448        public Boolean isShowNodeImages()
449        {
450            if (m_objShowNodeImages == null)
451            {
452                if (getNodeRenderFactoryBinding() == null)
453                {
454                    m_objShowNodeImages = Boolean.TRUE;
455                }
456                else
457                {
458                    if (m_objShowNodeImagesBinding != null)
459                    {
460                        m_objShowNodeImages = (Boolean) m_objShowNodeImagesBinding.getObject();
461                    }
462                    else
463                    {
464                        m_objShowNodeImages = Boolean.TRUE;
465                    }
466                }
467            }
468            return m_objShowNodeImages;
469        }
470    
471        public boolean getShowImages()
472        {
473            boolean bResult = isShowNodeImages().booleanValue();
474            return bResult;
475        }
476    
477        public boolean getShowWithoutImages()
478        {
479            boolean bResult = !isShowNodeImages().booleanValue();
480            return bResult;
481        }
482    
483        public String getOffsetStyle()
484        {
485            //return "width: " + getTreeDataView().getTreeDeep() * 15;
486            ITreeRowSource objTreeRowSource = getTreeRowSource();
487            TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
488            int nTreeRowDepth = 0;
489            if (objTreeRowObject != null)
490            {
491                nTreeRowDepth = objTreeRowObject.getTreeRowDepth();
492                if (nTreeRowDepth != 0)
493                    nTreeRowDepth = nTreeRowDepth - 1;
494            }
495            return "padding-left: " + nTreeRowDepth * 19 + "px";
496        }
497    
498        /**
499         * Returns the nodeRenderFactoryBinding.
500         * 
501         * @return IBinding
502         */
503        public IBinding getNodeRenderFactoryBinding()
504        {
505            return m_objNodeRenderFactoryBinding;
506        }
507    
508        /**
509         * Sets the nodeRenderFactoryBinding.
510         * 
511         * @param nodeRenderFactoryBinding
512         *            The nodeRenderFactoryBinding to set
513         */
514        public void setNodeRenderFactoryBinding(IBinding nodeRenderFactoryBinding)
515        {
516            m_objNodeRenderFactoryBinding = nodeRenderFactoryBinding;
517        }
518    
519        public INodeRenderFactory getNodeRenderFactory()
520        {
521            if (m_objNodeRenderFactory == null)
522            {
523                IBinding objBinding = getNodeRenderFactoryBinding();
524                if (objBinding != null)
525                {
526                    m_objNodeRenderFactory = (INodeRenderFactory) objBinding.getObject();
527                }
528                else
529                {
530                    m_objNodeRenderFactory = new SimpleNodeRenderFactory();
531                }
532            }
533            return m_objNodeRenderFactory;
534        }
535    
536        /**
537         * Returns the makeNodeDirectBinding.
538         * 
539         * @return IBinding
540         */
541        public IBinding getMakeNodeDirectBinding()
542        {
543            return m_objMakeNodeDirectBinding;
544        }
545    
546        /**
547         * Sets the makeNodeDirectBinding.
548         * 
549         * @param makeNodeDirectBinding
550         *            The makeNodeDirectBinding to set
551         */
552        public void setMakeNodeDirectBinding(IBinding makeNodeDirectBinding)
553        {
554            m_objMakeNodeDirectBinding = makeNodeDirectBinding;
555        }
556    
557        /**
558         * Returns the makeNodeDirect.
559         * 
560         * @return Boolean
561         */
562        public boolean getMakeNodeDirect()
563        {
564            if (m_objMakeNodeDirect == null)
565            {
566                IBinding objBinding = getMakeNodeDirectBinding();
567                if (objBinding != null)
568                {
569                    m_objMakeNodeDirect = (Boolean) objBinding.getObject();
570                }
571                else
572                {
573                    m_objMakeNodeDirect = Boolean.TRUE;
574                }
575            }
576            return m_objMakeNodeDirect.booleanValue();
577        }
578    
579        public boolean getMakeNodeNoDirect()
580        {
581            return !getMakeNodeDirect();
582        }
583    
584        public String getCleanSelectedID()
585        {
586            return getSelectedNodeID();
587        }
588    
589        public String getSelectedID()
590        {
591            ITreeRowSource objTreeRowSource = getTreeRowSource();
592            ITreeModelSource objTreeModelSource = getTreeModelSource();
593            TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
594            Object objNodeValueUID = objTreeRowObject.getTreeNodeUID();
595            Object objSelectedNode = objTreeModelSource.getTreeModel().getTreeStateModel()
596                    .getSelectedNode();
597            if (objNodeValueUID.equals(objSelectedNode))
598            {
599                return getSelectedNodeID();
600            }
601            return "";
602        }
603    
604        private String getSelectedNodeID()
605        {
606            //return getTreeDataView().getTreeView().getSelectedNodeID();
607            return "tree";
608        }
609    
610        public String getNodeStyleClass()
611        {
612            ITreeRowSource objTreeRowSource = getTreeRowSource();
613            ITreeModelSource objTreeModelSource = getTreeModelSource();
614            TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
615            boolean bResult = false;
616            if (objTreeRowObject != null)
617            {
618                Object objNodeValueUID = objTreeRowObject.getTreeNodeUID();
619                Object objSelectedNode = objTreeModelSource.getTreeModel().getTreeStateModel()
620                        .getSelectedNode();
621                bResult = objNodeValueUID.equals(objSelectedNode);
622            }
623            if (bResult)
624            {
625                return "selectedNodeViewClass";
626            }
627    
628            return "notSelectedNodeViewClass";
629        }
630    
631        public ITreeRowSource getTreeRowSource()
632        {
633            ITreeRowSource objSource = (ITreeRowSource) getPage().getRequestCycle().getAttribute(
634                    ITreeRowSource.TREE_ROW_SOURCE_ATTRIBUTE);
635            return objSource;
636        }
637    
638        public ITreeModelSource getTreeModelSource()
639        {
640            ITreeModelSource objSource = (ITreeModelSource) getPage().getRequestCycle().getAttribute(
641                    ITreeModelSource.TREE_MODEL_SOURCE_ATTRIBUTE);
642            return objSource;
643        }
644    
645        public boolean getShowConnectImage()
646        {
647            ITreeRowSource objRowSource = getTreeRowSource();
648            int nRowType = objRowSource.getTreeRow().getTreeRowPossiotionType();
649            if (TreeRowObject.MIDDLE_ROW == nRowType)
650                return true;
651            return false;
652        }
653    
654        public int[] getForeachConnectImageList()
655        {
656            ITreeRowSource objTreeRowSource = getTreeRowSource();
657            TreeRowObject objTreeRowObject = objTreeRowSource.getTreeRow();
658            return objTreeRowObject.getLineConnImages();
659        }
660    
661        public boolean getDisableLink()
662        {
663            ITreeRowSource objRowSource = getTreeRowSource();
664            boolean bLeaf = objRowSource.getTreeRow().getLeaf();
665            return bLeaf;
666        }
667    
668        /**
669         * Returns the openNodeImage.
670         * 
671         * @return IAsset nevalidno neshto
672         */
673        public IAsset getConnectImage()
674        {
675            IAsset objResult = null;
676            int nConnectImageType = getCurrentForeachConnectImageValue();
677            switch (nConnectImageType)
678            {
679                case TreeRowObject.EMPTY_CONN_IMG:
680                {
681                    objResult = getAsset("_whiteSpaceImage");
682                    break;
683                }
684    
685                case TreeRowObject.LINE_CONN_IMG:
686                {
687                    objResult = getAsset("_middleLineImage");
688                    break;
689                }
690    
691                default:
692                {
693                    objResult = getAsset("_whiteSpaceImage");
694                    break;
695                }
696            }
697            return objResult;
698        }
699    
700        /**
701         * @return Returns the m_CurrentForeachConnectImageValue.
702         */
703        public int getCurrentForeachConnectImageValue()
704        {
705            return m_CurrentForeachConnectImageValue;
706        }
707    
708        /**
709         * @param currentForeachConnectImageValue
710         *            The m_CurrentForeachConnectImageValue to set.
711         */
712        public void setCurrentForeachConnectImageValue(int currentForeachConnectImageValue)
713        {
714            m_CurrentForeachConnectImageValue = currentForeachConnectImageValue;
715        }
716    }