]> Shamusworld >> Repos - apple2/blobdiff - src/gui/diskselector.cpp
Improvements to timing, disk selector; added Double LoRes.
[apple2] / src / gui / diskselector.cpp
index dfdbd572d37098eae16bce72a49c2c28cb6c235f..d13d9bb7d112537b013f74affb6e229f5e891363 100644 (file)
 #include <vector>
 #include "apple2.h"
 #include "font10pt.h"
+#include "gui.h"
 #include "log.h"
 #include "settings.h"
 #include "video.h"
 
+// Icons, in GIMP "C" format
+#include "gfx/scroll-left.c"
+#include "gfx/scroll-right.c"
 
-enum { DSS_SHOWING, DSS_HIDING, DSS_SHOWN, DSS_HIDDEN };
 
-#define DS_WIDTH       400
-#define DS_HEIGHT      300
+struct Bitmap {
+       unsigned int width;
+       unsigned int height;
+       unsigned int bytesPerPixel;                                     // 3:RGB, 4:RGBA
+       unsigned char pixelData[];
+};
+
+enum { DSS_SHOWING, DSS_HIDING, DSS_SHOWN, DSS_HIDDEN, DSS_LSB_SHOWING, DSS_LSB_SHOWN, DSS_LSB_HIDING, DSS_RSB_SHOWING, DSS_RSB_SHOWN, DSS_RSB_HIDING, DSS_TEXT_SCROLLING };
+
+#define DS_WIDTH                       402
+#define DS_HEIGHT                      322
+#define SCROLL_HOT_WIDTH       48
+// Need to add logic for left/right scroll buttons (they show when the mouse
+// is in the left or right hand portion of the rect).
+#define DS_XPOS        ((VIRTUAL_SCREEN_WIDTH - DS_WIDTH) / 2)
+#define DS_YPOS        ((VIRTUAL_SCREEN_HEIGHT - DS_HEIGHT) / 2)
+
 
 bool entered = false;
 int driveNumber;
 int diskSelectorState = DSS_HIDDEN;
 int diskSelected = -1;
 int lastDiskSelected = -1;
+int numColumns;
+int colStart = 0;
+int dxLeft = 0;
+int dxRight = 0;
+int rsbPos = DS_WIDTH;
+int lsbPos = -40;
+int textScrollCount = 0;
+bool refresh = false;
+
+/*
+So, how this will work for multiple columns, where the number of columns is greater than 3, is to have an arrow button pop up on the left or right hand side (putting the mouse on the left or right side of the disk selector activates (shows) the button, if such a move can be made. Button hides when the mouse moves out of the hot zone or when it has no more effect.
+*/
 
 
 //
-// Case insensitve string comparison voodoo
+// Struct to hold filenames & full paths to same
 //
-struct ci_char_traits : public std::char_traits<char>
+struct FileStruct
 {
-       static bool eq(char c1, char c2) { return toupper(c1) == toupper(c2); }
-       static bool ne(char c1, char c2) { return toupper(c1) != toupper(c2); }
-       static bool lt(char c1, char c2) { return toupper(c1) <  toupper(c2); }
-       static int compare(const char * s1, const char * s2, size_t n)
-       {
-               while (n-- != 0)
-               {
-                       if (toupper(*s1) < toupper(*s2)) return -1;
-                       if (toupper(*s1) > toupper(*s2)) return 1;
-                       ++s1; ++s2;
-               }
-               return 0;
-       }
-       static const char * find(const char * s, int n, char a)
+       std::string image;
+       std::string fullPath;
+
+       // Functor, to presumably make the std::sort go faster
+       bool operator()(const FileStruct & a, const FileStruct & b) const
        {
-               while (n-- > 0 && toupper(*s) != toupper(a))
-               {
-                       ++s;
-               }
-               return s;
+               return (strcasecmp(a.image.c_str(), b.image.c_str()) < 0 ? true : false);
        }
 };
 
