OSDN Git Service

Refactoring UI editors
authornicolasroard <nicolasroard@google.com>
Tue, 8 Jan 2013 03:43:14 +0000 (19:43 -0800)
committernicolasroard <nicolasroard@google.com>
Tue, 8 Jan 2013 19:14:48 +0000 (11:14 -0800)
bug:7688780
Change-Id: I30f8691788332a01d591435474a84cc352b71138

res/layout/filtershow_activity.xml
res/layout/filtershow_crop_button.xml [new file with mode: 0644]
res/layout/filtershow_curves_button.xml [new file with mode: 0644]
src/com/android/gallery3d/filtershow/FilterShowActivity.java
src/com/android/gallery3d/filtershow/PanelController.java
src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
src/com/android/gallery3d/filtershow/ui/FramedTextButton.java
src/com/android/gallery3d/filtershow/ui/ImageCurves.java

index c26d858..ed562f9 100644 (file)
                 <FrameLayout
                     android:layout_width="fill_parent"
                     android:layout_height="fill_parent" >
-                    <com.android.gallery3d.filtershow.ui.FramedTextButton
-                        android:id="@+id/aspect"
-                        android:layout_width="84dip"
-                        android:layout_height="84dip"
-                        android:layout_gravity="center_vertical|left"
-                        android:background="@drawable/filtershow_button_background"
-                        android:scaleType="centerInside"
-                        android:visibility="gone"
-                        android:text="@string/aspectNone_effect" />
-
-                    <com.android.gallery3d.filtershow.ui.FramedTextButton
-                        android:id="@+id/pickCurvesChannel"
-                        android:layout_width="84dip"
-                        android:layout_height="84dip"
-                        android:layout_gravity="center_vertical|left"
-                        android:background="@drawable/filtershow_button_background"
-                        android:scaleType="centerInside"
-                        android:visibility="gone"
-                        android:text="@string/curves_channel_rgb" />
+
+                    <LinearLayout
+                        android:id="@+id/panelAccessoryViewList"
+                        android:layout_width="wrap_content"
+                        android:layout_height="fill_parent"
+                        android:orientation="horizontal"
+                        android:visibility="visible" />
 
                     <Button
                         android:id="@+id/applyEffect"
diff --git a/res/layout/filtershow_crop_button.xml b/res/layout/filtershow_crop_button.xml
new file mode 100644 (file)
index 0000000..b42d6b6
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.gallery3d.filtershow.ui.FramedTextButton
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/cropUtilityButton"
+    android:layout_width="84dip"
+    android:layout_height="84dip"
+    android:layout_gravity="center_vertical|left"
+    android:background="@drawable/filtershow_button_background"
+    android:scaleType="centerInside"
+    android:visibility="gone"
+    android:text="@string/aspectNone_effect" />
\ No newline at end of file
diff --git a/res/layout/filtershow_curves_button.xml b/res/layout/filtershow_curves_button.xml
new file mode 100644 (file)
index 0000000..31e8aed
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.gallery3d.filtershow.ui.FramedTextButton
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/curvesUtilityButton"
+    android:layout_width="84dip"
+    android:layout_height="84dip"
+    android:layout_gravity="center_vertical|left"
+    android:background="@drawable/filtershow_button_background"
+    android:scaleType="centerInside"
+    android:visibility="gone"
+    android:text="@string/curves_channel_rgb" />
index 475c4f1..bc90a31 100644 (file)
@@ -344,8 +344,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         mPanelController.addFilter(new ImageFilterRedEye());
 
         mPanelController.addView(findViewById(R.id.applyEffect));
-        mPanelController.addView(findViewById(R.id.pickCurvesChannel));
-        mPanelController.addView(findViewById(R.id.aspect));
         findViewById(R.id.resetOperationsButton).setOnClickListener(
                 createOnClickResetOperationsButton());
 
