4 // Floppy disk selector GUI
6 // © 2014 Underground Software
8 // JLH = James Hammons <jlhamm@acm.org>
11 // --- ---------- -----------------------------------------------------------
12 // JLH 10/13/2013 Created this file
18 #include "diskselector.h"
30 // Icons, in GIMP "C" format
31 #include "gfx/scroll-left.c"
32 #include "gfx/scroll-right.c"
38 unsigned int bytesPerPixel; // 3:RGB, 4:RGBA
39 unsigned char pixelData[];
42 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 };
46 #define SCROLL_HOT_WIDTH 48
47 // Need to add logic for left/right scroll buttons (they show when the mouse
48 // is in the left or right hand portion of the rect).
49 #define DS_XPOS ((VIRTUAL_SCREEN_WIDTH - DS_WIDTH) / 2)
50 #define DS_YPOS ((VIRTUAL_SCREEN_HEIGHT - DS_HEIGHT) / 2)
55 int diskSelectorState = DSS_HIDDEN;
56 int diskSelected = -1;
57 int lastDiskSelected = -1;
62 int rsbPos = DS_WIDTH;
64 int textScrollCount = 0;
68 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.
73 // Struct to hold filenames & full paths to same
80 // Functor, to presumably make the std::sort go faster
81 bool operator()(const FileStruct & a, const FileStruct & b) const
83 return (strcasecmp(a.image.c_str(), b.image.c_str()) < 0 ? true : false);
88 static SDL_Texture * window = NULL;
89 static SDL_Texture * charStamp = NULL;
90 static uint32_t windowPixels[DS_WIDTH * DS_HEIGHT];
91 static uint32_t stamp[FONT_WIDTH * FONT_HEIGHT];
92 SDL_Texture * scrollLeftIcon = NULL;
93 SDL_Texture * scrollRightIcon = NULL;
94 bool DiskSelector::showWindow = false;
95 std::vector<FileStruct> fsList;
98 void DiskSelector::Init(SDL_Renderer * renderer)
100 window = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
101 SDL_TEXTUREACCESS_TARGET, DS_WIDTH, DS_HEIGHT);
102 charStamp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
103 SDL_TEXTUREACCESS_TARGET, FONT_WIDTH, FONT_HEIGHT);
107 WriteLog("GUI (DiskSelector): Could not create window!\n");
111 if (SDL_SetTextureBlendMode(window, SDL_BLENDMODE_BLEND) == -1)
112 WriteLog("GUI (DiskSelector): Could not set blend mode for window.\n");
114 if (SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND) == -1)
115 WriteLog("GUI (DiskSelector): Could not set blend mode for charStamp.\n");
117 scrollLeftIcon = GUI::CreateTexture(renderer, &scroll_left);
118 scrollRightIcon = GUI::CreateTexture(renderer, &scroll_right);
120 for(uint32_t i=0; i<DS_WIDTH*DS_HEIGHT; i++)
121 windowPixels[i] = 0xEF007F00;
123 SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
125 DrawFilenames(renderer);
130 // Find all disks images top level call
132 void DiskSelector::FindDisks(void)
135 FindDisks(settings.disksPath);
136 std::sort(fsList.begin(), fsList.end(), FileStruct());
137 // Calculate the number of columns in the file selector...
138 numColumns = (int)ceilf((float)fsList.size() / 27.0f);
139 WriteLog("GUI (DiskSelector)::FindDisks(): # of columns is %i (%i files)\n", numColumns, fsList.size());
144 // Find all disks images within path (recursive call does depth first search)
146 void DiskSelector::FindDisks(const char * path)
148 DIR * dir = opendir(path);
152 WriteLog("GUI (DiskSelector)::FindDisks: Could not open directory \"%s\%!\n", path);
158 while ((ent = readdir(dir)) != NULL)
161 sprintf(buf, "%s/%s", path, ent->d_name);
163 if ((ent->d_type == DT_REG) && HasLegalExtension(ent->d_name))
166 fs.image = ent->d_name;
168 fsList.push_back(fs);
170 else if (ent->d_type == DT_DIR)
172 // Only recurse if the directory is not one of the special ones...
173 if ((strcmp(ent->d_name, "..") != 0)
174 && (strcmp(ent->d_name, ".") != 0))
183 bool DiskSelector::HasLegalExtension(const char * name)
185 // Find the file's extension, if any
186 const char * ext = strrchr(name, '.');
188 // No extension, so fuggetaboutit
192 // Otherwise, look for a legal extension
193 // We should be smarter than this, and look at headers & file sizes instead
194 if ((strcasecmp(ext, ".dsk") == 0)
195 || (strcasecmp(ext, ".do") == 0)
196 || (strcasecmp(ext, ".po") == 0)
197 || (strcasecmp(ext, ".nib") == 0))
204 void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
206 if (SDL_SetRenderTarget(renderer, window) < 0)
208 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
212 // 3 columns of 16 chars apiece (with 8X16 font), 18 rows
213 // 3 columns of 18 chars apiece (with 7X12 font), 24 rows
214 // 3 columns of 21 chars apiece (with 6X11 font), 27 rows
216 unsigned int count = 0;
217 unsigned int fsStart = colStart * 27;
220 // Draw partial columns (for scrolling left/right)
221 // [could probably combine these...]
222 if (textScrollCount < 0)
224 int partialColStart = (colStart - 1) * 27;
225 offset = -1 * textScrollCount;
227 for(unsigned int y=0; y<27; y++)
229 for(unsigned int i=22+textScrollCount, x=0; i<21; i++, x++)
231 if (i >= fsList[partialColStart + y].image.length())
234 DrawCharacter(renderer, x + 1, y + 1, fsList[partialColStart + y].image[i], false);
238 else if (textScrollCount > 0)
240 offset = 22 - textScrollCount;
242 for(unsigned int y=0; y<27; y++)
244 for(unsigned int i=textScrollCount, x=0; i<21; i++, x++)
246 if (i >= fsList[fsStart + y].image.length())
249 DrawCharacter(renderer, x + 1, y + 1, fsList[fsStart + y].image[i], false);
256 while (fsStart < fsList.size())
258 // int currentX = (count / 18) * 17;
259 // int currentY = (count % 18);
260 // int currentX = (count / 24) * 19;
261 // int currentY = (count % 24);
262 int currentX = (count / 27) * 22;
263 int currentY = (count % 27);
265 // for(unsigned int i=0; i<16; i++)
266 // for(unsigned int i=0; i<18; i++)
267 for(unsigned int i=0; i<21; i++)
269 if (i >= fsList[fsStart].image.length())
272 bool invert = (diskSelected == (int)fsStart ? true : false);
273 DrawCharacter(renderer, currentX + i + 1 + offset, currentY + 1, fsList[fsStart].image[i], invert);
279 // if (count >= (18 * 3))
280 // if (count >= (24 * 3))
281 if (count >= (27 * 3))
285 // If a disk is selected, show it on the top line in inverse video
286 if (diskSelected > -1)
288 for(unsigned int i=0; i<65; i++)
290 if (i >= fsList[diskSelected].image.length())
293 DrawCharacter(renderer, i + 1, 0, fsList[diskSelected].image[i], true);
297 // Set render target back to default
298 SDL_SetRenderTarget(renderer, NULL);
302 void DiskSelector::DrawCharacter(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*=false*/)
304 uint32_t inv = (invert ? 0x000000FF : 0x00000000);
305 uint32_t pixel = 0xFFFFC000; // RRGGBBAA
306 uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
308 dst.x = x * FONT_WIDTH, dst.y = y * FONT_HEIGHT, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
310 for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
311 stamp[i] = (pixel | ptr[i]) ^ inv;
313 SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
314 SDL_RenderCopy(renderer, charStamp, NULL, &dst);
318 void DiskSelector::ShowWindow(int drive)
326 void DiskSelector::MouseDown(int32_t x, int32_t y, uint32_t buttons)
334 if ((diskSelectorState == DSS_LSB_SHOWING) || (diskSelectorState == DSS_LSB_SHOWN))
339 textScrollCount = 21;
343 diskSelectorState = DSS_LSB_HIDING;
351 if ((diskSelectorState == DSS_RSB_SHOWING) || (diskSelectorState == DSS_RSB_SHOWN))
353 if (colStart + 3 < numColumns)
356 textScrollCount = -21;
358 if ((colStart + 3) == numColumns)
360 diskSelectorState = DSS_RSB_HIDING;
368 if (diskSelected != -1)
370 floppyDrive.LoadImage(fsList[diskSelected].fullPath.c_str(), driveNumber);
377 void DiskSelector::MouseUp(int32_t x, int32_t y, uint32_t buttons)
385 void DiskSelector::MouseMove(int32_t x, int32_t y, uint32_t buttons)
390 // Check to see if DS has been hovered yet, and, if so, set a flag to show
392 if (!entered && ((x >= DS_XPOS) && (x <= (DS_XPOS + DS_WIDTH))
393 && (y >= DS_YPOS) && (y <= (DS_YPOS + DS_HEIGHT))))
396 // Check to see if the DS, since being hovered, is now no longer being
398 if (entered && ((x < DS_XPOS) || (x > (DS_XPOS + DS_WIDTH))
399 || (y < DS_YPOS) || (y > (DS_YPOS + DS_HEIGHT))))
405 if (entered && (colStart > 0))
407 if (diskSelectorState != DSS_LSB_SHOWN)
409 if (x < (DS_XPOS + SCROLL_HOT_WIDTH))
411 diskSelectorState = DSS_LSB_SHOWING;
416 diskSelectorState = DSS_LSB_HIDING;
422 if (x >= (DS_XPOS + SCROLL_HOT_WIDTH))
424 diskSelectorState = DSS_LSB_HIDING;
430 if (entered && ((colStart + 3) < numColumns))
432 if (diskSelectorState != DSS_RSB_SHOWN)
434 if (x > (DS_XPOS + DS_WIDTH - SCROLL_HOT_WIDTH))
436 diskSelectorState = DSS_RSB_SHOWING;
441 diskSelectorState = DSS_RSB_HIDING;
447 if (x <= (DS_XPOS + DS_WIDTH - SCROLL_HOT_WIDTH))
449 diskSelectorState = DSS_RSB_HIDING;
455 // The -1 terms move the origin to the upper left corner (from 1 in, and 1
457 int xChar = ((x - DS_XPOS) / FONT_WIDTH) - 1;
458 int yChar = ((y - DS_YPOS) / FONT_HEIGHT) - 1;
459 diskSelected = ((xChar / 22) * 27) + yChar + (colStart * 27);
461 if ((yChar < 0) || (yChar >= 27)
462 || (diskSelected >= (int)fsList.size())
463 || (diskSelectorState == DSS_LSB_SHOWING)
464 || (diskSelectorState == DSS_LSB_SHOWN)
465 || (diskSelectorState == DSS_RSB_SHOWING)
466 || (diskSelectorState == DSS_RSB_SHOWN))
469 if (diskSelected != lastDiskSelected)
471 HandleSelection(sdlRenderer);
472 lastDiskSelected = diskSelected;
477 void DiskSelector::HandleGUIState(void)
482 if ((lsbPos > (SCROLL_HOT_WIDTH - 40)) && (diskSelectorState == DSS_LSB_SHOWING))
484 diskSelectorState = DSS_LSB_SHOWN;
485 lsbPos = SCROLL_HOT_WIDTH - 40;
488 else if ((lsbPos < -40) && (diskSelectorState == DSS_LSB_HIDING))
490 diskSelectorState = DSS_SHOWN;
494 else if ((rsbPos < (DS_WIDTH - SCROLL_HOT_WIDTH)) && (diskSelectorState == DSS_RSB_SHOWING))
496 diskSelectorState = DSS_RSB_SHOWN;
497 rsbPos = DS_WIDTH - SCROLL_HOT_WIDTH;
500 else if ((rsbPos > DS_WIDTH) && (diskSelectorState == DSS_RSB_HIDING))
502 diskSelectorState = DSS_SHOWN;
507 if (textScrollCount < 0)
509 textScrollCount += 2;
511 if (textScrollCount > 0)
517 else if (textScrollCount > 0)
519 textScrollCount -= 2;
521 if (textScrollCount < 0)
530 void DiskSelector::HandleSelection(SDL_Renderer * renderer)
532 SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
533 DrawFilenames(renderer);
538 void DiskSelector::Render(SDL_Renderer * renderer)
540 if (!(window && showWindow))
545 if (((diskSelectorState != DSS_LSB_SHOWN)
546 && (diskSelectorState != DSS_RSB_SHOWN)
547 && (diskSelectorState != DSS_SHOWN))
548 || (textScrollCount != 0) || refresh)
549 HandleSelection(renderer);
551 // Render scroll arrows (need to figure out why no alpha!)
552 SDL_SetRenderTarget(renderer, window);
553 SDL_Rect dst2 = { 0, ((DS_HEIGHT - 40) / 2), 40, 40 };
555 SDL_RenderCopy(renderer, scrollLeftIcon, NULL, &dst2);
556 SDL_Rect dst3 = { 0, ((DS_HEIGHT - 40) / 2), 40, 40 };
558 SDL_RenderCopy(renderer, scrollRightIcon, NULL, &dst3);
559 SDL_SetRenderTarget(renderer, NULL);
561 SDL_Rect dst = { DS_XPOS, DS_YPOS, DS_WIDTH, DS_HEIGHT };
562 SDL_RenderCopy(renderer, window, NULL, &dst);