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 com.sun.midp.chameleon;
028:
029: import javax.microedition.lcdui.*;
030: import com.sun.midp.chameleon.skins.*;
031:
032: /**
033: * This class represents a "layer". A layer is an element used to comprise
034: * a higher-level "window" (see CWindow.java). A layer has several properties
035: * such as its visibility, whether it accepts input, its size, etc. A layer
036: * also has content, such as its background and its foreground.
037: */
038: public class CLayer {
039:
040: /** Flag indicating this layer is in need of repainting. */
041: private boolean dirty;
042:
043: /** Array holding a bounding rectangle of an area needing repainting. */
044: protected int[] dirtyBounds;
045:
046: /** Copy of the layer bounds needed to unlock the layers for painting */
047: protected int[] boundsCopy;
048:
049: /** Copy of the dirty bounds needed to unlock the layers for painting */
050: protected int[] dirtyBoundsCopy;
051:
052: /** Flag indicating if this layer has a transparent background or not. */
053: protected boolean transparent;
054:
055: /** Flag indicating the current visibility state of this layer. */
056: protected boolean visible;
057:
058: /** Flag indicating the ability of this layer to support key/pen input. */
059: protected boolean supportsInput;
060:
061: /**
062: * Flag indicating this layer is either completely opaque, or not.
063: * By default, a layer is not opaque, and thus requires the background
064: * and any layers below it to be painted in addition to itself. However,
065: * if a layer is opaque, it does not require anything it is obscuring to
066: * be painted, just itself. All layers are opaque by default.
067: */
068: protected boolean opaque = true;
069:
070: /**
071: * If this layer has a filled color background,
072: * the 0xrrggbbaa value of that color
073: */
074: protected int bgColor;
075:
076: /**
077: * The image to use as a background for this layer if this layer
078: * is not transparent and does not use a fill color.
079: */
080: protected Image[] bgImage;
081:
082: /**
083: * If this layer is not transparent and uses a background image,
084: * a flag indicating if that image should be tiled or otherwise centered.
085: */
086: protected boolean tileBG;
087:
088: /**
089: * The window which owns this layer. If this layer has not been added
090: * to a window or it has been removed from a window, the owner will be
091: * null.
092: */
093: protected CWindow owner;
094:
095: /**
096: * An array holding the bounds of this layer. The indices are
097: * as follows:
098: * 0 = layer's 'x' coordinate
099: * 1 = layer's 'y' coordinate
100: * 2 = layer's width
101: * 3 = layer's height
102: * The 'x' and 'y' coordinates are in the coordinate space of the
103: * window which contains this layer.
104: */
105: public int[] bounds;
106:
107: /** Constant used to reference the '0' index of the bounds array */
108: public static final int X = 0;
109:
110: /** Constant used to reference the '1' index of the bounds array */
111: public static final int Y = 1;
112:
113: /** Constant used to reference the '2' index of the bounds array */
114: public static final int W = 2;
115:
116: /** Constant used to reference the '3' index of the bounds array */
117: public static final int H = 3;
118:
119: /**
120: * When in the paint() routine, graphicsColor will be set to the
121: * default graphics color. This is a convenience variable for
122: * subclasses to easily modify and then reset the graphics color.
123: */
124: int graphicsColor;
125:
126: /**
127: * When in the paint() routine, graphicsFont will be set to the
128: * default graphics Font. This is a convenience variable for
129: * subclasses to easily modify and then reset the graphics font.
130: */
131: Font graphicsFont;
132:
133: /**
134: * Construct a default, transparent layer. As a result, the
135: * 'transparent' value will be set to true.
136: */
137: public CLayer() {
138: this ((Image) null, -1);
139: }
140:
141: /**
142: * Construct a layer with the given background image if it is not null
143: * or with a background fill color.
144: * The color should be in the 0xaarrggbb format. If the bgColor is
145: * invalid and bgImage is null, this layer will be transparent and
146: * equal to the no-argument constructor.
147: *
148: * @param bgImage The background image to use for this layer.
149: * @param bgColor The color (0xaarrggbb) to use as the background fill
150: */
151: public CLayer(Image bgImage, int bgColor) {
152: if (bgImage != null) {
153: this .bgImage = new Image[] { bgImage };
154: this .tileBG = true;
155: transparent = false;
156: } else {
157: transparent = (bgColor < 0);
158: }
159: this .bgColor = bgColor;
160: initialize();
161: }
162:
163: /**
164: * Construct a layer with the given background images (if not null)
165: * or with a background fill color and border. The background images
166: * should be a 9 element array, creating a 9-piece image background.
167: * The color should be in the 0xaarrggbb format. If the bgColor is
168: * invalid and bgImages are null, this layer will be transparent and
169: * equal to the no-argument constructor.
170: *
171: * @param bgImages The background image to use for this layer.
172: * @param bgColor The color (0xaarrggbb) to use as the background fill
173: */
174: public CLayer(Image[] bgImages, int bgColor) {
175: if (bgImages != null) {
176: this .bgImage = new Image[bgImages.length];
177: System.arraycopy(bgImages, 0, this .bgImage, 0,
178: bgImages.length);
179: this .tileBG = false;
180: transparent = false;
181: } else {
182: transparent = (bgColor < 0);
183: }
184: this .bgColor = bgColor;
185: initialize();
186: }
187:
188: /**
189: * Finish initialization of this CLayer. This can
190: * be extended by subclasses. The dimensions of the
191: * CLayer are stored in its bounds[]. The 'x' and 'y'
192: * coordinates are in the coordinate space of the
193: * window which contains this layer. By default, a layer is
194: * located at the origin and is as large as the screen size.
195: *
196: * The X and Y coordinates represent the upper left position
197: * of this CLayer in the containing CWindow's coordinate space.
198: *
199: */
200: protected void initialize() {
201: bounds = new int[4];
202: bounds[X] = 0;
203: bounds[Y] = 0;
204: bounds[W] = ScreenSkin.WIDTH;
205: bounds[H] = ScreenSkin.HEIGHT;
206:
207: dirtyBounds = new int[4];
208: dirtyBoundsCopy = new int[4];
209: boundsCopy = new int[4];
210: cleanDirtyRegions();
211:
212: // IMPL_NOTE : center the background image by default
213: }
214:
215: /**
216: * Establish a background. This method will evaluate the parameters
217: * and create a background which is appropriate. If the image is non-null,
218: * the image will be used to create the background. If the image is null,
219: * the values for the colors will be used and the background will be
220: * painted in fill color instead. If the image is null, and the background
221: * color is a negative value, this layer will become transparent and no
222: * background will be painted.
223: *
224: * @param bgImage the image to use for the background tile (or null)
225: * @param tileBG If true, then tile the background image as necessary
226: * to fill a larger screen. If false, treat the image
227: * as fullsize.
228: * @param bgColor if the image is null, use this color as a background
229: * fill color
230: */
231: public void setBackground(Image bgImage, boolean tileBG, int bgColor) {
232: if (bgImage != null) {
233: this .bgImage = new Image[] { bgImage };
234: this .tileBG = tileBG;
235: transparent = false;
236: } else {
237: this .bgImage = null;
238: transparent = (bgColor < 0);
239: }
240: this .bgColor = bgColor;
241: addDirtyRegion();
242: }
243:
244: /**
245: * Establish a background. This method will evaluate the parameters
246: * and create a background which is appropriate. If the images are
247: * non-null, the images will be used to create a 9-piece background.
248: * If the images are null, the value for the color will be used and
249: * the background will be painted in fill color instead. If the images
250: * are null, and the background color is a negative value, this layer
251: * will become transparent and no background will be painted.
252: *
253: * @param bgImages an array containing a 9-piece image set to be used
254: * as the background for this layer
255: * @param bgColor if the images are null, use this color as a background
256: * fill color
257: */
258: public void setBackground(Image[] bgImages, int bgColor) {
259: if (bgImages != null) {
260: this .bgImage = new Image[bgImages.length];
261: System.arraycopy(bgImages, 0, this .bgImage, 0,
262: bgImages.length);
263: this .tileBG = false;
264: transparent = false;
265: } else {
266: this .bgImage = null;
267: transparent = (bgColor < 0);
268: }
269: this .bgColor = bgColor;
270: addDirtyRegion();
271: }
272:
273: /**
274: * Establish the bounds of this layer. The coordinate space for
275: * the 'x' and 'y' anchor will be interpreted in that of the window
276: * which contains this layer.
277: *
278: * @param x The 'x' coordinate of this layer's origin
279: * @param y The 'y' coordinate of this layer's origin
280: * @param w The width of this layer
281: * @param h The height of this layer
282: */
283: public void setBounds(int x, int y, int w, int h) {
284: if (bounds == null) {
285: bounds = new int[4];
286: }
287: bounds[X] = x;
288: bounds[Y] = y;
289: bounds[W] = w;
290: bounds[H] = h;
291: }
292:
293: /**
294: * Return the bounds of this layer (in the coordinate space of
295: * its parent Window). Returns a 4 element array, containing the
296: * x, y, width, and height representing this layer's bounding
297: * rectangle
298: *
299: * @return this layer's bounding rectangle in the coordinate space
300: * of its parent window
301: */
302: public int[] getBounds() {
303: return bounds;
304: }
305:
306: /**
307: * Determine the current visibility of this layer. Note that this
308: * state only pertains to the layer's visibility status within its
309: * containing window. The window itself may or may not be actually
310: * visible on the physical display.
311: *
312: * @return true if this layer is currently visible in a window
313: */
314: public boolean isVisible() {
315: return visible;
316: }
317:
318: /**
319: * Toggle the visibility state of this layer within its containing
320: * window.
321: *
322: * @param visible If true, this layer will be painted as part of its
323: * containing window, as well as receive events if it
324: * supports input.
325: */
326: public void setVisible(boolean visible) {
327: this .visible = visible;
328: addDirtyRegion();
329: }
330:
331: /**
332: * Determine if this layer is opaque
333: *
334: * @return true if this layer does not have any transparent
335: * or translucent areas
336: */
337: public boolean isOpaque() {
338: return opaque;
339: }
340:
341: /**
342: * Set the opacity flag for this layer. True means that this
343: * layer does not have any transparent or translucent areas
344: *
345: * @param opaque a flag indicating the layer's opacity
346: */
347: public void setOpaque(boolean opaque) {
348: this .opaque = opaque;
349: }
350:
351: /**
352: * Returns true if this layer is in need of repainting.
353: *
354: * @return true if this layer is marked as 'dirty' and needs repainting.
355: */
356: public boolean isDirty() {
357: return this .dirty;
358: }
359:
360: /**
361: * Mark this layer as being dirty.
362: * By default, this will also mark the containing window (if there is one)
363: * as being dirty as well.
364: */
365: protected void setDirty() {
366: setDirtyButNotNotifyOwner();
367: if (owner != null) {
368: owner.setDirty();
369: }
370: }
371:
372: /**
373: * Mark this layer as being dirty
374: * but don't mark the containing window.
375: */
376: protected void setDirtyButNotNotifyOwner() {
377: this .dirty = true;
378: }
379:
380: /** Clean any dirty regions of the layer. */
381: protected void cleanDirtyRegions() {
382: dirtyBounds[X] = dirtyBounds[Y] = dirtyBounds[W] = dirtyBounds[H] = -1;
383: }
384:
385: /**
386: * Determines whether dirty regions are empty.
387: *
388: * @return true if dirty regions are not set,
389: * false otherwise
390: */
391: protected boolean isEmptyDirtyRegions() {
392: return dirtyBounds[X] == -1;
393: }
394:
395: /** Clean any dirty regions of the layer and mark layer as not dirty. */
396: protected void cleanDirty() {
397: dirty = false;
398: cleanDirtyRegions();
399: }
400:
401: /**
402: * Determine if this layer supports input, such as key and pen events.
403: *
404: * @return true if this layer supports handling input events
405: */
406: public boolean supportsInput() {
407: return supportsInput;
408: }
409:
410: /**
411: * Toggle the ability of this layer to receive input.
412: *
413: * @param support If true, this layer will receive user input events
414: * (as long as the layer is also visible)
415: */
416: public void setSupportsInput(boolean support) {
417: this .supportsInput = support;
418: }
419:
420: /**
421: * Handle input from a pen tap.
422: *
423: * Parameters describe the type of pen event and the x,y location in the
424: * layer at which the event occurred.
425: *
426: * Important: the x,y location of the pen tap will already be translated
427: * into the coordinate space of the layer.
428: *
429: * @param type the type of pen event
430: * @param x the x coordinate of the event
431: * @param y the y coordinate of the event
432: * @return
433: */
434: public boolean pointerInput(int type, int x, int y) {
435: return false;
436: }
437:
438: /**
439: * Handle key input from a keypad. Parameters describe
440: * the type of key event and the platform-specific
441: * code for the key. (Codes are translated using the
442: * lcdui.Canvas)
443: *
444: * @param type the type of key event
445: * @param code the numeric code assigned to the key
446: * @return
447: */
448: public boolean keyInput(int type, int code) {
449: return false;
450: }
451:
452: /**
453: * Handle input from some type of device-dependent
454: * input method. This could be input from something
455: * such as T9, or a phonebook lookup, etc.
456: *
457: * @param str the text to handle as direct input
458: * @return
459: */
460: public boolean methodInput(String str) {
461: return false;
462: }
463:
464: /**
465: * Utility method to determine if the given point lies within
466: * the bounds of this layer. The point should be in the coordinate
467: * space of this layer's containing CWindow.
468: *
469: * @param x the "x" coordinate of the point
470: * @param y the "y" coordinate of the point
471: * @return true if the coordinate lies in the bounds of this layer
472: */
473: public boolean containsPoint(int x, int y) {
474: return (visible && x >= bounds[X]
475: && x <= (bounds[X] + bounds[W]) && y >= bounds[Y] && y <= (bounds[Y] + bounds[H]));
476: }
477:
478: /**
479: * Utility method to determine if this layer wanna handle
480: * the given point. By default the layer handles the point if it
481: * lies within the bounds of this layer. The point should be in
482: * the coordinate space of this layer's containing CWindow.
483: *
484: * @param x the "x" coordinate of the point
485: * @param y the "y" coordinate of the point
486: * @return true if the coordinate lies in the bounds of this layer
487: */
488: public boolean handlePoint(int x, int y) {
489: return containsPoint(x, y);
490: }
491:
492: /**
493: * Add this layer's entire area to be marked for repaint. Any pending
494: * dirty regions will be cleared and the entire layer will be painted
495: * on the next repaint.
496: */
497: public void addDirtyRegion() {
498: if (CGraphicsQ.DEBUG) {
499: System.err.println("Layer " + layerID() + ":");
500: System.err.println("\tMarking entire layer dirty");
501: }
502: cleanDirtyRegions();
503: setDirty();
504: }
505:
506: /**
507: * Add an area to be marked for repaint to this layer. This could
508: * be needed for a variety of reasons, such as this layer being
509: * obscured by another layer or window element. The new region should
510: * be in the coordinate space of this layer.
511: *
512: * @param x the x coordinate of the region
513: * @param y the y coordinate of the region
514: * @param w the width of the region
515: * @param h the height of the region
516: * @return true if dirty region of the layer was changed,
517: * false otherwise
518: */
519: public boolean addDirtyRegion(int x, int y, int w, int h) {
520: if (CGraphicsQ.DEBUG) {
521: System.err.println("Layer " + this + ":");
522: System.err.println("\tAdd dirty: " + x + ", " + y + ", "
523: + w + ", " + h);
524: }
525:
526: // The whole layer is dirty already
527: if (isDirty() && isEmptyDirtyRegions()) {
528: if (CGraphicsQ.DEBUG) {
529: System.err.println("\tWhole layer is dirty already");
530: }
531: return false;
532: }
533:
534: // Cache layer bounds
535: int bw = bounds[W];
536: int bh = bounds[H];
537: int x2 = x + w;
538: int y2 = y + h;
539:
540: // Dirty region can be outside of the layer
541: if (x >= bw || y >= bh || x2 <= 0 || y2 <= 0) {
542: if (CGraphicsQ.DEBUG) {
543: System.err
544: .println("\tAdded region is outside of the layer");
545: }
546: return false;
547: }
548:
549: boolean res = false;
550: int dx, dy, dx2, dy2;
551: if (isEmptyDirtyRegions()) {
552: dx = x;
553: dy = y;
554: dx2 = x2;
555: dy2 = y2;
556: } else {
557: dx = dirtyBounds[X];
558: dy = dirtyBounds[Y];
559: dx2 = dx + dirtyBounds[W];
560: dy2 = dy + dirtyBounds[H];
561: if (dx2 < x2)
562: dx2 = x2;
563: if (dy2 < y2)
564: dy2 = y2;
565: if (x < dx)
566: dx = x;
567: if (y < dy)
568: dy = y;
569: }
570:
571: // Lastly, we carefully restrict the dirty region
572: // to be within the bounds of this layer
573: if (dx < 0)
574: dx = 0;
575: if (dy < 0)
576: dy = 0;
577: if (dx2 > bw)
578: dx2 = bw;
579: if (dy2 > bh)
580: dy2 = bh;
581:
582: // Update changed dirty region
583: int dw = dx2 - dx;
584: int dh = dy2 - dy;
585: if (dirtyBounds[W] != dw || dirtyBounds[H] != dh) {
586: if (dw == bw && dh == bh) {
587: // The entire layer is dirty now
588: cleanDirtyRegions();
589:
590: if (CGraphicsQ.DEBUG) {
591: System.err
592: .println("\tThe entire layer became dirty");
593: }
594: } else {
595: dirtyBounds[X] = dx;
596: dirtyBounds[Y] = dy;
597: dirtyBounds[W] = dw;
598: dirtyBounds[H] = dh;
599:
600: if (CGraphicsQ.DEBUG) {
601: System.err.println("\tCurrent dirty: "
602: + dirtyBounds[X] + ", " + dirtyBounds[Y]
603: + ", " + dirtyBounds[W] + ", "
604: + dirtyBounds[H]);
605: }
606: }
607: res = true;
608: setDirty();
609: }
610:
611: return res;
612: }
613:
614: /**
615: * Subtract a layer area not needed for repaint of this layer.
616: * It could be needed for a variety of reasons, such as layer being
617: * overlapped with opague higher layer or window element.
618: * The subtracted region should be in the coordinate space
619: * of this layer.
620: *
621: * @param x the x coordinate of the region
622: * @param y the y coordinate of the region
623: * @param w the width of the region
624: * @param h the height of the region
625: * @return true if dirty region of the layer was changed,
626: * false otherwise
627: */
628: boolean subDirtyRegion(int x, int y, int w, int h) {
629: if (!isDirty()) {
630: return false;
631: }
632:
633: if (CGraphicsQ.DEBUG) {
634: System.err.println("Layer " + this + ":");
635: System.err.println("\tSub dirty: " + x + ", " + y + ", "
636: + w + ", " + h);
637: }
638:
639: int x2 = x + w;
640: int y2 = y + h;
641: boolean res = false;
642: int dx, dy, dx2, dy2;
643: if (isEmptyDirtyRegions()) {
644: dx = 0;
645: dy = 0;
646: dx2 = bounds[W];
647: dy2 = bounds[H];
648: } else {
649: dx = dirtyBounds[X];
650: dy = dirtyBounds[Y];
651: dx2 = dx + dirtyBounds[W];
652: dy2 = dy + dirtyBounds[H];
653: }
654:
655: // Subtracted region can be outside of the dirty area
656: if (x >= dx2 || y >= dy2 || x2 <= dx || y2 <= dy) {
657: if (CGraphicsQ.DEBUG) {
658: System.err
659: .println("\tSubtracted region is outside of dirty area");
660: }
661: return false;
662: }
663:
664: boolean insideVert = (y <= dy && y2 >= dy2);
665: boolean insideHorz = (x <= dx && x2 >= dx2);
666:
667: if (insideVert) {
668: if (dx >= x && dx < x2)
669: dx = x2;
670: else if (dx2 > x && dx2 <= x2)
671: dx2 = x;
672: } else if (insideHorz) {
673: if (dy >= y && dy < y2)
674: dy = y2;
675: else if (dy2 > y && dy2 <= y2)
676: dy2 = y;
677: } else {
678: // We can subtract only such a regions that result
679: // of subtraction is a rectangular area
680: if (CGraphicsQ.DEBUG) {
681: System.err.println("\tSubtraction can't be done");
682: }
683: return false;
684:
685: }
686:
687: // Result of subtraction can be an empty dirty region
688: if (dx >= dx2 || dy >= dy2) {
689: cleanDirty();
690: res = true;
691:
692: if (CGraphicsQ.DEBUG) {
693: System.err.println("\tThe layer is no more dirty");
694: }
695:
696: } else if (dirtyBounds[W] != dx2 - dx
697: || dirtyBounds[H] != dy2 - dy) {
698: // Update changed dirty region
699: dirtyBounds[X] = dx;
700: dirtyBounds[Y] = dy;
701: dirtyBounds[W] = dx2 - dx;
702: dirtyBounds[H] = dy2 - dy;
703: res = true;
704:
705: if (CGraphicsQ.DEBUG) {
706: System.err.println("\tCurrent dirty: " + dirtyBounds[X]
707: + ", " + dirtyBounds[Y] + ", " + dirtyBounds[W]
708: + ", " + dirtyBounds[H]);
709: }
710: } else {
711: if (CGraphicsQ.DEBUG) {
712: System.err.println("\tDirty area has not been changed");
713: }
714: }
715:
716: return res;
717: }
718:
719: /**
720: * Copy bounds of the layer to use them on dirty layers painting
721: * when the layers are not locked for changes from other threads
722: */
723: void copyLayerBounds() {
724: if (dirtyBounds[X] == -1) {
725: // Whole layer is dirty
726: dirtyBoundsCopy[X] = 0;
727: dirtyBoundsCopy[Y] = 0;
728: dirtyBoundsCopy[W] = bounds[W];
729: dirtyBoundsCopy[H] = bounds[H];
730: } else {
731: System.arraycopy(dirtyBounds, 0, dirtyBoundsCopy, 0, 4);
732: }
733: System.arraycopy(bounds, 0, boundsCopy, 0, 4);
734: }
735:
736: /**
737: * Request a repaint for the entire contents of this layer.
738: */
739: public void requestRepaint() {
740: requestRepaint(0, 0, bounds[W], bounds[H]);
741: }
742:
743: /**
744: * Request a repaint for a specific region of this layer.
745: *
746: * @param x The 'x' coordinate of the upper left corner of the
747: * repaint region
748: * @param y The 'y' coordinate of the upper right corner of the
749: * repaint region
750: * @param w The width of the repaint region
751: * @param h The height of the repaint region
752: */
753: public void requestRepaint(int x, int y, int w, int h) {
754: addDirtyRegion(x, y, w, h);
755: if (owner != null && visible) {
756: // We request a repaint of our parent window after translating
757: // the origin into the coordinate space of the window.
758: owner.requestRepaint();
759: }
760: }
761:
762: /**
763: * Paint this layer. This method should not generally be overridden by
764: * subclasses. This method carefully stores the clip, translation, and
765: * color before calling into subclasses. The graphics region will be
766: * translated such that it is in this layer's coordinate space (0,0 is
767: * the top left corner of this layer). The paintBackground() method will
768: * be called first, then the paintBody() method. The graphics will then
769: * carefully be put back into its original state before returning.
770: * Subclasses should override the paintBody() method and do not generally
771: * need to do any translates or bother to undo any changes made to
772: * the Graphics object.
773: *
774: * @param g The graphics object to use to paint this layer.
775: */
776: public void paint(Graphics g) {
777: try {
778: // We first reset our dirty flag
779: this .dirty = false;
780:
781: graphicsColor = g.getColor();
782: graphicsFont = g.getFont();
783:
784: // Paint the background
785: if (!transparent) {
786: paintBackground(g);
787: }
788:
789: // Just in case subclasses modified these values,
790: // return them to standard
791: g.setColor(graphicsColor);
792: g.setFont(graphicsFont);
793:
794: // Paint the body
795: paintBody(g);
796:
797: // Just in case subclasses modified these values,
798: // return them to standard
799: g.setColor(graphicsColor);
800: g.setFont(graphicsFont);
801:
802: // We reset our dirty bounds region
803: cleanDirtyRegions();
804:
805: } catch (Throwable t) {
806: t.printStackTrace();
807: }
808: }
809:
810: /**
811: * Paint the background of this layer. This method will automatically
812: * handle a transparent layer, a layer with a background color (and/or
813: * border color), and a layer with a background image (either centered
814: * or tiled). Subclasses may override this method if they require a
815: * more advanced background (note that the transparent value should be
816: * set to false in order for the paintBackground() method to be called),
817: * but subclasses must be careful to reset any modifications made to
818: * the Graphics object before returning.
819: *
820: * @param g The Graphics object to use to paint the background.
821: */
822: protected void paintBackground(Graphics g) {
823: if (bgImage == null) {
824: // background is null, just fill using the fill color
825: g.setColor(bgColor);
826: g.fillRect(g.getClipX(), g.getClipY(), g.getClipWidth(), g
827: .getClipHeight());
828: } else {
829: if (bgImage.length == 1) {
830: CGraphicsUtil.paintBackground(g, bgImage[0], tileBG,
831: bgColor, bounds[W], bounds[H]);
832: } else if (bgImage.length == 9) {
833: CGraphicsUtil.draw9pcsBackground(g, 0, 0, bounds[W],
834: bounds[H], bgImage);
835:
836: } else if (bgImage.length == 3) {
837: CGraphicsUtil.draw3pcsBackground(g, 0, 0, bounds[W],
838: bgImage);
839: }
840: }
841: }
842:
843: /**
844: * Paint the body or content of this layer. This method should be
845: * overridden by subclasses. Note that the Graphics object will
846: * already be translated into this layer's coordinate space. Subclasses
847: * do not need to reset any changes made to the Graphics object as the
848: * CLayer paint() routine will do that automatically.
849: *
850: * @param g The Graphics object to use to paint the content of this layer
851: */
852: protected void paintBody(Graphics g) {
853: }
854:
855: public void update(CLayer[] mainLayers) {
856: if (visible) {
857: addDirtyRegion();
858: }
859: }
860:
861: /**
862: * A String identifier used by subclasses (for debug purposes)
863: * @return abbreviated class name of the layer instance
864: */
865: protected String layerID() {
866: int i;
867: String res = getClass().getName();
868: if (res != null && (i = res.lastIndexOf('.')) > -1) {
869: res = res.substring(i + 1);
870: }
871: return res + "@" + Integer.toHexString(hashCode());
872: }
873:
874: /**
875: * Called by CWindow to notify the layer that is has been
876: * added to the active stack. By default this method do nothing.
877: * This method could be re-implemented by particular layer to
878: * do some specific action as soon as it's added to the stack
879: */
880: public void addNotify() {
881: };
882:
883: /**
884: * Called by CWindow to notify the layer that is has been
885: * removed from the active stack. By default this method do nothing.
886: * This method could be re-implemented by particular layer to
887: * do some specific action as soon as it's removed from the stack
888: * @param owner an instance of CWindow this layer has been removed from
889: */
890: public void removeNotify(CWindow owner) {
891: };
892:
893: /**
894: * Called by CWindow to notify the layer that is has been
895: * moved to another location. By default this method do nothing.
896: * This method could be re-implemented by particular layer to
897: * do some specific action as soon as it's moved
898: * @param oldBounds original bounds of this layer before it has been moved
899: */
900: public void relocateNotify(int[] oldBounds) {
901: };
902:
903: /**
904: * Get the layer details including
905: * its bound and dirty region information
906: *
907: * @return String with layer details
908: */
909: public String toString() {
910: String res = layerID() + " [" + bounds[X] + ", " + bounds[Y]
911: + ", " + bounds[W] + ", " + bounds[H] + "]";
912:
913: if (isDirty()) {
914: res += ", dirty";
915: if (!isEmptyDirtyRegions()) {
916: res += " (" + dirtyBounds[X] + ", " + dirtyBounds[Y]
917: + ", " + dirtyBounds[W] + ", " + dirtyBounds[H]
918: + ")";
919: }
920: }
921: res += ", opaque: " + (opaque ? 1 : 0);
922: res += ", visible: " + (visible ? 1 : 0);
923: res += ", transparent: " + (transparent ? 1 : 0);
924: return res;
925: }
926:
927: }
|