@@ -367,8 +365,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         mImageTinyPlanet.setSeekBar(seekBar);
         mPanelController.setRowPanel(findViewById(R.id.secondRowPanel));
         mPanelController.setUtilityPanel(this, findViewById(R.id.filterButtonsList),
-                findViewById(R.id.applyEffect), findViewById(R.id.aspect),
-                findViewById(R.id.pickCurvesChannel));
+                findViewById(R.id.panelAccessoryViewList),
+                findViewById(R.id.applyEffect));
         mPanelController.setMasterImage(mImageShow);
         mPanelController.setCurrentPanel(mFxButton);
         Intent intent = getIntent();
index 94259e9..c7b294e 100644 (file)
@@ -18,35 +18,19 @@ package com.android.gallery3d.filtershow;
 
 import android.content.Context;
 import android.text.Html;
-import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewPropertyAnimator;
-import android.widget.PopupMenu;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.gallery3d.R;
 import com.android.gallery3d.filtershow.filters.ImageFilter;
-import com.android.gallery3d.filtershow.filters.ImageFilterBwFilter;
-import com.android.gallery3d.filtershow.filters.ImageFilterContrast;
-import com.android.gallery3d.filtershow.filters.ImageFilterCurves;
-import com.android.gallery3d.filtershow.filters.ImageFilterExposure;
-import com.android.gallery3d.filtershow.filters.ImageFilterHue;
-import com.android.gallery3d.filtershow.filters.ImageFilterNegative;
-import com.android.gallery3d.filtershow.filters.ImageFilterRedEye;
-import com.android.gallery3d.filtershow.filters.ImageFilterSaturated;
-import com.android.gallery3d.filtershow.filters.ImageFilterShadows;
-import com.android.gallery3d.filtershow.filters.ImageFilterSharpen;
 import com.android.gallery3d.filtershow.filters.ImageFilterTinyPlanet;
-import com.android.gallery3d.filtershow.filters.ImageFilterVibrance;
-import com.android.gallery3d.filtershow.filters.ImageFilterVignette;
-import com.android.gallery3d.filtershow.filters.ImageFilterWBalance;
 import com.android.gallery3d.filtershow.imageshow.ImageCrop;
 import com.android.gallery3d.filtershow.imageshow.ImageShow;
 import com.android.gallery3d.filtershow.imageshow.ImageSmallFilter;
 import com.android.gallery3d.filtershow.presets.ImagePreset;
-import com.android.gallery3d.filtershow.ui.FramedTextButton;
-import com.android.gallery3d.filtershow.ui.ImageCurves;
 
 import java.util.HashMap;
 import java.util.Vector;
