commit 3eaf5f2a381b3013c9d23790164f6edf2f627ef3
Author: leszek <leszek@koltunski.pl>
Date:   Wed Jan 17 23:30:40 2024 +0100

    Progress with sorting the objects by various criteria.

diff --git a/src/main/java/org/distorted/config/ConfigScreen.java b/src/main/java/org/distorted/config/ConfigScreen.java
index c14e67cd..461f62b7 100644
--- a/src/main/java/org/distorted/config/ConfigScreen.java
+++ b/src/main/java/org/distorted/config/ConfigScreen.java
@@ -39,7 +39,6 @@ public class ConfigScreen
   private static final float MARGIN    = 0.0024f;
   private static final int NUM_COLUMNS = 5;
   private static final int[] mLocation = new int[2];
-  private static final float POPUP_PADDING = 0.028f;
   private static final float POPUP_MARGIN  = 0.016f;
 
   private TransparentImageButton mBackButton, mObjectButton, mPrevButton, mNextButton;
@@ -62,20 +61,23 @@ public class ConfigScreen
 
     int cubeSize  = (int)( (Math.min(width,(int)(height*0.7f))) / 9 );
     int margin    = (int)(height*POPUP_MARGIN);
-    int padding   = (int)(height*POPUP_PADDING);
     mObjectSize   = (int)(cubeSize + 2*margin + 0.5f);
     mMaxRowCount  = (int)((height-mBarHeight)/mObjectSize);
 
     ScrollView view = (ScrollView)inflate( act, R.layout.popup_object_simple, null);
     GridLayout objectGrid = view.findViewById(R.id.objectGrid);
 
