123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- /* GCSx
- ** IMGCHOOSE.CPP
- **
- ** Image "chooser" popup window or control
- */
- /*****************************************************************************
- ** Copyright (C) 2003-2006 Janson
- **
- ** This program 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.
- **
- ** This program 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 this program; if not, write to the Free Software
- ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- *****************************************************************************/
- #include "all.h"
- ImageChooser::ImageChooser(TileSetEdit* myTileset) : Window() { start_func
- cursor = 0;
- dirtyRange.w = 0;
- haveFocus = 0;
-
- myFrame = NULL;
- myScroll = NULL;
-
- tileset = myTileset;
- if (tileset) tileset->markLock(); // @TODO: throw_File
-
- reloadTileStats();
- }
- ImageChooser::~ImageChooser() { start_func
- if (tileset) tileset->markUnlock();
- }
- void ImageChooser::addTo(Dialog* dialog, int showWidth, int showHeight, int cId) { start_func
- assert(!myFrame);
- assert(dialog);
- myScroll = new WidgetScroll(WidgetScroll::FRAMETYPE_BEVEL_BK, this, showWidth, showHeight, cId);
- dialog->addWidget(myScroll);
- }
- int ImageChooser::runModal(int xPos, int yPos) { start_func
- assert(!myScroll);
- // Prevent duplication
- if (myFrame) return 0;
- // Keep a backup cause we clear myFrame on SDL_CLOSE
- FrameWindow* myFrameBackup;
- myFrameBackup = new FrameWindow(blankString, FrameWindow::RESIZING_SNAP, FrameWindow::FRAMETYPE_BEVEL_BK, this, FrameWindow::TITLEBAR_OFF);
- myFrame = myFrameBackup;
- // Frame window can NOT delete itself- we want to do that, to ensure it isn't
- // deleted twice on exception
- myFrameBackup->setWantsToBeDeleted(0);
-
- // (10 is an approximate offset to improve visuals)
- myFrameBackup->show(xPos, max(0, yPos - height - 10), FrameWindow::SHOW_CURRENT, FrameWindow::SHOW_CURRENT);
- // Recurse into desktop event loop
- int returnValue = desktop->eventLoop();
- // Delete frame
- delete myFrameBackup;
- myFrame = NULL;
- return returnValue;
- }
- void ImageChooser::setDirtyBox(int begin, int end) { start_func
- if (numTiles == 0) return;
- assert(begin >= 0);
- assert(begin < numTiles);
- assert(end >= 0);
- // ('end' can be greater than number of tiles to complete a box pattern)
- assert(begin <= end);
- assert((begin % tilesPerLine) <= (end % tilesPerLine));
- // Create a rectangle, include outermost gridlines
- Rect range;
- range.x = (begin % tilesPerLine) * tileWidth;
- range.y = (begin / tilesPerLine) * tileHeight;
- range.w = (end % tilesPerLine - begin % tilesPerLine + 1) * tileWidth;
- range.h = (end / tilesPerLine - begin / tilesPerLine + 1) * tileHeight;
-
- // Add rectangle into dirty range
- boundRects(dirtyRange, range);
- setDirty();
- }
- void ImageChooser::setDirtyRange(int first, int last) { start_func
- if (numTiles == 0) return;
- assert(first >= 0);
- assert(first < numTiles);
- assert(last >= 0);
- assert(last < numTiles);
-
- if (first > last) swap(first, last);
- // On the same line?
- if (first / tilesPerLine == last / tilesPerLine) {
- setDirtyBox(first, last);
- }
- else {
- // Have to cover entire lines
- setDirtyBox(first - first % tilesPerLine, last - last % tilesPerLine + tilesPerLine - 1);
- }
- }
- void ImageChooser::setTileset(TileSetEdit* newTileset) { start_func
- if (tileset) {
- tileset->markUnlock();
- tileset = NULL;
- }
- if (newTileset) {
- tileset = newTileset;
- tileset->markLock(); // @TODO: throw_File
- reloadTileStats();
- }
- else {
- setDirty(1);
- }
- }
- void ImageChooser::reloadTileStats() { start_func
- if (tileset) {
- numTiles = tileset->getCount();
- tileWidth = tileset->getWidth();
- tileHeight = tileset->getHeight();
- tilesPerLine = tileset->getTilesPerLine();
- fixedTilesPerLine = tilesPerLine ? 1 : 0;
-
- // (never put cursor below 0)
- if (numTiles) {
- if (cursor >= numTiles) cursor = numTiles - 1;
- }
- }
- else {
- cursor = tilesPerLine = fixedTilesPerLine = numTiles = 0;
- tileWidth = tileHeight = 1; // Prevents potential div/0
- }
-
- resize(width, height);
- setDirty(1);
- }
- void ImageChooser::resize(int newWidth, int newHeight, int newViewWidth, int newViewHeight, int fromParent) { start_func
- if (newViewWidth == -1) newViewWidth = viewWidth;
- // Calculate tiles per line, if not fixed
- if (!fixedTilesPerLine) {
- // For non fixed tiles, default width is 3/4ths desktop
- if (newViewWidth <= 0) newViewWidth = desktop->desktopWidth() * 3 / 4;
- tilesPerLine = newViewWidth / tileWidth;
- if (tilesPerLine < 1) tilesPerLine = 1;
- }
- // Size to match
- int myWidth = tilesPerLine * tileWidth;
- int myHeight = numTiles / tilesPerLine;
- if (numTiles % tilesPerLine) ++myHeight;
- myHeight = myHeight * tileHeight;
-
- // Recurse back to parent until we get a match
- if ((myWidth != width) || (myHeight != height)) fromParent = 0;
- Window::resize(myWidth, myHeight, newViewWidth, newViewHeight, fromParent);
- if (myFrame) myFrame->setScroll(tileWidth, tileHeight);
- if (myScroll) myScroll->setScroll(tileWidth, tileHeight);
- }
- int ImageChooser::event(int hasFocus, const SDL_Event* event) { start_func
- int key;
- int target;
- int changed = 0;
- ObjChange* obj;
- switch (event->type) {
- case SDL_CLOSE:
- myFrame = NULL;
- myScroll = NULL;
- return 1;
- case SDL_OBJECTCHANGE:
- obj = (ObjChange*)event->user.data1;
-
- if ((event->user.code & OBJ_TILESET) && (obj->obj == tileset)) {
- if (event->user.code & OBJMOD_DELETE) {
- tileset = NULL;
- closeWindow();
- }
- // @TODO: OBJMOD_COUNTCOLL- don't need to reload tile stats,
- // but other stuff will need reloading once we can edit collisions
- if (event->user.code & (OBJMOD_WIDTH | OBJMOD_HEIGHT | OBJMOD_COUNT | OBJMOD_PERLINE)) {
- reloadTileStats();
- }
- if (event->user.code & OBJMOD_TILE) {
- setDirtyRange(obj->info1 - 1, obj->info2 - 1);
- }
- }
- if ((event->user.code & OBJ_WORLD) && (tileset) && (obj->obj == tileset->getWorld())) {
- if (event->user.code & OBJMOD_DELETE) {
- tileset = NULL;
- closeWindow();
- }
- }
- return 1;
-
- case SDL_MOUSEBUTTONDOWN:
- case SDL_MOUSEBUTTONDBL:
- if (event->button.button == SDL_BUTTON_LEFT) {
- int mX = event->button.x / tileWidth;
- int mY = event->button.y / tileHeight;
-
- // (clip to edge, in cse off of it)
- if (mX >= tilesPerLine) mX = tilesPerLine - 1;
- if (mY >= (numTiles + tilesPerLine - 1) / tilesPerLine) mY = (numTiles + tilesPerLine - 1) / tilesPerLine - 1;
-
- cursorState(mX + mY * tilesPerLine);
- return 1;
- }
- break;
-
- case SDL_MOUSEBUTTONUP:
- if (event->button.button == SDL_BUTTON_LEFT) {
- if (myFrame) {
- desktop->setModalReturn(cursor + 1);
- closeWindow();
- }
- return 1;
- }
- break;
-
- case SDL_MOUSEMOTION:
- if (event->motion.state & SDL_BUTTON_LMASK) {
- int mX = ((Sint16)event->motion.x) / tileWidth;
- int mY = ((Sint16)event->motion.y) / tileHeight;
-
- // (clip to edge, in case off of it)
- if (mX < 0) mX = 0;
- if (mY < 0) mY = 0;
- if (mX >= tilesPerLine) mX = tilesPerLine - 1;
- if (mY >= (numTiles + tilesPerLine - 1) / tilesPerLine) mY = (numTiles + tilesPerLine - 1) / tilesPerLine - 1;
-
- cursorState(mX + mY * tilesPerLine);
- }
- return 1;
- case SDL_SPECIAL:
- // Refresh cursor color?
- if ((event->user.code == SDL_IDLECURSOR) && (haveFocus)) {
- setDirtyRange(cursor, cursor);
- }
- return 1;
- case SDL_INPUTFOCUS:
- if (event->user.code & 1) {
- if (!haveFocus) {
- haveFocus = 1;
- changed = 1;
- }
- }
- if (!(event->user.code & 1)) {
- if (haveFocus) {
- haveFocus = 0;
- changed = 1;
- }
- }
-
- if (changed) {
- setDirtyRange(cursor, cursor);
- }
- return 1;
- case SDL_KEYDOWN:
- key = combineKey(event->key.keysym.sym, event->key.keysym.mod & ~KMOD_SHIFT);
-
- switch (key) {
- case SDLK_ESCAPE:
- if (myFrame) {
- desktop->setModalReturn(0);
- closeWindow();
- }
- return 1;
- case SDLK_KP_ENTER:
- case SDLK_RETURN:
- case SDLK_SPACE:
- if (myFrame) {
- desktop->setModalReturn(cursor + 1);
- closeWindow();
- }
- return 1;
-
- case SDLK_RIGHT:
- cursorState(cursor + 1);
- return 1;
-
- case combineKey(SDLK_RIGHT, KMOD_CTRL):
- case SDLK_END:
- cursorState(cursor + tilesPerLine - (cursor % tilesPerLine) - 1);
- return 1;
-
- case SDLK_LEFT:
- if (cursor > 0) cursorState(cursor - 1);
- return 1;
- case combineKey(SDLK_LEFT, KMOD_CTRL):
- case SDLK_HOME:
- cursorState(cursor - (cursor % tilesPerLine));
- return 1;
-
- case SDLK_DOWN:
- cursorState(cursor + tilesPerLine);
- return 1;
- case SDLK_PAGEDOWN:
- cursorState(cursor + (viewHeight / tileHeight - 1) * tilesPerLine);
- return 1;
- case SDLK_PAGEUP:
- target = cursor - (viewHeight / tileHeight - 1) * tilesPerLine;
- if (target > 0) cursorState(target);
- else cursorState(0);
- return 1;
- case SDLK_UP:
- if (cursor - tilesPerLine > 0) cursorState(cursor - tilesPerLine);
- else cursorState(0);
- return 1;
- case combineKey(SDLK_HOME, KMOD_CTRL):
- cursorState(0);
- return 1;
- case combineKey(SDLK_END, KMOD_CTRL):
- cursorState(numTiles - 1);
- return 1;
- }
- break;
- }
- return 0;
- }
- int ImageChooser::cursorState(int newPos) { start_func
- if (newPos >= 0) {
- // Clip on right
- if ((newPos >= numTiles) && (numTiles)) newPos = numTiles - 1;
-
- // Dirty previous selection
- setDirtyRange(cursor,cursor);
- cursor = newPos;
- // Scroll?
- int newY = cursor / tilesPerLine * tileHeight;
- int newX = (cursor % tilesPerLine) * tileWidth;
- if (myFrame) myFrame->scrollToView(newX, newY, tileWidth, tileHeight);
- if (myScroll) myScroll->scrollToView(newX, newY, tileWidth, tileHeight);
- // Dirty
- setDirtyRange(cursor,cursor);
- }
-
- return cursor;
- }
- void ImageChooser::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
- assert(destSurface);
- if (visible) {
- // If dirty, redraw range or all
- if (dirty) {
- if (totalDirty) {
- getRect(toDisplay);
- toDisplay.x += xOffset;
- toDisplay.y += yOffset;
- }
- else {
- dirtyRange.x += x + xOffset;
- dirtyRange.y += y + yOffset;
- // Range must include requested update area as well
- boundRects(toDisplay, dirtyRange);
- }
- dirty = totalDirty = 0;
- dirtyRange.w = 0;
- intersectRects(toDisplay, clipArea);
- }
-
- xOffset += x;
- yOffset += y;
-
- // Anything to draw?
- if (toDisplay.w) {
- SDL_SetClipRect(destSurface, &toDisplay);
- SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_BKFILL]);
-
- if (!tileset) return;
- int dX = xOffset;
- int lastX = xOffset + width;
-
- // Draw starting at the first line of the displayed area;
- int skip = (toDisplay.y - yOffset) / tileHeight;
- int dY = yOffset + skip * tileHeight;
- int lastY = toDisplay.y + toDisplay.h;
- int pos = tilesPerLine * skip;
- // Determine first/last tiles of each row we need to show
- // These numbers would be, for example, 0 for first tile, 1 for second,
- // up to tilesPerLine - 1
- int leftmost = (toDisplay.x - xOffset) / tileWidth;
- int rightmost = (toDisplay.x + toDisplay.w - xOffset - 1) / tileWidth;
-
- pos += leftmost;
- dX += tileWidth * leftmost;
-
- for (; (pos < numTiles) && (dY < lastY); ++pos) {
- int posMod = pos % tilesPerLine;
-
- // We use modulo to determine if this tile needs displaying
- if ((posMod >= leftmost) && (posMod <= rightmost)) {
- // Draw tile
- tileset->blitTile(pos + 1, destSurface, dX, dY);
-
- if (pos == cursor) {
- drawSelectRect(dX, dY, tileWidth, tileHeight,
- guiPacked[haveFocus ? COLOR_TILECURSOR : COLOR_TILESELECTION], destSurface,
- haveFocus ? desktop->currentCursorAlpha() : 128);
- drawBox(dX, dY, tileWidth, tileHeight, guiPacked[COLOR_TILECURSORBORDER1], destSurface);
- drawBox(dX + 1, dY + 1, tileWidth - 2, tileHeight - 2, guiPacked[COLOR_TILECURSORBORDER2], destSurface);
- }
- }
-
- dX += tileWidth;
- if (dX + tileWidth > lastX) {
- dX = xOffset;
- dY += tileHeight;
- }
- }
- }
- }
- }
- Window::WindowType ImageChooser::windowType() const { start_func
- return WINDOW_CLIENT;
- }
- Window::WindowSort ImageChooser::windowSort() const { start_func
- if (myScroll) return WINDOWSORT_NORMAL;
- return WINDOWSORT_MODAL;
- }
- int ImageChooser::wantsToBeDeleted() const { start_func
- if (myScroll) return 1;
- return 0;
- }
|