commit 4c0cd60057d21d2f931b070b2498d9d3f6c99017
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Mar 25 18:24:40 2020 +0000

    Change the series of Object buttons in the Play state into a PopupWindow.

diff --git a/src/main/java/org/distorted/dialog/RubikDialogEffects.java b/src/main/java/org/distorted/dialog/RubikDialogEffects.java
new file mode 100644
index 00000000..4e63f695
--- /dev/null
+++ b/src/main/java/org/distorted/dialog/RubikDialogEffects.java
@@ -0,0 +1,282 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.dialog;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatDialogFragment;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import org.distorted.effect.BaseEffect;
+import org.distorted.magic.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDialogEffects extends AppCompatDialogFragment implements SeekBar.OnSeekBarChangeListener, AdapterView.OnItemSelectedListener
+  {
+  private TextView[] mDurationText;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void addSettingsSection(FragmentActivity act, LinearLayout layout, int index)
+    {
+    BaseEffect.Type beType = BaseEffect.Type.getType(index);
+    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
+    float scale = metrics.density;
+
+    int textH=32;
+    int layoH=36;
+    int margH=10;
+
+    ///// OUTER LAYOUT ///////////////////////////////////////////////////////////////////
+
+    int margin = (int)(scale*margH + 0.5f);
+    int color  = ContextCompat.getColor(act, R.color.grey);
+    LinearLayout outerLayout = new LinearLayout(act);
+    LinearLayout.LayoutParams outerLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT, 0.5f);
+    outerLayoutParams.topMargin    = margin;
+    outerLayoutParams.bottomMargin = 0;
+    outerLayoutParams.leftMargin   = margin;
+    outerLayoutParams.rightMargin  = margin;
+
+    outerLayout.setLayoutParams(outerLayoutParams);
+    outerLayout.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
+    outerLayout.setBackgroundColor(color);
+    outerLayout.setOrientation(LinearLayout.VERTICAL);
+    layout.addView(outerLayout);
+
+    ///// TEXT ///////////////////////////////////////////////////////////////////////////
+
+    int layoutHeight = (int)(scale*textH + 0.5f);
+    int padding      = (int)(scale*10    + 0.5f);
+
+    LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,layoutHeight);
+
+    TextView textView = new TextView(act);
+    textView.setText(beType.getText());
+    textView.setLayoutParams(textParams);
+    textView.setGravity(Gravity.CENTER);
+    textView.setPadding(padding,0,padding,0);
+    textView.setTextAppearance(android.R.style.TextAppearance_Small);
+    outerLayout.addView(textView);
+
+    ///// INNER LAYOUT1 //////////////////////////////////////////////////////////////////
+
+    int innerLayout1Height = (int)(scale*layoH + 0.5f);
+    LinearLayout innerLayout1 = new LinearLayout(act);
+    LinearLayout.LayoutParams innerLayout1Params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,innerLayout1Height);
+
+    innerLayout1.setLayoutParams(innerLayout1Params);
+    innerLayout1.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
+    innerLayout1.setOrientation(LinearLayout.HORIZONTAL);
+    outerLayout.addView(innerLayout1);
+
+    ///// STUFF INSIDE INNER LAYOUT1 /////////////////////////////////////////////////////
+
+    int text1Padding = (int)(scale*5 + 0.5f);
+    LinearLayout.LayoutParams text1LayoutParams = new LinearLayout.LayoutParams(0,layoutHeight,0.2f);
+
+    TextView text1View = new TextView(act);
+    text1View.setText(R.string.duration);
+    text1View.setLayoutParams(text1LayoutParams);
+    text1View.setGravity(Gravity.START|Gravity.CENTER);
+    text1View.setPadding(text1Padding,0,text1Padding,0);
+    text1View.setTextAppearance(android.R.style.TextAppearance_Small);
+    innerLayout1.addView(text1View);
+    //////////////////////////////////////////////////////////////////
+    int text2Padding = (int)(scale*5 + 0.5f);
+    LinearLayout.LayoutParams text2LayoutParams = new LinearLayout.LayoutParams(0,layoutHeight,0.2f);
+
+    mDurationText[index] = new TextView(act);
+    mDurationText[index].setLayoutParams(text2LayoutParams);
+    mDurationText[index].setGravity(Gravity.END|Gravity.CENTER);
+    mDurationText[index].setPadding(text2Padding,0,text2Padding,0);
+    mDurationText[index].setTextAppearance(android.R.style.TextAppearance_Small);
+    innerLayout1.addView(mDurationText[index]);
+    //////////////////////////////////////////////////////////////////
+    int seekPadding = (int)(scale*10 + 0.5f);
+    LinearLayout.LayoutParams seekLayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.6f);
+
+    SeekBar seekBar = new SeekBar(act);
+    seekBar.setLayoutParams(seekLayoutParams);
+    seekBar.setPadding(seekPadding,0,seekPadding,0);
+    seekBar.setId(index);
+    innerLayout1.addView(seekBar);
+
+    seekBar.setOnSeekBarChangeListener(this);
+    seekBar.setProgress(beType.getCurrentPos());
+
+    ///// INNER LAYOUT2 //////////////////////////////////////////////////////////////////
+
+    int innerLayout2Height = (int)(scale*layoH + 0.5f);
+    LinearLayout innerLayout2 = new LinearLayout(act);
+    LinearLayout.LayoutParams innerLayout2Params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,innerLayout2Height);
+
+    innerLayout2.setLayoutParams(innerLayout2Params);
+    innerLayout2.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
+    innerLayout2.setOrientation(LinearLayout.HORIZONTAL);
+    outerLayout.addView(innerLayout2);
+
+    ///// STUFF INSIDE INNER LAYOUT2 /////////////////////////////////////////////////////
+
+    int text3Padding = (int)(scale*5 + 0.5f);
+    LinearLayout.LayoutParams text3LayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.4f);
+
+    TextView text3View = new TextView(act);
+    text3View.setText(R.string.type);
+    text3View.setLayoutParams(text3LayoutParams);
+    text3View.setGravity(Gravity.START|Gravity.CENTER);
+    text3View.setPadding(text3Padding,0,text3Padding,0);
+    text3View.setTextAppearance(android.R.style.TextAppearance_Small);
+    innerLayout2.addView(text3View);
+    //////////////////////////////////////////////////////////////////
+    int spinnerPadding = (int)(scale*10 + 0.5f);
+    LinearLayout.LayoutParams spinnerLayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.6f);
+
+    Spinner spinner = new Spinner(act);
+    spinner.setLayoutParams(spinnerLayoutParams);
+    spinner.setPadding(spinnerPadding,0,spinnerPadding,0);
+    spinner.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
+    spinner.setId(index+BaseEffect.Type.LENGTH);
+    innerLayout2.addView(spinner);
+
+    spinner.setOnItemSelectedListener(this);
+    String[] appear = BaseEffect.Type.getType(index).getNames();
+
+    ArrayAdapter<String> adapterType = new ArrayAdapter<>(act,android.R.layout.simple_spinner_item, appear);
+    adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    spinner.setAdapter(adapterType);
+    spinner.setSelection(beType.getCurrentType());
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikDialogEffects()
+    {
+    mDurationText = new TextView[BaseEffect.Type.LENGTH];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onStart()
+    {
+    super.onStart();
+
+    Window window = getDialog().getWindow();
+    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @NonNull
+  @Override
+  public Dialog onCreateDialog(Bundle savedInstanceState)
+    {
+    FragmentActivity act = getActivity();
+    LayoutInflater inflater = act.getLayoutInflater();
+    AlertDialog.Builder builder = new AlertDialog.Builder(act);
+    TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
+    tv.setText(R.string.effects);
+    builder.setCustomTitle(tv);
+
+    builder.setCancelable(true);
+    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
+      {
+      @Override
+      public void onClick(DialogInterface dialog, int which)
+        {
+
+        }
+      });
+
+    final View view = inflater.inflate(R.layout.dialog_settings, null);
+    builder.setView(view);
+
+    LinearLayout linearLayout = view.findViewById(R.id.settingsLayout);
+
+    if( linearLayout!=null )
+      {
+      for (int i=0; i< BaseEffect.Type.LENGTH; i++)
+        {
+        addSettingsSection(act,linearLayout,i);
+        }
+      }
+    else
+      {
+      android.util.Log.e("dialog_settings", "linearLayout NULL!");
+      }
+
+    return builder.create();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
+    {
+    int parentID = parent.getId();
+    int len = BaseEffect.Type.LENGTH;
+
+    if( parentID>=len && parentID< 2*len) // ith spinner's ID is equal to i+LENGTH (see createSettingSection)
+      {
+      BaseEffect.Type.getType(parentID-len).setCurrentType(pos);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
+    {
+    int barID = bar.getId();
+
+    if( barID>=0 && barID< BaseEffect.Type.LENGTH) // ith seekbar's ID is equal to i (see createSettingSection)
+      {
+      BaseEffect.Type.getType(barID).setCurrentPos(progress);
+      int ms = BaseEffect.Type.translatePos(progress);
+      mDurationText[barID].setText(getString(R.string.ms_placeholder,ms));
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onNothingSelected(AdapterView<?> parent) { }
+  public void onStartTrackingTouch(SeekBar bar) { }
+  public void onStopTrackingTouch(SeekBar bar)  { }
+  }
diff --git a/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java b/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java
index 2006f332..ac646813 100644
--- a/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java
+++ b/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java
@@ -82,12 +82,14 @@ class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresD
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void serverError(final String error)
+  public void error(final String error)
     {
     char errorNumber = error.charAt(0);
 
     switch(errorNumber)
       {
+      case '1': message("Client error");
+                break;
       case '2': RubikScores scores = RubikScores.getInstance();
                 Bundle bundle = new Bundle();
                 bundle.putString("name", scores.getName() );
@@ -101,6 +103,7 @@ class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresD
                 break;
       case '3': message("Server error");
                 break;
+      default : message("Unexpected error");
       }
     }
 
diff --git a/src/main/java/org/distorted/dialog/RubikDialogSettings.java b/src/main/java/org/distorted/dialog/RubikDialogSettings.java
deleted file mode 100644
index a3a09a32..00000000
--- a/src/main/java/org/distorted/dialog/RubikDialogSettings.java
+++ /dev/null
@@ -1,282 +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.dialog;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatDialogFragment;
-import android.util.DisplayMetrics;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import org.distorted.effect.BaseEffect;
-import org.distorted.magic.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogSettings extends AppCompatDialogFragment implements SeekBar.OnSeekBarChangeListener, AdapterView.OnItemSelectedListener
-  {
-  private TextView[] mDurationText;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void addSettingsSection(FragmentActivity act, LinearLayout layout, int index)
-    {
-    BaseEffect.Type beType = BaseEffect.Type.getType(index);
-    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
-    float scale = metrics.density;
-
-    int textH=32;
-    int layoH=36;
-    int margH=10;
-
-    ///// OUTER LAYOUT ///////////////////////////////////////////////////////////////////
-
-    int margin = (int)(scale*margH + 0.5f);
-    int color  = ContextCompat.getColor(act, R.color.grey);
-    LinearLayout outerLayout = new LinearLayout(act);
-    LinearLayout.LayoutParams outerLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT, 0.5f);
-    outerLayoutParams.topMargin    = margin;
-    outerLayoutParams.bottomMargin = 0;
-    outerLayoutParams.leftMargin   = margin;
-    outerLayoutParams.rightMargin  = margin;
-
-    outerLayout.setLayoutParams(outerLayoutParams);
-    outerLayout.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
-    outerLayout.setBackgroundColor(color);
-    outerLayout.setOrientation(LinearLayout.VERTICAL);
-    layout.addView(outerLayout);
-
-    ///// TEXT ///////////////////////////////////////////////////////////////////////////
-
-    int layoutHeight = (int)(scale*textH + 0.5f);
-    int padding      = (int)(scale*10    + 0.5f);
-
-    LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,layoutHeight);
-
-    TextView textView = new TextView(act);
-    textView.setText(beType.getText());
-    textView.setLayoutParams(textParams);
-    textView.setGravity(Gravity.CENTER);
-    textView.setPadding(padding,0,padding,0);
-    textView.setTextAppearance(android.R.style.TextAppearance_Small);
-    outerLayout.addView(textView);
-
-    ///// INNER LAYOUT1 //////////////////////////////////////////////////////////////////
-
-    int innerLayout1Height = (int)(scale*layoH + 0.5f);
-    LinearLayout innerLayout1 = new LinearLayout(act);
-    LinearLayout.LayoutParams innerLayout1Params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,innerLayout1Height);
-
-    innerLayout1.setLayoutParams(innerLayout1Params);
-    innerLayout1.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
-    innerLayout1.setOrientation(LinearLayout.HORIZONTAL);
-    outerLayout.addView(innerLayout1);
-
-    ///// STUFF INSIDE INNER LAYOUT1 /////////////////////////////////////////////////////
-
-    int text1Padding = (int)(scale*5 + 0.5f);
-    LinearLayout.LayoutParams text1LayoutParams = new LinearLayout.LayoutParams(0,layoutHeight,0.2f);
-
-    TextView text1View = new TextView(act);
-    text1View.setText(R.string.duration);
-    text1View.setLayoutParams(text1LayoutParams);
-    text1View.setGravity(Gravity.START|Gravity.CENTER);
-    text1View.setPadding(text1Padding,0,text1Padding,0);
-    text1View.setTextAppearance(android.R.style.TextAppearance_Small);
-    innerLayout1.addView(text1View);
-    //////////////////////////////////////////////////////////////////
-    int text2Padding = (int)(scale*5 + 0.5f);
-    LinearLayout.LayoutParams text2LayoutParams = new LinearLayout.LayoutParams(0,layoutHeight,0.2f);
-
-    mDurationText[index] = new TextView(act);
-    mDurationText[index].setLayoutParams(text2LayoutParams);
-    mDurationText[index].setGravity(Gravity.END|Gravity.CENTER);
-    mDurationText[index].setPadding(text2Padding,0,text2Padding,0);
-    mDurationText[index].setTextAppearance(android.R.style.TextAppearance_Small);
-    innerLayout1.addView(mDurationText[index]);
-    //////////////////////////////////////////////////////////////////
-    int seekPadding = (int)(scale*10 + 0.5f);
-    LinearLayout.LayoutParams seekLayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.6f);
-
-    SeekBar seekBar = new SeekBar(act);
-    seekBar.setLayoutParams(seekLayoutParams);
-    seekBar.setPadding(seekPadding,0,seekPadding,0);
-    seekBar.setId(index);
-    innerLayout1.addView(seekBar);
-
-    seekBar.setOnSeekBarChangeListener(this);
-    seekBar.setProgress(beType.getCurrentPos());
-
-    ///// INNER LAYOUT2 //////////////////////////////////////////////////////////////////
-
-    int innerLayout2Height = (int)(scale*layoH + 0.5f);
-    LinearLayout innerLayout2 = new LinearLayout(act);
-    LinearLayout.LayoutParams innerLayout2Params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,innerLayout2Height);
-
-    innerLayout2.setLayoutParams(innerLayout2Params);
-    innerLayout2.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
-    innerLayout2.setOrientation(LinearLayout.HORIZONTAL);
-    outerLayout.addView(innerLayout2);
-
-    ///// STUFF INSIDE INNER LAYOUT2 /////////////////////////////////////////////////////
-
-    int text3Padding = (int)(scale*5 + 0.5f);
-    LinearLayout.LayoutParams text3LayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.4f);
-
-    TextView text3View = new TextView(act);
-    text3View.setText(R.string.type);
-    text3View.setLayoutParams(text3LayoutParams);
-    text3View.setGravity(Gravity.START|Gravity.CENTER);
-    text3View.setPadding(text3Padding,0,text3Padding,0);
-    text3View.setTextAppearance(android.R.style.TextAppearance_Small);
-    innerLayout2.addView(text3View);
-    //////////////////////////////////////////////////////////////////
-    int spinnerPadding = (int)(scale*10 + 0.5f);
-    LinearLayout.LayoutParams spinnerLayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.6f);
-
-    Spinner spinner = new Spinner(act);
-    spinner.setLayoutParams(spinnerLayoutParams);
-    spinner.setPadding(spinnerPadding,0,spinnerPadding,0);
-    spinner.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
-    spinner.setId(index+BaseEffect.Type.LENGTH);
-    innerLayout2.addView(spinner);
-
-    spinner.setOnItemSelectedListener(this);
-    String[] appear = BaseEffect.Type.getType(index).getNames();
-
-    ArrayAdapter<String> adapterType = new ArrayAdapter<>(act,android.R.layout.simple_spinner_item, appear);
-    adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    spinner.setAdapter(adapterType);
-    spinner.setSelection(beType.getCurrentType());
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public RubikDialogSettings()
-    {
-    mDurationText = new TextView[BaseEffect.Type.LENGTH];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onStart()
-    {
-    super.onStart();
-
-    Window window = getDialog().getWindow();
-    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
-                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @NonNull
-  @Override
-  public Dialog onCreateDialog(Bundle savedInstanceState)
-    {
-    FragmentActivity act = getActivity();
-    LayoutInflater inflater = act.getLayoutInflater();
-    AlertDialog.Builder builder = new AlertDialog.Builder(act);
-    TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
-    tv.setText(R.string.settings);
-    builder.setCustomTitle(tv);
-
-    builder.setCancelable(true);
-    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
-      {
-      @Override
-      public void onClick(DialogInterface dialog, int which)
-        {
-
-        }
-      });
-
-    final View view = inflater.inflate(R.layout.dialog_settings, null);
-    builder.setView(view);
-
-    LinearLayout linearLayout = view.findViewById(R.id.settingsLayout);
-
-    if( linearLayout!=null )
-      {
-      for (int i=0; i< BaseEffect.Type.LENGTH; i++)
-        {
-        addSettingsSection(act,linearLayout,i);
-        }
-      }
-    else
-      {
-      android.util.Log.e("dialog_settings", "linearLayout NULL!");
-      }
-
-    return builder.create();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
-    {
-    int parentID = parent.getId();
-    int len = BaseEffect.Type.LENGTH;
-
-    if( parentID>=len && parentID< 2*len) // ith spinner's ID is equal to i+LENGTH (see createSettingSection)
-      {
-      BaseEffect.Type.getType(parentID-len).setCurrentType(pos);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
-    {
-    int barID = bar.getId();
-
-    if( barID>=0 && barID< BaseEffect.Type.LENGTH) // ith seekbar's ID is equal to i (see createSettingSection)
-      {
-      BaseEffect.Type.getType(barID).setCurrentPos(progress);
-      int ms = BaseEffect.Type.translatePos(progress);
-      mDurationText[barID].setText(getString(R.string.ms_placeholder,ms));
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onNothingSelected(AdapterView<?> parent) { }
-  public void onStartTrackingTouch(SeekBar bar) { }
-  public void onStopTrackingTouch(SeekBar bar)  { }
-  }
diff --git a/src/main/java/org/distorted/magic/RubikActivity.java b/src/main/java/org/distorted/magic/RubikActivity.java
index 852aeeb3..ace9252f 100644
--- a/src/main/java/org/distorted/magic/RubikActivity.java
+++ b/src/main/java/org/distorted/magic/RubikActivity.java
@@ -27,7 +27,7 @@ import android.view.View;
 
 import org.distorted.dialog.RubikDialogAbout;
 import org.distorted.dialog.RubikDialogScores;
-import org.distorted.dialog.RubikDialogSettings;
+import org.distorted.dialog.RubikDialogEffects;
 import org.distorted.effect.BaseEffect;
 import org.distorted.library.main.DistortedLibrary;
 
@@ -91,10 +91,17 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
       RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
       int object = play.getObject();
       int size   = play.getSize();
-      RubikObjectList obj = RubikObjectList.getObject(object);
-      int objectSize = obj.getSizes()[size];
 
-      view.getRenderer().createObject( obj, objectSize );
+      if( object>=0 && object<RubikObjectList.NUM_OBJECTS )
+        {
+        RubikObjectList obj = RubikObjectList.getObject(object);
+        int[] sizes = obj.getSizes();
+
+        if( size>=0 && size<sizes.length )
+          {
+          view.getRenderer().createObject( obj, sizes[size] );
+          }
+        }
       }
     
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -111,30 +118,33 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
     @Override
     public void onClick(View v)
       {
-      int id = v.getId();
-
-      if( id>=0 && id< RubikObjectList.getTotal() )
+      switch(v.getId())
         {
-        int object = RubikObjectList.unpackObject(id);
-        int size= RubikObjectList.unpackSize(id);
+        case RubikStateAbstract.BUTTON_ID_OBJECT: RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
+                                                  play.bringUpPopup(this,v);
+                                                  break;
+        case RubikStateAbstract.BUTTON_ID_BACK:   RubikState.goBack(this);
+                                                  break;
+        }
+      }
 
-        RubikObjectList obj = RubikObjectList.getObject(object);
-        int objectSize = obj.getSizes()[size];
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
-        RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-        boolean success = view.getRenderer().createObject(obj,objectSize);
+    public void changeObject(int object, int size)
+      {
+      RubikObjectList obj = RubikObjectList.getObject(object);
+      int objectSize = obj.getSizes()[size];
 
-        if( success )
-          {
-          RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
-          play.markButton(this,object,size);
-          }
-        }
+      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
+      view.getRenderer().createObject(obj,objectSize);
+      }
 
-      if( id == RubikStateAbstract.BUTTON_ID_BACK )
-        {
-        RubikState.goBack(this);
-        }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public boolean isVertical()
+      {
+      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
+      return view.isVertical();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -196,7 +206,7 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
 
     public void Settings(View v)
       {
-      RubikDialogSettings settings = new RubikDialogSettings();
+      RubikDialogEffects settings = new RubikDialogEffects();
       settings.show(getSupportFragmentManager(), null);
       }
 
diff --git a/src/main/java/org/distorted/magic/RubikSurfaceView.java b/src/main/java/org/distorted/magic/RubikSurfaceView.java
index 81b03669..421ca84b 100644
--- a/src/main/java/org/distorted/magic/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/magic/RubikSurfaceView.java
@@ -82,6 +82,13 @@ public class RubikSurfaceView extends GLSurfaceView
       mScreenMin = Math.min(width, height);
       }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    boolean isVertical()
+      {
+      return mScreenHeight>mScreenWidth;
+      }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     RubikRenderer getRenderer()
diff --git a/src/main/java/org/distorted/object/RubikObjectList.java b/src/main/java/org/distorted/object/RubikObjectList.java
index 006f09f1..452926ed 100644
--- a/src/main/java/org/distorted/object/RubikObjectList.java
+++ b/src/main/java/org/distorted/object/RubikObjectList.java
@@ -29,10 +29,24 @@ import org.distorted.magic.R;
 
 public enum RubikObjectList
   {
-  CUBE     ( new int[][] { {2,R.drawable.cube2} , {3,R.drawable.cube3} , {4,R.drawable.cube4} },
-             new RubikCubeMovement() ),
-  PYRAMINX ( new int[][] { {3,R.drawable.pyra3} , {4,R.drawable.pyra4} },
-             new RubikPyraminxMovement() ),
+  CUBE (
+         new int[][] {
+                       {2 , R.drawable.cube2} ,
+                       {3 , R.drawable.cube3} ,
+                       {4 , R.drawable.cube4} ,
+                       {5 , R.drawable.cube5}
+                     },
+         new RubikCubeMovement()
+       ),
+
+  PYRA (
+         new int[][] {
+                       {3 , R.drawable.pyra3} ,
+                       {4 , R.drawable.pyra4} ,
+                       {5 , R.drawable.pyra5}
+                     },
+         new RubikPyraminxMovement()
+       ),
   ;
 
   public static final int NUM_OBJECTS = values().length;
diff --git a/src/main/java/org/distorted/scores/RubikScores.java b/src/main/java/org/distorted/scores/RubikScores.java
index a178a02f..5141a59e 100644
--- a/src/main/java/org/distorted/scores/RubikScores.java
+++ b/src/main/java/org/distorted/scores/RubikScores.java
@@ -228,24 +228,26 @@ public class RubikScores
           submStr = subStr.substring(comma+1);
 
           object = RubikObjectList.getOrdinal(nameStr);
-          size   = RubikObjectList.getSize(object,Integer.parseInt(sizeStr));
-          time   = Long.parseLong(timeStr);
-          subm   = Integer.parseInt(submStr);
 
-          if( object>=0 && object< NUM_OBJECTS && size>=0 && size<MAX_SIZE && subm>=0 && subm<=1 )
+          if( object>=0 && object< NUM_OBJECTS )
             {
-            mRecords  [object][size][scramble] = time;
-            mSubmitted[object][size][scramble] = subm;
-/*
-            if( time<NO_RECORD )
+            size = RubikObjectList.getSize(object,Integer.parseInt(sizeStr));
+            time = Long.parseLong(timeStr);
+            subm = Integer.parseInt(submStr);
+
+            if( size>=0 && size<MAX_SIZE && subm>=0 && subm<=1 )
+              {
+              mRecords  [object][size][scramble] = time;
+              mSubmitted[object][size][scramble] = subm;
+              }
+            else
               {
-              android.util.Log.e("solv", "Set record for: object="+object+" size="+size+" scramble="+scramble+" time: "+time+" submitted: "+subm);
+              android.util.Log.e("solv", "error: size="+size+" subm="+subm);
               }
-*/
             }
           else
             {
-            android.util.Log.e("solv", "error: object="+object+" size="+size);
+            android.util.Log.e("solv", "error: object="+object);
             }
           }
         }
@@ -270,7 +272,8 @@ public class RubikScores
       {
       if( mRecords[object][size][scramble-1]> timeTaken )
         {
-        mRecords[object][size][scramble-1] = timeTaken;
+        mRecords  [object][size][scramble-1] = timeTaken;
+        mSubmitted[object][size][scramble-1] = 0;
         return true;
         }
       }
@@ -421,10 +424,12 @@ public class RubikScores
 
       for(int size=0; size<length; size++)
         for(int scramble=0; scramble<MAX_SCRAMBLE; scramble++)
+          {
           if( mSubmitted[object][size][scramble]==0 && mRecords[object][size][scramble]<NO_RECORD )
             {
             return true;
             }
+          }
       }
 
     return false;
diff --git a/src/main/java/org/distorted/scores/RubikScoresDownloader.java b/src/main/java/org/distorted/scores/RubikScoresDownloader.java
index c5a8a627..428da06f 100644
--- a/src/main/java/org/distorted/scores/RubikScoresDownloader.java
+++ b/src/main/java/org/distorted/scores/RubikScoresDownloader.java
@@ -39,7 +39,7 @@ public class RubikScoresDownloader implements Runnable
     {
     void receive(String[][][] country, String[][][] name, float[][][] time);
     void message(String mess);
-    void serverError(String error);
+    void error(String error);
     }
 
   public static final int MAX_PLACES = 10;
@@ -131,6 +131,17 @@ public class RubikScoresDownloader implements Runnable
     int begin=-1 ,end, len = mScores.length();
     String row;
 
+    if( len==0 )
+      {
+      mReceiver.error("1");
+      return false;
+      }
+    else if( len==1 )
+      {
+      mReceiver.error(mScores);
+      return false;
+      }
+
     for(int i=0; i<mTotal; i++)
       for(int j=0; j<MAX_SCRAMBLE; j++)
         {
@@ -145,16 +156,7 @@ public class RubikScoresDownloader implements Runnable
       try
         {
         row = mScores.substring(begin+1,end);
-
-        if( row.length()==1 )
-          {
-          mReceiver.serverError(row);
-          return false;
-          }
-        else
-          {
-          fillRow(row);
-          }
+        fillRow(row);
         }
       catch(Exception ex)
         {
@@ -242,7 +244,7 @@ public class RubikScoresDownloader implements Runnable
 
   private boolean network(String url)
     {
-    android.util.Log.e("down", "url: "+url);
+    //android.util.Log.e("down", "url: "+url);
 
     try
       {
diff --git a/src/main/java/org/distorted/uistate/RubikStateAbstract.java b/src/main/java/org/distorted/uistate/RubikStateAbstract.java
index 7e39cbcb..16b3ad07 100644
--- a/src/main/java/org/distorted/uistate/RubikStateAbstract.java
+++ b/src/main/java/org/distorted/uistate/RubikStateAbstract.java
@@ -26,7 +26,8 @@ import org.distorted.magic.RubikActivity;
 
 public abstract class RubikStateAbstract
   {
-  public static final int BUTTON_ID_BACK= 1023;
+  public static final int BUTTON_ID_BACK  = 1023;
+  public static final int BUTTON_ID_OBJECT= 1022;
 
   abstract void enterState(RubikActivity act);
   abstract void leaveState(RubikActivity act);
diff --git a/src/main/java/org/distorted/uistate/RubikStateMain.java b/src/main/java/org/distorted/uistate/RubikStateMain.java
index 6c349479..d62af824 100644
--- a/src/main/java/org/distorted/uistate/RubikStateMain.java
+++ b/src/main/java/org/distorted/uistate/RubikStateMain.java
@@ -75,22 +75,16 @@ public class RubikStateMain extends RubikStateAbstract
     layoutTop.addView(text);
 
     // BOT ////////////////////////////
-    LinearLayout layoutBot = act.findViewById(R.id.mainBar);
-    layoutBot.removeAllViews();
+    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 size = (int)(60*scale +0.5f);
-    int padding = (int)(5*scale + 0.5f);
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,size,0.5f);
-
-    Button buttonL = new Button(act);
-    buttonL.setLayoutParams(params);
-    buttonL.setId(BUTTON_ID_BACK);
-    buttonL.setPadding(padding,0,padding,0);
-    buttonL.setText(R.string.back);
-    buttonL.setOnClickListener(act);
-    layoutBot.addView(buttonL);
+    int size    = (int)(60*scale + 0.5f);
+    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);
@@ -98,9 +92,7 @@ public class RubikStateMain extends RubikStateAbstract
     buttonR.setPadding(padding,0,padding,0);
     buttonR.setText(R.string.exit);
     buttonR.setOnClickListener(act);
-    layoutBot.addView(buttonR);
-
-    buttonL.setVisibility(INVISIBLE);
+    layoutRight.addView(buttonR);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/uistate/RubikStatePlay.java b/src/main/java/org/distorted/uistate/RubikStatePlay.java
index 3b4b20a9..c46d247b 100644
--- a/src/main/java/org/distorted/uistate/RubikStatePlay.java
+++ b/src/main/java/org/distorted/uistate/RubikStatePlay.java
@@ -19,17 +19,17 @@
 
 package org.distorted.uistate;
 
+import android.content.Context;
 import android.content.SharedPreferences;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.v4.content.ContextCompat;
+import android.graphics.drawable.BitmapDrawable;
 import android.util.DisplayMetrics;
+import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
+import android.widget.PopupWindow;
 
 import org.distorted.component.HorizontalNumberPicker;
 import org.distorted.magic.R;
@@ -46,10 +46,15 @@ public class RubikStatePlay extends RubikStateAbstract
   private static final int DEF_OBJECT   = RubikObjectList.CUBE.ordinal();
   private static final int DEF_SIZE     =  1;  // i.e. the second from the list of CUBE's sizes
 
+  private ImageButton mObjButton;
+  private Button mBackButton;
+  private PopupWindow mPopup;
   private HorizontalNumberPicker mPicker;
   private int mPickerValue;
   private int mObject = DEF_OBJECT;
   private int mSize   = DEF_SIZE;
+  private int mLayoutWidth, mLayoutHeight;
+  private LinearLayout mLayout;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -60,66 +65,126 @@ public class RubikStatePlay extends RubikStateAbstract
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  void enterState(RubikActivity act)
+  void enterState(final RubikActivity act)
     {
     LayoutInflater inflater = act.getLayoutInflater();
 
     // TOP ////////////////////////////
+    final View viewTop = inflater.inflate(R.layout.play_title, null);
+
     LinearLayout layoutTop = act.findViewById(R.id.mainTitle);
     layoutTop.removeAllViews();
-
-    final View viewTop = inflater.inflate(R.layout.play_title, null);
     layoutTop.addView(viewTop);
 
     // BOT ////////////////////////////
-    LinearLayout layoutBot = act.findViewById(R.id.mainBar);
-    layoutBot.removeAllViews();
-
     DisplayMetrics metrics = act.getResources().getDisplayMetrics();
-    float scale = metrics.density;
-    int size    = (int)(60*scale + 0.5f);
-    int padding = (int)( 3*scale + 0.5f);
-    ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(size,size);
+    final float scale = metrics.density;
+    int padding = (int)(3*scale + 0.5f);
 
-    for(int i=0; i< RubikObjectList.NUM_OBJECTS; i++)
+    if( mObjButton==null )
       {
-      int[] iconIDs = RubikObjectList.getObject(i).getIconIDs();
-      int len = iconIDs.length;
+      LinearLayout.LayoutParams objectParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.MATCH_PARENT);
+      mObjButton = new ImageButton(act);
+      mObjButton.setLayoutParams(objectParams);
+      mObjButton.setId(BUTTON_ID_OBJECT);
+      mObjButton.setPadding(padding,0,padding,0);
+      mObjButton.setImageResource(R.drawable.cube_menu);
+      mObjButton.setOnClickListener(act);
+      }
+    LinearLayout layoutLeft = act.findViewById(R.id.mainBarLeft);
+    layoutLeft.removeAllViews();
+    layoutLeft.addView(mObjButton);
 
-      for(int s=0; s<len; s++)
-        {
-        ImageButton button = new ImageButton(act);
-        button.setLayoutParams(params);
-        button.setId(RubikObjectList.pack(i,s));
-        button.setPadding(padding,0,padding,0);
-        button.setImageResource(iconIDs[s]);
-        button.setOnClickListener(act);
-        layoutBot.addView(button);
-        }
+    if( mBackButton==null )
+      {
+      LinearLayout.LayoutParams backParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
+      mBackButton = new Button(act);
+      mBackButton.setLayoutParams(backParams);
+      mBackButton.setId(BUTTON_ID_BACK);
+      mBackButton.setPadding(padding,0,padding,0);
+      mBackButton.setText(R.string.back);
+      mBackButton.setOnClickListener(act);
       }
+    LinearLayout layoutRight = act.findViewById(R.id.mainBarRight);
+    layoutRight.removeAllViews();
+    layoutRight.addView(mBackButton);
 
-    ViewGroup.LayoutParams params2 = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,size);
+    mPicker = act.findViewById(R.id.rubikNumberPicker);
+    mPicker.setMin(MIN_SCRAMBLE);
+    mPicker.setMax(MAX_SCRAMBLE);
+    mPicker.setValue(mPickerValue);
 
-    Button button = new Button(act);
-    button.setLayoutParams(params2);
-    button.setId(BUTTON_ID_BACK);
-    button.setPadding(padding,0,padding,0);
-    button.setText(R.string.back);
-    button.setOnClickListener(act);
-    layoutBot.addView(button);
+    if( mPopup==null )
+      {
+      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);
 
-    markButton(act,mObject,mSize);
+      mPopup = new PopupWindow(act);
+      mPopup.setContentView(layout);
+      mPopup.setFocusable(true);
+      int margin = (int)(5*scale + 0.5f);
 
-    mPicker = act.findViewById(R.id.rubikNumberPicker);
+      BitmapDrawable bd = (BitmapDrawable) act.getResources().getDrawable(R.drawable.cube2);
+      int cubeWidth  = bd.getIntrinsicWidth();
+      int cubeHeight = bd.getIntrinsicHeight();
 
-    if( mPicker!=null )
-      {
-      mPicker.setMin(MIN_SCRAMBLE);
-      mPicker.setMax(MAX_SCRAMBLE);
-      mPicker.setValue(mPickerValue);
+      mLayoutWidth = (int)(cubeWidth + 2*margin + 0.5f);
+      mLayoutHeight= (int)(cubeHeight+ 2*margin + 0.5f);
+
+      for(int object=0; object<RubikObjectList.NUM_OBJECTS; object++)
+        {
+        RubikObjectList list = RubikObjectList.getObject(object);
+        int[] sizes = list.getSizes();
+        int[] icons = list.getIconIDs();
+        int len = sizes.length;
+        final int obj = object;
+
+        for(int i=0; i<len; i++)
+          {
+          final int size = i;
+
+          LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
+          p.setMargins(margin, margin, margin, margin);
+
+          ImageButton button = new ImageButton(act);
+          button.setLayoutParams(p);
+
+          button.setBackgroundResource(icons[i]);
+          button.setOnClickListener( new View.OnClickListener()
+            {
+            @Override
+            public void onClick(View v)
+              {
+              mObject = obj;
+              mSize   = size;
+              act.changeObject(obj,size);
+              mPopup.dismiss();
+              }
+            });
+
+          mLayout.addView(button);
+          }
+        }
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void bringUpPopup(RubikActivity act, View view)
+    {
+    int total = RubikObjectList.getTotal();
+    boolean vertical = act.isVertical();
+    mLayout.setOrientation(vertical ? LinearLayout.VERTICAL:LinearLayout.HORIZONTAL);
+
+    int height = view.getHeight();
+    int width  = view.getWidth();
+    int laywid = mLayoutWidth * (vertical? 1:total);
+    int layhei = mLayoutHeight* (vertical? total:1);
+
+    mPopup.showAsDropDown(view, (width-laywid)/2, -height-layhei, Gravity.LEFT);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void savePreferences(SharedPreferences.Editor editor)
@@ -131,6 +196,15 @@ public class RubikStatePlay extends RubikStateAbstract
 
     editor.putInt("statePlay_object", mObject);
     editor.putInt("statePlay_size"  , mSize);
+
+    mObjButton = null;
+    mBackButton= null;
+
+    if( mPopup!=null )
+      {
+      mPopup.dismiss();
+      mPopup     = null;
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -162,29 +236,4 @@ public class RubikStatePlay extends RubikStateAbstract
     {
     return mSize;
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void markButton(RubikActivity act, int object, int size)
-    {
-    mObject = object;
-    mSize   = size;
-
-    int lookingFor = RubikObjectList.pack(object,size);
-    int len = RubikObjectList.getTotal();
-
-    for(int button=0; button<len; button++)
-      {
-      Drawable d = act.findViewById(button).getBackground();
-
-      if( button==lookingFor )
-        {
-        d.setColorFilter(ContextCompat.getColor(act,R.color.red), PorterDuff.Mode.MULTIPLY);
-        }
-      else
-        {
-        d.clearColorFilter();
-        }
-      }
-    }
   }
diff --git a/src/main/java/org/distorted/uistate/RubikStateSolving.java b/src/main/java/org/distorted/uistate/RubikStateSolving.java
index a13ecdce..70e1eff3 100644
--- a/src/main/java/org/distorted/uistate/RubikStateSolving.java
+++ b/src/main/java/org/distorted/uistate/RubikStateSolving.java
@@ -72,22 +72,15 @@ public class RubikStateSolving extends RubikStateAbstract
     layoutTop.addView(mTime);
 
     // BOT ////////////////////////////
-    LinearLayout layoutBot = act.findViewById(R.id.mainBar);
-    layoutBot.removeAllViews();
+    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 size = (int)(60*scale +0.5f);
     int padding = (int)(5*scale + 0.5f);
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,size,0.5f);
-
-    Button buttonL = new Button(act);
-    buttonL.setLayoutParams(params);
-    buttonL.setId(BUTTON_ID_BACK);
-    buttonL.setPadding(padding,0,padding,0);
-    buttonL.setText(R.string.back);
-    buttonL.setOnClickListener(act);
-    layoutBot.addView(buttonL);
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
 
     mBack = new Button(act);
     mBack.setLayoutParams(params);
@@ -95,9 +88,7 @@ public class RubikStateSolving extends RubikStateAbstract
     mBack.setPadding(padding,0,padding,0);
     mBack.setText(R.string.back);
     mBack.setOnClickListener(act);
-    layoutBot.addView(mBack);
-
-    buttonL.setVisibility(android.view.View.INVISIBLE);
+    layoutRight.addView(mBack);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/res/layout/dialog_main.xml b/src/main/res/layout/dialog_main.xml
index 82567e7b..4066e822 100644
--- a/src/main/res/layout/dialog_main.xml
+++ b/src/main/res/layout/dialog_main.xml
@@ -29,7 +29,7 @@
          android:onClick="Settings"
          android:layout_marginLeft="10dp"
          android:layout_marginRight="10dp"
-         android:text="@string/settings" />
+         android:text="@string/effects" />
 
      <Button
          android:id="@+id/rubikScores"
diff --git a/src/main/res/layout/main.xml b/src/main/res/layout/main.xml
index 25720d1a..00cc9c17 100644
--- a/src/main/res/layout/main.xml
+++ b/src/main/res/layout/main.xml
@@ -19,11 +19,26 @@
         android:layout_weight="1" />
 
     <LinearLayout
-        android:id="@+id/mainBar"
-        android:layout_width="fill_parent"
+        android:layout_width="match_parent"
         android:layout_height="60dp"
-        android:gravity="right"
         android:orientation="horizontal">
+
+        <LinearLayout
+            android:id="@+id/mainBarLeft"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:orientation="horizontal">
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/mainBarRight"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:orientation="horizontal">
+        </LinearLayout>
+
     </LinearLayout>
 
 </LinearLayout>
diff --git a/src/main/res/layout/popup_objects.xml b/src/main/res/layout/popup_objects.xml
new file mode 100644
index 00000000..ecf9f121
--- /dev/null
+++ b/src/main/res/layout/popup_objects.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+   android:layout_width="wrap_content"
+   android:id="@+id/popup"
+   android:layout_height="wrap_content"
+   android:orientation="vertical" >
+</LinearLayout>
\ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index ff885e4d..e92fe92e 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -6,7 +6,7 @@
     <string name="solve">Solve</string>
     <string name="exit">Exit</string>
     <string name="play">Play</string>
-    <string name="settings">Settings</string>
+    <string name="effects">Effects</string>
     <string name="scores">High Scores</string>
     <string name="about">About</string>
     <string name="solved">Solved</string>