-typedef std::basic_string<char, ci_char_traits> ci_string;
-//
-// END Case insensitve string comparison voodoo
-//
-
 
 static SDL_Texture * window = NULL;
 static SDL_Texture * charStamp = NULL;
 static uint32_t windowPixels[DS_WIDTH * DS_HEIGHT];
 static uint32_t stamp[FONT_WIDTH * FONT_HEIGHT];
+SDL_Texture * scrollLeftIcon = NULL;
+SDL_Texture * scrollRightIcon = NULL;
 bool DiskSelector::showWindow = false;
-std::vector<ci_string> imageList;
+std::vector<FileStruct> fsList;
 
 
 void DiskSelector::Init(SDL_Renderer * renderer)
@@ -100,45 +114,87 @@ void DiskSelector::Init(SDL_Renderer * renderer)
        if (SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND) == -1)
                WriteLog("GUI (DiskSelector): Could not set blend mode for charStamp.\n");
 
+       scrollLeftIcon  = GUI::CreateTexture(renderer, &scroll_left);
+       scrollRightIcon = GUI::CreateTexture(renderer, &scroll_right);
+
        for(uint32_t i=0; i<DS_WIDTH*DS_HEIGHT; i++)
                windowPixels[i] = 0xEF007F00;
 
        SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
-       FindDisks(NULL);
+       FindDisks();
        DrawFilenames(renderer);
 }
 
 
