commit e03e0352047b240e1ff3da4894ff0eabd833c7cc
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Thu Jun 25 21:53:16 2020 +0100

    Remove the 'MAIN' state.
    Fix one crasher with two-fingered dragging in the View.

diff --git a/src/main/java/org/distorted/dialogs/RubikDialogMain.java b/src/main/java/org/distorted/dialogs/RubikDialogMain.java
deleted file mode 100644
index 4e8f4c7b..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogMain.java
+++ /dev/null
@@ -1,98 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2019 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is free software: you can redistribute it and/or modify                            //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Magic Cube is distributed in the hope that it will be useful,                                 //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.FragmentActivity;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatDialogFragment;
-
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.Button;
-
-import org.distorted.main.R;
-import org.distorted.main.RubikActivity;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogMain extends AppCompatDialogFragment
-  {
-  @NonNull
-  @Override
-  public Dialog onCreateDialog(Bundle savedInstanceState)
-    {
-    FragmentActivity act = getActivity();
-    AlertDialog.Builder builder = new AlertDialog.Builder(act);
-    LayoutInflater inflater = act.getLayoutInflater();
-    final View view = inflater.inflate(R.layout.dialog_main, null);
-
-    DisplayMetrics displaymetrics = new DisplayMetrics();
-    act.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
-    float textSize = displaymetrics.widthPixels * RubikActivity.MENU_MEDIUM_TEXT_SIZE;
-    int buttonSize = (int)(2.5f*textSize);
-
-    Button play = view.findViewById(R.id.rubikPlay);
-    play.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    play.setHeight(buttonSize);
-    Button scor = view.findViewById(R.id.rubikScores);
-    scor.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    scor.setHeight(buttonSize);
-    Button patt = view.findViewById(R.id.rubikPatterns);
-    patt.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    patt.setHeight(buttonSize);
-    Button solv = view.findViewById(R.id.rubikSolver);
-    solv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    solv.setHeight(buttonSize);
-    Button abou = view.findViewById(R.id.rubikAbout);
-    abou.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    abou.setHeight(buttonSize);
-
-    builder.setView(view);
-
-    Dialog dialog = builder.create();
-
-    dialog.setCanceledOnTouchOutside(false);
-
-    Window window = dialog.getWindow();
-
-    if( window!=null )
-      {
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
-                      WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-      }
-
-    return dialog;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static String getDialogTag()
-    {
-    return "DialogMain";
-    }
-  }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/main/RubikActivity.java b/src/main/java/org/distorted/main/RubikActivity.java
index 2b3d5031..ac1ec2b1 100644
--- a/src/main/java/org/distorted/main/RubikActivity.java
+++ b/src/main/java/org/distorted/main/RubikActivity.java
@@ -25,13 +25,10 @@ import android.preference.PreferenceManager;
 import androidx.appcompat.app.AppCompatActivity;
 
 import android.util.DisplayMetrics;
-import android.view.View;
 
 import com.google.firebase.analytics.FirebaseAnalytics;
 
-import org.distorted.dialogs.RubikDialogAbout;
 import org.distorted.dialogs.RubikDialogError;
-import org.distorted.dialogs.RubikDialogScores;
 import org.distorted.effects.BaseEffect;
 import org.distorted.library.main.DistortedLibrary;
 
@@ -96,6 +93,7 @@ public class RubikActivity extends AppCompatActivity
       super.onResume();
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
       view.onResume();
+      view.initialize();
       restorePreferences();
       RubikState.setState(this);
 
@@ -296,51 +294,4 @@ public class RubikActivity extends AppCompatActivity
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
       return view.isVertical();
       }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void Play(View v)
