PieMenu.java revision 4be9bc7f7f38723ae8c4ca1d3203de212cf214bd
1376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb/*
2376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Copyright (C) 2010 The Android Open Source Project
3376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb *
4376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License");
5376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * you may not use this file except in compliance with the License.
6376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * You may obtain a copy of the License at
7376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb *
8376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb *      http://www.apache.org/licenses/LICENSE-2.0
9376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb *
10376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Unless required by applicable law or agreed to in writing, software
11376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS,
12376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * See the License for the specific language governing permissions and
14376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * limitations under the License.
15376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */
16376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
17376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbpackage com.android.browser.view;
18376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
19376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport com.android.browser.R;
20376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
21376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.content.Context;
22376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.content.res.Resources;
23376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Canvas;
24376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Paint;
25376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Path;
26376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Point;
27376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.PointF;
28376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Rect;
29376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.RectF;
30376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.util.AttributeSet;
31376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.view.MotionEvent;
32376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.view.View;
334be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reckimport android.view.ViewGroup;
34376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.widget.FrameLayout;
35376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
36376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.ArrayList;
37376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.HashMap;
38376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.List;
39376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.Map;
40376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
41376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbpublic class PieMenu extends FrameLayout {
42376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
43376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private static final int RADIUS_GAP = 10;
44376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
45376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public interface PieController {
46376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        /**
47376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb         * called before menu opens to customize menu
48376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb         * returns if pie state has been changed
49376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb         */
50376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        public boolean onOpen();
51376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
52376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private Point mCenter;
53376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private int mRadius;
54376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private int mRadiusInc;
55376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private int mSlop;
56376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
57376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private boolean mOpen;
58376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private Paint mPaint;
59376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private Paint mSelectedPaint;
60376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private PieController mController;
61376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
62376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private Map<View, List<View>> mMenu;
63376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private List<View> mStack;
64376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
65376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private boolean mDirty;
66376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
67376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    /**
68376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param context
69376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param attrs
70376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param defStyle
71376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     */
72376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public PieMenu(Context context, AttributeSet attrs, int defStyle) {
73376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        super(context, attrs, defStyle);
74376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        init(context);
75376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
76376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
77376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    /**
78376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param context
79376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param attrs
80376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     */
81376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public PieMenu(Context context, AttributeSet attrs) {
82376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        super(context, attrs);
83376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        init(context);
84376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
85376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
86376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    /**
87376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param context
88376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     */
89376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public PieMenu(Context context) {
90376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        super(context);
91376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        init(context);
92376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
93376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
94376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private void init(Context ctx) {
95376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        this.setTag(new MenuTag(0));
96376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mStack = new ArrayList<View>();
97376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mStack.add(this);
98376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        Resources res = ctx.getResources();
99376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mRadius = (int) res.getDimension(R.dimen.qc_radius);
100376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mRadiusInc = (int) res.getDimension(R.dimen.qc_radius_inc);
101376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mSlop = (int) res.getDimension(R.dimen.qc_slop);
102376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mPaint = new Paint();
103376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mPaint.setAntiAlias(true);
104376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mPaint.setColor(res.getColor(R.color.qc_slice_normal));
105376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mSelectedPaint = new Paint();
106376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mSelectedPaint.setAntiAlias(true);
107376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mSelectedPaint.setColor(res.getColor(R.color.qc_slice_active));
108376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mOpen = false;
109376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mMenu = new HashMap<View, List<View>>();
110376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        setWillNotDraw(false);
111376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        setDrawingCacheEnabled(false);
112376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mCenter = new Point(0,0);
113376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mDirty = true;
114376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
115376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
116376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public void setController(PieController ctl) {
117376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mController = ctl;
118376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
119376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
120376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public void setRadius(int r) {
121376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mRadius = r;
122376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        requestLayout();
123376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
124376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
125376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public void setRadiusIncrement(int ri) {
126376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mRadiusInc = ri;
127376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        requestLayout();
128376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
129376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
130376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    /**
131376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * add a menu item to another item as a submenu
132376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param item
133376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param parent
134376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     */
135376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public void addItem(View item, View parent) {
136376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        List<View> subs = mMenu.get(parent);
137376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (subs == null) {
138376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            subs = new ArrayList<View>();
139376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            mMenu.put(parent, subs);
140376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
141376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        subs.add(item);
142376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        MenuTag tag = new MenuTag(((MenuTag) parent.getTag()).level + 1);
143376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        item.setTag(tag);
144376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
145376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
146376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public void addItem(View view) {
147376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        // add the item to the pie itself
148376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        addItem(view, this);
149376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
150376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
151376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public void removeItem(View view) {
152376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        List<View> subs = mMenu.get(view);
153376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mMenu.remove(view);
154376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        for (View p : mMenu.keySet()) {
155376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            List<View> sl = mMenu.get(p);
156376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (sl != null) {
157376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                sl.remove(view);
158376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
159376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
160376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
161376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
162376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public void clearItems(View parent) {
163376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        List<View> subs = mMenu.remove(parent);
164376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (subs != null) {
165376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            for (View sub: subs) {
166376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                clearItems(sub);
167376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
168376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
169376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
170376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
171376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public void clearItems() {
172376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mMenu.clear();
173376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
174376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
175376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
176376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public void show(boolean show) {
177376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mOpen = show;
178376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (mOpen) {
179376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (mController != null) {
180376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                boolean changed = mController.onOpen();
181376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
182376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            mDirty = true;
183376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
184376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (!show) {
185376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            // hide sub items
186376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            mStack.clear();
187376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            mStack.add(this);
188376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
189376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        invalidate();
190376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
191376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
192376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private void setCenter(int x, int y) {
193376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (x < mSlop) {
194376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            mCenter.x = 0;
195376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        } else {
196376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            mCenter.x = getWidth();
197376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
198376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mCenter.y = y;
199376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
200376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
201376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private boolean onTheLeft() {
202376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        return mCenter.x < mSlop;
203376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
204376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
205376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    @Override
206376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    protected void onDraw(Canvas canvas) {
207376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (mOpen) {
208376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            int radius = mRadius;
209376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            // start in the center for 0 level menu
210376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            float anchor = (float) Math.PI / 2;
211376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            PointF angles = new PointF();
212376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            int state = canvas.save();
213376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (onTheLeft()) {
214376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                // left handed
215376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                canvas.scale(-1, 1);
216376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
217376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            for (View parent : mStack) {
218376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                List<View> subs = mMenu.get(parent);
219376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                if (subs != null) {
220376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    setGeometry(anchor, subs.size(), angles);
221376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                }
222376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                anchor = drawSlices(canvas, subs, radius, angles.x, angles.y);
223376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                radius += mRadiusInc + RADIUS_GAP;
224376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
225376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            canvas.restoreToCount(state);
226376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            mDirty = false;
227376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
228376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
229376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
230376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    /**
231376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * draw the set of slices
232376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param canvas
233376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param items
234376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param radius
235376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param start
236376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param sweep
237376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @return the angle of the selected slice
238376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     */
239376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private float drawSlices(Canvas canvas, List<View> items, int radius,
240376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            float start, float sweep) {
241376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        float angle = start + sweep / 2;
242376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        // gap between slices in degrees
243376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        float gap = 1f;
244376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        float newanchor = 0f;
245376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        for (View item : items) {
246376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (mDirty) {
247376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                item.measure(item.getLayoutParams().width,
248376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                        item.getLayoutParams().height);
249376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                int w = item.getMeasuredWidth();
250376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                int h = item.getMeasuredHeight();
251376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                int x = (int) (radius * Math.sin(angle));
252376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                int y =  mCenter.y - (int) (radius * Math.cos(angle)) - h / 2;
253376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                if (onTheLeft()) {
254376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    x = mCenter.x + x - w / 2;
255376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                } else {
256376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    x = mCenter.x - x - w / 2;
257376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                }
258376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                item.layout(x, y, x + w, y + h);
259376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
260376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            float itemstart = angle - sweep / 2;
261376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            int inner = radius - mRadiusInc / 2;
262376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            int outer = radius + mRadiusInc / 2;
263376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            Path slice = makeSlice(getDegrees(itemstart) - gap,
264376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    getDegrees(itemstart + sweep) + gap,
265376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    outer, inner, mCenter);
266376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            MenuTag tag = (MenuTag) item.getTag();
267376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            tag.start = itemstart;
268376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            tag.sweep = sweep;
269376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            tag.inner = inner;
270376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            tag.outer = outer;
271376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
272376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            Paint p = item.isPressed() ? mSelectedPaint : mPaint;
273376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            canvas.drawPath(slice, p);
274376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            int state = canvas.save();
275376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (onTheLeft()) {
276376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                canvas.scale(-1, 1);
277376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
278376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            canvas.translate(item.getX(), item.getY());
279376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            item.draw(canvas);
280376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            canvas.restoreToCount(state);
281376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (mStack.contains(item)) {
282376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                // item is anchor for sub menu
283376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                newanchor = angle;
284376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
285376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            angle += sweep;
286376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
287376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        return newanchor;
288376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
289376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
290376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    /**
291376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * converts a
292376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param angle from 0..PI to Android degrees (clockwise starting at 3 o'clock)
293376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @return skia angle
294376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     */
295376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private float getDegrees(double angle) {
296376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        return (float) (270 - 180 * angle / Math.PI);
297376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
298376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
299376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private Path makeSlice(float startangle, float endangle, int outerradius,
300376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            int innerradius, Point center) {
301376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        RectF bb = new RectF(center.x - outerradius, center.y - outerradius,
302376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                center.x + outerradius, center.y + outerradius);
303376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        RectF bbi = new RectF(center.x - innerradius, center.y - innerradius,
304376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                center.x + innerradius, center.y + innerradius);
305376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        Path path = new Path();
306376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        path.arcTo(bb, startangle, endangle - startangle, true);
307376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        path.arcTo(bbi, endangle, startangle - endangle);
308376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        path.close();
309376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        return path;
310376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
311376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
312376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    /**
313376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * all angles are 0 .. MATH.PI where 0 points up, and rotate counterclockwise
314376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * set the startangle and slice sweep in result
315376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param anchorangle : angle at which the menu is anchored
316376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param nslices
317376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param result : x : start, y : sweep
318376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     */
319376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private void setGeometry(float anchorangle, int nslices, PointF result) {
320376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        float span = (float) Math.min(anchorangle, Math.PI - anchorangle);
321376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        float sweep = 2 * span / (nslices + 1);
322376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        result.x = anchorangle - span + sweep / 2;
323376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        result.y = sweep;
324376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
325376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
326376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    // touch handling for pie
327376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
328376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    View mCurrentView;
329376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    Rect mHitRect = new Rect();
330376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
331376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    @Override
332376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    public boolean onTouchEvent(MotionEvent evt) {
333376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        float x = evt.getX();
334376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        float y = evt.getY();
335376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        int action = evt.getActionMasked();
336376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        int edges = evt.getEdgeFlags();
337376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (MotionEvent.ACTION_DOWN == action) {
338376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if ((x > getWidth() - mSlop) || (x < mSlop)) {
339376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                setCenter((int) x, (int) y);
340376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                show(true);
341376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                return true;
342376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
343376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        } else if (MotionEvent.ACTION_UP == action) {
344376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (mOpen) {
345376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                View v = mCurrentView;
346376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                deselect();
347376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                if (v != null) {
348376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    v.performClick();
349376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                }
350376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                show(false);
351376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                return true;
352376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
353376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        } else if (MotionEvent.ACTION_CANCEL == action) {
354376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (mOpen) {
355376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                show(false);
356376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
357376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            deselect();
358376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            return false;
359376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        } else if (MotionEvent.ACTION_MOVE == action) {
360bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb            PointF polar = getPolar(x, y);
361bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb            if (polar.y > mRadius + 2 * mRadiusInc) {
362bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb                show(false);
363bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb                deselect();
3644be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck                evt.setAction(MotionEvent.ACTION_DOWN);
3654be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck                if (getParent() != null) {
3664be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck                    ((ViewGroup) getParent()).dispatchTouchEvent(evt);
3674be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck                }
368bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb                return false;
369bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb            }
370bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb            View v = findView(polar);
371376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (mCurrentView != v) {
372376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                onEnter(v);
373376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                invalidate();
374376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
375376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
376376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        // always re-dispatch event
377376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        return false;
378376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
379376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
380376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    /**
381376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * enter a slice for a view
382376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * updates model only
383376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     * @param view
384376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb     */
385376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private void onEnter(View view) {
386376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        // deselect
387376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (mCurrentView != null) {
388376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (getLevel(mCurrentView) >= getLevel(view)) {
389376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                mCurrentView.setPressed(false);
390376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
391376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
392376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (view != null) {
393376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            // clear up stack
394376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            MenuTag tag = (MenuTag) view.getTag();
395376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            int i = mStack.size() - 1;
396376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            while (i > 0) {
397376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                View v = mStack.get(i);
398376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                if (((MenuTag) v.getTag()).level >= tag.level) {
399376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    v.setPressed(false);
400376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    mStack.remove(i);
401376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                } else {
402376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    break;
403376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                }
404376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                i--;
405376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
406376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            List<View> items = mMenu.get(view);
407376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (items != null) {
408376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                mStack.add(view);
409376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                mDirty = true;
410376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
411376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            view.setPressed(true);
412376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
413376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mCurrentView = view;
414376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
415376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
416376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private void deselect() {
417376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (mCurrentView != null) {
418376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            mCurrentView.setPressed(false);
419376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
420376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        mCurrentView = null;
421376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
422376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
423376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    private int getLevel(View v) {
424376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (v == null) return -1;
425376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        return ((MenuTag) v.getTag()).level;
426376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
427376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
428bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb    private PointF getPolar(float x, float y) {
429bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb        PointF res = new PointF();
430376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        // get angle and radius from x/y
431bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb        res.x = (float) Math.PI / 2;
432376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        x = mCenter.x - x;
433376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (mCenter.x < mSlop) {
434376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            x = -x;
435376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
436376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        y = mCenter.y - y;
437bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb        res.y = (float) Math.sqrt(x * x + y * y);
438376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        if (y > 0) {
439bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb            res.x = (float) Math.asin(x / res.y);
440376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        } else if (y < 0) {
441bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb            res.x = (float) (Math.PI - Math.asin(x / res.y ));
442376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
443bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb        return res;
444bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb    }
445bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb
446bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb    /**
447bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb     *
448bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb     * @param polar x: angle, y: dist
449bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb     * @return
450bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb     */
451bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb    private View findView(PointF polar) {
452376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        // find the matching item:
453376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        for (View parent : mStack) {
454376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            List<View> subs = mMenu.get(parent);
455376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            if (subs != null) {
456376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                for (View item : subs) {
457376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    MenuTag tag = (MenuTag) item.getTag();
458bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb                    if ((tag.inner < polar.y)
459bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb                            && (tag.outer > polar.y)
460bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb                            && (tag.start < polar.x)
461bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb                            && (tag.start + tag.sweep > polar.x)) {
462376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                        return item;
463376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                    }
464376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb                }
465376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            }
466376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
467376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        return null;
468376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
469376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
470376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    class MenuTag {
471376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
472376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        int level;
473376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        float start;
474376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        float sweep;
475376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        int inner;
476376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        int outer;
477376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
478376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        public MenuTag(int l) {
479376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb            level = l;
480376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb        }
481376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
482376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb    }
483376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb
484376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb}
485