+//
+// Find all disks images top level call
+//
+void DiskSelector::FindDisks(void)
+{
+       fsList.clear();
+       FindDisks(settings.disksPath);
+       std::sort(fsList.begin(), fsList.end(), FileStruct());
+       // Calculate the number of columns in the file selector...
+       numColumns = (int)ceilf((float)fsList.size() / 27.0f);
+       WriteLog("GUI (DiskSelector)::FindDisks(): # of columns is %i (%i files)\n", numColumns, fsList.size());
+}
+
+
+//
+// Find all disks images within path (recursive call does depth first search)
+//
 void DiskSelector::FindDisks(const char * path)
 {
-       DIR * dir = opendir(settings.disksPath);
+       DIR * dir = opendir(path);
 
        if (!dir)
        {
-               WriteLog("GUI (DiskSelector)::FindDisks: Could not open directory \"%s\%!\n", settings.disksPath);
+               WriteLog("GUI (DiskSelector)::FindDisks: Could not open directory \"%s\%!\n", path);
                return;
        }
 
-       imageList.clear();
        dirent * ent;
 
        while ((ent = readdir(dir)) != NULL)
        {
-               if (HasLegalExtension(ent->d_name))
-                       imageList.push_back(ci_string(ent->d_name));
+               char buf[0x10000];
+               sprintf(buf, "%s/%s", path, ent->d_name);
+
+               if ((ent->d_type == DT_REG) && HasLegalExtension(ent->d_name))
+               {
+                       FileStruct fs;
+                       fs.image = ent->d_name;
+                       fs.fullPath = buf;
+                       fsList.push_back(fs);
+               }
+               else if (ent->d_type == DT_DIR)
+               {
+                       // Only recurse if the directory is not one of the special ones...
+                       if ((strcmp(ent->d_name, "..") != 0)
+                               && (strcmp(ent->d_name, ".") != 0))
+                               FindDisks(buf);
+               }
        }
 
        closedir(dir);
-       std::sort(imageList.begin(), imageList.end());
 }
 
 
 bool DiskSelector::HasLegalExtension(const char * name)
 {
+       // Find the file's extension, if any
        const char * ext = strrchr(name, '.');
 
-       if ((strcasecmp(ext, ".dsk") == 0) || (strcasecmp(ext, ".do") == 0)
-               || (strcasecmp(ext, ".po") == 0) || (strcasecmp(ext, ".nib") == 0))
+       // No extension, so fuggetaboutit
+       if (ext == NULL)
+               return false;
+
+       // Otherwise, look for a legal extension
+       // We should be smarter than this, and look at headers & file sizes instead
+       if ((strcasecmp(ext, ".dsk") == 0)
+               || (strcasecmp(ext, ".do") == 0)
+               || (strcasecmp(ext, ".po") == 0)
+               || (strcasecmp(ext, ".nib") == 0))
                return true;
 
        return false;
@@ -158,8 +214,46 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
        // 3 columns of 21 chars apiece (with 6X11 font), 27 rows
 
        unsigned int count = 0;
+       unsigned int fsStart = colStart * 27;
+       int offset = 0;
+
+       // Draw partial columns (for scrolling left/right)
+       // [could probably combine these...]
+       if (textScrollCount < 0)
+       {
+               int partialColStart = (colStart - 1) * 27;
+               offset = -1 * textScrollCount;
 
-       while (count < imageList.size())
+               for(unsigned int y=0; y<27; y++)
+               {
+                       for(unsigned int i=22+textScrollCount, x=0; i<21; i++, x++)
+                       {
+                               if (i >= fsList[partialColStart + y].image.length())
+                                       break;
+
+                               DrawCharacter(renderer, x + 1, y + 1, fsList[partialColStart + y].image[i], false);
+                       }
+               }
+       }
+       else if (textScrollCount > 0)
+       {
+               offset = 22 - textScrollCount;
+
+               for(unsigned int y=0; y<27; y++)
+               {
+                       for(unsigned int i=textScrollCount, x=0; i<21; i++, x++)
+                       {
+                               if (i >= fsList[fsStart + y].image.length())
+                                       break;
+
+                               DrawCharacter(renderer, x + 1, y + 1, fsList[fsStart + y].image[i], false);
+                       }
+               }
+
+               fsStart += 27;
+       }
+
+       while (fsStart < fsList.size())
        {
 //             int currentX = (count / 18) * 17;
 //             int currentY = (count % 18);
@@ -172,14 +266,15 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
 //             for(unsigned int i=0; i<18; i++)
                for(unsigned int i=0; i<21; i++)
                {
-                       if (i >= imageList[count].length())
+                       if (i >= fsList[fsStart].image.length())
                                break;
 
-                       bool invert = (diskSelected == (int)count ? true : false);
-                       DrawCharacter(renderer, currentX + i, currentY, imageList[count][i], invert);
+                       bool invert = (diskSelected == (int)fsStart ? true : false);
+                       DrawCharacter(renderer, currentX + i + 1 + offset, currentY + 1, fsList[fsStart].image[i], invert);
                }
 
                count++;
+               fsStart++;
 
 //             if (count >= (18 * 3))
 //             if (count >= (24 * 3))
@@ -187,6 +282,18 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
                        break;
        }
 
+       // If a disk is selected, show it on the top line in inverse video
+       if (diskSelected > -1)
+       {
+               for(unsigned int i=0; i<65; i++)
+               {
+                       if (i >= fsList[diskSelected].image.length())
+                               break;
+
+                       DrawCharacter(renderer, i + 1, 0, fsList[diskSelected].image[i], true);
+               }
+       }
+
        // Set render target back to default
        SDL_SetRenderTarget(renderer, NULL);
 }
@@ -224,11 +331,43 @@ void DiskSelector::MouseDown(int32_t x, int32_t y, uint32_t buttons)
        if (!entered)
                return;
 
+       if ((diskSelectorState == DSS_LSB_SHOWING) || (diskSelectorState == DSS_LSB_SHOWN))
+       {
+               if (colStart > 0)
+               {
+                       colStart--;
+                       textScrollCount = 21;
+
+                       if (colStart == 0)
+                       {
+                               diskSelectorState = DSS_LSB_HIDING;
+                               dxLeft = -8;
+                       }
+               }
+
+               return;
+       }
+
+       if ((diskSelectorState == DSS_RSB_SHOWING) || (diskSelectorState == DSS_RSB_SHOWN))
+       {
+               if (colStart + 3 < numColumns)
+               {
+                       colStart++;
+                       textScrollCount = -21;
+
+                       if ((colStart + 3) == numColumns)
+                       {
+                               diskSelectorState = DSS_RSB_HIDING;
+                               dxRight = 8;
+                       }
+               }
+
+               return;
+       }
+
        if (diskSelected != -1)
        {
-               char buffer[2048];
-               sprintf(buffer, "%s/%s", settings.disksPath, &imageList[diskSelected][0]);
-               floppyDrive.LoadImage(buffer, driveNumber);
+               floppyDrive.LoadImage(fsList[diskSelected].fullPath.c_str(), driveNumber);
        }
 
        showWindow = false;