@@ -139,102 +123,33 @@ public class PanelController implements OnClickListener {
     class UtilityPanel {
         private final Context mContext;
         private final View mView;
+        private final LinearLayout mAccessoryViewList;
+        private Vector<View> mAccessoryViews = new Vector<View>();
         private final TextView mTextView;
         private boolean mSelected = false;
         private String mEffectName = null;
         private int mParameterValue = 0;
         private boolean mShowParameterValue = false;
-        private View mAspectButton = null;
-        private View mCurvesButton = null;
         boolean firstTimeCropDisplayed = true;
 
-        public UtilityPanel(Context context, View view, View textView,
-                View aspectButton, View curvesButton) {
+        public UtilityPanel(Context context, View view, View accessoryViewList,
+                View textView) {
             mContext = context;
             mView = view;
+            mAccessoryViewList = (LinearLayout) accessoryViewList;
             mTextView = (TextView) textView;
-            mAspectButton = aspectButton;
-            mCurvesButton = curvesButton;
         }
 
         public boolean selected() {
             return mSelected;
         }
 
-        public void setAspectButton(FramedTextButton button, int itemId) {
-            ImageCrop imageCrop = (ImageCrop) mCurrentImage;
-            switch (itemId) {
-                case R.id.crop_menu_1to1: {
-                    String t = mContext.getString(R.string.aspect1to1_effect);
-                    button.setText(t);
-                    imageCrop.apply(1, 1);
-                    imageCrop.setAspectString(t);
-                    break;
-                }
-                case R.id.crop_menu_4to3: {
-                    String t = mContext.getString(R.string.aspect4to3_effect);
-                    button.setText(t);
-                    imageCrop.apply(4, 3);
-                    imageCrop.setAspectString(t);
-                    break;
-                }
-                case R.id.crop_menu_3to4: {
-                    String t = mContext.getString(R.string.aspect3to4_effect);
-                    button.setText(t);
-                    imageCrop.apply(3, 4);
-                    imageCrop.setAspectString(t);
-                    break;
-                }
-                case R.id.crop_menu_5to7: {
-                    String t = mContext.getString(R.string.aspect5to7_effect);
-                    button.setText(t);
-                    imageCrop.apply(5, 7);
-                    imageCrop.setAspectString(t);
-                    break;
-                }
-                case R.id.crop_menu_7to5: {
-                    String t = mContext.getString(R.string.aspect7to5_effect);
-                    button.setText(t);
-                    imageCrop.apply(7, 5);
-                    imageCrop.setAspectString(t);
-                    break;
-                }
-                case R.id.crop_menu_none: {
-                    String t = mContext.getString(R.string.aspectNone_effect);
-                    button.setText(t);
-                    imageCrop.applyClear();
-                    imageCrop.setAspectString(t);
-                    break;
-                }
-                case R.id.crop_menu_original: {
-                    String t = mContext.getString(R.string.aspectOriginal_effect);
-                    button.setText(t);
-                    imageCrop.applyOriginal();
-                    imageCrop.setAspectString(t);
-                    break;
-                }
+        public void hideAccessoryViews() {
+            int childCount = mAccessoryViewList.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                View child = mAccessoryViewList.getChildAt(i);
+                child.setVisibility(View.GONE);
             }
-            imageCrop.invalidate();
-        }
-
-        public void showAspectButtons() {
-            if (mAspectButton != null)
-                mAspectButton.setVisibility(View.VISIBLE);
-        }
-
-        public void hideAspectButtons() {
-            if (mAspectButton != null)
-                mAspectButton.setVisibility(View.GONE);
-        }
-
-        public void showCurvesButtons() {
-            if (mCurvesButton != null)
-                mCurvesButton.setVisibility(View.VISIBLE);
-        }
-
-        public void hideCurvesButtons() {
-            if (mCurvesButton != null)
-                mCurvesButton.setVisibility(View.GONE);
         }
 
         public void onNewValue(int value) {
@@ -290,6 +205,7 @@ public class PanelController implements OnClickListener {
             mSelected = true;
             return anim;
         }
+
     }
 
     class ViewType {
@@ -397,10 +313,10 @@ public class PanelController implements OnClickListener {
         mRowPanel = rowPanel;
     }
 
-    public void setUtilityPanel(Context context, View utilityPanel, View textView,
-            View aspectButton, View curvesButton) {
-        mUtilityPanel = new UtilityPanel(context, utilityPanel, textView,
-                aspectButton, curvesButton);
+    public void setUtilityPanel(Context context, View utilityPanel,
+            View accessoryViewList, View textView) {
+        mUtilityPanel = new UtilityPanel(context, utilityPanel,
+                accessoryViewList, textView);
     }
 
     public void setMasterImage(ImageShow imageShow) {
@@ -516,33 +432,6 @@ public class PanelController implements OnClickListener {
         }
     }
 
-    private void showCurvesPopupMenu(final ImageCurves curves, final FramedTextButton anchor) {
-        PopupMenu popupMenu = new PopupMenu(mCurrentImage.getContext(), anchor);
-        popupMenu.getMenuInflater().inflate(R.menu.filtershow_menu_curves, popupMenu.getMenu());
-        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
-            @Override
-            public boolean onMenuItemClick(MenuItem item) {
-                curves.setChannel(item.getItemId());
-                anchor.setTextFrom(item.getItemId());
-                return true;
-            }
-        });
-        popupMenu.show();
-    }
-
-    private void showCropPopupMenu(final FramedTextButton anchor) {
-        PopupMenu popupMenu = new PopupMenu(mCurrentImage.getContext(), anchor);
-        popupMenu.getMenuInflater().inflate(R.menu.filtershow_menu_crop, popupMenu.getMenu());
-        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
-            @Override
-            public boolean onMenuItemClick(MenuItem item) {
-                mUtilityPanel.setAspectButton(anchor, item.getItemId());
-                return true;
-            }
-        });
-        popupMenu.show();
-    }
-
     public void showComponent(View view) {
         if (mUtilityPanel != null && !mUtilityPanel.selected()) {
             Panel current = mPanels.get(mCurrentPanel);
@@ -554,22 +443,10 @@ public class PanelController implements OnClickListener {
             }
         }
 
-        if (view.getId() == R.id.pickCurvesChannel) {
-            ImageCurves curves = (ImageCurves) showImageView(R.id.imageCurves);
-            showCurvesPopupMenu(curves, (FramedTextButton) view);
-            return;
-        }
-
-        if (view.getId() == R.id.aspect) {
-            showCropPopupMenu((FramedTextButton) view);
-            return;
-        }
-
         if (mCurrentImage != null) {
             mCurrentImage.unselect();
         }
-        mUtilityPanel.hideAspectButtons();
-        mUtilityPanel.hideCurvesButtons();
+        mUtilityPanel.hideAccessoryViews();
 
         if (view instanceof ImageSmallFilter) {
             ImageSmallFilter component = (ImageSmallFilter) view;
@@ -579,10 +456,8 @@ public class PanelController implements OnClickListener {
                 mCurrentImage.setShowControls(filter.showEditingControls());
                 String ename = mCurrentImage.getContext().getString(filter.getTextId());
                 mUtilityPanel.setEffectName(ename);
-                if (view.getId() == R.id.curvesButtonRGB) {
-                    // TODO: delegate to the filter / editing view the management of the
-                    // panel accessory view
-                    mUtilityPanel.showCurvesButtons();
+                if (mCurrentImage.useUtilityPanel()) {
+                    mCurrentImage.openUtilityPanel(mUtilityPanel.mAccessoryViewList);
                 }
                 mUtilityPanel.setShowParameter(filter.showParameterValue());
                 ensureFilter(ename);
@@ -618,9 +493,7 @@ public class PanelController implements OnClickListener {
                     ((ImageCrop) mCurrentImage).clear();
                     mUtilityPanel.firstTimeCropDisplayed = false;
                 }
-                if (!mFixedAspect) {
-                    mUtilityPanel.showAspectButtons();
-                }
+                ((ImageCrop) mCurrentImage).setFixedAspect(mFixedAspect);
                 break;
             }
             case R.id.rotateButton: {
@@ -643,10 +516,6 @@ public class PanelController implements OnClickListener {
                 ensureFilter(ename);
                 break;
             }
-            case R.id.aspect: {
-                mUtilityPanel.showAspectButtons();
-                break;
-            }
             case R.id.applyEffect: {
                 if (mMasterImage.getCurrentFilter() instanceof ImageFilterTinyPlanet) {
                     mActivity.saveImage();
@@ -659,6 +528,9 @@ public class PanelController implements OnClickListener {
                 break;
             }
         }
+        if (mCurrentImage.useUtilityPanel()) {
+            mCurrentImage.openUtilityPanel(mUtilityPanel.mAccessoryViewList);
+        }
         mCurrentImage.select();
     }
 }
index cd1ad51..e2f7db0 100644 (file)
@@ -27,9 +27,17 @@ import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.PopupMenu;
 
 import com.android.gallery3d.R;
 import com.android.gallery3d.filtershow.CropExtras;
+import com.android.gallery3d.filtershow.ui.FramedTextButton;
 
 public class ImageCrop extends ImageGeometry {
     private static final boolean LOGV = false;
@@ -75,6 +83,8 @@ public class ImageCrop extends ImageGeometry {
     private String mAspect = "";
     private int mAspectTextSize = 24;
 
+    private boolean mFixedAspect = false;
+
     public void setAspectTextSize(int textSize) {
         mAspectTextSize = textSize;
     }
@@ -665,4 +675,108 @@ public class ImageCrop extends ImageGeometry {
         }
     }
 
+    private void setAspectButton(Button button, int itemId) {
+        switch (itemId) {
+            case R.id.crop_menu_1to1: {
+                String t = getActivity().getString(R.string.aspect1to1_effect);
+                button.setText(t);
+                apply(1, 1);
+                setAspectString(t);
+                break;
+            }
+            case R.id.crop_menu_4to3: {
+                String t = getActivity().getString(R.string.aspect4to3_effect);
+                button.setText(t);
+                apply(4, 3);
+                setAspectString(t);
+                break;
+            }
+            case R.id.crop_menu_3to4: {
+                String t = getActivity().getString(R.string.aspect3to4_effect);
+                button.setText(t);
+                apply(3, 4);
+                setAspectString(t);
+                break;
+            }
+            case R.id.crop_menu_5to7: {
+                String t = getActivity().getString(R.string.aspect5to7_effect);
+                button.setText(t);
+                apply(5, 7);
+                setAspectString(t);
+                break;
+            }
+            case R.id.crop_menu_7to5: {
+                String t = getActivity().getString(R.string.aspect7to5_effect);
+                button.setText(t);
+                apply(7, 5);
+                setAspectString(t);
+                break;
+            }
+            case R.id.crop_menu_none: {
+                String t = getActivity().getString(R.string.aspectNone_effect);
+                button.setText(t);
+                applyClear();
+                setAspectString(t);
+                break;
+            }
+            case R.id.crop_menu_original: {
+                String t = getActivity().getString(R.string.aspectOriginal_effect);
+                button.setText(t);
+                applyOriginal();
+                setAspectString(t);
+                break;
+            }
+        }
+        invalidate();
+    }
+
+    public void setFixedAspect(boolean fixedAspect) {
+        mFixedAspect = fixedAspect;
+    }
+
+    @Override
+    public boolean useUtilityPanel() {
+        // Only shows the aspect ratio popup if we are not fixed
+        return !mFixedAspect;
+    }
+
+    private void showPopupMenu(LinearLayout accessoryViewList) {
+        final Button button = (Button) accessoryViewList.findViewById(
+                R.id.cropUtilityButton);
+        if (button == null) {
+            return;
+        }
+        PopupMenu popupMenu = new PopupMenu(getActivity(), button);
+        popupMenu.getMenuInflater().inflate(R.menu.filtershow_menu_crop, popupMenu.getMenu());
+        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+            @Override
+            public boolean onMenuItemClick(MenuItem item) {
+                setAspectButton(button, item.getItemId());
+                return true;
+            }
+        });
+        popupMenu.show();
+    }
+
+    @Override
+    public void openUtilityPanel(final LinearLayout accessoryViewList) {
+        View view = accessoryViewList.findViewById(R.id.cropUtilityButton);
+        if (view == null) {
+            LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService
+                    (Context.LAYOUT_INFLATER_SERVICE);
+            view = inflater.inflate(R.layout.filtershow_crop_button, accessoryViewList, false);
+            accessoryViewList.addView(view, view.getLayoutParams());
+            view.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View arg0) {
+                    showPopupMenu(accessoryViewList);
+                }
+            });
+        }
+
+        if (view != null) {
+            view.setVisibility(View.VISIBLE);
+        }
+    }
+
 }
