+bool exitGUI = false; // GUI (emulator) done variable
+int mouseX, mouseY;
+uint32 background[1280 * 256]; // GUI background buffer
+
+char separator[] = "--------------------------------------------------------";
+
+//
+// Case insensitive string compare function
+// Taken straight out of Thinking In C++ by Bruce Eckel. Thanks Bruce!
+//
+
+int stringCmpi(const string &s1, const string &s2)
+{
+ // Select the first element of each string:
+ string::const_iterator p1 = s1.begin(), p2 = s2.begin();
+
+ while (p1 != s1.end() && p2 != s2.end()) // Don\92t run past the end
+ {
+ if (toupper(*p1) != toupper(*p2)) // Compare upper-cased chars
+ return (toupper(*p1) < toupper(*p2) ? -1 : 1);// Report which was lexically greater
+
+ p1++;
+ p2++;
+ }
+
+ // If they match up to the detected eos, say which was longer. Return 0 if the same.
+ return s2.size() - s1.size();
+}
+
+//
+// Local GUI classes
+//
+
+enum { WINDOW_CLOSE, MENU_ITEM_CHOSEN };
+
+class Element
+{
+ public:
+ Element(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0)
+ { extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
+ virtual void HandleKey(SDLKey key) = 0; // These are "pure" virtual functions...
+ virtual void HandleMouseMove(uint32 x, uint32 y) = 0;
+ virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) = 0;
+ virtual void Draw(uint32, uint32) = 0;
+ virtual void Notify(Element *) = 0;
+//Needed? virtual ~Element() = 0;
+//We're not allocating anything in the base class, so the answer would be NO.
+ bool Inside(uint32 x, uint32 y);
+ // Class method
+// static void SetScreenAndPitch(int16 * s, uint32 p) { screenBuffer = s, pitch = p; }
+ static void SetScreenAndPitch(uint32 * s, uint32 p) { screenBuffer = s, pitch = p; }
+
+ protected:
+ SDL_Rect extents;
+ uint32 state;
+ // Class variables...
+// static int16 * screenBuffer;
+ static uint32 * screenBuffer;
+ static uint32 pitch;
+};
+
+// Initialize class variables (Element)
+//int16 * Element::screenBuffer = NULL;
+uint32 * Element::screenBuffer = NULL;
+uint32 Element::pitch = 0;
+
+bool Element::Inside(uint32 x, uint32 y)
+{
+ return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
+ && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false);
+}
+
+
+class Button: public Element
+{
+ public:
+ Button(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
+ activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
+ bgColor(0xFF00FF00), pic(NULL), elementToTell(NULL) {}
+ Button(uint32 x, uint32 y, uint32 w, uint32 h, uint32 * p): Element(x, y, w, h),
+ activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
+ bgColor(0xFF00FF00), pic(p), elementToTell(NULL) {}
+// Button(uint32 x, uint32 y, uint32 * p): Element(x, y, 0, 0),
+ Button(uint32 x, uint32 y, uint32 * p, uint32 * pH = NULL, uint32 * pD = NULL): Element(x, y, 0, 0),
+ activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
+ bgColor(0xFF00FF00), pic(p), picHover(pH), picDown(pD), elementToTell(NULL)
+ { if (pic) extents.w = pic[0], extents.h = pic[1]; }
+ Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
+ activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
+ bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL) {}
+ Button(uint32 x, uint32 y, string s): Element(x, y, 0, FONT_HEIGHT),
+ activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
+ bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL)
+ { extents.w = s.length() * FONT_WIDTH; }
+ virtual void HandleKey(SDLKey key) {}
+ virtual void HandleMouseMove(uint32 x, uint32 y);
+ virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
+ virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
+ virtual void Notify(Element *) {}
+ bool ButtonClicked(void) { return activated; }
+ void SetNotificationElement(Element * e) { elementToTell = e; }
+
+ protected:
+ bool activated, clicked, inside;
+ uint32 fgColor, bgColor;
+ uint32 * pic, * picHover, * picDown;
+ string text;
+ Element * elementToTell;
+};
+
+void Button::HandleMouseMove(uint32 x, uint32 y)
+{
+ inside = Inside(x, y);
+}
+
+void Button::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
+{
+ if (inside)
+ {
+ if (mouseDown)
+ clicked = true;
+
+ if (clicked && !mouseDown)
+ {
+ clicked = false, activated = true;
+
+ // Send a message that we're activated (if there's someone to tell, that is)
+ if (elementToTell)
+ elementToTell->Notify(this);
+ }
+ }
+ else
+ clicked = activated = false;
+}
+
+void Button::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
+{
+ uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
+
+ if (text.length() > 0) // Simple text button
+// if (pic == NULL)
+ {
+ for(uint32 y=0; y<extents.h; y++)
+ {
+ for(uint32 x=0; x<extents.w; x++)
+ {
+ // Doesn't clip in y axis! !!! FIX !!!
+ if (extents.x + x < pitch)
+ screenBuffer[addr + x + (y * pitch)]
+// = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
+//43F0 -> 010000 11111 10000 -> 0100 0001 1111 1111 1000 0100 -> 41 FF 84
+ = (clicked && inside ? fgColor : (inside ? 0xFF84FF41 : bgColor));
+ }
+ }
+
+ DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
+ }
+ else // Graphical button
+ {
+ uint32 * picToShow = pic;
+
+ if (picHover != NULL && inside && !clicked)
+ picToShow = picHover;
+
+ if (picDown != NULL && inside && clicked)
+ picToShow = picDown;
+
+ DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, picToShow);
+ }
+}
+
+
+class PushButton: public Element
+{
+// How to handle?
+// Save state externally?
+//We pass in a state variable if we want to track it externally, otherwise we use our own
+//internal state var. Still need to do some kind of callback for pushbuttons that do things
+//like change from fullscreen to windowed... !!! FIX !!!
+
+ public:
+// PushButton(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
+// activated(false), clicked(false), inside(false), fgColor(0xFFFF),
+// bgColor(0x03E0), pic(NULL), elementToTell(NULL) {}
+ PushButton(uint32 x, uint32 y, bool * st, string s): Element(x, y, 8, 8), state(st),
+ inside(false), text(s) { if (st == NULL) state = &internalState; }
+/* Button(uint32 x, uint32 y, uint32 w, uint32 h, uint32 * p): Element(x, y, w, h),
+ activated(false), clicked(false), inside(false), fgColor(0xFFFF),
+ bgColor(0x03E0), pic(p), elementToTell(NULL) {}
+ Button(uint32 x, uint32 y, uint32 * p): Element(x, y, 0, 0),
+ activated(false), clicked(false), inside(false), fgColor(0xFFFF),
+ bgColor(0x03E0), pic(p), elementToTell(NULL)
+ { if (pic) extents.w = pic[0], extents.h = pic[1]; }
+ Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
+ activated(false), clicked(false), inside(false), fgColor(0xFFFF),
+ bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL) {}
+ PushButton(uint32 x, uint32 y, string s): Element(x, y, 0, 8),
+ activated(false), clicked(false), inside(false), fgColor(0xFFFF),
+ bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL)
+ { extents.w = s.length() * 8; }*/
+ virtual void HandleKey(SDLKey key) {}
+ virtual void HandleMouseMove(uint32 x, uint32 y);
+ virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
+ virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
+ virtual void Notify(Element *) {}
+// bool ButtonClicked(void) { return activated; }
+// void SetNotificationElement(Element * e) { elementToTell = e; }
+
+ protected:
+ bool * state;
+ bool inside;
+// bool activated, clicked, inside;
+// uint16 fgColor, bgColor;
+// uint32 * pic;
+ string text;
+// Element * elementToTell;
+ bool internalState;
+};
+
+void PushButton::HandleMouseMove(uint32 x, uint32 y)
+{
+ inside = Inside(x, y);
+}
+
+void PushButton::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
+{
+ if (inside && mouseDown)
+ {
+/* if (mouseDown)
+ clicked = true;
+
+ if (clicked && !mouseDown)
+ {
+ clicked = false, activated = true;
+
+ // Send a message that we're activated (if there's someone to tell, that is)
+ if (elementToTell)
+ elementToTell->Notify(this);
+ }*/
+ *state = !(*state);
+ }
+// else
+// clicked = activated = false;
+}
+
+void PushButton::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
+{
+/* uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
+
+ for(uint32 y=0; y<extents.h; y++)
+ {
+ for(uint32 x=0; x<extents.w; x++)
+ {
+ // Doesn't clip in y axis! !!! FIX !!!
+ if (extents.x + x < pitch)
+ screenBuffer[addr + x + (y * pitch)]
+ = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
+ }
+ }*/
+
+ DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, (*state ? pushButtonDown : pushButtonUp));
+// DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, (*state ? pushButtonDown : pushButtonUp), (*state ? pbdAlpha : pbuAlpha));
+ if (text.length() > 0)
+ DrawString(screenBuffer, extents.x + offsetX + 12, extents.y + offsetY, false, "%s", text.c_str());
+}
+
+
+class SlideSwitch: public Element
+{
+// How to handle?
+// Save state externally?
+//Seems to be handled the same as PushButton, but without sanity checks. !!! FIX !!!
+
+ public:
+ SlideSwitch(uint32 x, uint32 y, bool * st, string s1, string s2): Element(x, y, 8, 16), state(st),
+ inside(false), text1(s1), text2(s2) {}
+ virtual void HandleKey(SDLKey key) {}
+ virtual void HandleMouseMove(uint32 x, uint32 y);
+ virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
+ virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
+ virtual void Notify(Element *) {}
+// bool ButtonClicked(void) { return activated; }
+// void SetNotificationElement(Element * e) { elementToTell = e; }
+
+ protected:
+ bool * state;
+ bool inside;
+// bool activated, clicked, inside;
+// uint16 fgColor, bgColor;
+// uint32 * pic;
+ string text1, text2;
+// Element * elementToTell;
+};
+
+void SlideSwitch::HandleMouseMove(uint32 x, uint32 y)
+{
+ inside = Inside(x, y);
+}
+
+void SlideSwitch::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
+{
+ if (inside && mouseDown)
+ {
+/* if (mouseDown)
+ clicked = true;
+
+ if (clicked && !mouseDown)
+ {
+ clicked = false, activated = true;
+
+ // Send a message that we're activated (if there's someone to tell, that is)
+ if (elementToTell)
+ elementToTell->Notify(this);
+ }*/
+ *state = !(*state);
+ }
+// else
+// clicked = activated = false;
+}
+
+void SlideSwitch::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
+{
+ DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, (*state ? slideSwitchDown : slideSwitchUp));
+ if (text1.length() > 0)
+ DrawString(screenBuffer, extents.x + offsetX + 12, extents.y + offsetY, false, "%s", text1.c_str());
+ if (text2.length() > 0)
+ DrawString(screenBuffer, extents.x + offsetX + 12, extents.y + offsetY + 8, false, "%s", text2.c_str());
+}
+
+
+class Window: public Element
+{
+ public:
+/* Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
+ fgColor(0x4FF0), bgColor(0xFE10)
+ { close = new Button(w - 8, 1, closeBox); list.push_back(close); }*/
+ Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0,
+ void (* f)(Element *) = NULL): Element(x, y, w, h),
+// /*clicked(false), inside(false),*/ fgColor(0x4FF0), bgColor(0x1E10),
+//4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
+//1E10 -> 000111 10000 10000 -> 0001 1111 1000 0100 1000 0100 -> 1F 84 84
+ /*clicked(false), inside(false),*/ fgColor(0xFF84FF4D), bgColor(0xFF84841F),
+ handler(f)
+ { close = new Button(w - (CLOSEBOX_WIDTH + 1), 1, closeBox, closeBoxHover, closeBoxDown);
+ list.push_back(close);
+ close->SetNotificationElement(this); }
+ virtual ~Window();
+ virtual void HandleKey(SDLKey key);
+ virtual void HandleMouseMove(uint32 x, uint32 y);
+ virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
+ virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
+ virtual void Notify(Element * e);
+ void AddElement(Element * e);
+// bool WindowActive(void) { return true; }//return !close->ButtonClicked(); }
+
+ protected:
+// bool clicked, inside;
+ uint32 fgColor, bgColor;
+ void (* handler)(Element *);
+ Button * close;
+//We have to use a list of Element *pointers* because we can't make a list that will hold
+//all the different object types in the same list...
+ vector<Element *> list;
+};
+
+Window::~Window()
+{
+ for(uint32 i=0; i<list.size(); i++)
+ if (list[i])
+ delete list[i];
+}
+
+void Window::HandleKey(SDLKey key)
+{
+ if (key == SDLK_ESCAPE)
+ {
+ SDL_Event event;
+ event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
+ SDL_PushEvent(&event);
+ }
+
+ // Handle the items this window contains...
+ for(uint32 i=0; i<list.size(); i++)
+ // Make coords relative to upper right corner of this window...
+ list[i]->HandleKey(key);
+}
+
+void Window::HandleMouseMove(uint32 x, uint32 y)
+{
+ // Handle the items this window contains...
+ for(uint32 i=0; i<list.size(); i++)
+ // Make coords relative to upper right corner of this window...
+ list[i]->HandleMouseMove(x - extents.x, y - extents.y);
+}
+
+void Window::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
+{
+ // Handle the items this window contains...
+ for(uint32 i=0; i<list.size(); i++)
+ // Make coords relative to upper right corner of this window...
+ list[i]->HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
+}
+
+void Window::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
+{
+ uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
+
+ for(uint32 y=0; y<extents.h; y++)
+ {
+ for(uint32 x=0; x<extents.w; x++)
+ {
+ // Doesn't clip in y axis! !!! FIX !!!
+ if (extents.x + x < pitch)
+ screenBuffer[addr + x + (y * pitch)] = bgColor;
+ }
+ }
+
+ // Handle the items this window contains...
+ for(uint32 i=0; i<list.size(); i++)
+ list[i]->Draw(extents.x, extents.y);
+}
+
+void Window::AddElement(Element * e)
+{
+ list.push_back(e);
+}
+
+void Window::Notify(Element * e)
+{
+ if (e == close)
+ {
+ SDL_Event event;
+ event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
+ SDL_PushEvent(&event);
+ }
+}
+
+
+class Text: public Element
+{
+ public:
+// Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
+// fgColor(0x4FF0), bgColor(0xFE10) {}
+// Text(uint32 x, uint32 y, string s, uint16 fg = 0x4FF0, uint16 bg = 0xFE10): Element(x, y, 0, 0),
+// fgColor(fg), bgColor(bg), text(s) {}
+//4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
+//FE10 -> 111111 10000 10000 -> 1111 1111 1000 0100 1000 0100 -> FF 84 84
+ Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
+ fgColor(0xFF8484FF), bgColor(0xFF84FF4D) {}
+ Text(uint32 x, uint32 y, string s, uint32 fg = 0xFF8484FF, uint32 bg = 0xFF84FF4D):
+ Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s) {}
+ virtual void HandleKey(SDLKey key) {}
+ virtual void HandleMouseMove(uint32 x, uint32 y) {}
+ virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
+ virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
+ virtual void Notify(Element *) {}
+
+ protected:
+ uint32 fgColor, bgColor;
+ string text;
+};
+
+void Text::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
+{
+ if (text.length() > 0)
+// DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
+ DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
+}
+
+
+class ListBox: public Element
+//class ListBox: public Window
+{
+ public:
+// ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
+ ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);//: Window(x, y, w, h),
+// windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1), charHeight(h / 8),
+// elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
+// downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox) {}
+ virtual void HandleKey(SDLKey key);
+ virtual void HandleMouseMove(uint32 x, uint32 y);
+ virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
+ virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
+ virtual void Notify(Element * e);
+ void SetNotificationElement(Element * e) { elementToTell = e; }
+ void AddItem(string s);
+ string GetSelectedItem(void);
+
+ protected:
+ bool thumbClicked;
+ uint32 windowPtr, cursor, limit;
+ uint32 charWidth, charHeight; // Box width/height in characters
+ Element * elementToTell;
+ Button upArrow, downArrow, upArrow2;
+ vector<string> item;
+
+ private:
+ uint32 yRelativePoint;
+};
+
+ListBox::ListBox(uint32 x, uint32 y, uint32 w, uint32 h): Element(x, y, w, h),
+ thumbClicked(false), windowPtr(0), cursor(0), limit(0), charWidth((w / FONT_WIDTH) - 1),
+ charHeight(h / FONT_HEIGHT), elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
+ downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox)
+{
+ upArrow.SetNotificationElement(this);
+ downArrow.SetNotificationElement(this);
+ upArrow2.SetNotificationElement(this);
+ extents.w -= 8; // Make room for scrollbar...
+}
+
+void ListBox::HandleKey(SDLKey key)
+{
+ if (key == SDLK_DOWN)
+ {
+ if (cursor != limit - 1) // Cursor is within its window
+ cursor++;
+ else // Otherwise, scroll the window...
+ {
+ if (cursor + windowPtr != item.size() - 1)
+ windowPtr++;
+ }
+ }
+ else if (key == SDLK_UP)
+ {
+ if (cursor != 0)
+ cursor--;
+ else
+ {
+ if (windowPtr != 0)
+ windowPtr--;
+ }
+ }
+ else if (key == SDLK_PAGEDOWN)
+ {
+ if (cursor != limit - 1)
+ cursor = limit - 1;
+ else
+ {
+ windowPtr += limit;
+ if (windowPtr > item.size() - limit)
+ windowPtr = item.size() - limit;
+ }
+ }
+ else if (key == SDLK_PAGEUP)
+ {
+ if (cursor != 0)
+ cursor = 0;
+ else
+ {
+ if (windowPtr < limit)
+ windowPtr = 0;
+ else
+ windowPtr -= limit;
+ }
+ }
+ else if (key >= SDLK_a && key <= SDLK_z)
+ {
+ // Advance cursor to filename with first letter pressed...
+ uint8 which = (key - SDLK_a) + 65; // Convert key to A-Z char
+
+ for(uint32 i=0; i<item.size(); i++)
+ {
+ if ((item[i][0] & 0xDF) == which)
+ {
+ cursor = i - windowPtr;
+ if (i > windowPtr + limit - 1)
+ windowPtr = i - limit + 1, cursor = limit - 1;
+ if (i < windowPtr)
+ windowPtr = i, cursor = 0;
+ break;
+ }
+ }
+ }
+}
+
+void ListBox::HandleMouseMove(uint32 x, uint32 y)
+{
+ upArrow.HandleMouseMove(x - extents.x, y - extents.y);
+ downArrow.HandleMouseMove(x - extents.x, y - extents.y);
+ upArrow2.HandleMouseMove(x - extents.x, y - extents.y);
+
+ if (thumbClicked)
+ {
+ uint32 sbHeight = extents.h - 24,
+ thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight);
+
+//yRelativePoint is the spot on the thumb where we clicked...
+ int32 newThumbStart = y - yRelativePoint;
+
+ if (newThumbStart < 0)
+ newThumbStart = 0;
+
+ if ((uint32)newThumbStart > sbHeight - thumb)
+ newThumbStart = sbHeight - thumb;
+
+ windowPtr = (uint32)(((float)newThumbStart / (float)sbHeight) * (float)item.size());
+//Check for cursor bounds as well... Or do we need to???
+//Actually, we don't...!
+ }
+}
+
+void ListBox::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
+{
+ if (Inside(x, y) && mouseDown)
+ {
+ // Why do we have to do this??? (- extents.y?)
+ // I guess it's because only the Window class has offsetting implemented... !!! FIX !!!
+// cursor = (y - extents.y) / 8;
+ cursor = (y - extents.y) / FONT_HEIGHT;
+ }
+
+ // Check for a hit on the scrollbar...
+ if (x > (uint32)(extents.x + extents.w) && x <= (uint32)(extents.x + extents.w + 8)
+ && y > (uint32)(extents.y + 8) && y <= (uint32)(extents.y + extents.h - 16))
+ {
+ if (mouseDown)
+ {
+// This shiaut should be calculated in AddItem(), not here... (or in Draw() for that matter)
+ uint32 sbHeight = extents.h - 24,
+ thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
+ thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
+
+ // Did we hit the thumb?
+ if (y >= (extents.y + 8 + thumbStart) && y < (extents.y + 8 + thumbStart + thumb))
+ thumbClicked = true, yRelativePoint = y - thumbStart;
+ }
+//Seems that this is useless--never reached except in rare cases and that the code outside is
+//more effective...
+// else
+// thumbClicked = false;
+ }
+
+ if (!mouseDown)
+ thumbClicked = false;
+
+ upArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
+ downArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
+ upArrow2.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
+}
+
+void ListBox::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
+{
+ for(uint32 i=0; i<limit; i++)
+ {
+ // Strip off the extension
+ // (extension stripping should be an option, not default!)
+ string s(item[windowPtr + i], 0, item[windowPtr + i].length() - 4);
+// DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*8,
+ DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*FONT_HEIGHT,
+ (cursor == i ? true : false), "%-*.*s", charWidth, charWidth, s.c_str());
+ }
+
+ upArrow.Draw(extents.x + offsetX, extents.y + offsetY);
+ downArrow.Draw(extents.x + offsetX, extents.y + offsetY);
+ upArrow2.Draw(extents.x + offsetX, extents.y + offsetY);
+
+ uint32 sbHeight = extents.h - 24,
+ thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
+ thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
+
+ for(uint32 y=extents.y+offsetY+8; y<extents.y+offsetY+extents.h-16; y++)
+ {
+// for(uint32 x=extents.x+offsetX+extents.w-8; x<extents.x+offsetX+extents.w; x++)
+ for(uint32 x=extents.x+offsetX+extents.w; x<extents.x+offsetX+extents.w+8; x++)
+ {
+ if (y >= thumbStart + (extents.y+offsetY+8) && y < thumbStart + thumb + (extents.y+offsetY+8))
+// screenBuffer[x + (y * pitch)] = (thumbClicked ? 0x458E : 0xFFFF);
+//458E -> 01 0001 0 1100 0 1110 -> 0100 0101 0110 0011 0111 0011 -> 45 63 73
+ screenBuffer[x + (y * pitch)] = (thumbClicked ? 0xFF736345 : 0xFFFFFFFF);
+ else
+// screenBuffer[x + (y * pitch)] = 0x0200;
+//0200 -> 000000 10000 00000 -> 00 1000 0100 00
+ screenBuffer[x + (y * pitch)] = 0xFF008400;
+ }
+ }
+}
+
+void ListBox::Notify(Element * e)
+{
+ if (e == &upArrow || e == &upArrow2)
+ {
+ if (windowPtr != 0)
+ {
+ windowPtr--;
+
+ if (cursor < limit - 1)
+ cursor++;
+ }
+ }
+ else if (e == &downArrow)
+ {
+ if (windowPtr < item.size() - limit)
+ {
+ windowPtr++;
+
+ if (cursor != 0)
+ cursor--;
+ }
+ }
+}
+
+void ListBox::AddItem(string s)
+{
+ // Do a simple insertion sort
+ bool inserted = false;
+
+ for(vector<string>::iterator i=item.begin(); i<item.end(); i++)
+ {
+ if (stringCmpi(s, *i) == -1)
+ {
+ item.insert(i, s);
+ inserted = true;
+ break;
+ }
+ }
+
+ if (!inserted)
+ item.push_back(s);
+
+ limit = (item.size() > charHeight ? charHeight : item.size());
+}
+
+string ListBox::GetSelectedItem(void)
+{
+ return item[windowPtr + cursor];
+}
+
+
+class FileList: public Window
+{
+ public:
+ FileList(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);
+ virtual ~FileList() {}
+ virtual void HandleKey(SDLKey key);
+ virtual void HandleMouseMove(uint32 x, uint32 y) { Window::HandleMouseMove(x, y); }
+ virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) { Window::HandleMouseButton(x, y, mouseDown); }
+ virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) { Window::Draw(offsetX, offsetY); }
+ virtual void Notify(Element * e);
+
+ protected:
+ ListBox * files;
+ Button * load;
+};
+
+//Need 4 buttons, one scrollbar...
+FileList::FileList(uint32 x, uint32 y, uint32 w, uint32 h): Window(x, y, w, h)
+{
+ files = new ListBox(8, 8, w - 16, h - 32);
+ AddElement(files);
+ load = new Button(8, h - 16, " Load ");
+ AddElement(load);
+ load->SetNotificationElement(this);
+
+//!!! FIX !!! Directory might not exist--this shouldn't cause VJ to crash!
+ DIR * dp = opendir(vjs.ROMPath);
+ dirent * de;
+
+ if (dp != NULL)
+ {
+ while ((de = readdir(dp)) != NULL)
+ {
+ char * ext = strrchr(de->d_name, '.');
+
+ if (ext != NULL)
+ if (strcasecmp(ext, ".zip") == 0 || strcasecmp(ext, ".j64") == 0
+ || strcasecmp(ext, ".abs") == 0 || strcasecmp(ext, ".jag") == 0
+ || strcasecmp(ext, ".rom") == 0)
+ files->AddItem(string(de->d_name));
+ }
+
+ closedir(dp);
+ }
+ else
+ {
+//Give a diagnostic message here so that the (l)user can figure out what went wrong. !!! FIX !!!
+ }
+}
+
+void FileList::HandleKey(SDLKey key)
+{
+ if (key == SDLK_RETURN)
+ Notify(load);
+ else
+ Window::HandleKey(key);
+}
+
+void FileList::Notify(Element * e)
+{
+ if (e == load)
+ {
+ char filename[MAX_PATH];
+ strcpy(filename, vjs.ROMPath);
+
+ if (strlen(filename) > 0)
+ if (filename[strlen(filename) - 1] != '/')
+ strcat(filename, "/");
+
+ strcat(filename, files->GetSelectedItem().c_str());
+
+// uint32 romSize = JaguarLoadROM(jaguar_mainRom, filename);
+// JaguarLoadCart(jaguar_mainRom, filename);
+ if (JaguarLoadFile(filename))
+ {
+ SDL_Event event;
+ event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
+ SDL_PushEvent(&event);
+
+ event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
+ event.user.data1 = (void *)ResetJaguar;
+ SDL_PushEvent(&event);
+ }
+ else
+ {
+ SDL_Event event;
+ event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
+ SDL_PushEvent(&event);
+
+ // Handle the error, but don't run...
+ // Tell the user that we couldn't run their file for some reason... !!! FIX !!!
+//how to kludge: Make a function like ResetJaguar which creates the dialog window
+ }
+ }
+ else
+ Window::Notify(e);
+}
+
+
+struct NameAction
+{
+ string name;
+ Window * (* action)(void);
+ SDLKey hotKey;
+
+ NameAction(string n, Window * (* a)(void) = NULL, SDLKey k = SDLK_UNKNOWN): name(n),
+ action(a), hotKey(k) {}
+};
+
+
+class MenuItems
+{
+ public:
+ MenuItems(): charLength(0) {}
+ bool Inside(uint32 x, uint32 y)
+ { return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
+ && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false); }
+
+ string title;
+ vector<NameAction> item;
+ uint32 charLength;
+ SDL_Rect extents;
+};
+
+class Menu: public Element
+{
+ public:
+// 1CFF -> 0 001 11 00 111 1 1111
+// 421F -> 0 100 00 10 000 1 1111
+ Menu(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = FONT_HEIGHT,
+/* uint16 fgc = 0x1CFF, uint16 bgc = 0x000F, uint16 fgch = 0x421F,
+ uint16 bgch = 0x1CFF): Element(x, y, w, h), activated(false), clicked(false),*/
+/* uint32 fgc = 0xFF3F3F00, uint32 bgc = 0x7F000000, uint32 fgch = 0xFF878700,
+ uint32 bgch = 0xFF3F3F00): Element(x, y, w, h), activated(false), clicked(false),*/
+/* uint32 fgc = 0xFFFF3F3F, uint32 bgc = 0xFF7F0000, uint32 fgch = 0xFFFF8787,
+ uint32 bgch = 0xFFFF3F3F): Element(x, y, w, h), activated(false), clicked(false),*/
+ uint32 fgc = 0xFF7F0000, uint32 bgc = 0xFFFF3F3F, uint32 fgch = 0xFFFF3F3F,
+ uint32 bgch = 0xFFFF8787): Element(x, y, w, h), activated(false), clicked(false),
+ inside(0), insidePopup(0), fgColor(fgc), bgColor(bgc), fgColorHL(fgch),
+ bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1) {}
+ virtual void HandleKey(SDLKey key);
+ virtual void HandleMouseMove(uint32 x, uint32 y);
+ virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
+ virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
+ virtual void Notify(Element *) {}
+ void Add(MenuItems mi);
+
+ protected:
+ bool activated, clicked;
+ uint32 inside, insidePopup;
+// uint16 fgColor, bgColor, fgColorHL, bgColorHL;
+ uint32 fgColor, bgColor, fgColorHL, bgColorHL;
+ int menuChosen, menuItemChosen;
+
+ private:
+ vector<MenuItems> itemList;
+};
+
+void Menu::HandleKey(SDLKey key)
+{
+ for(uint32 i=0; i<itemList.size(); i++)
+ {
+ for(uint32 j=0; j<itemList[i].item.size(); j++)
+ {
+ if (itemList[i].item[j].hotKey == key)
+ {
+ SDL_Event event;
+ event.type = SDL_USEREVENT;
+ event.user.code = MENU_ITEM_CHOSEN;
+ event.user.data1 = (void *)itemList[i].item[j].action;
+ SDL_PushEvent(&event);
+
+ clicked = false, menuChosen = menuItemChosen = -1;
+ break;
+ }
+ }
+ }
+}
+
+void Menu::HandleMouseMove(uint32 x, uint32 y)
+{
+ inside = insidePopup = 0;
+
+ if (Inside(x, y))
+ {
+ // Find out *where* we are inside the menu bar
+ uint32 xpos = extents.x;
+
+ for(uint32 i=0; i<itemList.size(); i++)
+ {
+ uint32 width = (itemList[i].title.length() + 2) * FONT_WIDTH;
+
+ if (x >= xpos && x < xpos + width)
+ {
+ inside = i + 1;
+ menuChosen = i;
+ break;
+ }
+
+ xpos += width;
+ }
+ }
+
+ if (!Inside(x, y) && !clicked)
+ {
+ menuChosen = -1;
+ }
+
+ if (itemList[menuChosen].Inside(x, y) && clicked)
+ {
+ insidePopup = ((y - itemList[menuChosen].extents.y) / FONT_HEIGHT) + 1;
+ menuItemChosen = insidePopup - 1;
+ }
+}
+
+void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
+{
+ if (!clicked)
+ {
+ if (mouseDown)
+ {
+ if (inside)
+ clicked = true;
+ else
+ menuChosen = -1; // clicked is already false...!
+ }
+ }
+ else // clicked == true
+ {
+ if (insidePopup && !mouseDown) // I.e., mouse-button-up
+ {
+ activated = true;
+ if (itemList[menuChosen].item[menuItemChosen].action != NULL)
+ {
+// itemList[menuChosen].item[menuItemChosen].action();
+ SDL_Event event;
+ event.type = SDL_USEREVENT;
+ event.user.code = MENU_ITEM_CHOSEN;
+ event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
+ SDL_PushEvent(&event);
+
+ clicked = false, menuChosen = menuItemChosen = -1;
+
+/* SDL_Event event;
+ while (SDL_PollEvent(&event)); // Flush the event queue...
+ event.type = SDL_MOUSEMOTION;
+ int mx, my;
+ SDL_GetMouseState(&mx, &my);
+ event.motion.x = mx, event.motion.y = my;
+ SDL_PushEvent(&event); // & update mouse position...!
+*/ }
+ }
+
+ if (!inside && !insidePopup && mouseDown)
+ clicked = false, menuChosen = menuItemChosen = -1;
+ }
+}
+
+void Menu::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
+{
+ uint32 xpos = extents.x + offsetX;
+
+ for(uint32 i=0; i<itemList.size(); i++)
+ {
+// uint16 color1 = fgColor, color2 = bgColor;
+ uint32 color1 = fgColor, color2 = bgColor;
+ if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
+ color1 = fgColorHL, color2 = bgColorHL;
+
+ DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
+ " %s ", itemList[i].title.c_str());
+ xpos += (itemList[i].title.length() + 2) * FONT_WIDTH;
+ }
+
+ // Draw sub menu (but only if active)
+ if (clicked)
+ {
+ uint32 ypos = extents.y + FONT_HEIGHT + 1;
+
+ for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
+ {
+// uint16 color1 = fgColor, color2 = bgColor;
+ uint32 color1 = fgColor, color2 = bgColor;
+
+ if (insidePopup == i + 1)
+ color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
+
+ if (itemList[menuChosen].item[i].name.length() > 0)
+ DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
+ color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
+ itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
+ else
+ DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
+ fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
+
+ ypos += FONT_HEIGHT;
+ }
+ }
+}
+
+void Menu::Add(MenuItems mi)
+{
+ for(uint32 i=0; i<mi.item.size(); i++)
+ if (mi.item[i].name.length() > mi.charLength)
+ mi.charLength = mi.item[i].name.length();
+
+ // Set extents here as well...
+ mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + FONT_HEIGHT + 1;
+ mi.extents.w = (mi.charLength + 2) * FONT_WIDTH, mi.extents.h = mi.item.size() * FONT_HEIGHT;
+
+ itemList.push_back(mi);
+ extents.w += (mi.title.length() + 2) * FONT_WIDTH;
+}
+
+
+//Do we even *need* this?
+//Doesn't seem like it...
+/*class RootWindow: public Window
+{
+ public:
+ RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
+//Do we even need to care about this crap?
+// { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
+ virtual void HandleKey(SDLKey key) {}
+ virtual void HandleMouseMove(uint32 x, uint32 y) {}
+ virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
+ virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) {}
+ virtual void Notify(Element *) {}
+
+ private:
+ Menu * menu;
+ Window * window;
+ int16 * rootImage[1280 * 240 * 2];
+};//*/
+
+
+//
+// Draw text at the given x/y coordinates. Can invert text as well.
+//
+void DrawString(uint32 * screen, uint32 x, uint32 y, bool invert, const char * text, ...)
+{
+ char string[4096];
+ va_list arg;
+
+ va_start(arg, text);
+ vsprintf(string, text, arg);
+ va_end(arg);
+
+ uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
+ uint32 length = strlen(string), address = x + (y * pitch);
+
+ uint32 color1 = 0x0080FF;
+ uint8 nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
+ uint8 xorMask = (invert ? 0xFF : 0x00);
+
+ for(uint32 i=0; i<length; i++)
+ {
+ uint8 c = string[i];
+ uint32 fontAddr = (uint32)(c < 32 ? 0 : c - 32) * FONT_WIDTH * FONT_HEIGHT;
+
+ for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
+ {
+ for(uint32 xx=0; xx<FONT_WIDTH; xx++)
+ {
+ uint32 existingColor = *(screen + address + xx + (yy * pitch));
+
+ uint8 eBlue = (existingColor >> 16) & 0xFF,
+ eGreen = (existingColor >> 8) & 0xFF,
+ eRed = existingColor & 0xFF;
+
+ uint8 trans = font2[fontAddr] ^ xorMask;
+ uint8 invTrans = trans ^ 0xFF;
+
+ uint32 bRed = (eRed * invTrans + nRed * trans) / 255,
+ bGreen = (eGreen * invTrans + nGreen * trans) / 255,
+ bBlue = (eBlue * invTrans + nBlue * trans) / 255;
+
+ *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
+ fontAddr++;
+ }
+ }
+
+ address += FONT_WIDTH;
+ }
+}
+
+//
+// Draw text at the given x/y coordinates, using FG/BG colors.
+//
+void DrawStringOpaque(uint32 * screen, uint32 x, uint32 y, uint32 color1, uint32 color2, const char * text, ...)
+{
+ char string[4096];
+ va_list arg;
+
+ va_start(arg, text);
+ vsprintf(string, text, arg);
+ va_end(arg);
+
+ uint32 pitch = sdlemuGetOverlayWidthInPixels();
+ uint32 length = strlen(string), address = x + (y * pitch);
+
+ uint8 eBlue = (color2 >> 16) & 0xFF, eGreen = (color2 >> 8) & 0xFF, eRed = color2 & 0xFF,
+ nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
+
+ for(uint32 i=0; i<length; i++)
+ {
+ uint8 c = string[i];
+ c = (c < 32 ? 0 : c - 32);
+ uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;