@@ -243,17 +382,19 @@ void DiskSelector::MouseUp(int32_t x, int32_t y, uint32_t buttons)
 }
 
 
-#define DS_XPOS        ((VIRTUAL_SCREEN_WIDTH - DS_WIDTH) / 2)
-#define DS_YPOS        ((VIRTUAL_SCREEN_HEIGHT - DS_HEIGHT) / 2)
 void DiskSelector::MouseMove(int32_t x, int32_t y, uint32_t buttons)
 {
        if (!showWindow)
                return;
 
+       // Check to see if DS has been hovered yet, and, if so, set a flag to show
+       // that it has
        if (!entered && ((x >= DS_XPOS) && (x <= (DS_XPOS + DS_WIDTH))
                && (y >= DS_YPOS) && (y <= (DS_YPOS + DS_HEIGHT))))
                entered = true;
 
+       // Check to see if the DS, since being hovered, is now no longer being
+       // hovered
        if (entered && ((x < DS_XPOS) || (x > (DS_XPOS + DS_WIDTH))
                || (y < DS_YPOS) || (y > (DS_YPOS + DS_HEIGHT))))
        {
@@ -261,11 +402,68 @@ void DiskSelector::MouseMove(int32_t x, int32_t y, uint32_t buttons)
                return;
        }
 
-       int xChar = (x - DS_XPOS) / FONT_WIDTH;
-       int yChar = (y - DS_YPOS) / FONT_HEIGHT;
-       diskSelected = ((xChar / 22) * 27) + yChar;
+       if (entered && (colStart > 0))
+       {
+               if (diskSelectorState != DSS_LSB_SHOWN)
+               {
+                       if (x < (DS_XPOS + SCROLL_HOT_WIDTH))
+                       {
+                               diskSelectorState = DSS_LSB_SHOWING;
+                               dxLeft = 8;
+                       }
+                       else
+                       {
+                               diskSelectorState = DSS_LSB_HIDING;
+                               dxLeft = -8;
+                       }
+               }
+               else
+               {
+                       if (x >= (DS_XPOS + SCROLL_HOT_WIDTH))
+                       {
+                               diskSelectorState = DSS_LSB_HIDING;
+                               dxLeft = -8;
+                       }
+               }
+       }
+
+       if (entered && ((colStart + 3) < numColumns))
+       {
+               if (diskSelectorState != DSS_RSB_SHOWN)
+               {
+                       if (x > (DS_XPOS + DS_WIDTH - SCROLL_HOT_WIDTH))
+                       {
+                               diskSelectorState = DSS_RSB_SHOWING;
+                               dxRight = -8;
+                       }
+                       else
+                       {
+                               diskSelectorState = DSS_RSB_HIDING;
+                               dxRight = 8;
+                       }
+               }
+               else
+               {
+                       if (x <= (DS_XPOS + DS_WIDTH - SCROLL_HOT_WIDTH))
+                       {
+                               diskSelectorState = DSS_RSB_HIDING;
+                               dxRight = 8;
+                       }
+               }
+       }
 
-       if ((yChar >= 27) || (diskSelected >= (int)imageList.size()))
+       // The -1 terms move the origin to the upper left corner (from 1 in, and 1
+       // down)
+       int xChar = ((x - DS_XPOS) / FONT_WIDTH) - 1;
+       int yChar = ((y - DS_YPOS) / FONT_HEIGHT) - 1;
+       diskSelected = ((xChar / 22) * 27) + yChar + (colStart * 27);
+
+       if ((yChar < 0) || (yChar >= 27)
+               || (diskSelected >= (int)fsList.size())
+               || (diskSelectorState == DSS_LSB_SHOWING)
+               || (diskSelectorState == DSS_LSB_SHOWN)
+               || (diskSelectorState == DSS_RSB_SHOWING)
+               || (diskSelectorState == DSS_RSB_SHOWN))
                diskSelected = -1;
 
        if (diskSelected != lastDiskSelected)
@@ -276,10 +474,64 @@ void DiskSelector::MouseMove(int32_t x, int32_t y, uint32_t buttons)
 }
 
 