index 4c74b16..69214bd 100644 (file)
@@ -32,6 +32,7 @@ import android.view.GestureDetector.OnGestureListener;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 
@@ -122,6 +123,10 @@ public class ImageShow extends View implements OnGestureListener,
         mBackgroundColor = value;
     }
 
+    public FilterShowActivity getActivity() {
+        return mActivity;
+    }
+
     public int getDefaultBackgroundColor() {
         return mBackgroundColor;
     }
@@ -823,4 +828,12 @@ public class ImageShow extends View implements OnGestureListener,
         return false;
     }
 
+    public boolean useUtilityPanel() {
+        return false;
+    }
+
+    public void openUtilityPanel(final LinearLayout accessoryViewList) {
+        // TODO Auto-generated method stub
+    }
+
 }
index c717b6e..c1e4109 100644 (file)
@@ -37,8 +37,6 @@ public class FramedTextButton extends ImageButton {
     private static int mTrianglePadding = 2;
     private static int mTriangleSize = 30;
 
-    private Context mContext = null;
-
     public static void setTextSize(int value) {
         mTextSize = value;
     }
@@ -63,28 +61,34 @@ public class FramedTextButton extends ImageButton {
     public void setTextFrom(int itemId) {
         switch (itemId) {
             case R.id.curve_menu_rgb: {
-                setText(mContext.getString(R.string.curves_channel_rgb));
+                setText(getContext().getString(R.string.curves_channel_rgb));
                 break;
             }
             case R.id.curve_menu_red: {
-                setText(mContext.getString(R.string.curves_channel_red));
+                setText(getContext().getString(R.string.curves_channel_red));
                 break;
             }
             case R.id.curve_menu_green: {
-                setText(mContext.getString(R.string.curves_channel_green));
+                setText(getContext().getString(R.string.curves_channel_green));
                 break;
             }
             case R.id.curve_menu_blue: {
-                setText(mContext.getString(R.string.curves_channel_blue));
+                setText(getContext().getString(R.string.curves_channel_blue));
                 break;
             }
         }
         invalidate();
     }
 
+    public FramedTextButton(Context context) {
+        this(context, null);
+    }
+
     public FramedTextButton(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mContext = context;
+        if (attrs == null) {
+            return;
+        }
         TypedArray a = getContext().obtainStyledAttributes(
                 attrs, R.styleable.ImageButtonTitle);
 
index 7b04133..5fbfc90 100644 (file)
@@ -26,7 +26,13 @@ import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.os.AsyncTask;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
 import android.view.MotionEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.PopupMenu;
 
 import com.android.gallery3d.R;
 import com.android.gallery3d.filtershow.filters.ImageFilterCurves;
@@ -62,6 +68,51 @@ public class ImageCurves extends ImageSlave {
         resetCurve();
     }
 
+    @Override
+    public boolean useUtilityPanel() {
+        return true;
+    }
+
+    private void showPopupMenu(LinearLayout accessoryViewList) {
+        final FramedTextButton button = (FramedTextButton) accessoryViewList.findViewById(
+                R.id.curvesUtilityButton);
+        if (button == null) {
+            return;
+        }
+        PopupMenu popupMenu = new PopupMenu(getActivity(), button);
+        popupMenu.getMenuInflater().inflate(R.menu.filtershow_menu_curves, popupMenu.getMenu());
+        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+            @Override
+            public boolean onMenuItemClick(MenuItem item) {
+                setChannel(item.getItemId());
+                button.setTextFrom(item.getItemId());
+                return true;
+            }
+        });
+        popupMenu.show();
+    }
+
+    @Override
+    public void openUtilityPanel(final LinearLayout accessoryViewList) {
+        View view = accessoryViewList.findViewById(R.id.curvesUtilityButton);
+        if (view == null) {
+            LayoutInflater inflater = (LayoutInflater)getActivity().getSystemService
+                    (Context.LAYOUT_INFLATER_SERVICE);
+            view = inflater.inflate(R.layout.filtershow_curves_button, accessoryViewList, false);
+            accessoryViewList.addView(view, view.getLayoutParams());
+            view.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View arg0) {
+                    showPopupMenu(accessoryViewList);
+                }
+            });
+        }
+
+        if (view != null) {
+            view.setVisibility(View.VISIBLE);
+        }
+    }
+
     public void nextChannel() {
         mCurrentCurveIndex = ((mCurrentCurveIndex + 1) % 4);
         invalidate();