001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package javax.microedition.lcdui.game;
028:
029: import javax.microedition.lcdui.Image;
030: import javax.microedition.lcdui.Graphics;
031: import javax.microedition.lcdui.Display;
032:
033: /**
034: * The LayerManager manages a series of Layers. The LayerManager simplifies
035: * the process of rendering the Layers that have been added to it by
036: * automatically rendering the correct regions of each Layer in the
037: * appropriate order.
038: * <p>
039: * The LayerManager maintains an ordered list to which Layers can be appended,
040: * inserted and removed. A Layer's index correlates to its z-order; the layer
041: * at index 0 is closest to the user while a the Layer with the highest index
042: * is furthest away from the user. The indices are always contiguous; that
043: * is, if a Layer is removed, the indices of subsequent Layers will be
044: * adjusted to maintain continuity.
045: * <p>
046: * The LayerManager class provides several features that control how the
047: * game's Layers are rendered on the screen.
048: * <p>
049: * The <em>view window</em> controls the size of the visible region and its
050: * position relative to the LayerManager's coordinate system. Changing the
051: * position of the view window enables effects such as scrolling or panning
052: * the user's view. For example, to scroll to the right, simply move the view
053: * window's location to the right. The size of the view window controls how
054: * large the user's view will be, and is usually fixed at a size that is
055: * appropriate for the device's screen.
056: * <P>
057: * In this example, the view window is set to 85 x 85 pixels and is located at
058: * (52, 11) in the LayerManager's coordinate system. The Layers appear at
059: * their respective positions relative to the LayerManager's origin.
060: * <br>
061: * <center><img src="doc-files/viewWindow.gif" width=558 height=292
062: * ALT="Specifying the View Window"></center>
063: * <br>
064: * <p>
065: * The {@link #paint(Graphics, int, int)} method includes an (x,y) location
066: * that controls where the view window is rendered relative to the screen.
067: * Changing these parameters does not change the contents of the view window,
068: * it simply changes the location where the view window is drawn. Note that
069: * this location is relative to the origin of the Graphics object, and thus
070: * it is subject to the translation attributes of the Graphics object.
071: * <P>
072: * For example, if a game uses the top of the screen to display the current
073: * score, the view window may be rendered at (17, 17) to provide enough space
074: * for the score.
075: * <br>
076: * <center><img src="doc-files/drawWindow.gif" width=321 height=324
077: * ALT="Drawing the View Window"></center>
078: * <br>
079: * <p>
080: **/
081: public class LayerManager {
082:
083: /**
084: * Creates a new LayerManager.
085: */
086: public LayerManager() {
087: setViewWindow(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
088: }
089:
090: /**
091: * Appends a Layer to this LayerManager. The Layer is appended to the
092: * list of existing Layers such that it has the highest index (i.e. it
093: * is furthest away from the user). The Layer is first removed
094: * from this LayerManager if it has already been added.
095: *
096: * @see #insert(Layer, int)
097: * @see #remove(Layer)
098: * @param l the <code>Layer</code> to be added
099: * @throws NullPointerException if the <code>Layer</code> is
100: * <code>null</code>
101: */
102: public void append(Layer l) {
103: // remove the Layer if it is already present
104: // will throw NullPointerException if the Layer is null
105: removeImpl(l);
106: addImpl(l, nlayers);
107: }
108:
109: /**
110: * Inserts a new Layer in this LayerManager at the specified index.
111: * The Layer is first removed from this LayerManager if it has already
112: * been added.
113: * @see #append(Layer)
114: * @see #remove(Layer)
115: * @param l the <code>Layer</code> to be inserted
116: * @param index the index at which the new <code>Layer</code> is
117: * to be inserted
118: * @throws NullPointerException if the <code>Layer</code> is
119: * <code>null</code>
120: * @throws IndexOutOfBoundsException if the index is less than
121: * <code>0</code> or
122: * greater than the number of Layers already added to the this or
123: * if the index is greater than the number of Layers already added
124: * to this LayerManager minus one and the Layer has already been added
125: * to this LayerManager
126: * <code>LayerManager</code>
127: */
128: public void insert(Layer l, int index) {
129: /* Check for correct arguments: index in bounds */
130: if ((index < 0) || (index > nlayers)
131: || (exist(l) && (index >= nlayers))) {
132: throw new IndexOutOfBoundsException();
133: }
134:
135: // if the Layer is already present
136: // remove it
137: // will throw NullPointerException if the Layer is null
138: removeImpl(l);
139: // insert it at the specified index
140: addImpl(l, index);
141: }
142:
143: /**
144: * Gets the Layer with the specified index.
145: * @param index the index of the desired Layer
146: * @return the Layer that has the specified index
147: * @throws IndexOutOfBoundsException if the specified
148: * <code>index</code> is less than
149: * zero, or if it is equal to or greater than the number of Layers added
150: * to the this <code>LayerManager</code>
151: **/
152: public Layer getLayerAt(int index) {
153: if ((index < 0) || (index >= nlayers)) {
154: throw new IndexOutOfBoundsException();
155: }
156: return component[index];
157: }
158:
159: /**
160: * Gets the number of Layers in this LayerManager.
161: * <p>
162: * @return the number of Layers
163: */
164: public int getSize() {
165: return nlayers;
166: }
167:
168: /**
169: * Removes the specified Layer from this LayerManager. This method does
170: * nothing if the specified Layer is not added to the this LayerManager.
171: * @see #append(Layer)
172: * @see #insert(Layer, int)
173: * @param l the <code>Layer</code> to be removed
174: * @throws NullPointerException if the specified <code>Layer</code> is
175: * <code>null</code>
176: */
177: public void remove(Layer l) {
178: removeImpl(l);
179: }
180:
181: /**
182: * Renders the LayerManager's current view window at the specified
183: * location.
184: * <p>
185: * The LayerManager renders each of its layers in order of descending
186: * index, thereby implementing the correct z-order. Layers that are
187: * completely outside of the view window are not rendered.
188: * <p>
189: * The coordinates passed to this method determine where the
190: * LayerManager's view window will be rendered relative to the origin
191: * of the Graphics object. For example, a game may use the top of the
192: * screen to display the current score, so to render the game's layers
193: * below that area, the view window might be rendered at (0, 20). The
194: * location is relative to the Graphics object's origin, so translating
195: * the Graphics object will change where the view window is rendered on
196: * the screen.
197: * <p>
198: * The clip region of the Graphics object is intersected with a region
199: * having the same dimensions as the view window and located at (x,y).
200: * The LayerManager then translates the graphics object such that the
201: * point (x,y) corresponds to the location of the viewWindow in the
202: * coordinate system of the LayerManager. The Layers are then rendered
203: * in the appropriate order. The translation and clip region of the
204: * Graphics object are restored to their prior values before this method
205: * returns.
206: * <p>
207: * Rendering is subject to the clip region and translation of the Graphics
208: * object. Thus, only part of the specified view window may be rendered
209: * if the clip region is not large enough.
210: * <p>
211: * For performance reasons, this method may ignore Layers that are
212: * invisible or that would be rendered entirely outside of the Graphics
213: * object's clip region. The attributes of the Graphics object are not
214: * restored to a known state between calls to the Layers' paint methods.
215: * The clip region may extend beyond the bounds of a Layer; it is the
216: * responsibility of the Layer to ensure that rendering operations are
217: * performed within its bounds.
218: * <p>
219: * @see #setViewWindow
220: * @param g the graphics instance with which to draw the LayerManager
221: * @param x the horizontal location at which to render the view window,
222: * relative to the Graphics' translated origin
223: * @param y the vertical location at which to render the view window,
224: * relative to the Graphics' translated origin
225: * @throws NullPointerException if <code>g</code> is <code>null</code>
226: */
227: public void paint(Graphics g, int x, int y) {
228:
229: // if g == null g.getClipX will throw NullPointerException;
230:
231: // save the original clip
232: int clipX = g.getClipX();
233: int clipY = g.getClipY();
234: int clipW = g.getClipWidth();
235: int clipH = g.getClipHeight();
236:
237: // translate the LayerManager co-ordinates to Screen co-ordinates
238: g.translate(x - viewX, y - viewY);
239: // set the clip to view window
240: g.clipRect(viewX, viewY, viewWidth, viewHeight);
241:
242: // draw last to first
243: for (int i = nlayers; --i >= 0;) {
244: Layer comp = component[i];
245: if (comp.visible) {
246: // IMPL NOTE: do this if outside Graphics clip region don't paint
247: // (comp.contains(x - comp.x, y - comp.y)) &&
248: // paint will happen only in clipped region of view window
249: comp.paint(g);
250: }
251: }
252:
253: // restore Screen co-ordinates origin and clip
254:
255: g.translate(-x + viewX, -y + viewY);
256: g.setClip(clipX, clipY, clipW, clipH);
257: }
258:
259: /**
260: * Sets the view window on the LayerManager.
261: * <p>
262: * The view window specifies the region that the LayerManager draws when
263: * its {@link #paint} method is called. It allows the developer to
264: * control the size of the visible region, as well as the location of the
265: * view window relative to the LayerManager's coordinate system.
266: * <p>
267: * The view window stays in effect until it is modified by another call
268: * to this method. By default, the view window is located at (0,0) in
269: * the LayerManager's coordinate system and its width and height are both
270: * set to Integer.MAX_VALUE.
271: *
272: * @param x the horizontal location of the view window relative to the
273: * LayerManager's origin
274: * @param y the vertical location of the view window relative to the
275: * LayerManager's origin
276: * @param width the width of the view window
277: * @param height the height of the view window
278: * @throws IllegalArgumentException if the <code>width</code> or
279: * <code>height</code> is less than <code>0</code>
280: */
281: public void setViewWindow(int x, int y, int width, int height) {
282:
283: if (width < 0 || height < 0) {
284: throw new IllegalArgumentException();
285: }
286:
287: viewX = x;
288: viewY = y;
289: viewWidth = width;
290: viewHeight = height;
291: }
292:
293: /**
294: * add or insert a layer
295: * @param layer The Layer to be inserted
296: * @param index the position at which to insert the layer
297: */
298: private void addImpl(Layer layer, int index) {
299: // Add component to list;
300: // allocate new array if necessary.
301:
302: if (nlayers == component.length) {
303: Layer newcomponents[] = new Layer[nlayers + 4];
304: System.arraycopy(component, 0, newcomponents, 0, nlayers);
305: System.arraycopy(component, index, newcomponents,
306: index + 1, nlayers - index);
307: component = newcomponents;
308: } else {
309: System.arraycopy(component, index, component, index + 1,
310: nlayers - index);
311: }
312:
313: component[index] = layer;
314: nlayers++;
315: }
316:
317: /**
318: * Removes the specified Layer from this LayerManager.
319: * @param l The Layer to be removed
320: * @throws NullPointerException if the specified Layer is null
321: **/
322: private void removeImpl(Layer l) {
323: if (l == null) {
324: throw new NullPointerException();
325: }
326: /**
327: * Search backwards, expect that more recent additions
328: * are more likely to be removed.
329: */
330:
331: for (int i = nlayers; --i >= 0;) {
332: if (component[i] == l) {
333: remove(i);
334: }
335: }
336: }
337:
338: /**
339: * Check existence the specified Layer in this LayerManager.
340: * @param l The Layer to be check
341: * @return true if Layer exist
342: **/
343: private boolean exist(Layer l) {
344: if (l == null) {
345: return false;
346: }
347:
348: for (int i = nlayers; --i >= 0;) {
349: if (component[i] == l) {
350: return true;
351: }
352: }
353: return false;
354: }
355:
356: /**
357: * remove a layer at the specified index.
358: * @param index the position at which to insert the layer,
359: */
360: private void remove(int index) {
361: System.arraycopy(component, index + 1, component, index,
362: nlayers - index - 1);
363: component[--nlayers] = null;
364: }
365:
366: /**
367: * The number of layers in this LayerManager.
368: * This value can be null.
369: */
370: private int nlayers; // = 0;
371:
372: /**
373: * The Layers in this LayerManager.
374: * @see #append(Layer)
375: */
376: private Layer component[] = new Layer[4];
377:
378: /**
379: * the view window co-ordinates.
380: */
381: private int viewX, viewY, viewWidth, viewHeight; // = 0;
382:
383: }
|