KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tonbeller > wcf > changeorder > ChangeOrderMgr


1 /*
2  * ====================================================================
3  * This software is subject to the terms of the Common Public License
4  * Agreement, available at the following URL:
5  * http://www.opensource.org/licenses/cpl.html .
6  * Copyright (C) 2003-2004 TONBELLER AG.
7  * All Rights Reserved.
8  * You must accept the terms of that agreement to use this software.
9  * ====================================================================
10  *
11  *
12  */

13 package com.tonbeller.wcf.changeorder;
14
15 import org.w3c.dom.Element JavaDoc;
16
17 import com.tonbeller.tbutils.res.Resources;
18 import com.tonbeller.wcf.component.Form;
19 import com.tonbeller.wcf.component.RenderListener;
20 import com.tonbeller.wcf.controller.Dispatcher;
21 import com.tonbeller.wcf.controller.DispatcherSupport;
22 import com.tonbeller.wcf.controller.RequestContext;
23 import com.tonbeller.wcf.controller.RequestListener;
24 import com.tonbeller.wcf.scroller.Scroller;
25 import com.tonbeller.wcf.utils.DomUtils;
26
27 /**
28  * generates buttons to move objects in a list or tree
29  *
30  * @author av
31  */

32 public class ChangeOrderMgr implements RenderListener {
33   Dispatcher dispatcher = new DispatcherSupport();
34   ChangeOrderModel model;
35
36   /** name of the DOM element */
37   static final String JavaDoc BUTTON_ELEMENT = "move-button";
38   /** attribute name of a BUTTON_ELEMENT_NAME element, value is STYLE_xxx */
39   static final String JavaDoc STYLE_ATTR = "style";
40   /** value for STYLE_ATTR */
41   static final String JavaDoc STYLE_FWD = "fwd";
42   /** value for STYLE_ATTR */
43   static final String JavaDoc STYLE_BWD = "bwd";
44   /** value for STYLE_ATTR */
45   static final String JavaDoc STYLE_CUT = "cut";
46   /** value for STYLE_ATTR */
47   static final String JavaDoc STYLE_UNCUT = "uncut";
48   /** value for STYLE_ATTR */
49   static final String JavaDoc STYLE_PASTE_BEFORE = "paste-before";
50   /** value for STYLE_ATTR */
51   static final String JavaDoc STYLE_PASTE_AFTER = "paste-after";
52   
53   static final String JavaDoc TITLE_ATTR = "title";
54   String JavaDoc titleFwd = "";
55   String JavaDoc titleBwd = "";
56   String JavaDoc titleCut = "";
57   String JavaDoc titleUncut = "";
58   String JavaDoc titlePasteBefore = "";
59   String JavaDoc titlePasteAfter = "";
60
61   Form form;
62
63   public ChangeOrderMgr(Dispatcher parentDispatcher, Form form) {
64     this.form = form;
65     this.model = new DefaultChangeOrderModel();
66     parentDispatcher.addRequestListener(null, null, dispatcher);
67   }
68
69   /**
70    * creates new ChangeOrderMgr
71    * @param parentDispatcher
72    * @param form
73    * @param model null or the model to use
74    */

75   public ChangeOrderMgr(Dispatcher parentDispatcher, Form form, ChangeOrderModel model) {
76     this.form = form;
77     this.model = model;
78     parentDispatcher.addRequestListener(null, null, dispatcher);
79   }
80
81   /**
82    * appends dom elements "move-button" with attribute "style=fwd|bwd" and "id",
83    * e.g. <move-button id="wcf.e5f8fbfc" style="bwd"/>
84    * <p />
85    * There is one current ButtonRenderer which defines the state of the ChangeOrderMgr
86    *
87    * @param elem the parent element to which the "move-button" element is appended as a child
88    * @param scope defines the valid targets, where <code>node</code> may be moved. <code>node</code>
89    * may be moved among other nodes that belong to the same scope. In a tree, the parent is a good choice
90    * to be the scope of its child nodes.
91    * @param nodeIndex the index of the the current <code>node</code> among its siblings
92    * @param nodeCount number of nodes that belong to <code>scope</code>.
93    */

94   interface ButtonRenderer extends RenderListener {
95     void renderButton(Element JavaDoc elem, Object JavaDoc scope, Object JavaDoc node, int nodeIndex, int nodeCount);
96   }
97
98   /**
99    * creates "move forward" and "move backward" buttons. Allows the user to move a node
100    * for one position with each click. This is easy for small amount of nodes, but requires
101    * many clicks for larger amount.
102    *
103    * @author av
104    * @since 12.01.2005
105    */

106   class ForwardBackwardButtonRenderer implements ButtonRenderer {
107     /** implement ButtonRenderer */
108     public void renderButton(Element JavaDoc elem, Object JavaDoc scope, Object JavaDoc node, int nodeIndex, int nodeCount) {
109       if (nodeIndex == 0) {
110         // first node can move forward only
111
// leave space for backward button only if there are inner nodes
112
if (nodeCount > 2)
113           appendEmptyButton(elem);
114         appendMoveButton(elem, scope, node, STYLE_FWD, titleFwd, nodeIndex, nodeIndex + 1);
115       } else if (nodeIndex == nodeCount - 1) {
116         // last node can move backward only
117
appendMoveButton(elem, scope, node, STYLE_BWD, titleBwd, nodeIndex, nodeIndex - 1);
118         // leave space for forward button only if there are inner nodes
119
if (nodeCount > 2)
120           appendEmptyButton(elem);
121       } else {
122         // inner nodes can move forward and backward
123
appendMoveButton(elem, scope, node, STYLE_BWD, titleBwd, nodeIndex, nodeIndex - 1);
124         appendMoveButton(elem, scope, node, STYLE_FWD, titleFwd, nodeIndex, nodeIndex + 1);
125       }
126     }
127
128     public void startRendering(RequestContext context) {
129     }
130
131     public void stopRendering() {
132     }
133
134     void appendEmptyButton(Element JavaDoc elem) {
135       Element JavaDoc buttonElem = elem.getOwnerDocument().createElement(BUTTON_ELEMENT);
136       elem.appendChild(buttonElem);
137     }
138
139     void appendMoveButton(Element JavaDoc elem, Object JavaDoc scope, Object JavaDoc item, String JavaDoc style, String JavaDoc title, int oldIndex,
140         int newIndex) {
141       Element JavaDoc buttonElem = elem.getOwnerDocument().createElement(BUTTON_ELEMENT);
142       elem.appendChild(buttonElem);
143       buttonElem.setAttribute(STYLE_ATTR, style);
144       buttonElem.setAttribute(TITLE_ATTR, title);
145       String JavaDoc id = DomUtils.randomId();
146       buttonElem.setAttribute("id", id);
147       dispatcher.addRequestListener(id, null, new ChangeOrderButtonHandler(scope, item, oldIndex,
148           newIndex));
149     }
150   };
151
152   /**
153    * renders a button that allows the user to "cut" a node which means to mark it for move.
154    * If the user "cut"s a node, the PasteButtonRenderer will become the current renderer to
155    * render the paste buttons.
156    * @author av
157    * @since 12.01.2005
158    */

159   class CutButtonRenderer implements ButtonRenderer {
160     public void renderButton(Element JavaDoc elem, Object JavaDoc scope, Object JavaDoc item, int nodeIndex, int nodeCount) {
161       Element JavaDoc buttonElem = elem.getOwnerDocument().createElement(BUTTON_ELEMENT);
162       elem.appendChild(buttonElem);
163       buttonElem.setAttribute(STYLE_ATTR, STYLE_CUT);
164       buttonElem.setAttribute(TITLE_ATTR, titleCut);
165       String JavaDoc id = DomUtils.randomId();
166       buttonElem.setAttribute("id", id);
167       dispatcher.addRequestListener(id, null, new CutButtonHandler(scope, item, nodeIndex));
168     }
169
170     public void startRendering(RequestContext context) {
171     }
172
173     public void stopRendering() {
174     }
175   };
176
177   /**
178    * RequestHandler for a "Cut" button. Creates a PasteButtonRenderer for the selected node
179    * and makes it the current buttonRenderer.
180    *
181    * @author av
182    * @since 12.01.2005
183    */

184   class CutButtonHandler implements RequestListener {
185     Object JavaDoc scope;
186     Object JavaDoc item;
187     int nodeIndex;
188
189     CutButtonHandler(Object JavaDoc scope, Object JavaDoc item, int nodeIndex) {
190       this.scope = scope;
191       this.item = item;
192       this.nodeIndex = nodeIndex;
193     }
194
195     public void request(RequestContext context) throws Exception JavaDoc {
196       Scroller.enableScroller(context);
197       form.validate(context);
198       if (model != null)
199         buttonRenderer = new PasteButtonRenderer(scope, item, nodeIndex);
200     }
201   }
202
203   /**
204    * renders paste buttons. When rendering has finished (stopRendering() was called), the
205    * current buttonRenderer is restored to a CutRenderer. So if the user collapses a tree node
206    * instead of pasting a previously cutted node, the tree goes back into cut mode automatically.
207    *
208    * @author av
209    * @since 12.01.2005
210    */

211   class PasteButtonRenderer implements ButtonRenderer {
212     Object JavaDoc scope;
213     Object JavaDoc item;
214     int nodeIndex;
215
216     PasteButtonRenderer(Object JavaDoc scope, Object JavaDoc item, int nodeIndex) {
217       this.scope = scope;
218       this.item = item;
219       this.nodeIndex = nodeIndex;
220     }
221
222     boolean equals(Object JavaDoc o1, Object JavaDoc o2) {
223       if (o1 == null)
224         return o2 == null;
225       return o1.equals(o2);
226     }
227
228     public void renderButton(Element JavaDoc elem, Object JavaDoc scope, Object JavaDoc item, int nodeIndex, int nodeCount) {
229       // may not move into another scope
230
if (!equals(this.scope, scope))
231         return;
232       // current item gets an "undo cut" button
233
Element JavaDoc buttonElem = elem.getOwnerDocument().createElement(BUTTON_ELEMENT);
234       elem.appendChild(buttonElem);
235       String JavaDoc id = DomUtils.randomId();
236       buttonElem.setAttribute("id", id);
237       if (this.nodeIndex == nodeIndex) {
238         // create a "undo cut" button
239
buttonElem.setAttribute(STYLE_ATTR, STYLE_UNCUT);
240         buttonElem.setAttribute(TITLE_ATTR, titleUncut);
241         dispatcher.addRequestListener(id, null, new UnCutButtonHandler());
242       } else if (this.nodeIndex < nodeIndex) {
243         // create a "paste after" button
244
buttonElem.setAttribute(STYLE_ATTR, STYLE_PASTE_AFTER);
245         buttonElem.setAttribute(TITLE_ATTR, titlePasteAfter);
246         dispatcher.addRequestListener(id, null, new ChangeOrderButtonHandler(scope, item,
247             this.nodeIndex, nodeIndex));
248       } else {
249         // create a "paste before" button
250
buttonElem.setAttribute(STYLE_ATTR, STYLE_PASTE_BEFORE);
251         buttonElem.setAttribute(TITLE_ATTR, titlePasteBefore);
252         dispatcher.addRequestListener(id, null, new ChangeOrderButtonHandler(scope, item,
253             this.nodeIndex, nodeIndex));
254       }
255     }
256
257     public void startRendering(RequestContext context) {
258     }
259
260     public void stopRendering() {
261       buttonRenderer = new CutButtonRenderer();
262     }
263   };
264
265   /**
266    * handler for the selected node in "paste mode". If the user clicks on that node again,
267    * "paste mode" will be reset to "cut mode".
268    * @author av
269    * @since 12.01.2005
270    */

271   class UnCutButtonHandler implements RequestListener {
272     public void request(RequestContext context) throws Exception JavaDoc {
273       Scroller.enableScroller(context);
274       form.validate(context);
275       buttonRenderer = new CutButtonRenderer();
276     }
277   }
278
279   /**
280    * button handler that moves a node from one position to another. Used in forward/backward mode as well
281    * as in paste mode.
282    * @author av
283    * @since 12.01.2005
284    */

285   class ChangeOrderButtonHandler implements RequestListener {
286     Object JavaDoc scope;
287     Object JavaDoc item;
288     int oldIndex;
289     int newIndex;
290
291     ChangeOrderButtonHandler(Object JavaDoc scope, Object JavaDoc item, int oldIndex, int newIndex) {
292       this.scope = scope;
293       this.item = item;
294       this.oldIndex = oldIndex;
295       this.newIndex = newIndex;
296     }
297
298     public void request(RequestContext context) throws Exception JavaDoc {
299       Scroller.enableScroller(context);
300       form.validate(context);
301       if (model != null)
302         model.move(scope, item, oldIndex, newIndex);
303     }
304   }
305   
306   /**
307    * the current buttonRenderer, implements some kind of state machine.
308    */

309   ButtonRenderer buttonRenderer = new ForwardBackwardButtonRenderer();
310
311   public void renderButton(Element JavaDoc elem, Object JavaDoc scope, Object JavaDoc node, int nodeIndex, int nodeCount) {
312     if (model == null || !model.mayMove(scope, node))
313       return;
314
315     // a single node can not be moved around
316
if (nodeCount < 2)
317       return;
318
319     buttonRenderer.renderButton(elem, scope, node, nodeIndex, nodeCount);
320   }
321
322
323   /**
324    * called once before rendering of buttons starts
325    */

326   public void startRendering(RequestContext context) {
327     Resources res = context.getResources(ChangeOrderMgr.class);
328     titleFwd = res.getString("ChangeOrderMgr.titleFwd");
329     titleBwd = res.getString("ChangeOrderMgr.titleBwd");
330     titleCut = res.getString("ChangeOrderMgr.titleCut");
331     titleUncut = res.getString("ChangeOrderMgr.titleUncut");
332     titlePasteBefore = res.getString("ChangeOrderMgr.titlePasteBefore");
333     titlePasteAfter = res.getString("ChangeOrderMgr.titlePasteAfter");
334     
335     dispatcher.clear();
336     buttonRenderer.startRendering(context);
337   }
338
339   /**
340    * called once after rendering of buttons has finished
341    */

342   public void stopRendering() {
343     buttonRenderer.stopRendering();
344   }
345
346   public ChangeOrderModel getModel() {
347     return model;
348   }
349
350   public void setModel(ChangeOrderModel model) {
351     this.model = model;
352   }
353
354   public void setCutPasteMode(boolean b) {
355     if (b)
356       buttonRenderer = new CutButtonRenderer();
357     else
358       buttonRenderer = new ForwardBackwardButtonRenderer();
359   }
360
361 }
Popular Tags