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