123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- /* GCSx
- ** LIST.CPP
- **
- ** Listbox, drop-down, etc. dialog widgets
- */
- /*****************************************************************************
- ** 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"
- // Listbox entries
- ListEntry& ListEntry::operator=(const ListEntry& right) { start_func
- label = right.label;
- selected = right.selected;
- disabled = right.disabled;
- id = right.id;
- code1 = right.code1;
- code2 = right.code2;
- code3 = right.code3;
- return *this;
- }
- ListEntry::ListEntry() { start_func
- label = blankString;
- selected = disabled = 0;
- id = 0;
- code1 = code2 = code3 = 0;
- }
- ListEntry::ListEntry(const ListEntry& from) { start_func
- label = from.label;
- selected = from.selected;
- disabled = from.disabled;
- id = from.id;
- code1 = from.code1;
- code2 = from.code2;
- code3 = from.code3;
- }
- ListEntry::ListEntry(const string& eLabel, int eId, int eCode1, int eCode2, int eCode3, int eDisabled) { start_func
- label = eLabel;
- selected = 0;
- disabled = eDisabled;
- id = eId;
- code1 = eCode1;
- code2 = eCode2;
- code3 = eCode3;
- }
- // Listbox widget
- WListBox::WListBox(int lId, int* lSelection, int lAllowZero, int lWidth, int lLines) : Widget(lId, blankString, lSelection), contents() { start_func
- assert(lSelection);
- wparent = NULL;
- valueHeight = fontHeight();
- valueWidth = 0;
- numValues = 0;
- cursorPos = 0;
- selectedItem = SELECTED_NONE;
- allowZero = lAllowZero;
- allowMany = 0;
-
- // Set to standard size
- if (lWidth > 0) {
- showWidth = lWidth;
- }
- else {
- string standard(GUI_LISTBOX_DEFAULTWIDTH, 'X');
- showWidth = GUI_LISTBOX_HORIZPAD * 2 + fontWidth(standard);
- }
- showLines = lLines < 1 ? GUI_LISTBOX_DEFAULTLINES : lLines;
-
- resize(0, 0);
- }
- // Needed due to virtual ~Widget() and our list members
- WListBox::~WListBox() { start_func
- }
- void WListBox::moveCursor(int id) { start_func
- // Clip position
- if (id < 0) id = 0;
- if (id >= numValues) id = numValues - 1;
- if (cursorPos == id) return;
-
- cursorPos = id;
- setDirty();
- if (wparent) wparent->scrollToView(0, cursorPos * valueHeight, width, valueHeight);
- }
- void WListBox::selectItem(int id, int select, int alone) { start_func
- assert(id >= 0);
- assert(id < numValues);
-
- int dirty = 0;
- // (if disabled, no effect at all)
- if (contents[id].disabled) return;
-
- // Force 'alone' if only one allowed to be selected
- if ((select) && (!allowMany)) alone = 1;
-
- // (manually deselect all so we don't send dirty/modified yet)
- // 'alone' has no meaning if deselecting
- if ((alone) && (select)) {
- if (selectedItem == SELECTED_MANY) {
- for (int pos = 0; pos < numValues; ++pos) {
- contents[pos].selected = 0;
- }
- dirty = 1;
- }
- else if (selectedItem != SELECTED_NONE) {
- contents[selectedItem].selected = 0;
- dirty = 1;
- }
- selectedItem = SELECTED_NONE;
- }
-
- if (select) {
- if (selectedItem == SELECTED_NONE) selectedItem = id;
- else selectedItem = SELECTED_MANY;
- contents[id].selected = 1;
- dirty = 1;
- }
- else if (contents[id].selected) {
- // Disallow deselection if not allowed to select zero
- if ((!allowZero) && (selectedItem == id)) return;
-
- contents[id].selected = 0;
- if (selectedItem == id) selectedItem = SELECTED_NONE;
- else {
- // Count how many are selected now
- selectedItem = SELECTED_NONE;
- for (int pos = 0; pos < numValues; ++pos) {
- if (contents[pos].selected) {
- if (selectedItem == SELECTED_NONE) selectedItem = pos;
- else {
- selectedItem = SELECTED_MANY;
- break;
- }
- }
- }
- }
- dirty = 1;
- }
- if (dirty) {
- // @TODO: 3 exceptions
- if (wparent) wparent->myParent()->childModified(this);
- setDirty();
- }
- }
- int WListBox::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_INPUTFOCUS:
- if (event->user.code & 1) {
- if (!haveFocus) {
- haveFocus = 1;
- setDirty();
- }
- }
- else {
- if (haveFocus) {
- haveFocus = 0;
- setDirty();
- }
- }
- return 1;
-
- case SDL_KEYDOWN:
- // @TODO: Ctrl should move cursor without selecting or select multiple
- // Shift should select a range
- switch (combineKey(event->key.keysym.sym, event->key.keysym.mod)) {
- case SDLK_SPACE:
- selectItem(cursorPos, 0);
- return 1;
- case SDLK_DOWN:
- case SDLK_RIGHT:
- // Next line
- moveCursor(cursorPos + 1);
- selectItem(cursorPos);
- return 1;
-
- case SDLK_UP:
- case SDLK_LEFT:
- // Previous line
- moveCursor(cursorPos - 1);
- selectItem(cursorPos);
- return 1;
- case SDLK_PAGEDOWN:
- // Next page
- moveCursor(cursorPos + showLines - 1);
- selectItem(cursorPos);
- return 1;
- case SDLK_PAGEUP:
- // Previous page
- moveCursor(cursorPos - showLines + 1);
- selectItem(cursorPos);
- return 1;
- case SDLK_HOME:
- // First item
- moveCursor(0);
- selectItem(cursorPos);
- return 1;
- case SDLK_END:
- // Last item
- moveCursor(numValues - 1);
- selectItem(cursorPos);
- return 1;
-
- default: {
- // Jump to first item beginning with a letter
- int key = tolower(event->key.keysym.sym);
- for (int pos = 0; pos < numValues; ++pos) {
- if (tolower(contents[pos].label[0]) == key) {
- moveCursor(pos);
- selectItem(cursorPos);
- return 1;
- }
- }
- break;
- }
- }
- break;
-
- case SDL_MOUSEBUTTONDBL:
- case SDL_MOUSEBUTTONDOWN:
- if (event->button.button == SDL_BUTTON_LEFT) {
- dragSet = 1;
-
- if (event->button.y < height) {
- int targetPos = event->button.y / valueHeight;
- // If CTRL and was already selected, just unselect
- if ((contents[targetPos].selected) && (SDL_GetModState() & KMOD_CTRL) && (allowZero)) {
- dragSet = 0;
- }
- moveCursor(targetPos);
- selectItem(cursorPos, dragSet);
- // Double-click?
- if (event->type == SDL_MOUSEBUTTONDBL) {
- // (find OK button)
- if (wparent->myParent()->buttonControl(Dialog::BUTTON_OK)) {
- wparent->myParent()->currentFocusDoAction();
- }
- }
- return 1;
- }
- }
- break;
- case SDL_MOUSEMOTION:
- if (event->motion.state & SDL_BUTTON_LMASK) {
- if (event->motion.y < height) {
- moveCursor(event->motion.y / valueHeight);
- // @TODO: Should select/deselect all in between
- selectItem(cursorPos, dragSet);
- return 1;
- }
- }
- break;
- }
- return 0;
- }
- void WListBox::load() { start_func
- int sel = *(int*)(setting);
- int count = 0;
- cursorPos = 0;
- selectedItem = SELECTED_NONE;
-
- for (int pos = 0; pos < numValues; ++pos) {
- if (contents[pos].id == sel) {
- if (!count) {
- contents[pos].selected = 1;
- selectedItem = cursorPos = pos;
- }
- ++count;
- }
- else contents[pos].selected = 0;
- }
-
- if ((!count) && (!allowZero) && (numValues)) {
- contents[0].selected = 1;
- selectedItem = cursorPos = 0;
- }
- }
- void WListBox::apply() { start_func
- *(int*)(setting) = 0;
-
- // @TODO: Support for multiple selections
- if (selectedItem >= 0) {
- *(int*)(setting) = contents[selectedItem].id;
- }
- }
- void WListBox::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 + GUI_LISTBOX_HORIZPAD * 2;
- if (viewWidth > shownWidth) shownWidth = viewWidth;
-
- for (; pos < last; ++pos) {
- 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 + GUI_LISTBOX_HORIZPAD, valueHeight * pos + yOffset, destSurface);
- }
- else {
- drawText(contents[pos].label, guiRGB[(disabled || contents[pos].disabled) ? COLOR_LIGHT2 : COLOR_TEXT],
- xOffset + GUI_LISTBOX_HORIZPAD, valueHeight * pos + yOffset, destSurface);
- }
-
- if ((haveFocus) && (pos == cursorPos)) {
- drawFocusBox(xOffset, valueHeight * pos + yOffset,
- shownWidth, valueHeight, destSurface);
- }
- }
- }
- }
- }
- void WListBox::addTo(Dialog* dialog) { start_func
- wparent = new WidgetScroll(WidgetScroll::FRAMETYPE_BEVEL, this, showWidth, showLines * valueHeight);
- dialog->addWidget(wparent);
- }
- void WListBox::clear() { start_func
- contents.clear();
- numValues = 0;
- valueWidth = 0;
- cursorPos = 0;
- selectedItem = SELECTED_NONE;
- resize(0, 0);
- }
- void WListBox::sortItems() { start_func
- sort(contents.begin(), contents.end());
- setDirty();
- // Find which item is selected now
- selectedItem = SELECTED_NONE;
- for (int pos = 0; pos < numValues; ++pos) {
- if (contents[pos].selected) {
- if (selectedItem == SELECTED_NONE) selectedItem = pos;
- else {
- selectedItem = SELECTED_MANY;
- break;
- }
- }
- }
- }
- void WListBox::modifyItem(int position, const std::string* newLabel, const int* newDisabled, const int* newCode1, const int* newCode2, const int* newCode3) { start_func
- if (newCode1) contents[position].code1 = *newCode1;
- if (newCode2) contents[position].code2 = *newCode2;
- if (newCode3) contents[position].code3 = *newCode3;
- if (newDisabled) {
- // If newly disabled, deselecte
- if ((*newDisabled) && (!contents[position].disabled) && (contents[id].selected)) {
- if ((!allowZero) && (selectedItem == position)) {
- // @TODO: If only item selected and can't select zero items
- }
- else {
- // Just deselect
- selectItem(position, 0, 0);
- }
- }
- contents[position].disabled = *newDisabled;
- }
- // Name change- update width
- if (newLabel) {
- contents[position].label = *newLabel;
- // (limit length of display but don't affect actual data)
- int itemWidth = fontWidth(newLabel->substr(0, MAX_LINELENGTH));
- if (itemWidth > valueWidth) {
- valueWidth = itemWidth;
- resize(valueWidth + GUI_LISTBOX_HORIZPAD * 2, valueHeight * numValues);
- }
- }
-
- // Update display
- setDirty();
- }
- void WListBox::addItem(const ListEntry& item, int selectIt) { start_func
- contents.push_back(item);
- 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(numValues - 1);
- selectItem(cursorPos);
- }
- // Dirties (since we are definately larger now)
- resize(valueWidth + GUI_LISTBOX_HORIZPAD * 2, valueHeight * numValues);
- }
- int WListBox::refuseAll() const { start_func
- if ((numValues == 0) || (disabled)) return 1;
- return 0;
- }
- const ListEntry* WListBox::findEntry(int id) const { start_func
- for (int pos = 0; pos < numValues; ++pos) {
- if (contents[pos].id == id) {
- return &contents[pos];
- }
- }
- return NULL;
- }
|