-      {
-      RubikState.switchState(this,RubikState.PLAY);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void Scores(View v)
-      {
-      RubikStatePlay play = (RubikStatePlay) RubikState.PLAY.getStateClass();
-      int object = play.getObject();
-      int size   = play.getSize();
-      int sizeIndex = RubikObjectList.getSizeIndex(object,size);
-
-      Bundle bundle = new Bundle();
-      bundle.putInt("tab", RubikObjectList.pack(object,sizeIndex) );
-      bundle.putBoolean("submitting", false);
-
-      RubikDialogScores scores = new RubikDialogScores();
-      scores.setArguments(bundle);
-      scores.show(getSupportFragmentManager(), null);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void Patterns(View v)
-      {
-      RubikState.switchState(this,RubikState.PATT);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void Solver(View v)
-      {
-      RubikState.switchState(this,RubikState.SVER);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void About(View v)
-      {
-      RubikDialogAbout diag = new RubikDialogAbout();
-      diag.show(getSupportFragmentManager(), null);
-      }
 }
diff --git a/src/main/java/org/distorted/main/RubikSurfaceView.java b/src/main/java/org/distorted/main/RubikSurfaceView.java
index cb61716e..1fb5d237 100644
--- a/src/main/java/org/distorted/main/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/main/RubikSurfaceView.java
@@ -633,6 +633,14 @@ public class RubikSurfaceView extends GLSurfaceView
         }
       }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    void initialize()
+      {
+      mPtrID1 = INVALID_POINTER_ID;
+      mPtrID2 = INVALID_POINTER_ID;
+      }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -652,9 +660,6 @@ public class RubikSurfaceView extends GLSurfaceView
         mFirstIndex =0;
         mLastIndex  =0;
 
-        mPtrID1 = INVALID_POINTER_ID;
-        mPtrID2 = INVALID_POINTER_ID;
-
         mRenderer  = new RubikRenderer(this);
         mPreRender = new RubikPreRender(this);
 
diff --git a/src/main/java/org/distorted/objects/RubikObject.java b/src/main/java/org/distorted/objects/RubikObject.java
index 963ac90e..2153a2f0 100644
--- a/src/main/java/org/distorted/objects/RubikObject.java
+++ b/src/main/java/org/distorted/objects/RubikObject.java
@@ -460,17 +460,8 @@ public abstract class RubikObject extends DistortedNode
     final float ratio = 1.0f/(NUM_FACES+1);
     final Static4D[] maps = new Static4D[NUM_FACES];
 
-    try
-      {
-      maps[face] = new Static4D( newColor*ratio, 0.0f, ratio, 1.0f);
-      mMesh.setTextureMap(maps,NUM_FACES*cubit);
-      }
-    catch(ArrayIndexOutOfBoundsException ignored)
-      {
-      // the object must have changed in between of us touching the screen and getting here
-      // (which happens after the next render, i.e. about 30 miliseconds later)
-      // ignore.
-      }
+    maps[face] = new Static4D( newColor*ratio, 0.0f, ratio, 1.0f);
+    mMesh.setTextureMap(maps,NUM_FACES*cubit);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objects/RubikPyraminxMovement.java b/src/main/java/org/distorted/objects/RubikPyraminxMovement.java
index 3ca29452..b56343c3 100644
--- a/src/main/java/org/distorted/objects/RubikPyraminxMovement.java
+++ b/src/main/java/org/distorted/objects/RubikPyraminxMovement.java
@@ -23,14 +23,14 @@ package org.distorted.objects;
 
 class RubikPyraminxMovement extends RubikObjectMovement
 {
-  private static final float SQ2 = (float)Math.sqrt(2);
+  private static final float SQ6 = (float)Math.sqrt(6);
   private static final float SQ3 = (float)Math.sqrt(3);
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   RubikPyraminxMovement()
     {
-    super(RubikPyraminx.AXIS, 1, SQ2*SQ3/12, SQ3/6);
+    super(RubikPyraminx.AXIS, 1, SQ6/12, SQ3/6);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/states/RubikState.java b/src/main/java/org/distorted/states/RubikState.java
index bc8e6d89..c6d5aa0c 100644
--- a/src/main/java/org/distorted/states/RubikState.java
+++ b/src/main/java/org/distorted/states/RubikState.java
@@ -31,14 +31,13 @@ import static org.distorted.main.RubikSurfaceView.*;
 
 public enum RubikState
   {
-  MAIN ( null , MODE_DRAG   , new RubikStateMain()     ),
-  PLAY ( MAIN , MODE_ROTATE , new RubikStatePlay()     ),
+  PLAY ( null , MODE_ROTATE , new RubikStatePlay()     ),
   SOLV ( PLAY , MODE_ROTATE , new RubikStateSolving()  ),
-  PATT ( MAIN , MODE_DRAG   , new RubikStatePattern()  ),
-  SVER ( MAIN , MODE_REPLACE, new RubikStateSolver()   ),
+  PATT ( PLAY , MODE_DRAG   , new RubikStatePattern()  ),
+  SVER ( PLAY , MODE_REPLACE, new RubikStateSolver()   ),
   SOLU ( SVER , MODE_DRAG   , new RubikStateSolution() ),
   READ ( PLAY , MODE_ROTATE , new RubikStateReady()    ),
-  DONE ( PLAY , MODE_DRAG   , new RubikStateDone()    ),
+  DONE ( PLAY , MODE_DRAG   , new RubikStateDone()     ),
   ;
 
   public static final int LENGTH = values().length;
@@ -65,7 +64,7 @@ public enum RubikState
 
   public static RubikState getState(int ordinal)
     {
-    return sizes[ordinal];
+    return ordinal>=0 && ordinal<LENGTH ?  sizes[ordinal] : PLAY;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -93,7 +92,7 @@ public enum RubikState
 
   public static void restorePreferences(SharedPreferences preferences)
     {
-    int currState = preferences.getInt("curr_state", RubikState.MAIN.ordinal() );
+    int currState = preferences.getInt("curr_state", RubikState.PLAY.ordinal() );
     mCurrState = getState(currState);
     }
 
diff --git a/src/main/java/org/distorted/states/RubikStateMain.java b/src/main/java/org/distorted/states/RubikStateMain.java
deleted file mode 100644
index 04b94564..00000000
--- a/src/main/java/org/distorted/states/RubikStateMain.java
+++ /dev/null
@@ -1,146 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is free software: you can redistribute it and/or modify                            //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Magic Cube is distributed in the hope that it will be useful,                                 //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.states;
-
-import android.content.SharedPreferences;
-import androidx.fragment.app.FragmentManager;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.google.firebase.crashlytics.FirebaseCrashlytics;
-
-import org.distorted.dialogs.RubikDialogMain;
-import org.distorted.main.BuildConfig;
-import org.distorted.main.R;
-import org.distorted.main.RubikActivity;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikStateMain extends RubikStateAbstract
-  {
-  void leaveState(RubikActivity act)
-    {
-    FragmentManager mana = act.getSupportFragmentManager();
-    RubikDialogMain diag = (RubikDialogMain) mana.findFragmentByTag(RubikDialogMain.getDialogTag());
-
-    if( diag!=null )
-      {
-      try
-        {
-        diag.dismiss();
-        }
-      catch(IllegalStateException ex)
-        {
-        if( !BuildConfig.DEBUG )
-          {
-          FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
-          crashlytics.setCustomKey("MainDialog", ex.toString());
-          crashlytics.recordException(ex);
-          }
-        }
-      }
-    else
-      {
-      if( !BuildConfig.DEBUG )
-        {
-        Exception ex = new Exception("Cannot find MainDialog");
-        FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
-        crashlytics.setCustomKey("MainDialog", ex.toString());
-        crashlytics.recordException(ex);
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void enterState(final RubikActivity act)
-    {
-    float width = act.getScreenWidthInPixels();
-    float buttonSize = width*RubikActivity.BUTTON_TEXT_SIZE;
-    float titleSize = width*RubikActivity.TITLE_TEXT_SIZE;
-
-    FragmentManager mana = act.getSupportFragmentManager();
-    RubikDialogMain diag = (RubikDialogMain) mana.findFragmentByTag(RubikDialogMain.getDialogTag());
-
-    if( diag==null )
-      {
-      RubikDialogMain diag2 = new RubikDialogMain();
-      diag2.show( mana, RubikDialogMain.getDialogTag() );
-      }
-
-    LayoutInflater inflater = act.getLayoutInflater();
-
-    // TOP ////////////////////////////
-    LinearLayout layoutTop = act.findViewById(R.id.upperBar);
-    layoutTop.removeAllViews();
-    final TextView text = (TextView)inflater.inflate(R.layout.upper_text, null);
-
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize);
-    text.setText(R.string.app_name);
-    layoutTop.addView(text);
-
-    // BOT ////////////////////////////
-    LinearLayout layoutLeft = act.findViewById(R.id.mainBarLeft);
-    layoutLeft.removeAllViews();
-    LinearLayout layoutRight = act.findViewById(R.id.mainBarRight);
-    layoutRight.removeAllViews();
-
-    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
-    float scale = metrics.density;
-    int padding = (int)( 5*scale + 0.5f);
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
-
-    Button buttonR = new Button(act);
-    buttonR.setLayoutParams(params);
-    buttonR.setPadding(padding,0,padding,0);
-    buttonR.setTextSize(TypedValue.COMPLEX_UNIT_PX, buttonSize);
-    buttonR.setText(R.string.exit);
-
-    buttonR.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        RubikState.goBack(act);
-        }
-      });
-
-    layoutRight.addView(buttonR);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void savePreferences(SharedPreferences.Editor editor)
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void restorePreferences(SharedPreferences preferences)
-    {
-
-    }
-  }
diff --git a/src/main/java/org/distorted/states/RubikStatePlay.java b/src/main/java/org/distorted/states/RubikStatePlay.java
index 7cc50569..c4f471ea 100644
--- a/src/main/java/org/distorted/states/RubikStatePlay.java
+++ b/src/main/java/org/distorted/states/RubikStatePlay.java
@@ -23,6 +23,7 @@ import android.content.Context;
 import android.content.SharedPreferences;
 import android.graphics.drawable.BitmapDrawable;
 import android.os.Build;
+import android.os.Bundle;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.Gravity;
@@ -40,7 +41,8 @@ import android.widget.TextView;
 import androidx.annotation.NonNull;
 import androidx.appcompat.widget.AppCompatSpinner;
 
-import org.distorted.dialogs.RubikDialogEffects;
+import org.distorted.dialogs.RubikDialogAbout;
+import org.distorted.dialogs.RubikDialogScores;
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
 import org.distorted.objects.RubikObjectList;
@@ -53,13 +55,16 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
   public  static final int DEF_OBJECT= RubikObjectList.CUBE.ordinal();
   public  static final int DEF_SIZE  =  3;
 
-  private ImageButton mObjButton, mSettingsButton;
+  private static int[] BUTTON_LABELS = { R.string.scores, R.string.patterns, R.string.solver, R.string.about };
+  private static final int NUM_BUTTONS = BUTTON_LABELS.length;
+
+  private ImageButton mObjButton, mMenuButton;
   private Button mBackButton, mSolveButton, mPlayButton;
-  private PopupWindow mPopup;
+  private PopupWindow mObjectPopup, mMenuPopup;
   private int mObject = DEF_OBJECT;
   private int mSize   = DEF_SIZE;
-  private int mLayoutWidth;
-  private LinearLayout mLayout;
+  private int mObjectLayoutWidth, mMenuLayoutHeight;
+  private LinearLayout mObjectLayout, mMenuLayout;
   private AppCompatSpinner mLevelSpinner;
   private ArrayAdapter<String> mSpinnerAdapter;
   private int mLevelValue;
@@ -94,13 +99,15 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
     setupPlayButton(act,scale);
     layoutTop.addView(mPlayButton);
 
+    setupObjectWindow(act, scale);
+
     // BOT ////////////////////////////
 
     LinearLayout layoutLeft = act.findViewById(R.id.mainBarLeft);
     layoutLeft.removeAllViews();
 
-    setupSettingsButton(act,scale,width);
-    layoutLeft.addView(mSettingsButton);
+    setupMenuButton(act,scale,width);
+    layoutLeft.addView(mMenuButton);
     setupSolveButton(act,scale);
     layoutLeft.addView(mSolveButton);
 
@@ -110,7 +117,7 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
     setupBackButton(act,scale);
     layoutRight.addView(mBackButton);
 
-    setupPopupWindow(act, scale);
+    setupMenuWindow(act, scale);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -133,17 +140,17 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
           {
           int total = RubikObjectList.getTotal();
           boolean vertical = act.isVertical();
-          mLayout.setOrientation(vertical ? LinearLayout.VERTICAL:LinearLayout.HORIZONTAL);
+          mObjectLayout.setOrientation(vertical ? LinearLayout.VERTICAL:LinearLayout.HORIZONTAL);
 
           int width  = view.getWidth();
-          int layhei = mLayoutWidth * (vertical? total:1);
-          int laywid = mLayoutWidth * (vertical? 1:total);
+          int layhei = mObjectLayoutWidth * (vertical? total:1);
+          int laywid = mObjectLayoutWidth * (vertical? 1:total);
 
-          mPopup.showAsDropDown(view, (width-laywid)/2, 0, Gravity.LEFT);
+          mObjectPopup.showAsDropDown(view, (width-laywid)/2, 0, Gravity.LEFT);
 
           if( android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1 )
             {
-            mPopup.update(view, laywid, layhei);
+            mObjectPopup.update(view, laywid, layhei);
             }
           }
         }
@@ -221,23 +228,30 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void setupSettingsButton(final RubikActivity act, final float scale, final float width)
+  private void setupMenuButton(final RubikActivity act, final float scale, final float width)
     {
     int padding = (int)(3*scale + 0.5f);
     int widthBut = (int)(width/6);
     LinearLayout.LayoutParams objectParams = new LinearLayout.LayoutParams(widthBut,LinearLayout.LayoutParams.MATCH_PARENT);
-    mSettingsButton = new ImageButton(act);
-    mSettingsButton.setLayoutParams(objectParams);
-    mSettingsButton.setPadding(padding,0,padding,0);
-    mSettingsButton.setImageResource(R.drawable.settings);
+    mMenuButton = new ImageButton(act);
+    mMenuButton.setLayoutParams(objectParams);
+    mMenuButton.setPadding(padding,0,padding,0);
+    mMenuButton.setImageResource(R.drawable.menu);
 
-    mSettingsButton.setOnClickListener( new View.OnClickListener()
+    mMenuButton.setOnClickListener( new View.OnClickListener()
       {
       @Override
       public void onClick(View view)
         {
-        RubikDialogEffects settings = new RubikDialogEffects();
-        settings.show(act.getSupportFragmentManager(), null);
+        if( act.getPreRender().canPlay() )
+          {
+          int total = RubikObjectList.getTotal();
+          boolean vertical = act.isVertical();
+          mMenuLayout.setOrientation(vertical ? LinearLayout.VERTICAL:LinearLayout.HORIZONTAL);
+
+          mMenuPopup.showAsDropDown(view, 0, -3*mMenuLayoutHeight, Gravity.LEFT);
+          mMenuPopup.update();
+          }
         }
       });
     }
@@ -275,7 +289,7 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
     mBackButton.setLayoutParams(backParams);
     mBackButton.setPadding(padding,0,padding,0);
     mBackButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, mButtonSize);
-    mBackButton.setText(R.string.back);
+    mBackButton.setText(R.string.exit);
 
     mBackButton.setOnClickListener( new View.OnClickListener()
       {
@@ -289,20 +303,20 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void setupPopupWindow(final RubikActivity act, final float scale)
+  private void setupObjectWindow(final RubikActivity act, final float scale)
     {
     LayoutInflater layoutInflater = (LayoutInflater)act.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     final View layout = layoutInflater.inflate(R.layout.popup_objects, null);
-    mLayout = layout.findViewById(R.id.popup);
+    mObjectLayout = layout.findViewById(R.id.popup);
 
-    mPopup = new PopupWindow(act);
-    mPopup.setContentView(layout);
-    mPopup.setFocusable(true);
+    mObjectPopup = new PopupWindow(act);
+    mObjectPopup.setContentView(layout);
+    mObjectPopup.setFocusable(true);
     int margin = (int)(5*scale + 0.5f);
 
     BitmapDrawable bd = (BitmapDrawable) act.getResources().getDrawable(R.drawable.cube2);
     int cubeWidth  = bd.getIntrinsicWidth();
-    mLayoutWidth = (int)(cubeWidth + 2*margin + 0.5f);
+    mObjectLayoutWidth = (int)(cubeWidth + 2*margin + 0.5f);
 
     for(int object=0; object<RubikObjectList.NUM_OBJECTS; object++)
       {
@@ -336,15 +350,83 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
               adjustSpinner(act);
               }
 
-            mPopup.dismiss();
+            mObjectPopup.dismiss();
             }
           });
 
-        mLayout.addView(button);
+        mObjectLayout.addView(button);
         }
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupMenuWindow(final RubikActivity act, final float scale)
+    {
+    LayoutInflater layoutInflater = (LayoutInflater)act.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    final View layout = layoutInflater.inflate(R.layout.popup_objects, null);
+    mMenuLayout = layout.findViewById(R.id.popup);
+
+    mMenuPopup = new PopupWindow(act);
+    mMenuPopup.setContentView(layout);
+    mMenuPopup.setFocusable(true);
+    int margin = (int)(5*scale + 0.5f);
+
+    LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
+    p.setMargins(margin, margin, margin, margin);
+
+    for(int i=0; i<NUM_BUTTONS; i++)
+      {
+      final int but = i;
+      Button button = new Button(act);
+      button.setLayoutParams(p);
+      button.setText(BUTTON_LABELS[i]);
+
+      button.setOnClickListener( new View.OnClickListener()
+        {
+        @Override
+        public void onClick(View v)
+          {
+          mMenuPopup.dismiss();
+          Action(act,but);
+          }
+        });
+
+      mMenuLayout.addView(button);
+      }
+
+    mMenuLayoutHeight= (int)(margin + NUM_BUTTONS*(mButtonSize+margin));
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void Action(RubikActivity act, int button)
+    {
+    switch(button)
+      {
+      case 0: RubikStatePlay play = (RubikStatePlay) RubikState.PLAY.getStateClass();
+              int object = play.getObject();
+              int size   = play.getSize();
+              int sizeIndex = RubikObjectList.getSizeIndex(object,size);
+
+              Bundle bundle = new Bundle();
+              bundle.putInt("tab", RubikObjectList.pack(object,sizeIndex) );
+              bundle.putBoolean("submitting", false);
+
+              RubikDialogScores scores = new RubikDialogScores();
+              scores.setArguments(bundle);
+              scores.show(act.getSupportFragmentManager(), null);
+              break;
+      case 1: RubikState.switchState(act,RubikState.PATT);
+              break;
+      case 2: RubikState.switchState(act,RubikState.SVER);
+              break;
+      case 3: RubikDialogAbout diag = new RubikDialogAbout();
+              diag.show(act.getSupportFragmentManager(), null);
+              break;
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void savePreferences(SharedPreferences.Editor editor)
@@ -353,10 +435,16 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
     editor.putInt("statePlay_object", mObject);
     editor.putInt("statePlay_size"  , mSize);
 
-    if( mPopup!=null )
+    if( mObjectPopup!=null )
+      {
+      mObjectPopup.dismiss();
+      mObjectPopup = null;
+      }
+
+    if( mMenuPopup!=null )
       {
-      mPopup.dismiss();
-      mPopup = null;
+      mMenuPopup.dismiss();
+      mMenuPopup = null;
       }
     }
 
diff --git a/src/main/res/layout/dialog_main.xml b/src/main/res/layout/dialog_main.xml
deleted file mode 100644
index ec396ff3..00000000
--- a/src/main/res/layout/dialog_main.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-     android:layout_width="fill_parent"
-     android:layout_height="fill_parent"
-     android:gravity="center|fill_horizontal"
-     android:layout_marginLeft="10dp"
-     android:layout_marginRight="10dp"
-     android:layout_marginTop="10dp"
-     android:layout_marginBottom="10dp"
-     android:background="@color/grey"
-     android:orientation="vertical">
-
-     <Button
-         android:id="@+id/rubikPlay"
-         android:layout_width="fill_parent"
-         android:layout_height="fill_parent"
-         android:layout_weight="1"
-         android:onClick="Play"
-         android:layout_marginTop="10dp"
-         android:layout_marginLeft="10dp"
-         android:layout_marginRight="10dp"
-         android:text="@string/play" />
-
-     <Button
-         android:id="@+id/rubikScores"
-         android:layout_width="fill_parent"
-         android:layout_height="fill_parent"
-         android:layout_weight="1"
-         android:onClick="Scores"
-         android:layout_marginLeft="10dp"
-         android:layout_marginRight="10dp"
-         android:text="@string/scores" />
-
-     <Button
-         android:id="@+id/rubikPatterns"
-         android:layout_width="fill_parent"
-         android:layout_height="fill_parent"
-         android:layout_weight="1"
-         android:onClick="Patterns"
-         android:layout_marginLeft="10dp"
-         android:layout_marginRight="10dp"
-         android:text="@string/patterns" />
-
-     <Button
-         android:id="@+id/rubikSolver"
-         android:layout_width="fill_parent"
-         android:layout_height="fill_parent"
-         android:layout_weight="1"
-         android:onClick="Solver"
-         android:layout_marginLeft="10dp"
-         android:layout_marginRight="10dp"
-         android:text="@string/solver" />
-
-     <Button
-         android:id="@+id/rubikAbout"
-         android:layout_width="fill_parent"
-         android:layout_height="fill_parent"
-         android:layout_weight="1"
-         android:onClick="About"
-         android:layout_marginLeft="10dp"
-         android:layout_marginRight="10dp"
-         android:layout_marginBottom="10dp"
-         android:text="@string/about" />
-
-</LinearLayout>
\ No newline at end of file
