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