/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** 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 3 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, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: GadgetListBox.cpp //////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Westwood Studios Pacific. // // Confidential Information // Copyright (C) 2001 - All Rights Reserved // //----------------------------------------------------------------------------- // // Project: RTS3 // // File name: ListBox.cpp // // Created: Dean Iverson, March 1998 // Colin Day, June 2001 // // Desc: ListBox GUI control // //----------------------------------------------------------------------------- /////////////////////////////////////////////////////////////////////////////// // SYSTEM INCLUDES //////////////////////////////////////////////////////////// // USER INCLUDES ////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/AudioEventRTS.h" #include "Common/Language.h" #include "Common/Debug.h" #include "Common/GameAudio.h" #include "GameClient/DisplayStringManager.h" #include "GameClient/GameWindow.h" #include "GameClient/Gadget.h" #include "GameClient/GameWindowManager.h" #include "GameClient/GadgetListBox.h" #include "GameClient/GadgetPushButton.h" #include "GameClient/GadgetSlider.h" #include "GameClient/GameWindowGlobal.h" #include "GameClient/Keyboard.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif // DEFINES //////////////////////////////////////////////////////////////////// // Sets up the user's OS set doubleclick time so if they don't like it... they can // change it in their OS. static UnsignedInt doubleClickTime = GetDoubleClickTime(); // PRIVATE TYPES ////////////////////////////////////////////////////////////// typedef struct _AddMessageStruct { Int row; // The row to add the data to Int column; // The column to add the data to const void *data; // void pointer, can be either an DisplayString or an Image Int type; // Can either be set to LISTBOX_TEXT or LISTBOX_IMAGE Bool overwrite; // Do we overwrite existing data? Int width; // set to -1 if we want the defaults Int height; // set to -1 if we want the defaults } AddMessageStruct; typedef struct _TextAndColor { UnicodeString string; // Holds a unicode String Color color; // holds a text's color } TextAndColor; /////////////////////////////////////////////////////////////////////////////// // PRIVATE DATA /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static void doAudioFeedback(GameWindow *window) { if (!window) return; ListboxData *lData = (ListboxData *)window->winGetUserData(); if (!lData) return; if (lData->audioFeedback) { AudioEventRTS buttonClick("GUIComboBoxClick"); if( TheAudio ) { TheAudio->addAudioEvent( &buttonClick ); } // end if } } static Int getListboxEntryBasedOnCoord(GameWindow *window, Int x, Int y, Int &row, Int &column) { Int pos; Int winx, winy, i; WinInstanceData *instData = window->winGetInstanceData(); ListboxData *list = (ListboxData *)window->winGetUserData(); window->winGetScreenPosition( &winx, &winy ); // Adjust for title if present if( instData->getTextLength() ) winy += TheWindowManager->winFontHeight( instData->getFont() ) + 1; pos = -2; for( i=0; ; i++ ) { if( i > 0 ) if( list->listData[ i - 1 ].listHeight > (list->displayPos + list->displayHeight ) ) { pos = -1; break; } if( i == list->endPos ) { pos = -1; break; } if( list->listData[i].listHeight > (y - winy + list->displayPos) ) break; } column = -1; if( pos == -2 ) { pos = i; Int total = 0; for( i = 0; i < list->columns ;i++) { total += list->columnWidth[i]; if(x - winx < total) { column = i; break; } } } row = pos; return pos; } Int GadgetListBoxGetEntryBasedOnXY( GameWindow *listbox, Int x, Int y, Int &row, Int &column) { return getListboxEntryBasedOnCoord( listbox, x, y, row, column ); } // getListboxTopEntry ========================================================= //============================================================================= static Int getListboxTopEntry( ListboxData *list ) { Int entry; // determin which entry is at the top of the display area for( entry=0; ; entry++ ) { if( list->listData[entry].listHeight > list->displayPos ) return entry; if( entry >= list->endPos ) return 0; } return 0; } // getListboxTopEntry ========================================================= //============================================================================= static Int getListboxBottomEntry( ListboxData *list ) { Int entry; // determin which entry is at the top of the display area for( entry=list->endPos - 1; ; entry-- ) { if( list->listData[entry].listHeight == list->displayPos + list->displayHeight ) return entry; if( list->listData[entry].listHeight < list->displayPos + list->displayHeight && entry != list->endPos - 1) return entry + 1; if( list->listData[entry].listHeight < list->displayPos + list->displayHeight) return entry; if( entry < 0 ) return 0; } return 0; } // removeSelection ============================================================ /** Remove Selection from a multiple selection list */ //============================================================================= static void removeSelection( ListboxData *list, Int i ) { memcpy( &list->selections[i], &list->selections[(i+1)], ((list->listLength - i) * sizeof(Int)) ); // put -1 at end of list just for safety list->selections[(list->listLength - 1)] = -1; } // adjustDisplay ============================================================== /** Update Display List information inlcuding scrollbar */ //============================================================================= static void adjustDisplay( GameWindow *window, Int adjustment, Bool updateSlider ) { Int entry; SliderData *sData; ListboxData *list = (ListboxData *)window->winGetUserData(); // determin which entry is at the top of the display area entry = getListboxTopEntry( list ) + adjustment; if( entry < 0 ) entry = 0; else if( entry >= list->endPos ) entry = list->endPos - 1; if( updateSlider ) { if( entry > 0 ) list->displayPos = list->listData[(entry - 1)].listHeight + 1; else list->displayPos = 0; } if( list->slider != NULL ) { ICoord2D sliderSize, sliderChildSize; GameWindow *child; sData = (SliderData *)list->slider->winGetUserData(); list->slider->winGetSize( &sliderSize.x, &sliderSize.y ); // Take into account that there is a line-drawn outline surrounding listbox sData->maxVal = list->totalHeight - ( list->displayHeight - TOTAL_OUTLINE_HEIGHT ) + 1; if( sData->maxVal < 0 ) { sData->maxVal = 0; } child = list->slider->winGetChild(); child->winGetSize( &sliderChildSize.x, &sliderChildSize.y ); sData->numTicks = (float)((sliderSize.y - sliderChildSize.y) / (float)sData->maxVal); if( updateSlider ) TheWindowManager->winSendSystemMsg( list->slider, GSM_SET_SLIDER, (sData->maxVal - list->displayPos), 0 ); } } // end adjustDisplay // computeTotalHeight ========================================================= /** Compute Total Height and fill in listHeight values */ //============================================================================= static void computeTotalHeight( GameWindow *window ) { Int i, height = 0; Int tempHeight; ListboxData *list = (ListboxData *)window->winGetUserData(); WinInstanceData *instData = window->winGetInstanceData(); for( i=0; iendPos; i++ ) { if(!list->listData[i].cell) continue; tempHeight = 0; for (Int j = 0; j < list->columns; j++) { Int cellHeight = 0; if(list->listData[i].cell[j].cellType == LISTBOX_TEXT) { if( BitTest( window->winGetStatus(), WIN_STATUS_ONE_LINE ) == TRUE ) { cellHeight = TheWindowManager->winFontHeight( instData->getFont() ); } else { DisplayString *displayString = (DisplayString *)list->listData[i].cell[j].data; if(displayString) displayString->getSize( NULL, &cellHeight ); }//else }//if else if(list->listData[i].cell[j].cellType == LISTBOX_IMAGE) { if(list->listData[i].cell[j].height > 0) cellHeight = list->listData[i].cell[j].height + 1; else cellHeight = TheWindowManager->winFontHeight( instData->getFont() ); } if(cellHeight > tempHeight) tempHeight = cellHeight; }//for list->listData[i].height = tempHeight; height += (list->listData[i].height + 1); list->listData[i].listHeight = height; } list->totalHeight = height; adjustDisplay( window, 0, TRUE ); } // addImageEntry ============================================================== /** Add Images to position and column. Row and Column are both based from starting Position 0 */ //============================================================================= static Int addImageEntry( const Image *image, Color color, Int row, Int column, GameWindow *window, Bool overwrite, Int width, Int height ) { // WinInstanceData *instData = window->winGetInstanceData(); ListboxData *list = (ListboxData *)window->winGetUserData(); if( column >= list->columns || row >= list->listLength ) { DEBUG_ASSERTCRASH(false, ("Tried to add Image to Listbox at invalid position")); return -1; } // If we want to just add an entry to the bottom, set the defaults if (row == -1) { row = list->insertPos; list->insertPos++; list->endPos++; } if( column == -1 ) column = 0; ListEntryRow *listRow = &list->listData[row]; // Check and see if we have allocated cells for that row yet, if not, allocate them if(!listRow->cell) { listRow->cell = NEW ListEntryCell[list->columns]; memset(listRow->cell,0,list->columns * sizeof(ListEntryCell)); } // if we're copying over strings, then lets first deallocate them. if(listRow->cell[column].cellType == LISTBOX_TEXT) { TheDisplayStringManager->freeDisplayString((DisplayString *)listRow->cell[column].data); } //add Image to selected row/cell listRow->cell[column].cellType = LISTBOX_IMAGE; listRow->cell[column].data = (void *)image; listRow->cell[column].color = color; listRow->cell[column].height = height; listRow->cell[column].width = width; computeTotalHeight( window ); return (row); }// static Int addImageEntry( Image image, Int column, GameWindow *window) // startingRow will get moved to startingRow+1, etc. This assumes there is space!!!!! static Int moveRowsDown(ListboxData *list, Int startingRow) { // // copy the cells down // Int copyLen = (list->endPos - startingRow) * sizeof(ListEntryRow); char *buf = NEW char[copyLen]; memcpy(buf, list->listData + startingRow, copyLen); memcpy(list->listData + startingRow + 1, buf, copyLen ); delete buf; list->endPos ++; list->insertPos = list->endPos; // // remove the display or links to images after the shift // list->listData[startingRow].cell = NULL; list->listData[startingRow].height = 0; list->listData[startingRow].listHeight = 0; if( list->multiSelect ) { Int i = 0; while( list->selections[i] >= 0 ) { if( startingRow <= list->selections[i] ) list->selections[i]++; i++; } } else { if( list->selectPos >= startingRow ) list->selectPos++; } /* if( list->displayPos > 0 ) adjustDisplay( window, (-1 * mData1), TRUE ); computeTotalHeight( window ); */ return 1; } // addEntry =================================================================== /** Add and process one string at insertPos */ //============================================================================= static Int addEntry( UnicodeString *string, Int color, Int row, Int column, GameWindow *window, Bool overwrite ) { // WinInstanceData *instData = window->winGetInstanceData(); ListboxData *list = (ListboxData *)window->winGetUserData(); Int width; DisplayString *displayString; // make sure our params are good if( column >= list->columns || row >= list->listLength ) { DEBUG_ASSERTCRASH(false, ("Tried to add text to Listbox at invalid position")); return -1; } // If we want to just add an entry to the bottom, set the defaults if (row == -1) { row = list->insertPos; list->insertPos++; list->endPos++; } if( column == -1 ) column = 0; width = list->columnWidth[column] - TEXT_WIDTH_OFFSET; Int rowsAdded = 0; ListEntryRow *listRow = &list->listData[row]; // Here I've decided to just overright what's in the row, if that's not what we want, change it here // Check and see if we have allocated cells for that row yet, if not, allocate them if(!listRow->cell) { listRow->cell = NEW ListEntryCell[list->columns]; memset(listRow->cell,0,list->columns * sizeof(ListEntryCell)); rowsAdded = 1; } else if (!overwrite) { // Shove things down moveRowsDown(list, row); listRow->cell = NEW ListEntryCell[list->columns]; memset(listRow->cell,0,list->columns * sizeof(ListEntryCell)); rowsAdded = 1; } //add Image to selected row/cell listRow->cell[column].cellType = LISTBOX_TEXT; // assign the color to the list data element listRow->cell[column].color = color; // copy text if( !listRow->cell[column].data ) listRow->cell[column].data = (void *) TheDisplayStringManager->newDisplayString(); displayString = (DisplayString *) listRow->cell[column].data; if ( BitTest( window->winGetStatus(), WIN_STATUS_ONE_LINE ) == FALSE ) displayString->setWordWrap( width ); displayString->setText( *string ); /** @todo we need for formalize this, but for now just set the font of this listbox entry to the font of the window */ displayString->setFont( window->winGetFont() ); if (overwrite) { Int oldRowHeight = listRow->height; Int oldTotalHeight = listRow->listHeight; Int rowHeight; Int totalHeight; if (!oldTotalHeight && row) { oldTotalHeight = list->listData[row-1].listHeight; } displayString->getSize( NULL, &rowHeight ); if (rowHeight > oldRowHeight) { totalHeight = oldTotalHeight + (rowHeight - oldRowHeight); listRow->height = rowHeight; listRow->listHeight = totalHeight + rowsAdded; list->totalHeight += (rowHeight - oldRowHeight) + rowsAdded; adjustDisplay( window, 0, TRUE ); } } else { computeTotalHeight( window ); } return (row); } // end addEntry // PUBLIC DATA //////////////////////////////////////////////////////////////// // PRIVATE PROTOTYPES ///////////////////////////////////////////////////////// // PRIVATE FUNCTIONS ////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // PUBLIC FUNCTIONS /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // GadgetListBoxInput ========================================================= /** Handle input for list box */ //============================================================================= WindowMsgHandledType GadgetListBoxInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { ListboxData *list = (ListboxData *)window->winGetUserData(); WinInstanceData *instData = window->winGetInstanceData(); switch (msg) { // ------------------------------------------------------------------------ case GWM_CHAR: { switch (mData1) { // -------------------------------------------------------------------- case KEY_ENTER: case KEY_SPACE: { if( BitTest( mData2, KEY_STATE_UP ) ) { doAudioFeedback(window); TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_DOUBLE_CLICKED, (WindowMsgData)window, list->selectPos ); } // end if break; } // end enter or space // -------------------------------------------------------------------- case KEY_DOWN: { if( BitTest( mData2, KEY_STATE_DOWN ) ) { if( list->selectPos == -1 ) { list->selectPos = 0; adjustDisplay( window, 0, TRUE ); } else if( list->selectPos < list->endPos - 1 ) { list->selectPos++; while (1) { Int cellBottom = list->listData[list->selectPos].listHeight; Int cellTop = cellBottom - list->listData[list->selectPos].height; Int displayTop = list->displayPos; Int displayBottom = list->displayPos + list->displayHeight - 1; // account for the border if ( cellTop < displayTop ) { adjustDisplay(window, -1, TRUE ); } else if ( cellBottom < displayBottom ) { adjustDisplay(window, 0, TRUE ); break; } else { adjustDisplay(window, 1, TRUE ); } } } TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_SELECTED, (WindowMsgData)window, list->selectPos ); } // end if break; } // end key down // -------------------------------------------------------------------- case KEY_UP: { if( BitTest( mData2, KEY_STATE_DOWN ) ) { if( list->selectPos == -1 ) { list->selectPos = 0; adjustDisplay( window, 0, TRUE ); } else if( list->selectPos > 0 ) { list->selectPos--; while (1) { if( list->listData[list->selectPos].listHeight - list->listData[list->selectPos].height < list->displayPos ) { list->displayPos = list->listData[list->selectPos].listHeight +1; adjustDisplay( window, -1, TRUE ); } else if( list->listData[list->selectPos].listHeight > list->displayPos + list->displayHeight) { list->displayPos = list->listData[list->selectPos].listHeight - list->displayHeight; adjustDisplay(window, 1, TRUE); } else { adjustDisplay(window, 0, TRUE); break; } } } TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_SELECTED, (WindowMsgData)window, list->selectPos ); } break; } // end key up // -------------------------------------------------------------------- case KEY_RIGHT: case KEY_TAB: if( BitTest( mData2, KEY_STATE_DOWN ) ) TheWindowManager->winNextTab(window); break; // -------------------------------------------------------------------- case KEY_LEFT: if( BitTest( mData2, KEY_STATE_DOWN ) ) TheWindowManager->winPrevTab(window); break; // -------------------------------------------------------------------- default: { Bool foundIt = false; if( BitTest( mData2, KEY_STATE_DOWN ) ) { // set the position to start looking for the line of text with this character Int position = list->selectPos; // only search the max number of times so that we're not looping over and over for(Int i = 0; i < list->endPos; ++i) { // start at the next position ++position; // if we've reached the end of the list, start at the beginning if( position >= list->endPos) position = 0; ListEntryCell *cell = NULL; // go through the columns until we find a column with text for(Int j = 0; j < list->columns; ++j) { cell = &list->listData[position].cell[j]; if(cell && cell->cellType == LISTBOX_TEXT && cell->data) { break; } } if(!cell || cell->cellType != LISTBOX_TEXT) continue; DisplayString *dString = (DisplayString *)cell->data; if(!dString) continue; for(j = 0; j < TheKeyboard->MAX_KEY_STATES; ++j) { if(dString->getText().getCharAt(0) == TheKeyboard->getPrintableKey(mData1, j)) { list->selectPos = position; Int prevPos = getListboxTopEntry(list); //list->displayPos = list->listData[position].listHeight - list->displayHeight; adjustDisplay(window, position - prevPos, TRUE); foundIt = TRUE; break; } } if(foundIt) { doAudioFeedback(window); break; } } } if (!foundIt) return MSG_IGNORED; } } // end switch( mData1 ) break; } // end case char // ------------------------------------------------------------------------ case GWM_WHEEL_DOWN: { if( list->endPos <= 0) break; if (list->listData[list->endPos - 1].listHeight > list->displayHeight + list->displayPos) adjustDisplay( window, 1, TRUE ); break; } // end wheel down // ------------------------------------------------------------------------ case GWM_WHEEL_UP: { if( list->endPos <= 0) break; adjustDisplay( window, -1, TRUE ); break; } // end wheel up // ------------------------------------------------------------------------ case GWM_LEFT_UP: { TheWindowManager->winSetFocus( window ); // Int mousex = mData1 & 0xFFFF; Int mousey = mData1 >> 16; Int x, y, i; Int oldPos = list->selectPos; window->winGetScreenPosition( &x, &y ); // Adjust for title if present if( instData->getTextLength() ) y += TheWindowManager->winFontHeight( instData->getFont() ) + 1; list->selectPos = -2; for( i=0; ; i++ ) { if( i > 0 ) if( list->listData[ i - 1 ].listHeight > (list->displayPos + list->displayHeight) ) { list->selectPos = -1; break; } if( i == list->endPos ) { list->selectPos = -1; break; } if( list->listData[i].listHeight > (mousey - y + list->displayPos) ) break; } //Bool dblClicked = FALSE; if( list->doubleClickTime + doubleClickTime > timeGetTime() && (i == oldPos || (oldPos == -1 && ( i>=0 && iendPos ) )) ) { int temp; list->doubleClickTime = 0; if( oldPos == -1 ) temp = i; else temp = oldPos; TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_DOUBLE_CLICKED, (WindowMsgData)window, temp ); //break; } if( (i == oldPos) && (list->forceSelect == FALSE) ) { list->selectPos = -1; } if( (list->selectPos == -2) && (i < list->endPos) ) { list->selectPos = i; } if( (list->selectPos < 0) && (list->forceSelect) ) { list->selectPos = oldPos; } list->doubleClickTime = timeGetTime(); TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_SELECTED, (WindowMsgData)window, list->selectPos ); break; } // end left click, left up // ------------------------------------------------------------------------ case GWM_RIGHT_DOWN: doAudioFeedback(window); break; // if we're in game, we want to eat this message because we're right clicking on a listbox case GWM_RIGHT_UP: { TheWindowManager->winSetFocus( window ); Int pos; Int mousex = mData1 & 0xFFFF; Int mousey = mData1 >> 16; Int x, y, i; RightClickStruct rc; window->winGetScreenPosition( &x, &y ); // Adjust for title if present if( instData->getTextLength() ) y += TheWindowManager->winFontHeight( instData->getFont() ) + 1; pos = -2; for( i=0; ; i++ ) { if( i > 0 ) if( list->listData[ i - 1 ].listHeight > (list->displayPos + list->displayHeight ) ) { pos = -1; break; } if( i == list->endPos ) { pos = -1; break; } if( list->listData[i].listHeight > (mousey - y + list->displayPos) ) break; } if( pos == -2 ) pos = i; rc.pos = pos; rc.mouseX = mousex; rc.mouseY = mousey; TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_RIGHT_CLICKED, (WindowMsgData)window, (WindowMsgData)&rc ); break; } // end right up, right click // ------------------------------------------------------------------------ case GWM_MOUSE_ENTERING: { if( BitTest( instData->getStyle(), GWS_MOUSE_TRACK ) ) { BitSet( instData->m_state, WIN_STATE_HILITED ); TheWindowManager->winSendSystemMsg( window->winGetOwner(), GBM_MOUSE_ENTERING, (WindowMsgData)window, 0 ); //TheWindowManager->winSetFocus( window ); } // end if break; } // end mouse entering // ------------------------------------------------------------------------ case GWM_MOUSE_LEAVING: { if( BitTest( instData->getStyle(), GWS_MOUSE_TRACK )) { BitClear( instData->m_state, WIN_STATE_HILITED ); TheWindowManager->winSendSystemMsg( window->winGetOwner(), GBM_MOUSE_LEAVING, (WindowMsgData)window, 0 ); } // end if break; } // end mouse leaving // ------------------------------------------------------------------------ case GWM_LEFT_DRAG: if (BitTest( instData->getStyle(), GWS_MOUSE_TRACK ) ) TheWindowManager->winSendSystemMsg( window->winGetOwner(), GGM_LEFT_DRAG, (WindowMsgData)window, 0 ); break; // ------------------------------------------------------------------------ case GWM_LEFT_DOWN: doAudioFeedback(window); // we want to eat the down... so we may receive the up. return MSG_HANDLED; //------------------------------------------------------------------------- default: return MSG_IGNORED; } // end switch msg return MSG_HANDLED; } // end GadgetListBoxInput // GadgetListBoxMultiInput ==================================================== /** Handle input for multiple selection list box */ //============================================================================= WindowMsgHandledType GadgetListBoxMultiInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { ListboxData *list = (ListboxData *)window->winGetUserData(); WinInstanceData *instData = window->winGetInstanceData(); switch( msg ) { // ------------------------------------------------------------------------ case GWM_CHAR: { switch( mData1 ) { // -------------------------------------------------------------------- case KEY_TAB: if( BitTest( mData2, KEY_STATE_DOWN ) ) window->winNextTab(); break; // -------------------------------------------------------------------- default: return MSG_IGNORED; } break; } // end char // ------------------------------------------------------------------------ case GWM_LEFT_UP: //case GWM_LEFT_CLICK: { TheWindowManager->winSetFocus( window ); // Int *selections = list->selections; Int selectPos = -2; // Int mousex = mData1 & 0xFFFF; Int mousey = mData1 >> 16; Int x, y, i; Bool removed = FALSE; window->winGetScreenPosition( &x, &y ); // Adjust for title if present if( instData->getTextLength() ) y += TheWindowManager->winFontHeight( instData->getFont() ) + 1; for( i = 0; ; i++ ) { if( i > 0 ) if( list->listData[ i - 1 ].listHeight > (list->displayPos + list->displayHeight ) ) { selectPos = -1; break; } if( i == list->endPos ) { selectPos = -1; break; } if( list->listData[i].listHeight > (mousey - y + list->displayPos) ) break; } if( selectPos == -2 ) selectPos = i; i = 0; while( list->selections[i] >= 0 ) { if( list->selections[i] == selectPos ) { removeSelection( list, i ); removed = TRUE; break; } i++; } if( removed == FALSE ) { list->selections[ i] = selectPos; list->selections[ i + 1 ] = -1; } TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_SELECTED, (WindowMsgData)window, selectPos ); break; } // end left up, left click // ------------------------------------------------------------------------ case GWM_RIGHT_UP: { /* Int selectPos = -2; // Int mousex = mData1 & 0xFFFF; Int mousey = mData1 >> 16; Int x, y, i; window->winGetScreenPosition( &x, &y ); // Adjust for title if present if( instData->getTextLength() ) y += TheWindowManager->winFontHeight( instData->getFont() ) + 1; for( i = 0; ; i++ ) { if( i > 0 ) if( list->listData[ i - 1 ].listHeight > (list->displayPos + list->displayHeight ) ) { selectPos = -1; break; } if( i == list->endPos ) { selectPos = -1; break; } if( list->listData[i].listHeight > (mousey - y + list->displayPos) ) break; } if( selectPos == -2 ) selectPos = i; TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_RIGHT_CLICKED, (WindowMsgData)window, selectPos );*/ TheWindowManager->winSetFocus( window ); Int pos; Int mousex = mData1 & 0xFFFF; Int mousey = mData1 >> 16; Int x, y, i; RightClickStruct rc; window->winGetScreenPosition( &x, &y ); // Adjust for title if present if( instData->getTextLength() ) y += TheWindowManager->winFontHeight( instData->getFont() ) + 1; pos = -2; for( i=0; ; i++ ) { if( i > 0 ) if( list->listData[ i - 1 ].listHeight > (list->displayPos + list->displayHeight ) ) { pos = -1; break; } if( i == list->endPos ) { pos = -1; break; } if( list->listData[i].listHeight > (mousey - y + list->displayPos) ) break; } if( pos == -2 ) pos = i; rc.pos = pos; rc.mouseX = mousex; rc.mouseY = mousey; TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_RIGHT_CLICKED, (WindowMsgData)window, (WindowMsgData)&rc ); break; } // end right up, right click // ------------------------------------------------------------------------ case GWM_WHEEL_DOWN: // Simulate the down button if it exists if( list->downButton ) { if( list->displayPos + list->displayHeight <= list->totalHeight ) adjustDisplay( window, 1, TRUE ); } break; // ------------------------------------------------------------------------ case GWM_WHEEL_UP: // Simulate the up button if it exists if( list->upButton ) { if( list->displayPos > 0 ) adjustDisplay( window, -1, TRUE ); } break; // ------------------------------------------------------------------------ case GWM_MOUSE_ENTERING: { if( BitTest( instData->getStyle(), GWS_MOUSE_TRACK ) ) { BitSet( instData->m_state, WIN_STATE_HILITED ); TheWindowManager->winSendSystemMsg( window->winGetOwner(), GBM_MOUSE_ENTERING, (WindowMsgData)window, 0 ); //TheWindowManager->winSetFocus( window ); } // end if break; } // end mouse entering // ------------------------------------------------------------------------ case GWM_MOUSE_LEAVING: { if( BitTest( instData->getStyle(), GWS_MOUSE_TRACK )) { BitClear( instData->m_state, WIN_STATE_HILITED ); TheWindowManager->winSendSystemMsg( window->winGetOwner(), GBM_MOUSE_LEAVING, (WindowMsgData)window, 0 ); } // end if break; } // end mouse leaving // ------------------------------------------------------------------------ case GWM_LEFT_DRAG: if (BitTest( instData->getStyle(), GWS_MOUSE_TRACK ) ) TheWindowManager->winSendSystemMsg( window->winGetOwner(), GGM_LEFT_DRAG, (WindowMsgData)window, 0 ); break; // ------------------------------------------------------------------------ case GWM_LEFT_DOWN: doAudioFeedback(window); // we want to eat the down... so we may receive the up. return MSG_HANDLED; // ------------------------------------------------------------------------ default: return MSG_IGNORED; } // end switch( msg ) return MSG_HANDLED; } // end GadgetListBoxMultiInput // GadgetListBoxSystem ======================================================== /** Handle system messages for list box */ //============================================================================= WindowMsgHandledType GadgetListBoxSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { ListboxData *list = (ListboxData *)window->winGetUserData(); WinInstanceData *instData = window->winGetInstanceData(); ICoord2D *pos; switch( msg ) { // ------------------------------------------------------------------------ case GGM_SET_LABEL: { instData->setText(*(UnicodeString*)mData1); break; } // end set lavel // ------------------------------------------------------------------------ case GLM_GET_TEXT: { pos = (ICoord2D *)mData1; TextAndColor *tAndC = (TextAndColor *)mData2; if(pos->x >= list->columns || pos->y >= list->listLength || list->listData[pos->y].cell[pos->x].cellType != LISTBOX_TEXT) { tAndC->string = UnicodeString.TheEmptyString; tAndC->color = 0; } else { tAndC->string = ((DisplayString *)list->listData[ pos->y ].cell[pos->x].data)->getText(); tAndC->color = list->listData[ pos->y ].cell[pos->x].color; } break; } // ------------------------------------------------------------------------ case GBM_SELECTED: { // See if the up button was selected if( (GameWindow *)mData1 == list->upButton ) { if( list->displayPos > 0 ) adjustDisplay( window, -1, TRUE ); } else if( (GameWindow *)mData1 == list->downButton ) { if( list->displayPos + list->displayHeight <= list->totalHeight ) adjustDisplay( window, 1, TRUE ); } break; } // end selected // ------------------------------------------------------------------------ case GGM_LEFT_DRAG: { if( (GameWindow *)mData1 == list->upButton ) { if( list->displayPos > 0 ) adjustDisplay( window, -1, TRUE ); } else if( (GameWindow *)mData1 == list->downButton ) { if( list->displayPos + list->displayHeight <= list->totalHeight ) adjustDisplay( window, 1, TRUE ); } break; } // end left drag // ------------------------------------------------------------------------ case GLM_DEL_ALL: { // // Reset the listbox by freeing all the display string stuff and setting // everything else to zero // // Loop through and destroy any display strings we've allocated that aren't used for( Int i = 0; i < list->listLength; i++ ) { // Loop though ListEntryCell *cells = list->listData[i].cell; for (int j = list->columns - 1; j >=0; j-- ) { if(!cells) break; if( cells[j].cellType == LISTBOX_TEXT ) { if ( cells[j].data ) { TheDisplayStringManager->freeDisplayString((DisplayString *) cells[j].data ); } } cells[j].userData = NULL; cells[j].data = NULL; } delete(list->listData[i].cell); list->listData[i].cell = NULL; } //zero out the header structure memset(list->listData,0,list->listLength * sizeof(ListEntryRow)); if( mData1 != GP_DONT_UPDATE ) { list->displayPos = 0; } if( list->multiSelect ) memset( list->selections, -1, list->listLength * sizeof( Int ) ); else list->selectPos = -1; list->insertPos = 0; list->endPos = 0; list->totalHeight = 0; adjustDisplay( window, 0, TRUE ); break; } // end delete all // ------------------------------------------------------------------------ case GLM_DEL_ENTRY: { Int i; if( list->endPos <= (Int)mData1 ) break; ListEntryCell *cells = list->listData[mData1].cell; if(cells) for( i = 0; i <= list->columns; i ++ ) { if( cells[i].cellType == LISTBOX_TEXT && cells[i].data ) TheDisplayStringManager->freeDisplayString((DisplayString *) cells[i].data ); cells[i].data = NULL; cells[i].userData = NULL; } delete [](list->listData[mData1].cell); memcpy( &list->listData[mData1], &list->listData[(mData1+1)], (list->endPos - mData1 - 1) * sizeof(ListEntryRow) ); list->endPos--; list->insertPos = list->endPos; if( list->multiSelect ) { i = 0; while( list->selections[i] >= 0 ) { if( (Int)mData1 < list->selections[i] ) list->selections[i]--; else if ( (Int)mData1 == list->selections[i] ) { removeSelection( list, i ); i--; // compensate for lost entry } i++; } } else { if( (Int)mData1 < list->selectPos ) list->selectPos--; else if ( (Int)mData1 == list->selectPos ) list->selectPos = -1; } computeTotalHeight( window ); break; } // end delete entry // ------------------------------------------------------------------------ case GLM_ADD_ENTRY: { Bool success = TRUE; Int addedIndex = -1; AddMessageStruct *addInfo = (AddMessageStruct*)mData1; if (addInfo->row >= list->insertPos) addInfo->row = -1; Int row = addInfo->row; // Special case, we're just appending and we've reached the end. if( addInfo->row == -1 && list->insertPos == list->listLength ) { row = list->insertPos; // Check to see if we've filled our buffer and need to scroll the window if( list->insertPos == list->listLength ) { if( list->autoPurge ) TheWindowManager->winSendSystemMsg( window, GLM_SCROLL_BUFFER, 1, 0 ); else success = FALSE; } } else if (addInfo->row != -1 && !addInfo->overwrite && list->insertPos == list->listLength) { // We're inserting into the middle with no space - see if we can scroll the window if( list->autoPurge ) TheWindowManager->winSendSystemMsg( window, GLM_SCROLL_BUFFER, 1, 0 ); else success = FALSE; } if(success) { if( addInfo->type == LISTBOX_TEXT ) { addedIndex = addEntry( (UnicodeString *)addInfo->data, mData2, addInfo->row, addInfo->column, window, addInfo->overwrite ); } else if ( addInfo->type == LISTBOX_IMAGE ) { addedIndex = addImageEntry( (const Image *)addInfo->data, mData2, addInfo->row, addInfo->column, window, addInfo->overwrite,addInfo->width, addInfo->height ); } else success = FALSE; } if( success ) { if( list->autoScroll ) { while( TRUE ) { // If off bottom of screen, scroll and try again. // we use -1 because insertPos was increased in addEntry if( row == -1 ) { if( list->listData[(list->insertPos - 1)].listHeight >= (list->displayPos + list->displayHeight) ) adjustDisplay( window, 1, TRUE ); else break; } else { if( list->listData[( row )].listHeight >= (list->displayPos + list->displayHeight) ) adjustDisplay( window, 1, TRUE ); else break; } } } if( list->multiSelect ) { Int i = 0; while( list->selections[i] >= 0 ) { if( (row = list->selections[i]) != 0 ) list->selections[i] = -1; i++; } // end while } // end if else { if( row == list->selectPos ) list->selectPos = -1; } } // end success return((WindowMsgHandledType) addedIndex ); } // end add entry // ------------------------------------------------------------------------ case GLM_TOGGLE_MULTI_SELECTION: { if( (Int)mData1 < 0 ) { // a negative number will purge the entire list. if( list->multiSelect ) memset( list->selections, -1, list->listLength * sizeof(Int) ); else { // this message has no effect in a non-multi listbox } break; } // if there is no cells we shouldn't be selecting this entry if( !list->listData[ mData1 ].cell ) break; if( list->multiSelect ) { Int i = 0; Bool removed = FALSE; while( list->selections[i] >= 0 ) { if( list->selections[i] == (Int)mData1 ) { removeSelection( list, i ); removed = TRUE; break; } i++; } if( removed == FALSE ) { list->selections[i] = (Int)mData1; list->selections[i+1] = -1; } } else { // this message has no effect in a non-multi listbox } break; } // end toggle multi-select // ------------------------------------------------------------------------ case GLM_SET_SELECTION: { const Int *selectList = (const Int *)mData1; Int selectCount = (Int)mData2; DEBUG_ASSERTCRASH( list->multiSelect || selectCount == 1, ("Bad selection size")); if( selectList[0] < 0 || list->listLength <= selectList[0] ) { if( list->multiSelect ) memset( list->selections, -1, list->listLength * sizeof(Int) ); else list->selectPos = -1; TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_SELECTED, (WindowMsgData)window, list->selectPos ); break; } if( list->multiSelect ) { // forced selections override the entire selection list. for (Int i=0; iendPos; ++i) { // don't select off the end if (list->listLength <= selectList[i]) { break; } // if there is no cells we shouldn't be selecting this entry if( !list->listData[ selectList[i] ].cell ) { break; } list->selections[i] = selectList[i]; } list->selections[i] = -1; } else { // if there is no cells we shouldn't be selecting this entry if( !list->listData[ selectList[0] ].cell ) { break; } list->selectPos = selectList[0]; GameWindow *parent = window->winGetParent(); if( parent && BitTest( parent->winGetStyle(), GWS_COMBO_BOX ) ) { TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_SELECTED, (WindowMsgData)window, list->selectPos ); break; } if( list->listData[list->selectPos].listHeight < list->displayPos ) { TheWindowManager->winSendSystemMsg( window, GLM_UPDATE_DISPLAY, list->selectPos, 0 ); } else if( list->listData[list->selectPos].listHeight > (list->displayPos + list->displayHeight) ) { if( list->selectPos > 0 ) list->displayPos = list->listData[list->selectPos].listHeight - list->displayHeight; else list->displayPos = 0; adjustDisplay( window, 0, TRUE ); } // end else if } // end else TheWindowManager->winSendSystemMsg( window->winGetOwner(), GLM_SELECTED, (WindowMsgData)window, list->selectPos ); break; } // end set selection // ------------------------------------------------------------------------ case GLM_SCROLL_BUFFER: { if( list->endPos < (Int)mData1 ) break; // // scroll buffer literally scrolls the entire list data buffer // up one entry, effectively removing the top entry. // // // Loop through and remove all the entries from the top up until we reach // the position mData1 contains // ListEntryCell *cells = NULL; for (Int i = 0; i < (Int)mData1; i++) { cells = list->listData[i].cell; if(cells) for( Int j = 0; j < list->columns; j++ ) { if( cells[j].cellType == LISTBOX_TEXT && cells[j].data ) TheDisplayStringManager->freeDisplayString((DisplayString *) cells[j].data ); // if (cells[i].userData) // free(cells[i].userData); cells[j].data = NULL; cells[j].userData = NULL; cells[j].color = 0; cells[j].cellType = 0; } delete(list->listData[i].cell); list->listData[i].cell = NULL; } // // copy the cells up // memcpy(list->listData, &list->listData[mData1], (list->endPos - mData1) * sizeof(ListEntryRow) ); list->endPos -= mData1; list->insertPos = list->endPos; // // remove the display or links to images after the shift // for(i = 0; i < (Int)mData1; i ++) { list->listData[list->endPos + i].cell = NULL; } if( list->multiSelect ) { Int i = 0; while( list->selections[i] >= 0 ) { if( (Int)mData1 >= list->selections[i] ) list->selections[i] -= (Int)mData1; else { removeSelection( list, i ); i--; // compensate for lost entry } i++; } } else { if( list->selectPos > 0 ) list->selectPos -= mData1; } if( list->displayPos > 0 ) adjustDisplay( window, (-1 * mData1), TRUE ); computeTotalHeight( window ); break; } // end scroll buffer // ------------------------------------------------------------------------ case GLM_GET_SELECTION: { if( list->multiSelect ) *(Int*)mData2 = (Int)list->selections; else *(Int*)mData2 = list->selectPos; break; } // end get selection // ------------------------------------------------------------------------ case GLM_SET_UP_BUTTON: list->upButton = (GameWindow *)mData1; break; // ------------------------------------------------------------------------ case GLM_SET_DOWN_BUTTON: list->downButton = (GameWindow *)mData1; break; // ------------------------------------------------------------------------ case GLM_SET_SLIDER: list->slider = (GameWindow *)mData1; break; // ------------------------------------------------------------------------ case GWM_CREATE: break; // ------------------------------------------------------------------------ case GGM_RESIZED: { Int width = (Int)mData1; Int height = (Int)mData2; ICoord2D downSize = {0, 0}; ICoord2D upSize = {0, 0}; ICoord2D sliderSize = {0, 0}; GameWindow *child = NULL; ICoord2D sliderChildSize = {0, 0}; // get needed window sizes if (list->downButton) list->downButton->winGetSize( &downSize.x, &downSize.y ); if (list->upButton) list->upButton->winGetSize( &upSize.x, &upSize.y ); if (list->slider) { list->slider->winGetSize( &sliderSize.x, &sliderSize.y ); child = list->slider->winGetChild(); if (child) child->winGetSize( &sliderChildSize.x, &sliderChildSize.y ); } if( list->upButton ) { list->upButton->winSetPosition( width - upSize.x - 2, 2 ); } if( list->downButton ) { list->downButton->winSetPosition( width - downSize.x - 2, height - downSize.y - 2 ); } if( list->slider ) { list->slider->winSetSize( sliderSize.x, height - (2 * upSize.y) -6 ); list->slider->winSetPosition( width - sliderSize.x -2, upSize.y + 3 ); } list->displayHeight = height; // store display height if( instData->getTextLength() ) { list->displayHeight -= TheWindowManager->winFontHeight( instData->getFont() ); } // // Setup listbox Columns // if( list->columns == 1 ) { list->columnWidth[0] = width; if( list->slider ) { ICoord2D sliderSize; list->slider->winGetSize( &sliderSize.x, &sliderSize.y ); list->columnWidth[0] -= sliderSize.x; } // end if }// if else { if( !list->columnWidthPercentage ) break; if(!list->columnWidth) break; Int totalWidth = width; if( list->slider ) { ICoord2D sliderSize; list->slider->winGetSize( &sliderSize.x, &sliderSize.y ); totalWidth -= sliderSize.x; } // end if for(Int i = 0; i < list->columns; i++ ) { list->columnWidth[i] = list->columnWidthPercentage[i] * totalWidth / 100; }// for }// else //reset the total height computeTotalHeight(window); break; } // end resized // ------------------------------------------------------------------------ case GLM_UPDATE_DISPLAY: { if( mData1 > 0 ) // set the display to the top of a specific entry // which is the previous listHeight + 1 list->displayPos = list->listData[(mData1 - 1)].listHeight + 1; else list->displayPos = 0; if( list->displayPos + list->displayHeight >= list->totalHeight ) { list->displayPos = list->totalHeight - list->displayHeight; } adjustDisplay( window, 0, TRUE ); break; } // end update display // ------------------------------------------------------------------------ case GWM_DESTROY: { Int i; // Loop through and destroy any display strings we've allocated for( i = 0; i < list->listLength; i++ ) { //We're now onto a row of cells we are not using anymore Pull off the cells and loop through them ListEntryCell *cells = list->listData[i].cell; for (int j = list->columns - 1; j >=0; j-- ) { if(!cells) break; if( cells[j].cellType == LISTBOX_TEXT ) { // If we can delete the stuff that won't be showing up in the new listData struture if ( cells[j].data ) { TheDisplayStringManager->freeDisplayString((DisplayString *) cells[j].data ); } } // if ( cells[j].userData ) // free(cells[j].userData); // Null out the data pointers so they're not destroyed when we free up this listdata cells[j].userData = NULL; cells[j].data = NULL; } delete[](list->listData[i].cell); list->listData[i].cell = NULL; } delete[]( list->listData ); if( list->columnWidth ) delete[]( list->columnWidth ); if( list->columnWidthPercentage ) delete[]( list->columnWidthPercentage ); if( list->multiSelect ) delete[]( list->selections ); delete( list ); break; } // end destroy // ------------------------------------------------------------------------ case GWM_INPUT_FOCUS: { // If we're losing focus if( mData1 == FALSE ) { BitClear( instData->m_state, WIN_STATE_HILITED ); } else { BitSet( instData->m_state, WIN_STATE_HILITED ); } TheWindowManager->winSendSystemMsg( window->winGetOwner(), GGM_FOCUS_CHANGE, mData1, window->winGetWindowId() ); *(Bool*)mData2 = TRUE; break; } // end input focus // ------------------------------------------------------------------------ case GSM_SLIDER_TRACK: { SliderData *sData = (SliderData *)list->slider->winGetUserData(); list->displayPos = sData->maxVal - mData2; if( list->displayPos > (list->totalHeight - list->displayHeight + 1) ) list->displayPos = (list->totalHeight - list->displayHeight + 1); if( list->displayPos < 0) list->displayPos = 0; adjustDisplay( window, 0, FALSE ); break; } // end slider track // ------------------------------------------------------------------------ case GLM_SET_ITEM_DATA: { void *data = (void *)mData2; pos = (ICoord2D *)mData1; if (pos->y >= 0 && pos->y < list->endPos && list->listData[pos->y].cell) list->listData[pos->y].cell[pos->x].userData = data; break; }//case GLM_SET_ITEM_DATA: // ------------------------------------------------------------------------ case GLM_GET_ITEM_DATA: { pos = (ICoord2D *)mData1; void **data = (void **)mData2; *data = NULL; // initialize to NULL if (pos->y >= 0 && pos->y < list->endPos && list->listData[pos->y].cell) *data = list->listData[pos->y].cell[pos->x].userData; break; }//case GLM_GET_ITEM_DATA: default: return MSG_IGNORED; } // end switch( msg ) return MSG_HANDLED; } // end GadgetListBoxSystem // GadgetListBoxSetColors ===================================================== /** Set the colors for a list box, note that this will also automatically * change the colors of any attached slider, slider thumb, and slider * buttons */ //============================================================================= void GadgetListBoxSetColors( GameWindow *listbox, Color enabledColor, Color enabledBorderColor, Color enabledSelectedItemColor, Color enabledSelectedItemBorderColor, Color disabledColor, Color disabledBorderColor, Color disabledSelectedItemColor, Color disabledSelectedItemBorderColor, Color hiliteColor, Color hiliteBorderColor, Color hiliteSelectedItemColor, Color hiliteSelectedItemBorderColor ) { ListboxData *listboxData = (ListboxData *)listbox->winGetUserData(); // enabled GadgetListBoxSetEnabledColor( listbox, enabledColor ); GadgetListBoxSetEnabledBorderColor( listbox, enabledBorderColor ); GadgetListBoxSetEnabledSelectedItemColor( listbox, enabledSelectedItemColor ); GadgetListBoxSetEnabledSelectedItemBorderColor( listbox, enabledSelectedItemBorderColor ); // disabled GadgetListBoxSetDisabledColor( listbox, disabledColor ); GadgetListBoxSetDisabledBorderColor( listbox, disabledBorderColor ); GadgetListBoxSetDisabledSelectedItemColor( listbox, disabledSelectedItemColor ); GadgetListBoxSetDisabledSelectedItemBorderColor( listbox, disabledSelectedItemBorderColor ); // hilited GadgetListBoxSetHiliteColor( listbox, hiliteColor ); GadgetListBoxSetHiliteBorderColor( listbox, hiliteBorderColor ); GadgetListBoxSetHiliteSelectedItemColor( listbox, hiliteSelectedItemColor ); GadgetListBoxSetHiliteSelectedItemBorderColor( listbox, hiliteSelectedItemBorderColor ); // assign default slider colors and images as part of the list box GameWindow *slider = listboxData->slider; if( slider ) { GameWindow *upButton = listboxData->upButton; GameWindow *downButton = listboxData->downButton; // slider and slider thumb ---------------------------------------------- // enabled GadgetSliderSetEnabledColor( slider, GadgetListBoxGetEnabledColor( listbox ) ); GadgetSliderSetEnabledBorderColor( slider, GadgetListBoxGetEnabledBorderColor( listbox ) ); // Disabled GadgetSliderSetDisabledColor( slider, GadgetListBoxGetDisabledColor( listbox ) ); GadgetSliderSetDisabledBorderColor( slider, GadgetListBoxGetDisabledBorderColor( listbox ) ); // Hilite GadgetSliderSetHiliteColor( slider, GadgetListBoxGetHiliteColor( listbox ) ); GadgetSliderSetHiliteBorderColor( slider, GadgetListBoxGetHiliteBorderColor( listbox ) ); // up button ------------------------------------------------------------ // enabled GadgetButtonSetEnabledColor( upButton, GadgetSliderGetEnabledColor( slider ) ); GadgetButtonSetEnabledBorderColor( upButton, GadgetSliderGetEnabledBorderColor( slider ) ); GadgetButtonSetEnabledSelectedColor( upButton, GadgetSliderGetEnabledSelectedThumbColor( slider ) ); GadgetButtonSetEnabledSelectedBorderColor( upButton, GadgetSliderGetEnabledSelectedThumbBorderColor( slider ) ); // disabled GadgetButtonSetDisabledColor( upButton, GadgetSliderGetDisabledColor( slider ) ); GadgetButtonSetDisabledBorderColor( upButton, GadgetSliderGetDisabledBorderColor( slider ) ); GadgetButtonSetDisabledSelectedColor( upButton, GadgetSliderGetDisabledSelectedThumbColor( slider ) ); GadgetButtonSetDisabledSelectedBorderColor( upButton, GadgetSliderGetDisabledSelectedThumbBorderColor( slider ) ); // hilite GadgetButtonSetHiliteColor( upButton, GadgetSliderGetHiliteColor( slider ) ); GadgetButtonSetHiliteBorderColor( upButton, GadgetSliderGetHiliteBorderColor( slider ) ); GadgetButtonSetHiliteSelectedColor( upButton, GadgetSliderGetHiliteSelectedThumbColor( slider ) ); GadgetButtonSetHiliteSelectedBorderColor( upButton, GadgetSliderGetHiliteSelectedThumbBorderColor( slider ) ); // down button ---------------------------------------------------------- // enabled GadgetButtonSetEnabledColor( downButton, GadgetSliderGetEnabledColor( slider ) ); GadgetButtonSetEnabledBorderColor( downButton, GadgetSliderGetEnabledBorderColor( slider ) ); GadgetButtonSetEnabledSelectedColor( downButton, GadgetSliderGetEnabledSelectedThumbColor( slider ) ); GadgetButtonSetEnabledSelectedBorderColor( downButton, GadgetSliderGetEnabledSelectedThumbBorderColor( slider ) ); // disabled GadgetButtonSetDisabledColor( downButton, GadgetSliderGetDisabledColor( slider ) ); GadgetButtonSetDisabledBorderColor( downButton, GadgetSliderGetDisabledBorderColor( slider ) ); GadgetButtonSetDisabledSelectedColor( downButton, GadgetSliderGetDisabledSelectedThumbColor( slider ) ); GadgetButtonSetDisabledSelectedBorderColor( downButton, GadgetSliderGetDisabledSelectedThumbBorderColor( slider ) ); // hilite GadgetButtonSetHiliteColor( downButton, GadgetSliderGetHiliteColor( slider ) ); GadgetButtonSetHiliteBorderColor( downButton, GadgetSliderGetHiliteBorderColor( slider ) ); GadgetButtonSetHiliteSelectedColor( downButton, GadgetSliderGetHiliteSelectedThumbColor( slider ) ); GadgetButtonSetHiliteSelectedBorderColor( downButton, GadgetSliderGetHiliteSelectedThumbBorderColor( slider ) ); } // end if } // end GadgetListBoxSetColors // GadgetListBoxGetText ======================================================= /** Get the text for a list box entry */ //============================================================================= UnicodeString GadgetListBoxGetText( GameWindow *listbox, Int row, Int column) { Color color; return GadgetListBoxGetTextAndColor( listbox,&color,row,column ); } // end GadgetListBoxGetText // GadgetListBoxGetText ======================================================= /** Get the text for a list box entry */ //============================================================================= UnicodeString GadgetListBoxGetTextAndColor( GameWindow *listbox, Color *color, Int row, Int column) { *color = 0; // sanity if( listbox == NULL || row == -1 || column == -1) return UnicodeString::TheEmptyString; // verify that this is a list box if( BitTest( listbox->winGetStyle(), GWS_SCROLL_LISTBOX ) == FALSE ) return UnicodeString::TheEmptyString; TextAndColor tAndC; //UnicodeString result; ICoord2D pos; pos.x = column; pos.y = row; TheWindowManager->winSendSystemMsg( listbox, GLM_GET_TEXT, (WindowMsgData)&pos, (WindowMsgData)&tAndC ); *color = tAndC.color; return tAndC.string; //return UnicodeString::TheEmptyString; } // end GadgetListBoxGetText // GadgetListBoxAddEntryText ================================================== /** Add a new string entry into the listbox at the insert position */ //============================================================================= Int GadgetListBoxAddEntryText( GameWindow *listbox, UnicodeString text, Color color, Int row, Int column, Bool overwrite ) { if (!listbox) return -1; if (text.isEmpty()) text = UnicodeString(L" "); Int index; AddMessageStruct addInfo; addInfo.row = row; addInfo.column = column; addInfo.type = LISTBOX_TEXT; addInfo.data = &text; addInfo.overwrite = overwrite; addInfo.height = -1; addInfo.width = -1; ListboxData *listData = (ListboxData *)listbox->winGetUserData(); Bool wasFull = (listData->listLength <= listData->endPos); Int newEntryOffset = (wasFull)?0:1; Int oldBottomIndex = GadgetListBoxGetBottomVisibleEntry(listbox); /// @TODO: Don't do this type cast! index = (Int) TheWindowManager->winSendSystemMsg( listbox, GLM_ADD_ENTRY, (WindowMsgData)&addInfo, color ); //DEBUG_ASSERTLOG(!listData->scrollIfAtEnd, ("Adding line %d (orig end was %d, newEntryOffset is %d, (%d-%d)?=%d, isFull=%d/%d ll=%d, end=%d\n", //index, oldBottomIndex, newEntryOffset, index, oldBottomIndex, newEntryOffset, wasFull, GadgetListBoxIsFull(listbox), listData->listLength, listData->endPos)); if(listData->scrollIfAtEnd && index - oldBottomIndex == newEntryOffset && GadgetListBoxIsFull(listbox)) { GadgetListBoxSetBottomVisibleEntry( listbox, index ); } return (index); } // end GadgetListBoxAddEntry // GadgetListBoxAddEntryImage ================================================= /** Add a new string entry into the listbox at the insert position */ //============================================================================= Int GadgetListBoxAddEntryImage( GameWindow *listbox, const Image *image, Int row, Int column, Int hight, Int width, Bool overwrite, Color color ) { Int index; AddMessageStruct addInfo; addInfo.row = row; addInfo.column = column; addInfo.type = LISTBOX_IMAGE; addInfo.data = image; addInfo.overwrite = overwrite; addInfo.height = hight; addInfo.width = width; /// @TODO: Don't do this type cast! index = (Int) TheWindowManager->winSendSystemMsg( listbox, GLM_ADD_ENTRY, (WindowMsgData)&addInfo, color ); return (index); } // end GadgetListBoxAddEntryImage Int GadgetListBoxAddEntryImage( GameWindow *listbox, const Image *image, Int row, Int column, Bool overwrite, Color color ) { return GadgetListBoxAddEntryImage(listbox, image, row, column, -1, -1, overwrite, color); } // GadgetListBoxSetFont ======================================================= /** Set the font for a listbox control, we need to set the window * text font, the tooltip font, and the edit text display strings for * the text data itself and the secret text */ //============================================================================= void GadgetListBoxSetFont( GameWindow *g, GameFont *font ) { ListboxData *listData = (ListboxData *)g->winGetUserData(); DisplayString *dString; // set the font for the display strings all windows have dString = g->winGetInstanceData()->getTextDisplayString(); if( dString ) dString->setFont( font ); dString = g->winGetInstanceData()->getTooltipDisplayString(); if( dString ) dString->setFont( font ); // listbox specific if( listData ) for( Int i = 0; i < listData->listLength; i++ ) { if(listData->listData[i].cell) for( Int j = 0; j < listData->columns; j++ ) { if( listData->listData[i].cell[j].cellType == LISTBOX_TEXT && listData->listData[i].cell[j].data ) { dString = (DisplayString *)listData->listData[i].cell[j].data; dString->setFont( font ); } } } // end for i } // end GadgetListBoxSetFont // GadgetListboxCreateScrollbar =============================================== /** Create the scroll bar using a slider and two buttons for a listbox */ //============================================================================= void GadgetListboxCreateScrollbar( GameWindow *listbox ) { ListboxData *listData = (ListboxData *)listbox->winGetUserData(); WinInstanceData winInstData; SliderData sData = { 0 }; Int buttonWidth, buttonHeight; Int sliderButtonWidth, sliderButtonHeight; Int fontHeight; Int top; Int bottom; UnsignedInt status = listbox->winGetStatus(); Bool title = FALSE; Int width, height; // get width and height of listbox listbox->winGetSize( &width, &height ); // do we have a title if( listbox->winGetTextLength() ) title = TRUE; // remove unwanted status bits. status &= ~(WIN_STATUS_BORDER | WIN_STATUS_HIDDEN | WIN_STATUS_NO_INPUT); fontHeight = TheWindowManager->winFontHeight( listbox->winGetFont() ); top = title ? (fontHeight + 1):0; bottom = title ? (height - (fontHeight + 1)):height; // intialize instData winInstData.init(); // size of button buttonWidth = 21;//GADGET_SIZE; buttonHeight = 22;//GADGET_SIZE; // ---------------------------------------------------------------------- // Create Top Button // ---------------------------------------------------------------------- status |= WIN_STATUS_IMAGE; winInstData.m_owner = listbox; winInstData.m_style = GWS_PUSH_BUTTON; // if listbox tracks, so will this sub control if( BitTest( listbox->winGetStyle(), GWS_MOUSE_TRACK ) ) BitSet( winInstData.m_style, GWS_MOUSE_TRACK ); listData->upButton = TheWindowManager->gogoGadgetPushButton( listbox, status | WIN_STATUS_ACTIVE | WIN_STATUS_ENABLED, width - buttonWidth -2, top+2, buttonWidth, buttonHeight, &winInstData, NULL, TRUE ); // ---------------------------------------------------------------------- // Create Bottom Button // ---------------------------------------------------------------------- winInstData.init(); winInstData.m_style = GWS_PUSH_BUTTON; winInstData.m_owner = listbox; // if listbox tracks, so will this sub control if( BitTest( listbox->winGetStyle(), GWS_MOUSE_TRACK ) ) BitSet( winInstData.m_style, GWS_MOUSE_TRACK ); listData->downButton = TheWindowManager->gogoGadgetPushButton( listbox, status | WIN_STATUS_ACTIVE | WIN_STATUS_ENABLED, width - buttonWidth -2, (top + bottom - buttonHeight -2), buttonWidth, buttonHeight, &winInstData, NULL, TRUE ); // ---------------------------------------------------------------------- // create the slider // ---------------------------------------------------------------------- // size of button sliderButtonWidth = buttonWidth;//GADGET_SIZE; sliderButtonHeight = GADGET_SIZE; // intialize instData winInstData.init(); winInstData.m_style = GWS_VERT_SLIDER; winInstData.m_owner = listbox; // if listbox tracks, so will this sub control if( BitTest( listbox->winGetStyle(), GWS_MOUSE_TRACK ) ) BitSet( winInstData.m_style, GWS_MOUSE_TRACK ); // intialize sData memset( &sData, 0, sizeof(SliderData) ); // Create Slider listData->slider = TheWindowManager->gogoGadgetSlider( listbox, status | WIN_STATUS_ACTIVE | WIN_STATUS_ENABLED, width - sliderButtonWidth - 2, (top + buttonHeight + 3), sliderButtonWidth, bottom - (2 * buttonHeight) - 6, &winInstData, &sData, NULL, TRUE ); // we now have all the scrollbar parts, this better be set :) listData->scrollBar = TRUE; } // end GadgetListBoxCreateScrollbar // GadgetListBoxAddMultiSelect ================================================ /** Enable multi selections for a listbox * * ASSUMPTION: The listLength must already be set at this time so * that we can make enough space to hold all the selection data, if you * change the list length you must also change the selection array * for multi select listboxes */ //============================================================================= void GadgetListBoxAddMultiSelect( GameWindow *listbox ) { ListboxData *listboxData = (ListboxData *)listbox->winGetUserData(); DEBUG_ASSERTCRASH(listboxData && listboxData->selections == NULL, ("selections is not NULL")); listboxData->selections = NEW Int [listboxData->listLength]; DEBUG_LOG(( "Enable list box multi select: listLength (select) = %d * %d = %d bytes;\n", listboxData->listLength, sizeof(Int), listboxData->listLength *sizeof(Int) )); if( listboxData->selections == NULL ) { delete( listboxData->listData ); return; } // end if memset( listboxData->selections, -1, listboxData->listLength * sizeof(Int) ); // set mutliselect flag listboxData->multiSelect = TRUE; // adjust the input procedure for the listbox listbox->winSetInputFunc( GadgetListBoxMultiInput ); } // end GadgetListBoxEnableMultiSelect // GadgetListBoxRemoveMultiSelect ============================================= /** Remove multi select capability from a listbox */ //============================================================================= void GadgetListBoxRemoveMultiSelect( GameWindow *listbox ) { ListboxData *listData = (ListboxData *)listbox->winGetUserData(); if( listData->selections ) { delete( listData->selections ); listData->selections = NULL; } // end if listData->multiSelect = FALSE; // adjust the input procedure for the listbox listbox->winSetInputFunc( GadgetListBoxInput ); } // end GadgetListBoxRemoveMultiSelect // GadgetListBoxSetListLength ================================================= /** Set OR reset the list length data contained in the listboxData * parameter. When adjusting the size of lists we also have to * adjust multiselection lists if present and any display * strings present */ //============================================================================= void GadgetListBoxSetListLength( GameWindow *listbox, Int newLength ) { ListboxData *listboxData = (ListboxData *)listbox->winGetUserData(); // ListboxData *listboxData = (ListboxData *)listbox->winGetUserData(); // ListEntry *newData = (ListEntry *)malloc(newLength * sizeof(ListEntry)); DEBUG_ASSERTCRASH(listboxData, ("We don't have our needed listboxData!")); if( !listboxData ) return; DEBUG_ASSERTCRASH(listboxData->columns > 0,("We need at least one Column in the listbox")); if( listboxData->columns < 1 ) return; Int columns = listboxData->columns; ListEntryRow *newData = NEW ListEntryRow[ newLength ]; DEBUG_ASSERTCRASH(newData, ("Unable to allocate new data structures for the Listbox")); if( !newData ) return; Int i; // zero out the new Data structure memset( newData, 0, newLength * sizeof( ListEntryRow ) ); // we want to copy over different amounts of data depending on if we're adding // to the list box or removing from the listbox if(newLength >= listboxData->listLength) { memcpy(newData,listboxData->listData,listboxData->listLength * sizeof( ListEntryRow ) ); } else { // If we're removing entries from the listbox, we need to reset the length, // position, and selection to their new places if( listboxData->displayPos >newLength) listboxData->displayPos = newLength; //if we're multiselect, just select no position if(listboxData->selectPos > newLength || listboxData->multiSelect) listboxData->selectPos = -1; if(listboxData->insertPos > newLength) listboxData->insertPos = newLength; listboxData->endPos = newLength; //copy only the data that we'll be needing. memcpy(newData,listboxData->listData,newLength * sizeof( ListEntryRow ) ); } // Loop through and destroy any display strings we've allocated that aren't used for( i = 0; i < listboxData->listLength; i++ ) { //We're now onto a row of cells we are not using anymore Pull off the cells and loop through them ListEntryCell *cells = listboxData->listData[i].cell; for (int j = columns - 1; j >=0; j-- ) { if(!cells) break; if ( i >= newLength ) { if( cells[j].cellType == LISTBOX_TEXT && i >= newLength) { // If we can delete the stuff that won't be showing up in the new listData struture if ( cells[j].data ) { TheDisplayStringManager->freeDisplayString((DisplayString *) cells[j].data ); } } // if ( cells[j].userData ) // free(cells[j].userData); } } if ( i >= newLength ) delete(listboxData->listData[i].cell); listboxData->listData[i].cell = NULL; } listboxData->listLength = newLength; if( listboxData->listData ) delete( listboxData->listData ); listboxData->listData = newData; //reset the total height computeTotalHeight(listbox); // Sanity check that everything was created properly if( listboxData->listData == NULL ) { DEBUG_LOG(( "Unable to allocate listbox data pointer\n" )); assert( 0 ); return; } // end if // adjust the selection array for multi select listboxes if( listboxData->multiSelect ) { GadgetListBoxRemoveMultiSelect( listbox ); GadgetListBoxAddMultiSelect( listbox ); } // end if } // end GadgetListBoxSetListLength // GadgetListBoxGetListLength ================================================= /** Get the list length data contained in the listboxData * parameter. */ //============================================================================= Int GadgetListBoxGetListLength( GameWindow *listbox ) { ListboxData *listboxData = (ListboxData *)listbox->winGetUserData(); if (listboxData->multiSelect) { return listboxData->listLength; } else { return 1; } } // end GadgetListBoxGetListLength // GadgetListBoxGetNumEntries ================================================= /** Get the list length data contained in the listboxData * parameter. */ //============================================================================= Int GadgetListBoxGetNumEntries( GameWindow *listbox ) { if (!listbox) return 0; ListboxData *listboxData = (ListboxData *)listbox->winGetUserData(); if (listboxData) return listboxData->endPos; return 0; } // end GadgetListBoxGetNumEntries //------------------------------------------------------------------------------------------------- /** Get the selected item(s) of a listbox. For a single select listbox the parameter * should be a single integer pointer. For a multi select listbox the parameter * should be an (Int *), this array returned will be an array of indices of the selected items * of the list box. An entry of -1 in this array is the "end" of the selected list, * and this list will never be larger than the max items in the list box */ //------------------------------------------------------------------------------------------------- void GadgetListBoxGetSelected( GameWindow *listbox, Int *selectList ) { // sanity if( listbox == NULL ) return; // get selected indeces via system message TheWindowManager->winSendSystemMsg( listbox, GLM_GET_SELECTION, 0, (WindowMsgData)selectList ); } // end GadgetListBoxGetSelected //------------------------------------------------------------------------------------------------- /** Set the selected item of a listbox. The parameter is a single integer. If * the selected index is less than 0, nothing is selected. */ //------------------------------------------------------------------------------------------------- void GadgetListBoxSetSelected( GameWindow *listbox, Int selectIndex ) { // sanity if( listbox == NULL ) return; // set selected index via system message TheWindowManager->winSendSystemMsg( listbox, GLM_SET_SELECTION, (WindowMsgData)(&selectIndex), 1 ); } // end GadgetListBoxSetSelected //------------------------------------------------------------------------------------------------- /** Set the selected item of a listbox. */ //------------------------------------------------------------------------------------------------- void GadgetListBoxSetSelected( GameWindow *listbox, const Int *selectList, Int selectCount ) { // sanity if( listbox == NULL ) return; // set selected index via system message TheWindowManager->winSendSystemMsg( listbox, GLM_SET_SELECTION, (WindowMsgData)selectList, selectCount ); } //------------------------------------------------------------------------------------------------- /** Reset the content of the listbox */ //------------------------------------------------------------------------------------------------- void GadgetListBoxReset( GameWindow *listbox ) { // sanity if( listbox == NULL ) return; // reset via system message TheWindowManager->winSendSystemMsg( listbox, GLM_DEL_ALL, 0, 0 ); } // end GadgetListBoxReset //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void GadgetListBoxSetItemData( GameWindow *listbox, void *data, Int row, Int column ) { ICoord2D pos; pos.x = column; pos.y = row; if (listbox) TheWindowManager->winSendSystemMsg( listbox, GLM_SET_ITEM_DATA, (WindowMsgData)&pos, (WindowMsgData)data); }// void GadgetListBoxSetItemData( Int index, void *data ) //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void *GadgetListBoxGetItemData( GameWindow *listbox, Int row, Int column) { void *data = NULL; ICoord2D pos; pos.x = column; pos.y = row; if (listbox) { TheWindowManager->winSendSystemMsg( listbox, GLM_GET_ITEM_DATA, (WindowMsgData)&pos, (WindowMsgData)&data); } return (data); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- Int GadgetListBoxGetBottomVisibleEntry( GameWindow *window ) { if (!window) return 0; ListboxData *listData = (ListboxData *)window->winGetUserData(); if (!listData) return 0; return getListboxBottomEntry(listData); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- bool GadgetListBoxIsFull(GameWindow *window) { if (!window) return FALSE; ListboxData *listData = (ListboxData *)window->winGetUserData(); if (!listData) return FALSE; Int entry = getListboxBottomEntry(listData); if(listData->listData[entry].listHeight >= listData->displayPos + listData->displayHeight - 5) return TRUE; else return FALSE; } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void GadgetListBoxSetBottomVisibleEntry( GameWindow *window, Int newPos ) { if (!window) return; ListboxData *listData = (ListboxData *)window->winGetUserData(); if (!listData) return; int prevPos = GadgetListBoxGetBottomVisibleEntry( window ); adjustDisplay(window, newPos - prevPos + 1, true); } // void GadgetListBoxSetTopVisibleEntry( GameWindow *window, Int newPos ) //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- Int GadgetListBoxGetTopVisibleEntry( GameWindow *window ) { if (!window) return 0; ListboxData *listData = (ListboxData *)window->winGetUserData(); if (!listData) return 0; return getListboxTopEntry(listData); } // Int GadgetListBoxGetTopVisibleEntry( GameWindow *window ) //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void GadgetListBoxSetTopVisibleEntry( GameWindow *window, Int newPos ) { if (!window) return; ListboxData *listData = (ListboxData *)window->winGetUserData(); if (!listData) return; int prevPos = GadgetListBoxGetTopVisibleEntry( window ); adjustDisplay(window, newPos - prevPos, true); } // void GadgetListBoxSetTopVisibleEntry( GameWindow *window, Int newPos ) //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void GadgetListBoxSetAudioFeedback( GameWindow *listbox, Bool enable ) { if (!listbox) return; ListboxData *listboxData = (ListboxData *)listbox->winGetUserData(); if (!listboxData) return; listboxData->audioFeedback = enable; } // end GadgetListBoxSetAudioFeedback //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- Int GadgetListBoxGetNumColumns( GameWindow *listbox ) { if (!listbox) return 0; ListboxData *listboxData = (ListboxData *)listbox->winGetUserData(); if (!listboxData) return 0; return listboxData->columns; } // end GadgetListBoxGetNumColumns //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- Int GadgetListBoxGetColumnWidth( GameWindow *listbox, Int column ) { if (!listbox) return 0; ListboxData *listboxData = (ListboxData *)listbox->winGetUserData(); if (!listboxData) return 0; if (listboxData->columns <= column || column < 0) return 0; return listboxData->columnWidth[column]; } // end GadgetListBoxGetNumColumns