1/*
2 * Copyright 2012 AndroidPlot.com
3 *
4 *    Licensed under the Apache License, Version 2.0 (the "License");
5 *    you may not use this file except in compliance with the License.
6 *    You may obtain a copy of the License at
7 *
8 *        http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *    Unless required by applicable law or agreed to in writing, software
11 *    distributed under the License is distributed on an "AS IS" BASIS,
12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *    See the License for the specific language governing permissions and
14 *    limitations under the License.
15 */
16
17package com.androidplot.ui;
18
19import android.graphics.*;
20import android.view.MotionEvent;
21import android.view.View;
22import com.androidplot.exception.PlotRenderException;
23import com.androidplot.ui.widget.Widget;
24import com.androidplot.util.DisplayDimensions;
25import com.androidplot.util.ZLinkedList;
26
27public class LayoutManager extends ZLinkedList<Widget>
28        implements View.OnTouchListener, Resizable {
29    private boolean drawAnchorsEnabled = false;
30    private Paint anchorPaint;
31    private boolean drawOutlinesEnabled = false;
32    private Paint outlinePaint;
33    private boolean drawOutlineShadowsEnabled = false;
34    private Paint outlineShadowPaint;
35    private boolean drawMarginsEnabled = false;
36    private Paint marginPaint;
37    private boolean drawPaddingEnabled = false;
38    private Paint paddingPaint;
39    private DisplayDimensions displayDims = new DisplayDimensions();
40
41    // cache of widget rects
42    //private HashMap<Widget, DisplayDimensions> widgetRects;
43
44    {
45        //widgetRects = new HashMap<Widget, DisplayDimensions>();
46        anchorPaint = new Paint();
47        anchorPaint.setStyle(Paint.Style.FILL);
48        anchorPaint.setColor(Color.GREEN);
49        outlinePaint = new Paint();
50        outlinePaint.setColor(Color.GREEN);
51        outlinePaint.setStyle(Paint.Style.STROKE);
52        marginPaint = new Paint();
53        marginPaint.setColor(Color.YELLOW);
54        marginPaint.setStyle(Paint.Style.FILL);
55        marginPaint.setAlpha(200);
56        paddingPaint= new Paint();
57        paddingPaint.setColor(Color.BLUE);
58        paddingPaint.setStyle(Paint.Style.FILL);
59        paddingPaint.setAlpha(200);
60    }
61
62    /**
63     * Invoked immediately following XML configuration.
64     */
65    public synchronized void onPostInit() {
66        for(Widget w : elements()) {
67            w.onPostInit();
68        }
69    }
70
71    public LayoutManager() {
72    }
73
74    public void setMarkupEnabled(boolean enabled) {
75        setDrawOutlinesEnabled(enabled);
76        setDrawAnchorsEnabled(enabled);
77        setDrawMarginsEnabled(enabled);
78        setDrawPaddingEnabled(enabled);
79        setDrawOutlineShadowsEnabled(enabled);
80    }
81
82    public void draw(Canvas canvas) throws PlotRenderException {
83        if(isDrawMarginsEnabled()) {
84            drawSpacing(canvas, displayDims.canvasRect, displayDims.marginatedRect, marginPaint);
85        }
86        if (isDrawPaddingEnabled()) {
87            drawSpacing(canvas, displayDims.marginatedRect, displayDims.paddedRect, paddingPaint);
88        }
89        for (Widget widget : elements()) {
90            //int canvasState = canvas.save(Canvas.ALL_SAVE_FLAG); // preserve clipping etc
91            try {
92                canvas.save();
93                PositionMetrics metrics = widget.getPositionMetrics();
94                float elementWidth = widget.getWidthPix(displayDims.paddedRect.width());
95                float elementHeight = widget.getHeightPix(displayDims.paddedRect.height());
96                PointF coords = widget.getElementCoordinates(elementHeight,
97                        elementWidth, displayDims.paddedRect, metrics);
98
99                //RectF widgetRect = new RectF(coords.x, coords.y, coords.x + elementWidth, coords.y + elementHeight);
100                //DisplayDimensions dims = widgetRects.get(widget);
101                DisplayDimensions dims = widget.getWidgetDimensions();
102                //RectF widgetRect = widgetRects.get(widget);
103
104                if (drawOutlineShadowsEnabled) {
105                    canvas.drawRect(dims.canvasRect, outlineShadowPaint);
106                }
107
108                // not positive why this is, but the rect clipped by clipRect is 1 less than the one drawn by drawRect.
109                // so this is necessary to avoid clipping borders.  I suspect that its a floating point
110                // jitter issue.
111                if (widget.isClippingEnabled()) {
112                    //RectF clipRect = new RectF(l-1, t-1, r + 1, b + 1);
113                    //canvas.clipRect(clipRect, Region.Op.REPLACE);
114                    canvas.clipRect(dims.canvasRect, Region.Op.INTERSECT);
115                }
116                widget.draw(canvas, dims.canvasRect);
117
118                //RectF marginatedWidgetRect = widget.getMarginatedRect(dims.canvasRect);
119                //RectF paddedWidgetRect = widget.getPaddedRect(marginatedWidgetRect);
120
121                if (drawMarginsEnabled) {
122                    drawSpacing(canvas, dims.canvasRect, dims.marginatedRect, getMarginPaint());
123                }
124
125                if (drawPaddingEnabled) {
126                    drawSpacing(canvas, dims.marginatedRect, dims.paddedRect, getPaddingPaint());
127                }
128
129                if (drawAnchorsEnabled) {
130                    PointF anchorCoords =
131                            Widget.getAnchorCoordinates(coords.x, coords.y, elementWidth,
132                                    elementHeight, metrics.getAnchor());
133                    drawAnchor(canvas, anchorCoords);
134                }
135
136
137                if (drawOutlinesEnabled) {
138                    outlinePaint.setAntiAlias(true);
139                    canvas.drawRect(dims.canvasRect, outlinePaint);
140                }
141            } finally {
142                //canvas.restoreToCount(canvasState);  // restore clipping etc.
143                canvas.restore();
144            }
145        }
146    }
147
148    private void drawSpacing(Canvas canvas, RectF outer, RectF inner, Paint paint) {
149        //int saved = canvas.save(Canvas.ALL_SAVE_FLAG);
150        try {
151            canvas.save();
152            canvas.clipOutRect(inner);
153            canvas.drawRect(outer, paint);
154            //canvas.restoreToCount(saved);
155        } finally {
156            canvas.restore();
157        }
158    }
159
160    protected void drawAnchor(Canvas canvas, PointF coords) {
161        float anchorSize = 4;
162        canvas.drawRect(coords.x-anchorSize, coords.y-anchorSize, coords.x+anchorSize, coords.y+anchorSize, anchorPaint);
163
164    }
165
166    public boolean isDrawOutlinesEnabled() {
167        return drawOutlinesEnabled;
168    }
169
170    public void setDrawOutlinesEnabled(boolean drawOutlinesEnabled) {
171        this.drawOutlinesEnabled = drawOutlinesEnabled;
172    }
173
174    public Paint getOutlinePaint() {
175        return outlinePaint;
176    }
177
178    public void setOutlinePaint(Paint outlinePaint) {
179        this.outlinePaint = outlinePaint;
180    }
181
182    public boolean isDrawAnchorsEnabled() {
183        return drawAnchorsEnabled;
184    }
185
186    public void setDrawAnchorsEnabled(boolean drawAnchorsEnabled) {
187        this.drawAnchorsEnabled = drawAnchorsEnabled;
188    }
189
190    public boolean isDrawMarginsEnabled() {
191        return drawMarginsEnabled;
192    }
193
194    public void setDrawMarginsEnabled(boolean drawMarginsEnabled) {
195        this.drawMarginsEnabled = drawMarginsEnabled;
196    }
197
198    public Paint getMarginPaint() {
199        return marginPaint;
200    }
201
202    public void setMarginPaint(Paint marginPaint) {
203        this.marginPaint = marginPaint;
204    }
205
206    public boolean isDrawPaddingEnabled() {
207        return drawPaddingEnabled;
208    }
209
210    public void setDrawPaddingEnabled(boolean drawPaddingEnabled) {
211        this.drawPaddingEnabled = drawPaddingEnabled;
212    }
213
214    public Paint getPaddingPaint() {
215        return paddingPaint;
216    }
217
218    public void setPaddingPaint(Paint paddingPaint) {
219        this.paddingPaint = paddingPaint;
220    }
221
222    public boolean isDrawOutlineShadowsEnabled() {
223        return drawOutlineShadowsEnabled;
224    }
225
226    public void setDrawOutlineShadowsEnabled(boolean drawOutlineShadowsEnabled) {
227        this.drawOutlineShadowsEnabled = drawOutlineShadowsEnabled;
228        if(drawOutlineShadowsEnabled && outlineShadowPaint == null) {
229            // use a default shadow effect in the case where none has been set:
230            outlineShadowPaint = new Paint();
231            outlineShadowPaint.setColor(Color.DKGRAY);
232            outlineShadowPaint.setStyle(Paint.Style.FILL);
233            outlineShadowPaint.setShadowLayer(3, 5, 5, Color.BLACK);
234        }
235    }
236
237    public Paint getOutlineShadowPaint() {
238        return outlineShadowPaint;
239    }
240
241    public void setOutlineShadowPaint(Paint outlineShadowPaint) {
242        this.outlineShadowPaint = outlineShadowPaint;
243    }
244
245    @Override
246    public boolean onTouch(View v, MotionEvent event) {
247        return false;
248    }
249
250    /**
251     * Recalculates layouts for all widgets using last set
252     * DisplayDimensions.  Care should be excersized when choosing when
253     * to call this method as it is a relatively slow operation.
254     */
255    public void refreshLayout() {
256        //widgetRects.clear();
257        for (Widget widget : elements()) {
258            widget.layout(displayDims);
259        }
260    }
261
262    @Override
263    public void layout(final DisplayDimensions dims) {
264        this.displayDims = dims;
265
266        refreshLayout();
267    }
268}
269