+void DiskSelector::HandleGUIState(void)
+{
+       lsbPos += dxLeft;
+       rsbPos += dxRight;
+
+       if ((lsbPos > (SCROLL_HOT_WIDTH - 40)) && (diskSelectorState == DSS_LSB_SHOWING))
+       {
+               diskSelectorState = DSS_LSB_SHOWN;
+               lsbPos = SCROLL_HOT_WIDTH - 40;
+               dxLeft = 0;
+       }
+       else if ((lsbPos < -40) && (diskSelectorState == DSS_LSB_HIDING))
+       {
+               diskSelectorState = DSS_SHOWN;
+               lsbPos = -40;
+               dxLeft = 0;
+       }
+       else if ((rsbPos < (DS_WIDTH - SCROLL_HOT_WIDTH)) && (diskSelectorState == DSS_RSB_SHOWING))
+       {
+               diskSelectorState = DSS_RSB_SHOWN;
+               rsbPos = DS_WIDTH - SCROLL_HOT_WIDTH;
+               dxRight = 0;
+       }
+       else if ((rsbPos > DS_WIDTH) && (diskSelectorState == DSS_RSB_HIDING))
+       {
+               diskSelectorState = DSS_SHOWN;
+               rsbPos = DS_WIDTH;
+               dxRight = 0;
+       }
+
+       if (textScrollCount < 0)
+       {
+               textScrollCount += 2;
+
+               if (textScrollCount > 0)
+               {
+                       textScrollCount = 0;
+                       refresh = true;
+               }
+       }
+       else if (textScrollCount > 0)
+       {
+               textScrollCount -= 2;
+
+               if (textScrollCount < 0)
+               {
+                       textScrollCount = 0;
+                       refresh = true;
+               }
+       }
+}
+
+
 void DiskSelector::HandleSelection(SDL_Renderer * renderer)
 {
        SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
        DrawFilenames(renderer);
+       refresh = false;
 }
 
 
@@ -288,6 +540,24 @@ void DiskSelector::Render(SDL_Renderer * renderer)
        if (!(window && showWindow))
                return;
 
+       HandleGUIState();
+
+       if (((diskSelectorState != DSS_LSB_SHOWN)
+               && (diskSelectorState != DSS_RSB_SHOWN)
+               && (diskSelectorState != DSS_SHOWN))
+               || (textScrollCount != 0) || refresh)
+               HandleSelection(renderer);
+
+       // Render scroll arrows (need to figure out why no alpha!)
+       SDL_SetRenderTarget(renderer, window);
+       SDL_Rect dst2 = { 0, ((DS_HEIGHT - 40) / 2), 40, 40 };
+       dst2.x = lsbPos;
+       SDL_RenderCopy(renderer, scrollLeftIcon, NULL, &dst2);
+       SDL_Rect dst3 = { 0, ((DS_HEIGHT - 40) / 2), 40, 40 };
+       dst3.x = rsbPos;
+       SDL_RenderCopy(renderer, scrollRightIcon, NULL, &dst3);
+       SDL_SetRenderTarget(renderer, NULL);
+
        SDL_Rect dst = { DS_XPOS, DS_YPOS, DS_WIDTH, DS_HEIGHT };
        SDL_RenderCopy(renderer, window, NULL, &dst);
 }