-    ObjectGridCreator.createObjectGridClassic(objectGrid,act,mRowCount,mColCount,numObjects,margin,cubeSize,padding);
+    int[] objects = new int[numObjects];
+    for(int i=0; i<numObjects; i++) objects[i] = i;
+
+    ObjectGridCreator creator = new ObjectGridCreator(width);
+    creator.createObjectGridClassic(objectGrid,act,objects);
 
     for(int child=0; child<numObjects; child++)
       {
       final RubikObject obj = RubikObjectList.getObject(child);
       View v = objectGrid.getChildAt(child);
-      ImageButton button = ObjectGridCreator.getButton(obj,v);
+      ImageButton button = creator.getButton(obj,v);
       final int ordinal = child;
 
       button.setOnClickListener( new View.OnClickListener()
diff --git a/src/main/java/org/distorted/helpers/ObjectGridCreator.java b/src/main/java/org/distorted/helpers/ObjectGridCreator.java
index a48f733b..6b4433e4 100644
--- a/src/main/java/org/distorted/helpers/ObjectGridCreator.java
+++ b/src/main/java/org/distorted/helpers/ObjectGridCreator.java
@@ -9,6 +9,10 @@
 
 package org.distorted.helpers;
 
+import static org.distorted.main.MainScrollGrid.NUM_COLUMNS;
+import static org.distorted.main.MainScrollGrid.POPUP_MARGIN;
+import static org.distorted.main.MainScrollGrid.POPUP_PADDING;
+
 import android.app.Activity;
 import android.content.Context;
 import android.view.LayoutInflater;
@@ -19,10 +23,12 @@ import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
+import android.widget.TextView;
 
 import org.distorted.main.MainSettingsPopup;
 import org.distorted.main.R;
 import org.distorted.objects.RubikObject;
+import org.distorted.objects.RubikObjectCategories;
 import org.distorted.objects.RubikObjectList;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -30,7 +36,19 @@ import org.distorted.objects.RubikObjectList;
 public class ObjectGridCreator
   {
   private GridLayout mGrid;
+  private GridLayout[] mCategoryGrids;
+  private RubikObjectCategories mROC;
   private int mSortMode;
+  private final int mMargin, mPadding, mCubeSize;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectGridCreator(float screenW)
+    {
+    mMargin   = (int)(screenW*POPUP_MARGIN);
+    mPadding  = (int)(screenW*POPUP_PADDING);
+    mCubeSize = (int)(screenW/NUM_COLUMNS - 2*mMargin);
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -42,24 +60,62 @@ public class ObjectGridCreator
       }
     else
       {
-      return null;
+      if( mROC!=null )
+        {
+        int category = mROC.getCategory(index);
+        int posInCat = mROC.getPosInCat(category,index);
+        return mCategoryGrids[category].getChildAt(posInCat);
+        }
+      else
+        {
+        android.util.Log.e("D", "ERROR 1 in ObjectGridCreator!");
+        return null;
+        }
       }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void createObjectGrid(ScrollView scrollView, Activity act, int rowCount, int colCount, int numObjects, int margin, int size, int pad, int mode)
+  public int getObjectIndex(int index)
     {
-    mSortMode = mode;
+    if( mSortMode==MainSettingsPopup.SORT_CLASSIC )
+      {
+      return index;
+      }
+    else
+      {
+      if( mROC!=null )
+        {
+        int category = mROC.getCategory(index);
+        int posInCat = mROC.getPosInCat(category,index);
+        return mROC.getObjectIndex(category,posInCat);
+        }
+      else
+        {
+        android.util.Log.e("D", "ERROR 2 in ObjectGridCreator!");
+        return -1;
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    if( mode==MainSettingsPopup.SORT_CLASSIC )
+  public void createObjectGrid(Activity act, ScrollView scrollView, int sortMode)
+    {
+    mSortMode = sortMode;
+
+    if( sortMode==MainSettingsPopup.SORT_CLASSIC )
       {
       mGrid = new GridLayout(act);
       ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
       mGrid.setLayoutParams(params);
       scrollView.addView(mGrid);
 
-      createObjectGridClassic(mGrid,act,rowCount,colCount,numObjects,margin,size,pad);
+      int numObjects= RubikObjectList.getNumObjects();
+      int[] objects = new int[numObjects];
+      for(int i=0; i<numObjects; i++) objects[i] = i;
+
+      createObjectGridClassic(mGrid,act,objects);
       }
     else
       {
@@ -67,17 +123,99 @@ public class ObjectGridCreator
       ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
       layout.setLayoutParams(params);
       layout.setOrientation(LinearLayout.VERTICAL);
+      layout.setPadding(0,mMargin,0,mMargin);
       scrollView.addView(layout);
 
+      mROC = new RubikObjectCategories(sortMode);
+      if( mROC.hasIcons() ) constructIconBasedGrid(act,layout,mROC,mCubeSize);
+      else                  constructIconlessGrid(act,layout,mROC,mCubeSize);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
+  private void constructIconBasedGrid(Activity act, LinearLayout layout, RubikObjectCategories roc, int height)
+    {
+    int numCategories = roc.getNumCategories();
 
+    mCategoryGrids = new GridLayout[numCategories];
+
+    for(int c=0; c<numCategories; c++)
+      {
+      int iconID = roc.getIconId(c);
+      View title = constructTitle(act,iconID,height);
+      layout.addView(title);
+      mCategoryGrids[c] = constructGrid(act,c);
+      layout.addView(mCategoryGrids[c]);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void constructIconlessGrid(Activity act, LinearLayout layout, RubikObjectCategories roc, int height)
+    {
+    int numCategories = roc.getNumCategories();
+
+    mCategoryGrids = new GridLayout[numCategories];
+
+    for(int c=0; c<numCategories; c++)
+      {
+      String titleStr = roc.getTitle(c);
+      View title = constructTitle(act,titleStr,height);
+      layout.addView(title);
+      mCategoryGrids[c] = constructGrid(act,c);
+      layout.addView(mCategoryGrids[c]);
       }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static void createObjectGridClassic(GridLayout grid, Activity act, int rowCount, int colCount, int numObjects, int margin, int size, int pad)
+  private View constructTitle(Activity act, int iconID, int height)
+    {
+    ImageView view = new ImageView(act);
+    ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,height);
+    view.setLayoutParams(params);
+    view.setImageResource(iconID);
+
+    return view;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private View constructTitle(Activity act, String title, int height)
+    {
+    TextView view = new TextView(act);
+    ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,height);
+    view.setLayoutParams(params);
+    view.setText(title);
+
+    return view;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private GridLayout constructGrid(Activity act, int category)
+    {
+    GridLayout grid = new GridLayout(act);
+    ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
+    grid.setLayoutParams(params);
+
+    int numInCat = mROC.getNumObjects(category);
+    int[] objects = new int[numInCat];
+    for(int o=0; o<numInCat; o++) objects[o] = mROC.getObjectIndex(category,o);
+
+    createObjectGridClassic(grid,act,objects);
+
+    return grid;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void createObjectGridClassic(GridLayout grid, Activity act, int[] objects)
     {
+    int rowCount = (objects.length + NUM_COLUMNS-1) / NUM_COLUMNS;
+    int colCount = NUM_COLUMNS;
+
     GridLayout.Spec[] rowSpecs = new GridLayout.Spec[rowCount];
     GridLayout.Spec[] colSpecs = new GridLayout.Spec[colCount];
 
@@ -98,19 +236,19 @@ public class ObjectGridCreator
 
     LayoutInflater layoutInflater = (LayoutInflater)act.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
-    for(int object=0; object<numObjects; object++)
+    for(int object : objects)
       {
-      View v = createView(act,layoutInflater,object,pad);
+      View v = createView(act,layoutInflater,object,mPadding );
       int row = object/colCount;
 
       GridLayout.LayoutParams params = new GridLayout.LayoutParams(rowSpecs[row],colSpecs[nextInRow[row]]);
-      params.bottomMargin = margin;
-      params.topMargin    = margin;
-      params.leftMargin   = margin;
-      params.rightMargin  = margin;
+      params.bottomMargin = mMargin;
+      params.topMargin    = mMargin;
+      params.leftMargin   = mMargin;
+      params.rightMargin  = mMargin;
 
-      params.width = size;
-      params.height= size;
+      params.width = mCubeSize;
+      params.height= mCubeSize;
 
       nextInRow[row]++;
 
@@ -120,14 +258,14 @@ public class ObjectGridCreator
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static ImageButton getButton(RubikObject object, View view)
+  public ImageButton getButton(RubikObject object, View view)
     {
     return object!=null && object.isFree() ? (ImageButton)view : view.findViewById(R.id.non_free_button);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static View createView(Activity act, LayoutInflater inflater, int ordinal, int pad)
+  private View createView(Activity act, LayoutInflater inflater, int ordinal, int pad)
     {
     final RubikObject obj = RubikObjectList.getObject(ordinal);
 
diff --git a/src/main/java/org/distorted/main/MainScrollGrid.java b/src/main/java/org/distorted/main/MainScrollGrid.java
index 48b2f824..39c86a0e 100644
--- a/src/main/java/org/distorted/main/MainScrollGrid.java
+++ b/src/main/java/org/distorted/main/MainScrollGrid.java
@@ -33,22 +33,15 @@ public class MainScrollGrid
 
   void createGrid(final MainActivity act, int windowWidth, int sortMode)
     {
-    if( mCreator==null ) mCreator = new ObjectGridCreator();
+    if( mCreator==null ) mCreator = new ObjectGridCreator(windowWidth);
 
     mSortMode = sortMode;
 
     int numObjects = RubikObjectList.getNumObjects();
-    int rowCount   = (numObjects + NUM_COLUMNS-1) / NUM_COLUMNS;
-    int colCount   = NUM_COLUMNS;
-    int objectSize = windowWidth / NUM_COLUMNS;
-    int margin     = (int)(windowWidth*POPUP_MARGIN);
-    int padding    = (int)(windowWidth*POPUP_PADDING);
-    int cubeSize   = objectSize - 2*margin;
-
     ScrollView scrollView = act.findViewById(R.id.objectScroll);
     scrollView.removeAllViews();
 
-    mCreator.createObjectGrid(scrollView,act,rowCount,colCount,numObjects,margin,cubeSize,padding,sortMode);
+    mCreator.createObjectGrid(act,scrollView,sortMode);
 
     DisplayMetrics displaymetrics = new DisplayMetrics();
     act.getWindowManager().getDefaultDisplay().getRealMetrics(displaymetrics);
@@ -57,8 +50,8 @@ public class MainScrollGrid
       {
       final RubikObject obj = RubikObjectList.getObject(child);
       View v = mCreator.getChildAt(child);
-      ImageButton button = ObjectGridCreator.getButton(obj,v);
-      final int ordinal = child;
+      ImageButton button = mCreator.getButton(obj,v);
+      final int ordinal = mCreator.getObjectIndex(child);
 
       button.setOnClickListener( new View.OnClickListener()
         {
diff --git a/src/main/java/org/distorted/objects/RubikObjectCategories.java b/src/main/java/org/distorted/objects/RubikObjectCategories.java
new file mode 100644
index 00000000..6bd3536d
--- /dev/null
+++ b/src/main/java/org/distorted/objects/RubikObjectCategories.java
@@ -0,0 +1,188 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2024 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.objects;
+
+import static org.distorted.main.MainSettingsPopup.*;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikObjectCategories
+{
+  private final int mNumCategories;
+  private int[][] mObjectIndices;
+  private int[] mIconIDs;
+  private String[] mTitles;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikObjectCategories(int sortMode)
+    {
+    switch(sortMode)
+      {
+      case SORT_CATEGORY  : buildSortCategory(); break;
+      case SORT_DIFFICULTY: buildSortDifficulty(); break;
+      case SORT_AUTHOR    : buildSortAuthor(); break;
+      case SORT_YEAR      : buildSortYear(); break;
+      }
+
+    mNumCategories = mObjectIndices.length;
+
+    switch(sortMode)
+      {
+      case SORT_CATEGORY  : buildIconsCategory(); break;
+      case SORT_DIFFICULTY: buildIconsDifficulty(); break;
+      case SORT_AUTHOR    : buildTitleAuthor(); break;
+      case SORT_YEAR      : buildTitleYear(); break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void buildSortCategory()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void buildSortDifficulty()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void buildSortAuthor()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void buildSortYear()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void buildIconsCategory()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void buildIconsDifficulty()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void buildTitleAuthor()
+    {
+    mTitles = new String[mNumCategories];
+
+    for(int t=0; t<mNumCategories; t++)
+      {
+      int obj = mObjectIndices[t][0];
+      RubikObject object = RubikObjectList.getObject(obj);
+
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void buildTitleYear()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+
+  public int getNumCategories()
+    {
+    return mNumCategories;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean hasIcons()
+    {
+    return mIconIDs!=null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getIconId(int category)
+    {
+    return ( mIconIDs!=null && category>=0 && category<mNumCategories ) ? mIconIDs[category] : -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String getTitle(int category)
+    {
+    return (mTitles!=null && category>=0 && category<mNumCategories) ? mTitles[category] : null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getNumObjects(int category)
+    {
+    return (category>=0 && category<mNumCategories) ? mObjectIndices[category].length : 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getObjectIndex(int category, int index)
+    {
+    if( category>=0 && category<mNumCategories )
+      {
+      int[] objects = mObjectIndices[category];
+      if( index>=0 && index<objects.length ) return objects[index];
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getCategory(int objectIndex)
+    {
+    int numC = mObjectIndices.length;
+
+    for(int c=0; c<numC; c++)
+      {
+      int[] objects = mObjectIndices[c];
+      int len = objects.length;
+
+      for(int o=0; o<len; o++)
+        if( o==objectIndex ) return c;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getPosInCat(int category, int objectIndex)
+    {
+    int[] objects = mObjectIndices[category];
+    int len = objects.length;
+
+    for(int o=0; o<len; o++)
+      if( o==objectIndex ) return o;
+
+    return -1;
+    }
+}
