123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- /* GCSx
- ** LAYERLIST.CPP
- **
- ** Listbox for layer selection
- */
- /*****************************************************************************
- ** 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"
- const string WLayerListBox::wtLayerVisible("o");
- const string WLayerListBox::wtLayerEditable("p");
- const string WLayerListBox::wtLayerSprites("s");
- const string WLayerListBox::wtLayerTile("q");
- const string WLayerListBox::wtLayerFont("r");
- const string WLayerListBox::wtLayerImage("g");
- WLayerListBox::WLayerListBox(int lId, int* lSelection, int lWidth, int lLines) : WListBox(lId, lSelection, 0, lWidth, lLines) { start_func
- // Determine sizing of icons, gutters and such
- int iconWidth[4];
- iconWidth[0] = fontWidth(wtLayerVisible, FONT_WIDGET);
- iconWidth[1] = fontWidth(wtLayerSprites, FONT_WIDGET);
- iconWidth[2] = fontWidth(wtLayerEditable, FONT_WIDGET);
- iconWidth[3] = max(fontWidth(wtLayerTile, FONT_WIDGET), fontWidth(wtLayerFont, FONT_WIDGET));
- iconWidth[3] = max(iconWidth[3], fontWidth(wtLayerImage, FONT_WIDGET));
- int iconGutter = iconWidth[0] / 2;
-
- int total = GUI_LISTBOX_HORIZPAD;
- for (int pos = 0; pos < 5; ++pos) {
- iconPos[pos] = total;
- if (pos < 4) total += iconWidth[pos] + iconGutter;
- }
-
- // Width of items is larger, for widgets
- showWidth += iconPos[4] - GUI_LISTBOX_HORIZPAD;
-
- // Height of items may need to be larger for widget
- valueHeight = max(valueHeight, fontHeight(FONT_WIDGET));
-
- // Center text and icons
- yAddIcon = yAddText = 0;
- int iconH = fontHeight(FONT_WIDGET);
- int textH = fontHeight();
- if (iconH > textH) yAddText = (iconH - textH) / 2;
- else yAddIcon = (textH - iconH) / 2;
-
- scene = NULL;
- }
- WLayerListBox::~WLayerListBox() { start_func
- }
- void WLayerListBox::setScene(SceneEdit* lScene) { start_func
- scene = lScene;
- }
- void WLayerListBox::removeItem(int position) { start_func
- // Remove item
- vector<ListEntry>::iterator pos = contents.begin();
- int posnum = 0;
- for (; (pos != contents.end()) && (posnum < position); ++pos, ++posnum) ;
- contents.erase(pos);
- --numValues;
- // Close up gap in ids
- for (int adjpos = position; adjpos < numValues; ++adjpos) {
- --contents[adjpos].id;
- }
- resize(valueWidth + GUI_LISTBOX_HORIZPAD + iconPos[3],
- valueHeight * numValues);
-
- // No items?
- if (!numValues) {
- dragSet = -1;
- cursorPos = 0;
- selectedItem = SELECTED_NONE;
- }
- else {
- // Adjust cursor/selected/drag?
- if (dragSet > posnum) --dragSet;
- else if (dragSet == posnum) dragSet = -1;
- if (selectedItem > posnum) --selectedItem;
- else if (selectedItem == posnum) selectedItem = -SELECTED_NONE;
- if (cursorPos > posnum) --cursorPos;
- else if (cursorPos == posnum) cursorPos = -1;
- if ((selectedItem == SELECTED_NONE) || (cursorPos < 0)) {
- // Move cursor to first non-disabled item
- int selpos = 0;
- for (; selpos < numValues; ++selpos) {
- if (!contents[selpos].disabled) break;
- }
- // (if nothing to select...)
- if (selpos >= numValues) selpos = 0;
- moveCursor(selpos);
- selectItem(selpos, 1);
- }
- }
- // Scroll, update screen
- if (wparent) wparent->scrollToView(0, cursorPos * valueHeight, width, valueHeight);
- setDirty();
- }
- void WLayerListBox::swapItems(int pos1, int pos2) { start_func
- // Swap cursor/selected/drag if applicable
- if (cursorPos == pos1) cursorPos = pos2;
- else if (cursorPos == pos2) cursorPos = pos1;
- if (selectedItem == pos1) selectedItem = pos2;
- else if (selectedItem == pos2) selectedItem = pos1;
- if (dragSet == pos1) dragSet = pos2;
- else if (dragSet == pos2) dragSet = pos1;
-
- // Swap the two items EXCEPT for ids (which are position based)
- swap(contents[pos1], contents[pos2]);
- swap(contents[pos1].id, contents[pos2].id);
-
- // Scroll, update screen
- if (wparent) wparent->scrollToView(0, cursorPos * valueHeight, width, valueHeight);
- setDirty();
- }
- void WLayerListBox::addItem(const ListEntry& item, int selectIt) { start_func
- addItem(numValues, item, selectIt);
- }
- void WLayerListBox::addItem(int position, const ListEntry& item, int selectIt) { start_func
- if (position >= numValues) {
- contents.push_back(item);
- }
- else {
- vector<ListEntry>::iterator pos = contents.begin();
- int posnum = 0;
- for (; (pos != contents.end()) && (posnum < position); ++pos, ++posnum) ;
- contents.insert(pos, item);
- position = posnum;
- // Modify ids (numValues hasn't changed yet)
- for (int adjpos = position + 1; adjpos <= numValues; ++adjpos) {
- ++contents[adjpos].id;
- }
- // Move cursor/selected/drag up if applicable
- if (cursorPos >= position) ++cursorPos;
- if (selectedItem >= position) ++selectedItem;
- if (dragSet >= position) ++dragSet;
- }
- numValues = contents.size();
- // (limit length of display but don't affect actual data)
- int itemWidth = fontWidth(item.label.substr(0, MAX_LINELENGTH));
- if (itemWidth > valueWidth) valueWidth = itemWidth;
-
- if (selectIt) {
- moveCursor(position);
- selectItem(cursorPos);
- }
- resize(valueWidth + GUI_LISTBOX_HORIZPAD + iconPos[3],
- valueHeight * numValues);
- }
- void WLayerListBox::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
- assert(destSurface);
- if (visible) {
- // If dirty, redraw all
- if (dirty) {
- getRect(toDisplay);
- toDisplay.x += xOffset;
- toDisplay.y += yOffset;
- // Always fill entire width of view
- if (viewWidth > toDisplay.w) toDisplay.w = viewWidth;
- dirty = 0;
- intersectRects(toDisplay, clipArea);
- }
-
- // Anything to draw?
- if (toDisplay.w) {
- SDL_SetClipRect(destSurface, &toDisplay);
-
- xOffset += x;
- yOffset += y;
- // This widget attempts to only redraw listbox entries within the
- // dirtied area.
- // Background
- SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_TEXTBOX]);
-
- // Draw all listbox entries we can see
- int pos = (toDisplay.y - yOffset) / valueHeight;
- // (add in value height - 1 so it rounds up)
- int last = (toDisplay.y + toDisplay.h - yOffset + valueHeight - 1) / valueHeight;
- if (last > numValues) last = numValues;
-
- int shownWidth = valueWidth + iconPos[4] + GUI_LISTBOX_HORIZPAD;
- if (viewWidth > shownWidth) shownWidth = viewWidth;
-
- for (; pos < last; ++pos) {
- int color = COLOR_TEXT;
-
- if ((contents[pos].selected) && (!disabled) && (!contents[pos].disabled)) {
- drawGradient(xOffset, valueHeight * pos + yOffset,
- shownWidth, valueHeight, guiRGB[COLOR_SELECTION1],
- guiRGB[COLOR_SELECTION2], destSurface);
- drawText(contents[pos].label, guiRGB[COLOR_TEXTBOX],
- xOffset + iconPos[4], valueHeight * pos + yOffset + yAddText, destSurface);
- color = COLOR_TEXTBOX;
- }
- else {
- if (disabled || contents[pos].disabled) color = COLOR_LIGHT2;
- drawText(contents[pos].label, guiRGB[color],
- xOffset + iconPos[4], valueHeight * pos + yOffset + yAddText, destSurface);
- }
-
- // Icons
- if (contents[pos].code1 & FLAG_VISIBLE) drawText(wtLayerVisible, guiRGB[contents[pos].code1 & FLAG_DIMMED ? COLOR_DARK2 : color == COLOR_LIGHT2 ? COLOR_TEXT : color], xOffset + iconPos[0], valueHeight * pos + yOffset + yAddIcon, destSurface, FONT_WIDGET);
- if (contents[pos].code1 & FLAG_SPRITES_VISIBLE) drawText(wtLayerSprites, guiRGB[contents[pos].code1 & FLAG_SPRITES_DIMMED ? COLOR_DARK2 : color == COLOR_LIGHT2 ? COLOR_TEXT : color], xOffset + iconPos[1], valueHeight * pos + yOffset + yAddIcon, destSurface, FONT_WIDGET);
- if (contents[pos].code1 & FLAG_EDITABLE) drawText(wtLayerEditable, guiRGB[color], xOffset + iconPos[2], valueHeight * pos + yOffset + yAddIcon, destSurface, FONT_WIDGET);
-
- const string* icon = NULL;
- switch (contents[pos].code2) {
- case Layer::LAYER_TILE:
- icon = &wtLayerTile;
- break;
- case Layer::LAYER_IMAGE:
- icon = &wtLayerImage;
- break;
- case Layer::LAYER_FONT:
- icon = &wtLayerFont;
- break;
- }
- if (icon) drawText(*icon, guiRGB[color], xOffset + iconPos[3], valueHeight * pos + yOffset + yAddIcon, destSurface, FONT_WIDGET);
-
- if ((haveFocus) && (pos == cursorPos)) {
- drawFocusBox(xOffset, valueHeight * pos + yOffset,
- shownWidth, valueHeight, destSurface);
- }
- }
- }
- }
- }
- void WLayerListBox::dblClickItem(int id) { start_func
- // @TODO: technically should pass our overall frame parent to this function
- // but getting that info is a lot of work
- scene->getLayerEdit(id)->propertiesDialog(0);
- }
- void WLayerListBox::rtClickItem(int id) { start_func
- dblClickItem(id);
- }
- void WLayerListBox::delClickItem(int id) { start_func
- // @TODO: Confirmation if last non-disabled layer in list
- try {
- // @TODO: technically should pass our overall frame parent to this function
- // but getting that info is a lot of work
- scene->deleteLayer(id);
- }
- catch (FileException& e) {
- guiErrorBox(string(e.details), errorTitleFile);
- }
- }
- int WLayerListBox::event(int hasFocus, const SDL_Event* event) { start_func
- assert(event);
- assert(parent);
-
- if ((numValues == 0) && (event->type != SDL_INPUTFOCUS)) return 0;
- switch (event->type) {
- case SDL_KEYDOWN:
- switch (combineKey(event->key.keysym.sym, event->key.keysym.mod)) {
- case SDLK_KP_ENTER:
- case SDLK_RETURN:
- case SDLK_SPACE:
- dblClickItem(cursorPos);
- return 1;
- case SDLK_DELETE:
- case SDLK_BACKSPACE:
- delClickItem(cursorPos);
- return 1;
- }
- break;
- case SDL_COMMAND:
- switch (event->user.code) {
- case VIEW_ALLLAYER:
- case VIEW_DIMLAYER:
- case VIEW_NOLAYER:
- // Change all non-selected entries
- for (int pos = 0; pos < numValues; ++pos) {
- if (!contents[pos].selected) {
- contents[pos].code1 = contents[pos].code1 & FLAG_EDITABLE;
- if (event->user.code != VIEW_NOLAYER) contents[pos].code1 = contents[pos].code1 | FLAG_VISIBLE | FLAG_SPRITES_VISIBLE;
- if (event->user.code == VIEW_DIMLAYER) contents[pos].code1 = contents[pos].code1 | FLAG_DIMMED | FLAG_SPRITES_DIMMED;
- }
- }
- if (wparent) wparent->myParent()->childModified((WListBox*)this);
- setDirty();
- return 1;
- case VIEW_NEXT:
- if (selectedItem >= 0) {
- for (int pos = selectedItem + 1; pos < numValues; ++pos) {
- if (!contents[pos].disabled) {
- selectItem(pos, 1, 1);
- break;
- }
- }
- }
- return 1;
- case VIEW_PREV:
- if (selectedItem >= 0) {
- for (int pos = selectedItem - 1; pos >= 0; --pos) {
- if (!contents[pos].disabled) {
- selectItem(pos, 1, 1);
- break;
- }
- }
- }
- return 1;
- }
- break;
-
- case SDL_MOUSEMOTION:
- // Dragging lets us move layers
- if (event->motion.state & SDL_BUTTON_LMASK) {
- int dragToPos = event->motion.y / valueHeight;
-
- // If dragging to another position, and something IS being dragged...
- if ((event->motion.y < height) && (dragToPos != dragSet) && (dragSet >= 0)) {
- try {
- // @TODO: technically should pass our overall frame parent to this function
- // but getting that info is a lot of work
- if (scene) scene->swapLayer(dragSet, dragToPos);
- }
- catch (UndoException &e) {
- }
- }
-
- return 1;
- }
- break;
- case SDL_MOUSEBUTTONDBL:
- case SDL_MOUSEBUTTONDOWN:
- // Right click- properties @TODO: context-menu
- if (event->button.button == SDL_BUTTON_RIGHT) {
- if (event->button.y < height) {
- // Move cursor, don't select
- moveCursor(event->motion.y / valueHeight);
- rtClickItem(cursorPos);
- return 1;
- }
- }
-
- if (event->button.button == SDL_BUTTON_LEFT) {
- int targetPos = event->button.y / valueHeight;
- // (nothing being dragged, by default)
- dragSet = -1;
- if (event->button.y < height) {
- if (event->button.x < iconPos[4]) {
- // (disallow invisible/dim/uneditable active layer(s))
- if (!contents[targetPos].selected) {
- // Which icon was clicked?
- if (event->button.x < iconPos[1]) {
- if (contents[targetPos].code1 & FLAG_DIMMED) contents[targetPos].code1 &= ~(FLAG_VISIBLE | FLAG_DIMMED);
- else if (contents[targetPos].code1 & FLAG_VISIBLE) contents[targetPos].code1 |= FLAG_DIMMED;
- else contents[targetPos].code1 |= FLAG_VISIBLE;
- // (make uneditable if invisible, also)
- if (!(contents[targetPos].code1 & FLAG_VISIBLE)) contents[targetPos].code1 &= ~FLAG_EDITABLE;
- }
-
- // Disallow editable disabled/invisible layers
- if ((event->button.x >= iconPos[2]) && (event->button.x < iconPos[3]) && (!contents[targetPos].disabled) && (contents[targetPos].code1 & FLAG_VISIBLE)) {
- contents[targetPos].code1 ^= FLAG_EDITABLE;
- }
- }
-
- // (sprite visibility currently unaffected by other things)
- if ((event->button.x >= iconPos[1]) && (event->button.x < iconPos[2])) {
- if (contents[targetPos].code1 & FLAG_SPRITES_DIMMED) contents[targetPos].code1 &= ~(FLAG_SPRITES_VISIBLE | FLAG_SPRITES_DIMMED);
- else if (contents[targetPos].code1 & FLAG_SPRITES_VISIBLE) contents[targetPos].code1 |= FLAG_SPRITES_DIMMED;
- else contents[targetPos].code1 |= FLAG_SPRITES_VISIBLE;
- }
-
- if (wparent) wparent->myParent()->childModified((WListBox*)this);
- setDirty();
- }
- else {
- // Select item, allow dragging or handle dbl-click
- moveCursor(targetPos);
- selectItem(cursorPos, 1);
-
- if (event->type == SDL_MOUSEBUTTONDBL) dblClickItem(cursorPos);
- else dragSet = cursorPos;
- }
- }
-
- return 1;
- }
- break;
- }
- return WListBox::event(hasFocus, event);
- }
- void WLayerListBox::selectItem(int id, int select, int alone) { start_func
- assert(id >= 0);
- assert(id < numValues);
-
- // (if disabled, no effect at all)
- if (contents[id].disabled) return;
-
- // We assume 'alone' and 'select' are both true
-
- // (manually deselect all so we don't send dirty/modified yet)
- for (int pos = 0; pos < numValues; ++pos) {
- if (contents[pos].selected) {
- // Change the item we're deselecting to match the item we're selecting
- // (visible, invisible, dim; editable)
- contents[pos].code1 = contents[id].code1;
- contents[pos].selected = 0;
- }
- }
-
- selectedItem = id;
- contents[id].selected = 1;
- // Item we're selecting is 100% visible and editable, now
- contents[id].code1 = FLAG_VISIBLE | FLAG_EDITABLE | FLAG_SPRITES_VISIBLE;
- if (wparent) wparent->myParent()->childModified(this);
- setDirty();
- }
- void WLayerListBox::setBitMask(Uint32 layersAffect, Uint32 layersView, Uint32 layersDim, int selectedLayer) { start_func
- int dirty = 0;
- for (int pos = 0; pos < numValues; ++pos) {
- int bit = 1 << pos;
- int viewType = 0;
- if (layersView & bit) {
- if (layersDim & bit) viewType = FLAG_VISIBLE | FLAG_DIMMED;
- else viewType = FLAG_VISIBLE;
- }
- if ((contents[pos].code1 & (FLAG_VISIBLE | FLAG_DIMMED)) != viewType) {
- contents[pos].code1 = (contents[pos].code1 & ~(FLAG_VISIBLE | FLAG_DIMMED)) | viewType;
- dirty = 1;
- }
- if (layersAffect & bit) {
- if (!(contents[pos].code1 & FLAG_EDITABLE)) {
- contents[pos].code1 |= FLAG_EDITABLE;
- dirty = 1;
- }
- }
- else if (contents[pos].code1 & FLAG_EDITABLE) {
- contents[pos].code1 &= ~FLAG_EDITABLE;
- dirty = 1;
- }
- // Selection modification?
- if (selectedLayer >= 0) {
- if (selectedLayer == pos) {
- if (!contents[pos].selected) {
- contents[pos].selected = 1;
- selectedItem = pos;
- dirty = 1;
- }
- }
- else if (contents[pos].selected) {
- contents[pos].selected = 0;
- dirty = 1;
- }
- }
- }
- if (dirty) setDirty();
- }
- void WLayerListBox::determineBitMasks(Uint32& layersView, Uint32& layersDim, Uint32& layersAffect) const { start_func
- layersView = 0;
- layersDim = 0;
- layersAffect = 0;
-
- for (int pos = 0; pos < numValues; ++pos) {
- int bit = 1 << pos;
- if (contents[pos].code1 & FLAG_VISIBLE) {
- layersView |= bit;
- if (contents[pos].code1 & FLAG_DIMMED) layersDim |= bit;
- }
- if (contents[pos].code1 & FLAG_EDITABLE) layersAffect |= bit;
- }
- }
|