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 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    }