123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385 |
- /* GCSx
- ** DIALOG.CPP
- **
- ** Dialog boxes
- */
- /*****************************************************************************
- ** 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"
- // Dialog class
- Dialog::Dialog(const string& dTitle, int dAutoApply, int dHasTitlebar, int toolPanel) : WindowCollection(), Window(), title(dTitle) { start_func
- isToolPanel = toolPanel;
- hasTitlebar = dHasTitlebar;
- lastButtonId = 0;
- open = 0;
- openModal = 0;
- nextTabId = 0;
- noAutoClose = 0;
- autoApply = dAutoApply;
- myFrame = NULL;
- lastWidgetArranged = -1;
- nextWidgetY = (toolPanel ? GUI_PANEL_TOPMARGIN : GUI_DIALOG_TOPMARGIN);
- inSiblingModify = 0;
- }
- Dialog::~Dialog() { start_func
- // (shall already have closed off of desktop)
- // Delete all widgets that wish to be
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- // If this were called during the wrong exception, windowDataDisplay COULD have NULLs in it
- if (*pos) {
- if ((*pos)->wantsToBeDeleted()) delete *pos;
- }
- }
- }
- void Dialog::setTitle(const string& newTitle) { start_func
- title = newTitle;
- if (myFrame) myFrame->setTitle(newTitle);
- }
- void Dialog::clearWidgets() { start_func
- assert(!open);
- // Delete all widgets that wish to be
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- // If this were called during the wrong exception, windowDataDisplay COULD have NULLs in it
- if (*pos) {
- if ((*pos)->wantsToBeDeleted()) delete *pos;
- }
- }
-
- windowDataDisplay.clear();
- windowDataFocus.clear();
- currentFocus = NULL;
- previousFocus = NULL;
- lastFocus = NULL;
- currentMouseFocus = NULL;
- modalPos = NULL;
- nextTabId = 0;
- lastWidgetArranged = -1;
- nextWidgetY = (isToolPanel ? GUI_PANEL_TOPMARGIN : GUI_DIALOG_TOPMARGIN);
-
- resize(1, 1);
- }
- #ifndef NDEBUG
- const char* Dialog::debugDump() const { start_func
- // return title.c_str();
- return "DIALOG";
- }
- #endif
- int Dialog::event(int hasFocus, const SDL_Event* event) { start_func
- assert(event);
- // Check for SDL_CLOSE events- this means we can close safely, frame window is closing
- if (event->type == SDL_CLOSE) {
- open = 0;
- openModal = 0;
- myFrame = NULL;
-
- // Frame window will delete us if we've requested being deleted, so we're done!
- return 1;
- }
-
- // We don't propogate these events (they'd infinite loop)
- // but we need to send them to the currently active element
- if (event->type == SDL_INPUTFOCUS) {
- if (event->user.code & 1) {
- changeInputFocus(lastFocus);
- lastFocus = NULL;
- }
- else {
- if (!lastFocus) {
- lastFocus = currentFocus;
- changeInputFocus(NULL);
- }
- }
- return 1;
- }
- else if (event->type == SDL_MOUSEFOCUS) {
- if (!(event->user.code & 1) && (currentMouseFocus)) {
- int result = windowRunEvent(currentMouseFocus, event);
- currentMouseFocus = NULL;
- return result;
- }
- return 0;
- }
- int result = handleEvent(event);
-
- // Handle special "global" keys like TAB
- if ((!result) && (event->type == SDL_KEYDOWN)) {
- int id = -1;
-
- switch (combineKey(event->key.keysym.sym, event->key.keysym.mod)) {
- case SDLK_TAB:
- // Next widget
- return nextControl();
-
- case combineKey(SDLK_TAB, KMOD_SHIFT):
- // Previous widget
- return prevControl();
-
- case SDLK_DOWN:
- case SDLK_RIGHT:
- // Next widget with same id
- if (currentFocus) id = dynamic_cast<Widget*>(currentFocus)->getId();
- return nextControl(id);
-
- case SDLK_UP:
- case SDLK_LEFT:
- // Previous widget with same id
- if (currentFocus) id = dynamic_cast<Widget*>(currentFocus)->getId();
- return prevControl(id);
-
- case SDLK_KP_ENTER:
- case SDLK_RETURN:
- if (noAutoClose) break;
- // Current button, or OK button if none
- if (currentFocus) {
- if (typeid(*currentFocus) == typeid(WButton)) {
- dynamic_cast<Widget*>(currentFocus)->doAction();
- return 1;
- }
- }
- // (find OK button)
- if (buttonControl(BUTTON_OK)) {
- dynamic_cast<Widget*>(currentFocus)->doAction();
- }
- return 1;
- case SDLK_ESCAPE:
- if (noAutoClose) break;
- // (find CANCEL button?)
- if (buttonControl(BUTTON_CANCEL)) {
- dynamic_cast<Widget*>(currentFocus)->doAction();
- }
- else {
- desktop->setModalReturn(0);
- closeWindow(); // Calls attemptClose
- }
- return 1;
-
- default:
- // Plain or ALT only
- if ((event->key.keysym.mod == KMOD_ALT) ||(event->key.keysym.mod == KMOD_NONE)) {
- // Hotkey?
- if (hotkeyControl(event->key.keysym.sym)) return 1;
- }
- break;
- }
- }
-
- return result;
- }
- void Dialog::currentFocusDoAction() { start_func
- if (currentFocus) {
- dynamic_cast<Widget*>(currentFocus)->doAction();
- }
- }
- void Dialog::resolutionChange(int fromW, int fromH, int fromBpp, int toW, int toH, int toBpp) { start_func
- Window::resolutionChange(fromW, fromH, fromBpp, toW, toH, toBpp);
- WindowCollection::resolutionChange(fromW, fromH, fromBpp, toW, toH, toBpp);
- }
- void Dialog::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;
- dirty = 0;
- updateRect.w = 0;
- }
- // Redraw portion requested?
- else if (updateRect.w) {
- updateRect.x += x + xOffset;
- updateRect.y += y + yOffset;
- boundRects(toDisplay, updateRect);
- updateRect.w = 0;
- }
-
- intersectRects(toDisplay, clipArea);
-
- // Anything to draw?
- if ((toDisplay.w) || (childDirty)) {
- // Fill with background
- if (toDisplay.w) {
- SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_FILL]);
- }
- xOffset += x;
- yOffset += y;
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- assert(*pos);
- // Start with clip area of entire widget
- Rect clip;
- (*pos)->getRect(clip);
- clip.x += xOffset;
- clip.y += yOffset;
- // If this intersects with our current update area OR widget is dirty
- if ((intersectRects(clip, toDisplay)) || ((*pos)->isDirty())) {
- // ...then display widget (we only request area that matches our update)
- (*pos)->display(destSurface, clip, clipArea, xOffset, yOffset);
- // and add in anything it did update to our update area
- boundRects(toDisplay, clip);
- }
- }
-
- childDirty = 0;
- }
- }
- }
- const char* Dialog::tooltip(int xPos, int yPos) const { start_func
- const Window* which = findWindowByCoords(xPos, yPos);
- if (which) {
- return which->tooltip(xPos - which->getX(), yPos - which->getY());
- }
- return NULL;
- }
- int Dialog::attemptClose() { start_func
- // If we know we're already closing...
- if ((!open) && (!openModal)) {
- // We're ok!
- return 1;
- }
- ButtonAction result = verifyEntry(0, BUTTON_CANCEL);
-
- // Action to take- closing is a cancel by default
- if (result == BUTTON_DEFAULT) result = BUTTON_CANCEL;
- // We can't call doAction because that calls closeWindow...
- if ((result == BUTTON_OK) || (result == BUTTON_APPLY)) {
- applySettings();
- }
- if ((result == BUTTON_OK) || (result == BUTTON_CANCEL)) {
- // We want to close
- return 1;
- }
-
- // We don't want to close
- return 0;
- }
- int Dialog::doAction(ButtonAction buttonType) { start_func
- if ((buttonType == BUTTON_OK) || (buttonType == BUTTON_APPLY)) {
- applySettings();
- }
- if ((buttonType == BUTTON_OK) || (buttonType == BUTTON_CANCEL)) {
- desktop->setModalReturn(0); // Defaults to 0 return, if not overridden
- closeWindow(1);
- return 1;
- }
-
- return 0;
- }
- Dialog::ButtonAction Dialog::verifyEntry(int buttonId, ButtonAction buttonType) { start_func
- if ((buttonType == BUTTON_APPLY) || (buttonType == BUTTON_OK)) {
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- assert(*pos);
- Widget* curr = dynamic_cast<Widget*>(*pos);
- if (curr->refuseAll()) continue;
- if (!curr->entryValid()) {
- bringToTop(curr);
- curr->tabAction();
- return BUTTON_NOTHING;
- }
- }
- }
- // (you must derive a new Dialog class to do anything here)
- return BUTTON_DEFAULT;
- }
-
- void Dialog::addWidget(Widget* newWidget) { start_func
- addWindow(newWidget, 0);
- newWidget->stateTabId(++nextTabId);
- }
- void Dialog::arrangeRow(int vertCenter, int leftColumnSize, int staticRight) { start_func
- list<Window*>::iterator end = windowDataDisplay.end();
- list<Window*>::iterator pos;
- int count = 0;
- for (pos = windowDataDisplay.begin(); (pos != end) && (count <= lastWidgetArranged); ++pos, ++count) ;
-
- // pos contains next widget to arrange
- if (pos == end) return;
-
- int tallest = 0;
- if (vertCenter) {
- // Determine tallest now, for centering
- for (; pos != end; ++pos) {
- if ((*pos)->getHeight() > tallest) tallest = (*pos)->getHeight();
- }
- // Start at beginning again
- int count = 0;
- for (pos = windowDataDisplay.begin(); (pos != end) && (count <= lastWidgetArranged); ++pos, ++count) ;
- }
-
- // Arrange in a row
- int widgetX = (isToolPanel ? GUI_PANEL_LEFTMARGIN : GUI_DIALOG_LEFTMARGIN);
- int totalWidth = -GUI_DIALOG_SEPWIDTH; // (because first seperation doesn't count)
- int first = 1;
- for (; pos != end; ++count, ++pos) {
- int wide = leftColumnSize;
- int rightAlign = 0;
- if ((!first) || (wide < 0)) {
- wide = (*pos)->getWidth();
- }
- // Right align?
- if ((staticRight) && (typeid(**pos) == typeid(WStatic))) {
- rightAlign = wide - (*pos)->getWidth();
- }
-
- if (vertCenter) {
- (*pos)->move(widgetX + rightAlign, nextWidgetY + (tallest - (*pos)->getHeight()) / 2);
- }
- else {
- (*pos)->move(widgetX + rightAlign, nextWidgetY);
- if ((*pos)->getHeight() > tallest) tallest = (*pos)->getHeight();
- }
- widgetX += wide + GUI_DIALOG_SEPWIDTH;
- totalWidth += wide + GUI_DIALOG_SEPWIDTH;
- first = 0;
- }
-
- // Next row goes where?
- nextWidgetY += tallest + GUI_DIALOG_SEPHEIGHT;
-
- // Resize dialog width?
- totalWidth += (isToolPanel ? (GUI_PANEL_LEFTMARGIN + GUI_PANEL_RIGHTMARGIN) : (GUI_DIALOG_LEFTMARGIN + GUI_DIALOG_RIGHTMARGIN));
- if (totalWidth < width) totalWidth = width;
-
- // Resize dialog, ensure we're dirty
- resize(totalWidth, nextWidgetY - GUI_DIALOG_SEPHEIGHT + (isToolPanel ? GUI_PANEL_BOTTOMMARGIN : GUI_DIALOG_BOTTOMMARGIN));
- setDirty(1);
-
- // Mark our spot for next time
- lastWidgetArranged = count - 1;
- }
- void Dialog::makePretty(int maxColumns, int vertCenter, int pureFlow, int staticRight) { start_func
- assert(maxColumns >= 1);
- assert(maxColumns <= GUI_DIALOG_NUMCOL);
- // Find width of largest widget in each column; sum up heights
- // Find tallest for use with vertical cemtering
- int widest[GUI_DIALOG_NUMCOL];
- int tallest[GUI_DIALOG_NUMROW];
- int currRow = 0;
- int totalHeight = nextWidgetY;
- // Of bottom row
- int totalButtonWidth = 0;
- int maxButtonHeight = 0;
- Window* firstButton = NULL;
- list<Window*>::iterator firstButtonPos;
- int skipButtons = 0;
- int prevColumn = -1;
- int rowMaxHeight = 0;
- int rowMaxSep = 0;
-
- // (pre-define column widests/tallests)
- for (int col = 0; col < GUI_DIALOG_NUMCOL; ++col) {
- widest[col] = 0;
- }
- for (int row = 0; row < GUI_DIALOG_NUMROW; ++row) {
- tallest[row] = 0;
- }
- list<Window*>::iterator end = windowDataDisplay.end();
- list<Window*>::iterator pos;
- int count = 0;
- for (pos = windowDataDisplay.begin(); (pos != end) && (count <= lastWidgetArranged); ++pos, ++count) ;
-
- // pos contains next widget to arrange
- if (pos == end) return;
- list<Window*>::iterator next;
- for (; pos != end; pos = next) {
- assert(*pos);
- next = pos;
- ++next;
-
- // Calculate a row of buttons
- if (typeid(**pos) == typeid(WButton)) {
- if (!skipButtons) {
- if (!firstButton) {
- firstButtonPos = pos;
- firstButton = *pos;
- }
- else {
- totalButtonWidth += GUI_DIALOG_SEPWIDTH;
- }
-
- if ((*pos)->getHeight() > maxButtonHeight) maxButtonHeight = (*pos)->getHeight();
- totalButtonWidth += (*pos)->getWidth();
- continue;
- }
- }
- // If not a button but we were doing a row of buttons, redo that row
- // as normal controls
- else if (firstButton) {
- firstButton = NULL;
- totalButtonWidth = 0;
- maxButtonHeight = 0;
- pos = firstButtonPos;
- --pos;
- skipButtons = 1;
- continue;
- }
- else {
- skipButtons = 0;
- }
-
- // Which column?
- int column = prevColumn + 1;
- // Pure flow- columns in order, period
- if (pureFlow) {
- if (column >= maxColumns) column = 0;
- }
- // Otherwise...
- else {
- // First time, we start in 2nd column
- if (prevColumn == -1) column = 1;
- // If we're past the column limit, reset to
- // 2nd column again, unless we've requested 1 column only
- if (column >= maxColumns) {
- if (maxColumns == 1) column = 0;
- else column = 1;
- }
- // Static labels always go in 1st column
- if (typeid(**pos) == typeid(WStatic)) {
- column = 0;
- }
- }
-
- // If same or less column, finish most recent row
- if (column <= prevColumn) {
- assert(currRow < GUI_DIALOG_NUMROW);
- tallest[currRow] = rowMaxHeight;
- totalHeight += rowMaxHeight + rowMaxSep;
- rowMaxHeight = 0;
- rowMaxSep = 0;
- ++currRow;
- }
- prevColumn = column;
-
- // Determine highest item in this row
- if ((*pos)->getHeight() > rowMaxHeight) rowMaxHeight = (*pos)->getHeight();
-
- // Determine separator below this row
- int sep;
- if (next == end) sep = 0;
- else if ((typeid(**pos) == typeid(*next)) &&
- ((typeid(*next) == typeid(WRadioButton)) ||
- (typeid(*next) == typeid(WCheckBox)))) sep = GUI_DIALOG_CHECKSEPHEIGHT;
- else sep = GUI_DIALOG_SEPHEIGHT;
- if (sep > rowMaxSep) rowMaxSep = sep;
-
- // Determine widest item in each column
- if ((*pos)->getWidth() > widest[column]) {
- widest[column] = (*pos)->getWidth();
- }
- }
- // Finish last row
- assert(currRow < GUI_DIALOG_NUMROW);
- tallest[currRow] = rowMaxHeight;
- totalHeight += rowMaxHeight;
-
- // Did we end up with a button row?
- if (firstButton) totalHeight += GUI_DIALOG_BUTTONSEPHEIGHT + maxButtonHeight;
-
- // Resize ourselves
- int colSum = widest[GUI_DIALOG_NUMCOL - 1];
- for (int col = 0; col < (GUI_DIALOG_NUMCOL - 1); ++col) {
- if ((widest[col]) && (widest[col + 1])) colSum += GUI_DIALOG_GUTTERWIDTH;
- colSum += widest[col];
- }
- if (totalButtonWidth > colSum) colSum = totalButtonWidth;
- // Resize dialog width?
- int totalWidth = colSum + (isToolPanel ? (GUI_PANEL_LEFTMARGIN + GUI_PANEL_RIGHTMARGIN) : (GUI_DIALOG_LEFTMARGIN + GUI_DIALOG_RIGHTMARGIN));
- if (totalWidth < width) totalWidth = width;
-
- // Resize dialog, ensure we're dirty
- resize(totalWidth, totalHeight + (isToolPanel ? GUI_PANEL_BOTTOMMARGIN : GUI_DIALOG_BOTTOMMARGIN));
- setDirty(1);
- // Restart
- count = 0;
- for (pos = windowDataDisplay.begin(); (pos != end) && (count <= lastWidgetArranged); ++pos, ++count) ;
- // pos contains next widget to arrange again
-
- // Place elements, starting at upper left margin
- int x;
- int y = nextWidgetY;
- int buttonMode = 0;
- int yCenter = 0;
- prevColumn = -1;
- rowMaxHeight = 0;
- rowMaxSep = 0;
- currRow = 0;
- for (; pos != end; pos = next, ++count) {
- assert(*pos);
- next = pos;
- ++next;
-
- if (*pos == firstButton) {
- buttonMode = 1;
- x = (width - totalButtonWidth) / 2;
- // Add separation and height from previous row (but not previous row sep)
- y += tallest[currRow] + GUI_DIALOG_BUTTONSEPHEIGHT;
- ++currRow;
- }
-
- if (buttonMode) {
- (*pos)->move(x, y);
- x += (*pos)->getWidth() + GUI_DIALOG_SEPWIDTH;
- }
- else {
- // Which column?
- int column = prevColumn + 1;
- int alignRight = 0;
- if (pureFlow) {
- if (column >= maxColumns) column = 0;
- if ((staticRight) && (typeid(**pos) == typeid(WStatic))) alignRight = 1;
- }
- else {
- if (prevColumn == -1) column = 1;
- if (column >= maxColumns) {
- if (maxColumns == 1) column = 0;
- else column = 1;
- }
- if (typeid(**pos) == typeid(WStatic)) {
- column = 0;
- alignRight = 1;
- }
- }
- // If same or less column, finish most recent row
- if (column <= prevColumn) {
- y += tallest[currRow] + rowMaxSep;
- ++currRow;
- rowMaxSep = 0;
- }
- prevColumn = column;
-
- // Determine separator below this row
- int sep;
- if (next == end) sep = 0;
- else if ((typeid(**pos) == typeid(*next)) &&
- ((typeid(*next) == typeid(WRadioButton)) ||
- (typeid(*next) == typeid(WCheckBox)))) sep = GUI_DIALOG_CHECKSEPHEIGHT;
- else sep = GUI_DIALOG_SEPHEIGHT;
- if (sep > rowMaxSep) rowMaxSep = sep;
-
- // Determine column x
- x = (isToolPanel ? GUI_PANEL_LEFTMARGIN : GUI_DIALOG_LEFTMARGIN);
- for (int col = 0; col < column; ++col) {
- if ((col < (GUI_DIALOG_NUMCOL - 1)) && (widest[col]) && (widest[col + 1])) x += GUI_DIALOG_GUTTERWIDTH;
- x += widest[col];
- }
-
- // Right align?
- if (alignRight) {
- x += widest[column] - (*pos)->getWidth();
- }
-
- // Vertically center?
- if (vertCenter) {
- yCenter = (tallest[currRow] - (*pos)->getHeight()) / 2;
- }
-
- (*pos)->move(x, y + yCenter);
- }
- }
-
- // Mark our spot for next time
- nextWidgetY = totalHeight + GUI_DIALOG_SEPHEIGHT;
- lastWidgetArranged = count - 1;
- }
-
- int Dialog::runModal(int xPos, int yPos) { start_func
- // Prevent duplication
- if (myFrame) return 0;
- // Keep a backup cause we clear myFrame on SDL_CLOSE
- FrameWindow* myFrameBackup;
- myFrameBackup = new FrameWindow(title, FrameWindow::RESIZING_OFF, FrameWindow::FRAMETYPE_DIALOG, this, hasTitlebar ? FrameWindow::TITLEBAR_NORMAL : FrameWindow::TITLEBAR_OFF);
- myFrame = myFrameBackup;
- // Frame window can NOT delete itself- we want to do that, to ensure it isn't
- // deleted twice on exception
- myFrameBackup->setWantsToBeDeleted(0);
-
- lastButtonId = 0;
- // We have to set this before showing the window, so the desktop knows it's modal
- openModal = 1;
- open = 1;
-
- loadSettings();
-
- // Modal dialogs default to center of SCREEN, not desktop
- if (xPos == -1) {
- xPos = (screenWidth - myFrameBackup->getWidth()) / 2;
- if (xPos < 0) xPos = 0;
- }
- if (yPos == -1) {
- yPos = (screenHeight - myFrameBackup->getHeight()) / 2;
- if (yPos < 0) yPos = 0;
- }
-
- myFrameBackup->show(xPos, yPos, FrameWindow::SHOW_CURRENT, FrameWindow::SHOW_CURRENT);
- firstControl();
- // So we don't deselect it immediately
- lastFocus = currentFocus;
- // Recurse into desktop event loop
- int returnValue = desktop->eventLoop();
- // Delete frame
- delete myFrameBackup;
- myFrame = NULL;
- return returnValue;
- }
- void Dialog::firstControl() { start_func
- changeInputFocus(NULL);
- nextControl();
- }
- void Dialog::runAsPanel() { start_func
- open = 1;
- noAutoClose = 1;
- loadSettings();
- // Select first control
- firstControl();
- // Deselect it immediately, now we know the proper "first" control
- lastFocus = currentFocus;
- changeInputFocus(NULL);
- }
-
- void Dialog::runWindowed(int xPos, int yPos) { start_func
- // Prevent duplication
- if (myFrame) {
- desktop->bringToTop(myFrame);
- return;
- }
- // We remember the frame pointer even though it'll delete itself
- myFrame = new FrameWindow(title, FrameWindow::RESIZING_OFF, FrameWindow::FRAMETYPE_DIALOG, this, hasTitlebar ? FrameWindow::TITLEBAR_NORMAL : FrameWindow::TITLEBAR_OFF);
- lastButtonId = 0;
- open = 1;
-
- loadSettings();
- // Non-modal dialogs go to center of DESKTOP, not entire screen
- if (xPos == -1) {
- xPos = desktop->desktopX() + (desktop->desktopWidth() - myFrame->getWidth()) / 2;
- if (xPos < 0) xPos = 0;
- }
- if (yPos == -1) {
- yPos = desktop->desktopY() + (desktop->desktopHeight() - myFrame->getHeight()) / 2;
- if (yPos < 0) yPos = 0;
- }
- myFrame->show(xPos, yPos, FrameWindow::SHOW_CURRENT, FrameWindow::SHOW_CURRENT);
- firstControl();
- // So we don't deselect it immediately
- lastFocus = currentFocus;
- }
-
- Widget* Dialog::findWidget(int id, int which) { start_func
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- assert(*pos);
- if (dynamic_cast<Widget*>(*pos)->getId() == id) {
- if (which) --which;
- else return dynamic_cast<Widget*>(*pos)->returnSelf();
- }
- }
- return NULL;
- }
- void Dialog::applySettings() { start_func
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- assert(*pos);
- dynamic_cast<Widget*>(*pos)->apply();
- }
- }
- void Dialog::loadSettings() { start_func
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- assert(*pos);
- dynamic_cast<Widget*>(*pos)->load();
- }
- }
- Window::WindowSort Dialog::windowSort() const { start_func
- if (openModal) return WINDOWSORT_MODAL;
- return WINDOWSORT_NORMAL;
- }
- Window::WindowType Dialog::windowType() const { start_func
- return WINDOW_CLIENT;
- }
- int Dialog::wantsToBeDeleted() const { start_func
- return 0;
- }
- Window::CommandSupport Dialog::supportsCommand(int code) const { start_func
- if (currentFocus) {
- return currentFocus->supportsCommand(code);
- }
- return Window::COMMAND_HIDE;
- }
- int Dialog::hotkeyControl(char keypress) { start_func
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- assert(*pos);
- Widget* curr = dynamic_cast<Widget*>(*pos);
- if (curr->getShortcut() == keypress) {
- int refuse = curr->refuseAll();
- if (refuse == WStatic::REFUSE_STATIC) {
- // Select next control, but no action
- nextControl(-1, curr);
- return 1;
- }
- if (refuse) {
- return 0;
- }
- bringToTop(curr);
- curr->doAction();
- return 1;
- }
- }
-
- return 0;
- }
- int Dialog::buttonControl(ButtonAction action) { start_func
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- assert(*pos);
- if (typeid(**pos) == typeid(WButton)) {
- if (dynamic_cast<Widget*>(*pos)->refuseAll()) continue;
- if (dynamic_cast<WButton*>(*pos)->getAction() == action) {
- bringToTop(dynamic_cast<Widget*>(*pos));
- return 1;
- }
- }
- }
-
- return 0;
- }
- int Dialog::nextControl(int id, const Widget* from) { start_func
- // Current widget tab id
- int currId = 0;
- if (from) currId = from->stateTabId();
- else if (currentFocus) currId = dynamic_cast<Widget*>(currentFocus)->stateTabId();
-
- // Search for the closest higher id; also record lowest id
- int closestId = 0x7FFF;
- Widget* closestWidget = NULL;
- int lowestId = 0x7FFF;
- Widget* lowestWidget = NULL;
-
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- assert(*pos);
- Widget* curr = dynamic_cast<Widget*>(*pos);
- int tabId = curr->stateTabId();
- if ((id >= 0) && (curr->getId() != id)) continue;
- if (curr->refuseAll()) continue;
- if ((tabId < closestId) && (tabId > currId)) {
- closestId = tabId;
- closestWidget = curr;
- }
- if (tabId < lowestId) {
- lowestId = tabId;
- lowestWidget = curr;
- }
- }
-
- if (!closestWidget) closestWidget = lowestWidget;
- if ((closestWidget) && (currentFocus != closestWidget)) {
- bringToTop(closestWidget);
- closestWidget->tabAction();
- return 1;
- }
-
- return 0;
- }
- int Dialog::prevControl(int id) { start_func
- // Current widget id
- int currId = 0;
- if (currentFocus) currId = dynamic_cast<Widget*>(currentFocus)->stateTabId();
-
- // Search for the closest lower id; also record highest id
- int closestId = 0;
- Widget* closestWidget = NULL;
- int highestId = 0;
- Widget* highestWidget = NULL;
-
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- assert(*pos);
- Widget* curr = dynamic_cast<Widget*>(*pos);
- int tabId = curr->stateTabId();
- if ((id >= 0) && (curr->getId() != id)) continue;
- if (curr->refuseAll()) continue;
- if ((tabId > closestId) && (tabId < currId)) {
- closestId = tabId;
- closestWidget = curr;
- }
- if (tabId > highestId) {
- highestId = tabId;
- highestWidget = curr;
- }
- }
-
- if (!closestWidget) closestWidget = highestWidget;
- if ((closestWidget) && (currentFocus != closestWidget)) {
- bringToTop(closestWidget);
- closestWidget->tabAction();
- return 1;
- }
-
- return 0;
- }
- void Dialog::childModified(Window* modified) { start_func
- assert(modified);
- Widget* widget = dynamic_cast<Widget*>(modified);
-
- if (widget) {
- if (autoApply) widget->apply();
- // Don't recurse modifies
- if (inSiblingModify) return;
- inSiblingModify = 1;
- // Tell all other widgets
- list<Window*>::iterator end = windowDataDisplay.end();
- for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
- if (*pos != modified) {
- Widget* lWidget = dynamic_cast<Widget*>(*pos);
- if (lWidget) lWidget->siblingModified(widget);
- }
- }
- inSiblingModify = 0;
- }
-
- Window::childModified(modified);
- }
- // Widget class
- Widget::Widget(int wId, const string& wLabel, void* wSetting) : Window(), label() { start_func
- assert(wId >= 0);
-
- setLabel(wLabel);
- disabled = 0;
- haveFocus = 0;
- id = wId;
- setting = wSetting;
- tabId = -1;
- tip = NULL;
- }
- Widget::~Widget() { start_func
- }
- void Widget::setLabel(const string& newLabel) { start_func
- label = newLabel;
- if (label.length() > MAX_LINELENGTH) label = label.substr(0, MAX_LINELENGTH);
- underline = convertGuiText(&label, &shortcut);
- }
- void Widget::setToolTip(const char* wTip) { start_func
- assert(wTip);
-
- tip = wTip;
- }
- const char* Widget::tooltip(int xPos, int yPos) const { start_func
- return tip;
- }
- Widget* Widget::returnSelf() { start_func
- return this;
- }
- void Widget::changeStorage(void* newSetting) { start_func
- setting = newSetting;
- }
- void Widget::addTo(Dialog* dialog) { start_func
- dialog->addWidget(this);
- }
- #ifndef NDEBUG
- const char* Widget::debugDump() const { start_func
- if (label.size()) return label.c_str();
- return "(widget)";
- }
- #endif
- int Widget::stateTabId(int wTabId) { start_func
- assert(wTabId > 0);
- tabId = wTabId;
- return tabId;
- }
- int Widget::stateTabId() const { start_func
- assert(tabId > 0);
- return tabId;
- }
- int Widget::refuseAll() const { start_func
- if (disabled) return 1;
- return 0;
- }
-
- void Widget::siblingModified(Widget* modified) { start_func
- assert(modified);
- assert(modified != this);
- }
- int Widget::entryValid() const { start_func
- return 1;
- }
- void Widget::doAction() { start_func
- }
- void Widget::tabAction() { start_func
- }
- int Widget::wantsToBeDeleted() const { start_func
- return 1;
- }
- Window::WindowType Widget::windowType() const { start_func
- return WINDOW_WIDGET;
- }
- char Widget::getShortcut() const { start_func
- return shortcut;
- }
- int Widget::getId() const { start_func
- return id;
- }
- void Widget::disable() { start_func
- if (!disabled) {
- disabled = 1;
- haveFocus = 0;
- setDirty(1);
- }
- }
- void Widget::enable() { start_func
- if (disabled) {
- disabled = 0;
- setDirty(1);
- }
- }
- Dialog* Widget::myParent() { start_func
- return dynamic_cast<Dialog*>(parent);
- }
- // Static widget
- WStatic::WStatic(int sId, const string& sLabel) : Widget(sId, sLabel, NULL) { start_func
- // Calculate size (add one to width so we can properly draw disabled text)
- resize(fontWidth(label) + 1, fontHeight());
- }
- int WStatic::refuseAll() const { start_func
- if (disabled) return 1;
- return WStatic::REFUSE_STATIC;
- }
-
- int WStatic::event(int hasFocus, const SDL_Event* event) { start_func
- assert(event);
- assert(parent);
-
- switch (event->type) {
- case SDL_INPUTFOCUS:
- // Refuse focus
- if (event->user.code & 1) {
- myParent()->sendToBottom(this);
- }
- return 1;
- }
- return 0;
- }
- void WStatic::changeText(const string& newLabel) { start_func
- setLabel(newLabel);
- // Recalculate size, but don't make smaller
- resize(max(width, fontWidth(label) + 1), height);
- setDirty();
- }
- void WStatic::load() { start_func
- // (static text does not store a value)
- }
- void WStatic::apply() { start_func
- // (static text does not store a value)
- }
- void WStatic::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;
- dirty = 0;
- intersectRects(toDisplay, clipArea);
- }
-
- // Anything to draw?
- if (toDisplay.w) {
- SDL_SetClipRect(destSurface, &toDisplay);
- xOffset += x;
- yOffset += y;
-
- // This widget makes no attempt at partial redraw other than the initial fill.
- SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_FILL]);
-
- if (disabled) {
- drawText(label, guiRGB[COLOR_LIGHT1], xOffset + 1, yOffset + 1, destSurface);
- drawText(label, guiRGB[COLOR_DARK1], xOffset, yOffset, destSurface);
- }
- else {
- drawText(label, guiRGB[COLOR_TEXT], xOffset, yOffset, destSurface);
- if (underline) drawTextUnderline(underline, guiPacked[COLOR_DARK2], xOffset, yOffset, destSurface);
- }
- }
- }
- }
- // Button widget
- WButton::WButton(int bId, const string& bLabel, void* bSetting) : Widget(bId, bLabel, bSetting) { start_func
- action = Dialog::BUTTON_NOTHING;
- pressed = spacePressed = hover = 0;
- icon = NULL;
- isIcon = 0;
- command = NO_COMMAND;
- }
- WButton::WButton(int bId, SDL_Surface* iIconSurface, int iX, int iY, int iW, int iH, void* bSetting) : Widget(bId, blankString, bSetting) { start_func
- constructIcon(iIconSurface, iX, iY, iW, iH);
- action = Dialog::BUTTON_NOTHING;
- pressed = spacePressed = hover = 0;
- command = NO_COMMAND;
- }
- void WButton::constructIcon(SDL_Surface* iIconSurface, int iX, int iY, int iW, int iH) { start_func
- assert(iX >= 0);
- assert(iY >= 0);
- assert(iW > 0);
- assert(iH > 0);
-
- isIcon = 1;
- icon = iIconSurface;
- iconX = iX;
- iconY = iY;
- iconW = iW;
- iconH = iH;
- // Calculate size
- resize(iW + GUI_ICON_MARGIN * 2, iH + GUI_ICON_MARGIN * 2);
- }
- WButton::WButton(int bId, const string& bLabel, Dialog::ButtonAction bAction, int bCommand) : Widget(bId, bLabel, NULL) { start_func
- assert(bAction > Dialog::BUTTON_DEFAULT);
- assert(bAction <= Dialog::BUTTON_LASTACT);
- action = bAction;
- pressed = spacePressed = hover = 0;
- icon = NULL;
- isIcon = 0;
- command = bCommand;
- // Calculate size
- resize(fontWidth(label) + GUI_BUTTON_LEFTMARGIN + GUI_BUTTON_RIGHTMARGIN,
- fontHeight() + GUI_BUTTON_TOPMARGIN + GUI_BUTTON_BOTTOMMARGIN);
- }
- WButton::WButton(int bId, SDL_Surface* iIconSurface, int iX, int iY, int iW, int iH, Dialog::ButtonAction bAction, int bCommand) : Widget(bId, blankString, NULL) { start_func
- assert(bAction > Dialog::BUTTON_DEFAULT);
- assert(bAction <= Dialog::BUTTON_LASTACT);
- constructIcon(iIconSurface, iX, iY, iW, iH);
- action = bAction;
- pressed = spacePressed = hover = 0;
- command = bCommand;
- }
- void WButton::changeIcon(int iX, int iY) { start_func
- assert(iX >= 0);
- assert(iY >= 0);
-
- iconX = iX;
- iconY = iY;
- setDirty();
- }
- void WButton::changeText(const string& newLabel) { start_func
- setLabel(newLabel);
- // Recalculate size, but don't make smaller
- resize(max(width, fontWidth(label) + GUI_BUTTON_LEFTMARGIN + GUI_BUTTON_RIGHTMARGIN),
- fontHeight() + GUI_BUTTON_TOPMARGIN + GUI_BUTTON_BOTTOMMARGIN);
- setDirty();
- }
- Dialog::ButtonAction WButton::getAction() const { start_func
- return action;
- }
- void WButton::doAction() { start_func
- // Command?
- if (command != NO_COMMAND) {
- desktop->broadcastEvent(SDL_COMMAND, command);
- desktop->broadcastEvent(SDL_COMMAND, CMD_RELEASE);
- }
-
- // Handle button press
- Dialog::ButtonAction result = myParent()->verifyEntry(id, action);
- if (result == Dialog::BUTTON_DEFAULT) result = action;
- if (myParent()->doAction(result)) {
- desktop->setModalReturn(id);
- }
- }
- int WButton::event(int hasFocus, const SDL_Event* event) { start_func
- assert(event);
- assert(parent);
- switch (event->type) {
- case SDL_INPUTFOCUS:
- if (event->user.code & 1) {
- if (!haveFocus) {
- haveFocus = 1;
- setDirty();
- }
- }
- else {
- if ((haveFocus) || (spacePressed)) {
- spacePressed = 0;
- haveFocus = 0;
- setDirty();
- }
- }
- return 1;
-
- case SDL_KEYDOWN:
- switch (combineKey(event->key.keysym.sym, event->key.keysym.mod)) {
- case SDLK_SPACE:
- if (!spacePressed) {
- spacePressed = 1;
- setDirty();
- }
- return 1;
- }
- break;
-
- case SDL_KEYUP:
- // (no modifiers on key up)
- switch (event->key.keysym.sym) {
- case SDLK_SPACE:
- if (spacePressed) {
- spacePressed = 0;
- doAction();
- setDirty();
- return 1;
- }
- default:
- break;
- }
- break;
- case SDL_MOUSEBUTTONUP:
- if (event->button.button == SDL_BUTTON_LEFT) {
- pressed = hover = 0;
- if ((event->button.x < width) && (event->button.y < height)) {
- doAction();
- }
- setDirty();
- return 1;
- }
- break;
- case SDL_MOUSEBUTTONDBL:
- case SDL_MOUSEBUTTONDOWN:
- if (event->button.button == SDL_BUTTON_LEFT) {
- if ((event->button.x < width) && (event->button.y < height)) {
- if ((!pressed) || (!hover)) {
- pressed = hover = 1;
- setDirty();
- }
- }
- else {
- if ((pressed) || (hover)) {
- pressed = hover = 0;
- setDirty();
- }
- }
- return 1;
- }
- break;
- case SDL_MOUSEMOTION:
- if ((event->motion.x < width) && (event->motion.y < height)) {
- if (!hover) {
- hover = 1;
- setDirty();
- }
- }
- else {
- if (hover) {
- hover = 0;
- setDirty();
- }
- }
- if (event->motion.state & SDL_BUTTON_LMASK) {
- if (pressed != hover) {
- pressed = hover;
- setDirty();
- }
- }
- else {
- if (pressed) {
- pressed = 0;
- setDirty();
- }
- }
- return 1;
-
- case SDL_MOUSEFOCUS:
- if (event->user.code & 1) {
- if (!hover) {
- hover = 1;
- setDirty();
- }
- }
- else {
- if (hover) {
- hover = 0;
- setDirty();
- }
- }
- return 1;
- }
-
- return 0;
- }
- void WButton::load() { start_func
- // (buttons do not store a value)
- if ((pressed) || (spacePressed)) {
- pressed = 0;
- spacePressed = 0;
- setDirty();
- }
- }
- void WButton::apply() { start_func
- // (buttons do not store a value)
- }
- void WButton::iconDisplay(SDL_Surface* destSurface, int atX, int atY, int pressed) { start_func
- assert(destSurface);
- int offset = 0;
- int disableMargin;
- // This widget makes no attempt at partial redraw
- if (pressed) {
- // Non-gradient: drawRect(atX, atY, width, height, guiPacked[COLOR_FILL], destSurface);
- drawGradient(atX, atY, width, height, guiRGB[COLOR_BUTTONDOWN1], guiRGB[COLOR_BUTTONDOWN2], destSurface);
- drawGuiBoxInvert(atX, atY, width, height, 2, destSurface);
- disableMargin = 2;
- offset = 1;
- }
- else {
- drawGuiBoxInvert(atX, atY, width, height, 1, destSurface);
- drawGuiBox(atX + 1, atY + 1, width - 2, height - 2, 2, destSurface);
- // Non-gradient: nothing
- drawGradient(atX + 3, atY + 3, width - 6, height - 6, guiRGB[COLOR_BUTTONFILL1], guiRGB[COLOR_BUTTONFILL2], destSurface);
- disableMargin = 3;
- }
- if (icon) {
- blit(iconX, iconY, icon, atX + GUI_ICON_MARGIN + offset, atY + GUI_ICON_MARGIN + offset, destSurface, iconW, iconH);
- if (disabled) {
- // 75% opacity to disable
- drawSelectRect(atX + disableMargin, atY + disableMargin, width - disableMargin * 2, height - disableMargin * 2, guiPacked[COLOR_FILL], destSurface, 192);
- }
- }
- if (haveFocus) {
- drawFocusBox(atX + GUI_ICON_FOCUSMARGIN, atY + GUI_ICON_FOCUSMARGIN,
- width - GUI_ICON_FOCUSMARGIN * 2,
- height - GUI_ICON_FOCUSMARGIN * 2, destSurface);
- }
- }
- void WButton::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;
- dirty = 0;
- intersectRects(toDisplay, clipArea);
- }
-
- // Anything to draw?
- if (toDisplay.w) {
- SDL_SetClipRect(destSurface, &toDisplay);
-
- int offset = 0;
- xOffset += x;
- yOffset += y;
-
- // Icon?
- if (isIcon) {
- iconDisplay(destSurface, xOffset, yOffset, pressed || spacePressed);
- }
- else {
- // This widget makes no attempt at partial redraw other than the initial fill.
- if ((pressed) || (spacePressed)) {
- // Non-gradient: SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_FILL]);
- drawGradient(xOffset, yOffset, width, height, guiRGB[COLOR_BUTTONDOWN1], guiRGB[COLOR_BUTTONDOWN2], destSurface);
- drawGuiBoxInvert(xOffset, yOffset, width, height, 2, destSurface);
- offset = 1;
- }
- else {
- drawGuiBoxInvert(xOffset, yOffset, width, height, 1, destSurface);
- drawGuiBox(xOffset + 1, yOffset + 1, width - 2, height - 2, 2, destSurface);
- // Non-gradient: nothing
- drawGradient(xOffset + 3, yOffset + 3, width - 6, height - 6, guiRGB[COLOR_BUTTONFILL1], guiRGB[COLOR_BUTTONFILL2], destSurface);
- }
- if (disabled) {
- drawText(label, guiRGB[COLOR_LIGHT1], xOffset + GUI_BUTTON_LEFTMARGIN + 1 + offset, yOffset + GUI_BUTTON_TOPMARGIN + 1 + offset, destSurface);
- drawText(label, guiRGB[COLOR_DARK1], xOffset + GUI_BUTTON_LEFTMARGIN + offset, yOffset + GUI_BUTTON_TOPMARGIN + offset, destSurface);
- }
- else {
- drawText(label, guiRGB[COLOR_TEXT], xOffset + GUI_BUTTON_LEFTMARGIN + offset, yOffset + GUI_BUTTON_TOPMARGIN + offset, destSurface);
- if (underline) drawTextUnderline(underline, guiPacked[COLOR_DARK2], xOffset + GUI_BUTTON_LEFTMARGIN + offset, yOffset + GUI_BUTTON_TOPMARGIN + offset, destSurface);
- }
- if (haveFocus) {
- drawFocusBox(xOffset + GUI_BUTTON_HFOCUSMARGIN, yOffset + GUI_BUTTON_VFOCUSMARGIN,
- width - GUI_BUTTON_HFOCUSMARGIN * 2,
- height - GUI_BUTTON_VFOCUSMARGIN * 2, destSurface);
- }
- }
- }
- }
- }
- // Checkbox widget
- int WCheckBox::checkboxSize = 0;
- int WCheckBox::checkboxX = 0;
- int WCheckBox::checkboxY = 0;
- int WCheckBox::checkboxXText = 0;
- WCheckBox::WCheckBox(int cId, const string& cLabel, int* cSetting, int cValue) : WButton(cId, cLabel, cSetting), affectControls() { start_func
- assert(cSetting);
- assert(cValue >= 0); // 0 is allowed for radio buttons
- value = cValue;
- currentStatus = 0;
- isRadio = 0;
- checkboxSize = fontHeight() * 4 / 5;
- checkboxX = GUI_CHECKBOX_PRECHECK + GUI_CHECKBOX_LEFTMARGIN;
- checkboxY = (fontHeight() - checkboxSize + 1) / 2 + GUI_CHECKBOX_TOPMARGIN;
- checkboxXText = GUI_CHECKBOX_POSTCHECK + GUI_CHECKBOX_LEFTMARGIN + checkboxSize;
- // Calculate size
- resize(fontWidth(label) + checkboxXText + GUI_CHECKBOX_RIGHTMARGIN,
- fontHeight() + GUI_CHECKBOX_TOPMARGIN + GUI_CHECKBOX_BOTTOMMARGIN);
- }
- WCheckBox::WCheckBox(int cId, SDL_Surface* iIconSurface, int iX, int iY, int iW, int iH, int* cSetting, int cValue) : WButton(cId, iIconSurface, iX, iY, iW, iH, cSetting), affectControls() { start_func
- assert(cSetting);
- assert(cValue >= 0); // 0 is allowed for radio buttons
- value = cValue;
- currentStatus = 0;
- isRadio = 0;
- }
- void WCheckBox::affectControl(int cId, int enable) { start_func
- affectControls[cId] = enable;
- }
- void WCheckBox::changeText(const string& newLabel) { start_func
- setLabel(newLabel);
- // Calculate size, but don't make smaller
- resize(max(width, fontWidth(label) + checkboxXText + GUI_CHECKBOX_RIGHTMARGIN),
- fontHeight() + GUI_CHECKBOX_TOPMARGIN + GUI_CHECKBOX_BOTTOMMARGIN);
- setDirty();
- }
- void WCheckBox::doAction() { start_func
- state(!currentStatus);
- setDirty();
- }
- void WCheckBox::load() { start_func
- state(*(int*)(setting) & value, 1);
- spacePressed = 0;
- pressed = 0;
- hover = 0;
- setDirty();
- }
- void WCheckBox::apply() { start_func
- *(int*)(setting) &= ~value;
- if (state()) *(int*)(setting) |= value;
- }
- int WCheckBox::state(int checked, int fromLoad) { start_func
- if (checked >= 0) {
- if (parent) {
- map<int, int>::iterator pos = affectControls.begin();
- for (; pos != affectControls.end(); ++pos) {
- Widget* affects = dynamic_cast<Dialog*>(parent)->findWidget((*pos).first);
- if (affects) {
- int doEnable = checked;
- if (!((*pos).second)) doEnable = !doEnable;
- if (doEnable) affects->enable();
- else affects->disable();
- }
- }
- }
- currentStatus = checked;
- setDirty();
- if ((!fromLoad) && (parent)) parent->childModified(this);
- }
- return currentStatus;
- }
- void WCheckBox::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;
- dirty = 0;
- intersectRects(toDisplay, clipArea);
- }
-
- // Anything to draw?
- if (toDisplay.w) {
- SDL_SetClipRect(destSurface, &toDisplay);
- int offset = 0;
- xOffset += x;
- yOffset += y;
- // Icon?
- if (isIcon) {
- iconDisplay(destSurface, xOffset, yOffset, pressed || spacePressed || state());
- }
- else {
- // This widget makes no attempt at partial redraw other than the initial fill.
- if ((pressed) || (spacePressed)) {
- // Non-gradient: SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_FILL]);
- drawGradient(xOffset, yOffset, width, height, guiRGB[COLOR_BUTTONDOWN1], guiRGB[COLOR_BUTTONDOWN2], destSurface);
- drawGuiBoxInvert(xOffset, yOffset, width, height, 1, destSurface);
- ++offset;
- }
- else if (hover) {
- drawGuiBox(xOffset, yOffset, width, height, 1, destSurface);
- // Non-gradient: nothing
- drawGradient(xOffset + 1, yOffset + 1, width - 2, height - 2, guiRGB[COLOR_BUTTONFILL1], guiRGB[COLOR_BUTTONFILL2], destSurface);
- }
- else {
- SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_FILL]);
- }
-
- if (disabled) {
- drawCheckbox(isRadio, state(), 1, xOffset + offset + checkboxX, yOffset + offset + checkboxY, checkboxSize, destSurface);
- drawText(label, guiRGB[COLOR_LIGHT1], xOffset + checkboxXText + 1 + offset, yOffset + 1 + offset + GUI_CHECKBOX_TOPMARGIN, destSurface);
- drawText(label, guiRGB[COLOR_DARK1], xOffset + checkboxXText + offset, yOffset + offset + GUI_CHECKBOX_TOPMARGIN, destSurface);
- }
- else {
- drawCheckbox(isRadio, state(), 0, xOffset + offset + checkboxX, yOffset + offset + checkboxY, checkboxSize, destSurface);
- drawText(label, guiRGB[COLOR_TEXT], xOffset + checkboxXText + offset, yOffset + offset + GUI_CHECKBOX_TOPMARGIN, destSurface);
- if (underline) drawTextUnderline(underline, guiPacked[COLOR_DARK2], xOffset + checkboxXText + offset, yOffset + offset + GUI_CHECKBOX_TOPMARGIN, destSurface);
- }
-
- if (haveFocus) {
- drawFocusBox(xOffset, yOffset, width, height, destSurface);
- }
- }
- }
- }
- }
- // Radio widget
- WRadioButton::WRadioButton(int rId, const string& rLabel, int* rSetting, int rValue) : WCheckBox(rId, rLabel, rSetting, rValue) { start_func
- isRadio = 1;
- }
- WRadioButton::WRadioButton(int rId, SDL_Surface* iIconSurface, int iX, int iY, int iW, int iH, int* rSetting, int rValue) : WCheckBox(rId, iIconSurface, iX, iY, iW, iH, rSetting, rValue) { start_func
- isRadio = 1;
- }
- void WRadioButton::doAction() { start_func
- state(1);
- }
- void WRadioButton::load() { start_func
- state(*(int*)(setting) == value, 1);
- spacePressed = 0;
- pressed = 0;
- hover = 0;
- setDirty();
- }
- void WRadioButton::apply() { start_func
- if (state()) *(int*)(setting) = value;
- }
- int WRadioButton::state(int checked, int fromLoad) { start_func
- if ((checked >= 0) && (parent)) {
- map<int, int>::iterator pos = affectControls.begin();
- for (; pos != affectControls.end(); ++pos) {
- Widget* affects = dynamic_cast<Dialog*>(parent)->findWidget((*pos).first);
- if (affects) {
- int doEnable = checked;
- if (!((*pos).second)) doEnable = !doEnable;
- if (doEnable) affects->enable();
- else affects->disable();
- }
- }
- }
- if (checked > 0) {
- if (!currentStatus) setDirty();
- currentStatus = 1;
-
- // Unset all others of same id
- int pos = 0;
- Widget* widget;
- // (assignment intentional)
- while ( (widget = dynamic_cast<Widget*>(myParent()->findWindow(Window::WINDOW_WIDGET, pos++))) ) {
- if ((typeid(*widget) == typeid(*this)) && (widget->getId() == id)) {
- if (widget != this) dynamic_cast<WRadioButton*>(widget)->state(0, fromLoad);
- }
- }
- if ((!fromLoad) && (parent)) parent->childModified(this);
- }
- else if (checked == 0) {
- if (currentStatus) setDirty();
- currentStatus = 0;
- // Doesn't alert to modified if cleared, as another radio button handles that
- }
- return currentStatus;
- }
- // Textbox widget
- const string WTextBox::wordChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
- WTextBox::WTextBox(int tId, string* tSetting, int tMaxSize, int tDisplayLength) : Widget(tId, blankString, tSetting) { start_func
- assert(tMaxSize >= 0);
- maxSize = tMaxSize;
- if ((maxSize == 0) || (maxSize > MAX_LINELENGTH)) maxSize = MAX_LINELENGTH;
- textX = GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN + GUI_TEXTBOX_LEFTPAD;
- textY = GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN;
- insertY = textY + 1;
- insertHeight = fontHeight() - 2;
- if (tDisplayLength == -1) {
- tDisplayLength = min(maxSize, (int)GUI_TEXTBOX_MAXSIZE);
- string standard(tDisplayLength, 'X');
- tDisplayLength = fontWidth(standard);
- }
-
- resize(tDisplayLength + (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN) * 2 + GUI_TEXTBOX_LEFTPAD + GUI_TEXTBOX_RIGHTPAD,
- fontHeight() + (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN) * 2);
- }
- // Needed due to virtual ~Widget() and our string member
- WTextBox::~WTextBox() { start_func
- }
- void WTextBox::setDirtyAndBlink() { start_func
- insertBlink = 1;
- insertBlinkMs = SDL_GetTicks();
- setDirty();
- }
- Window::CommandSupport WTextBox::supportsCommand(int code) const { start_func
- switch (code) {
- case EDIT_COPY:
- case EDIT_CUT:
- if (selectionEnd == selectionBegin) return Window::COMMAND_DISABLE;
- return Window::COMMAND_ENABLE;
-
- case EDIT_PASTE:
- if (canConvertClipboard(CLIPBOARD_TEXT_LINE)) return Window::COMMAND_ENABLE;
- return Window::COMMAND_DISABLE;
- case EDIT_SELECTALL:
- if (currentValue.size()) return Window::COMMAND_ENABLE;
- return Window::COMMAND_DISABLE;
-
- case EDIT_UNDO:
- if (undoType == UNDO_NONE) return Window::COMMAND_DISABLE;
- return Window::COMMAND_ENABLE;
- }
-
- return Window::COMMAND_HIDE;
- }
- int WTextBox::event(int hasFocus, const SDL_Event* event) { start_func
- assert(event);
- assert(parent);
- string::size_type found;
- int drag = 0;
- Sint32 key;
- Sint16 scroll;
- string clipStorage;
- switch (event->type) {
- case SDL_MOUSEBUTTONDOWN:
- if ((event->button.button == SDL_BUTTON_LEFT) &&
- (event->button.y >= (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN)) &&
- (event->button.y < (height - GUI_TEXTBOX_BORDERTHICKNESS - GUI_TEXTBOX_INNERMARGIN)) &&
- (event->button.x >= (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN)) &&
- (event->button.x < (width - GUI_TEXTBOX_BORDERTHICKNESS - GUI_TEXTBOX_INNERMARGIN))) {
- insertionState(insertWhere(event->button.x), SDL_GetModState() & KMOD_SHIFT);
- return 1;
- }
- break;
-
- case SDL_MOUSEBUTTONDBL:
- if ((event->button.y >= (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN)) &&
- (event->button.y < (height - GUI_TEXTBOX_BORDERTHICKNESS - GUI_TEXTBOX_INNERMARGIN)) &&
- (event->button.x >= (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN)) &&
- (event->button.x < (width - GUI_TEXTBOX_BORDERTHICKNESS - GUI_TEXTBOX_INNERMARGIN))) {
- found = insertWhere(event->button.x) - 1;
- if (found < 0) found = 0;
- found = currentValue.find_last_not_of(wordChars, found);
- if (found == string::npos) found = 0;
- else ++found;
- insertionState(found, 0);
- found = currentValue.find_first_not_of(wordChars, insertPoint);
- if (found == string::npos) found = currentValue.size();
- insertionState(found, 1);
- return 1;
- }
- break;
-
- case SDL_MOUSEMOTION:
- if (event->motion.state & SDL_BUTTON_LMASK) {
- scroll = (Sint16)event->motion.x;
- insertionState(insertWhere(scroll), 1);
- }
- else if ((event->motion.y >= (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN)) &&
- (event->motion.y < (height - GUI_TEXTBOX_BORDERTHICKNESS - GUI_TEXTBOX_INNERMARGIN)) &&
- (event->motion.x >= (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN)) &&
- (event->motion.x < (width - GUI_TEXTBOX_BORDERTHICKNESS - GUI_TEXTBOX_INNERMARGIN))) {
- selectMouse(MOUSE_BEAM);
- }
- else {
- selectMouse(MOUSE_NORMAL);
- }
- return 1;
- case SDL_INPUTFOCUS:
- if (event->user.code & 1) {
- if (!haveFocus) {
- haveFocus = 1;
- setDirtyAndBlink();
- }
- }
- else {
- if (haveFocus) {
- haveFocus = 0;
- setDirty();
- }
- }
- return 1;
- case SDL_SPECIAL:
- if ((event->user.code & SDL_IDLE) && (haveFocus)) {
- Uint32 ticks = SDL_GetTicks();
- // (catch wraparound)
- if (ticks < insertBlinkMs) insertBlinkMs = SDL_GetTicks();
- else if (ticks - insertBlinkMs > DELAY_CURSOR_BLINK) {
- insertBlink = !insertBlink;
- insertBlinkMs = SDL_GetTicks();
- setDirty();
- }
- return 1;
- }
- break;
-
- case SDL_COMMAND:
- switch (event->user.code) {
- case EDIT_COPY:
- copyText(clipStorage);
- if (clipStorage.size()) clipboardCopy(clipStorage);
- return 1;
- case EDIT_CUT:
- copyText(clipStorage);
- if (clipStorage.size()) {
- saveUndo(UNDO_DELETE);
- pasteText(blankString);
- clipboardCopy(clipStorage);
- }
- return 1;
- case EDIT_PASTE:
- if (canConvertClipboard(CLIPBOARD_TEXT_LINE)) {
- saveUndo(UNDO_OTHER);
- clipboardPasteTextLine(clipStorage);
- pasteText(clipStorage);
- }
- return 1;
- case EDIT_SELECTALL:
- selectAll();
- return 1;
-
- case EDIT_UNDO:
- case EDIT_REDO:
- undo();
- return 1;
- }
- break;
- case SDL_KEYDOWN:
- if (event->key.keysym.mod & KMOD_SHIFT) {
- drag = 1; // We'll check this if we care about SHIFT
- key = combineKey(event->key.keysym.sym, event->key.keysym.mod & ~KMOD_SHIFT);
- }
- else {
- key = combineKey(event->key.keysym.sym, event->key.keysym.mod);
- }
-
- switch (key) {
- case SDLK_DELETE:
- if (drag) break;
- saveUndo(UNDO_DELETE);
- keyDelete();
- return 1;
- case SDLK_BACKSPACE:
- if (drag) break;
- saveUndo(UNDO_DELETE);
- keyBackspace();
- return 1;
-
- case combineKey(SDLK_DELETE, KMOD_CTRL):
- if (drag) break;
- saveUndo(UNDO_DELETE);
- // If no selection, delete to end of line
- if (selectionBegin == selectionEnd) {
- insertionState(currentValue.size(), 1);
- }
-
- // Delete current selection
- keyDelete();
- return 1;
-
- case SDLK_RIGHT:
- case SDLK_DOWN:
- insertionState(insertPoint + 1, drag);
- return 1;
-
- case SDLK_LEFT:
- case SDLK_UP:
- if (insertPoint > 0) insertionState(insertPoint - 1, drag);
- return 1;
- case SDLK_HOME:
- insertionState(0, drag);
- return 1;
- case SDLK_END:
- insertionState(currentValue.size(), drag);
- return 1;
- case combineKey(SDLK_RIGHT, KMOD_CTRL):
- case combineKey(SDLK_DOWN, KMOD_CTRL):
- found = currentValue.find_first_not_of(wordChars, insertPoint);
- if (found == string::npos) found = currentValue.size();
- found = currentValue.find_first_of(wordChars, found);
- if (found == string::npos) found = currentValue.size();
- insertionState(found, drag);
- return 1;
-
- case combineKey(SDLK_LEFT, KMOD_CTRL):
- case combineKey(SDLK_UP, KMOD_CTRL):
- if (insertPoint <= 1) found = 0;
- else {
- found = currentValue.find_last_of(wordChars, insertPoint - 1);
- if (found == string::npos) found = 0;
- found = currentValue.find_last_not_of(wordChars, found);
- if (found == string::npos) found = 0;
- else if (found > 0) ++found;
- }
- insertionState(found, drag);
- return 1;
-
- default:
- if ((event->key.keysym.unicode) && !(event->key.keysym.mod & KMOD_ALT)) {
- if (event->key.keysym.unicode & 0xFF80) {
- key = '?';
- }
- else {
- key = event->key.keysym.unicode;
- if (key < 32) break;
- }
- saveUndo(UNDO_TYPING);
- string toInsert(1, key);
- pasteText(toInsert);
- return 1;
- }
- break;
- }
- break;
- }
-
- return 0;
- }
- void WTextBox::load() { start_func
- currentValue = *((string*)setting);
- if ((int)currentValue.length() > maxSize) currentValue = currentValue.substr(0, maxSize);
- undoValue = blankString;
- undoType = UNDO_NONE;
-
- insertPoint = 0;
- selectionBegin = 0;
- selectionEnd = 0;
- insertPointX = 0;
- selectionBeginX = 0;
- selectionEndX = 0;
-
- scrollPointX = 0;
- setDirty();
- }
- void WTextBox::apply() { start_func
- *((string*)setting) = currentValue;
- }
- void WTextBox::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;
- dirty = 0;
- intersectRects(toDisplay, clipArea);
- }
-
- // Anything to draw?
- if (toDisplay.w) {
- SDL_SetClipRect(destSurface, &toDisplay);
-
- xOffset += x;
- yOffset += y;
-
- // This widget makes no attempt at partial redraw.
- // (fill corners of inverted box first)
- drawRect(xOffset + width - GUI_TEXTBOX_BORDERTHICKNESS, yOffset, GUI_TEXTBOX_BORDERTHICKNESS, GUI_TEXTBOX_BORDERTHICKNESS, guiPacked[COLOR_FILL], destSurface);
- drawRect(xOffset, yOffset + height - GUI_TEXTBOX_BORDERTHICKNESS, GUI_TEXTBOX_BORDERTHICKNESS, GUI_TEXTBOX_BORDERTHICKNESS, guiPacked[COLOR_FILL], destSurface);
- drawGuiBoxInvert(xOffset, yOffset, width, height, GUI_TEXTBOX_BORDERTHICKNESS, destSurface);
-
- // Clear
- drawRect(xOffset + GUI_TEXTBOX_BORDERTHICKNESS, yOffset + GUI_TEXTBOX_BORDERTHICKNESS, width - 2 * GUI_TEXTBOX_BORDERTHICKNESS, height - 2 * GUI_TEXTBOX_BORDERTHICKNESS, guiPacked[disabled ? COLOR_FILL : COLOR_TEXTBOX], destSurface);
-
- // Text area
- Rect textClip = { xOffset + GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN,
- yOffset + GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN,
- width - 2 * (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN),
- height - 2 * (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN) };
-
- if (intersectRects(textClip, toDisplay)) {
- SDL_SetClipRect(destSurface, &textClip);
-
- // Paint text
- if (disabled) {
- drawText(currentValue, guiRGB[COLOR_LIGHT1], xOffset + textX + scrollPointX + 1, yOffset + textY + 1, destSurface);
- drawText(currentValue, guiRGB[COLOR_DARK1], xOffset + textX + scrollPointX, yOffset + textY, destSurface);
- }
- else {
- drawText(currentValue, guiRGB[COLOR_TEXT], xOffset + textX + scrollPointX, yOffset + textY, destSurface);
- }
-
- if (haveFocus) {
- if (selectionBegin != selectionEnd) {
- drawGradient(xOffset + textX + scrollPointX + selectionBeginX, yOffset + insertY,
- selectionEndX - selectionBeginX, insertHeight,
- guiRGB[COLOR_SELECTION1], guiRGB[COLOR_SELECTION2], destSurface);
- // Redraw highlighted text inverted
- drawText(currentValue.substr(selectionBegin, selectionEnd - selectionBegin), guiRGB[COLOR_TEXTBOX],
- xOffset + textX + scrollPointX + selectionBeginX, yOffset + textY, destSurface);
- }
-
- if (insertBlink) {
- // Insertion point
- drawRect(xOffset + textX + scrollPointX + insertPointX - 1, yOffset + insertY,
- GUI_TEXTBOX_INSERTPOINTWIDTH, insertHeight,
- guiPacked[COLOR_CURSOR], destSurface);
- }
- }
- }
- }
- }
- }
- int WTextBox::allowChange(const string& oldValue, string* newValue, int oldCursorPos, int* newCursorPos) const { start_func
- return 1;
- }
- const string& WTextBox::state() const { start_func
- return currentValue;
- }
- const string& WTextBox::state(const string& newValue) { start_func
- // (sets dirty)
- selectAll();
- pasteText(newValue);
- return currentValue;
- }
- void WTextBox::saveUndo(UndoType type) { start_func
- // Any sequence of delete or typing is treated as one action
- if ((type == undoType) && ((type == UNDO_TYPING) || (type == UNDO_DELETE))) return;
- // Any sequence of typing after deleting is treated as one action
- if ((type == UNDO_TYPING) && (undoType == UNDO_DELETE)) {
- undoType = type;
- return;
- }
- undoPoint = insertPoint;
- undoValue = currentValue;
- undoType = type;
- }
- void WTextBox::undo() { start_func
- if (undoType == UNDO_NONE) return;
- undoType = UNDO_OTHER;
- // Swap undo and buffer
- swap(undoValue, currentValue);
- // Swap undo point and current point
- int newUndoPoint = insertPoint;
- insertionState(undoPoint);
- undoPoint = newUndoPoint;
- parent->childModified(this);
- }
- void WTextBox::selectAll() { start_func
- // (sets dirty)
- insertionState(0, 0);
- insertionState(currentValue.size(), 1);
- }
- void WTextBox::keyBackspace() { start_func
- if (selectionBegin == selectionEnd) {
- if (insertPoint == 0) return;
- insertionState(insertPoint - 1, 1);
- }
- // (sets dirty)
- pasteText(blankString);
- }
- void WTextBox::keyDelete() { start_func
- if (selectionBegin == selectionEnd) {
- if (insertPoint == (int)currentValue.size()) return;
- insertionState(insertPoint + 1, 1);
- }
- // (sets dirty)
- pasteText(blankString);
- }
- void WTextBox::copyText(string& buffer) const { start_func
- if (selectionEnd == selectionBegin) buffer = blankString;
- else buffer = currentValue.substr(selectionBegin, selectionEnd - selectionBegin);
- }
- int WTextBox::insertionState(int newPos, int drag) { start_func
- if (newPos >= 0) {
- // Clip on right
- if (newPos > (int)currentValue.size()) newPos = currentValue.size();
-
- // Drag?
- if (drag) {
- // No current selection?
- if (selectionBegin == selectionEnd) {
- selectionBegin = selectionEnd = insertPoint;
- }
-
- // If selection beginning matches insert point
- if (selectionBegin == insertPoint) {
- // Drag beginning point
- selectionBegin = insertPoint = newPos;
- }
- else {
- // Drag end point
- selectionEnd = insertPoint = newPos;
- }
-
-
- // No selection?
- if (selectionBegin == selectionEnd) {
- selectionBegin = selectionEnd = 0;
- }
- else {
- // Ensure in proper order
- if (selectionEnd < selectionBegin) {
- swap(selectionBegin, selectionEnd);
- }
-
- // Determine point positions
- selectionBeginX = fontWidth(currentValue.substr(0, selectionBegin));
- selectionEndX = fontWidth(currentValue.substr(0, selectionEnd));
- }
- }
- else {
- // Reset selection
- selectionBegin = 0;
- selectionEnd = 0;
- selectionBeginX = 0;
- selectionEndX = 0;
- // Insertion point
- insertPoint = newPos;
- }
- // Insertion point position
- insertPointX = fontWidth(currentValue.substr(0, insertPoint));
-
- // Scroll?
- int rightMargin = GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN + GUI_TEXTBOX_RIGHTPAD;
- int fullWidth = fontWidth(currentValue);
- if ((textX + scrollPointX + insertPointX) > (width - rightMargin - GUI_TEXTBOX_SCROLLAHEAD)) {
- scrollPointX = width - rightMargin - GUI_TEXTBOX_SCROLLAHEAD - insertPointX - textX;
- }
- if ((fullWidth + scrollPointX - width + rightMargin + textX) < 0) {
- scrollPointX = width - rightMargin - textX - fullWidth;
- }
- if ((scrollPointX + insertPointX) < GUI_TEXTBOX_SCROLLAHEAD) {
- scrollPointX = GUI_TEXTBOX_SCROLLAHEAD - insertPointX;
- }
- if (scrollPointX > 0) scrollPointX = 0;
- // Dirty
- setDirtyAndBlink();
- }
-
- return insertPoint;
- }
- void WTextBox::pasteText(const string& text) { start_func
- string newText = currentValue;
- int newPoint = insertPoint;
- // First, clear selection, if any
- if (selectionBegin != selectionEnd) {
- newText.erase(selectionBegin, selectionEnd - selectionBegin);
- newPoint = selectionBegin;
- }
-
- // Insert text
- newText.insert(newPoint, text);
- newPoint += text.size();
-
- // Validate
- if ((int)newText.size() <= maxSize) {
- if (allowChange(currentValue, &newText, insertPoint, &newPoint)) {
- currentValue = newText;
- selectionBegin = selectionEnd = 0;
-
- // Move insertion point (and sets dirty)
- insertionState(newPoint);
- }
- }
- parent->childModified(this);
- }
- int WTextBox::insertWhere(int x) const { start_func
- // Adjust by insertion point width for a bit of a fudge
- x -= textX + scrollPointX - GUI_TEXTBOX_INSERTPOINTWIDTH;
- for (int pos = currentValue.size(); pos > 0; --pos) {
- if (fontWidth(currentValue.substr(0, pos)) < x) return pos;
- }
- return 0;
- }
- void WTextBox::tabAction() { start_func
- selectAll();
- }
- // Numberbox widget
- WNumberBox::WNumberBox(int tId, int* tSetting, int tMin, int tMax) : WTextBox(tId, NULL, 0) { start_func
- assert(tMin <= tMax);
- setting = tSetting;
-
- min = tMin;
- max = tMax;
-
- // Determine maximum size needed
- currentValue = intToStr(min);
- maxSize = currentValue.size();
- currentValue = intToStr(max);
- if ((int)currentValue.size() > maxSize) maxSize = currentValue.size();
- string standard(maxSize, 'X');
- resize(fontWidth(standard) + (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN) * 2 + GUI_TEXTBOX_LEFTPAD + GUI_TEXTBOX_RIGHTPAD,
- fontHeight() + (GUI_TEXTBOX_BORDERTHICKNESS + GUI_TEXTBOX_INNERMARGIN) * 2);
- }
- WNumberBox::~WNumberBox() { start_func
- }
- int WNumberBox::entryValid() const { start_func
- // Make sure numeric
- int valid = 1;
- int current;
- try {
- current = strToIntErr(currentValue);
- }
- catch (int) {
- valid = 0;
- }
-
- if ((current < min) || (current > max)) valid = 0;
-
- if (!valid) {
- string prompt = formatString("Please enter a value between %d and %d", min, max);
- guiErrorBox(prompt, errorTitleInvalidEntry);
- return 0;
- }
-
- return 1;
- }
- int WNumberBox::event(int hasFocus, const SDL_Event* event) { start_func
- assert(event);
- assert(parent);
-
- Sint32 key;
- switch (event->type) {
- case SDL_KEYDOWN:
- key = combineKey(event->key.keysym.sym, event->key.keysym.mod & ~KMOD_SHIFT);
- long current;
- long prevCurrent;
- int did = 0;
- // Make sure numeric
- try {
- current = strToIntErr(currentValue);
- }
- catch (int) {
- current = min;
- }
-
- prevCurrent = current;
-
- switch (key) {
- case SDLK_DOWN:
- did = 1;
- --current;
- break;
- case SDLK_UP:
- did = 1;
- ++current;
- break;
- case combineKey(SDLK_DOWN, KMOD_CTRL):
- case SDLK_PAGEDOWN:
- did = 1;
- current -= 10;
- break;
- case combineKey(SDLK_UP, KMOD_CTRL):
- case SDLK_PAGEUP:
- did = 1;
- current += 10;
- break;
- }
-
- if (did) {
- if (current > max) current = max;
- if (current < min) current = min;
- if (prevCurrent != current) {
- saveUndo(UNDO_TYPING);
- currentValue = intToStr(current);
- parent->childModified(this);
- insertionState(currentValue.size(), 0);
- }
- return 1;
- }
-
- break;
- }
- return WTextBox::event(hasFocus, event);
- }
- void WNumberBox::load() { start_func
- currentValue = intToStr(*((int*)setting));
- undoValue = blankString;
- undoType = UNDO_NONE;
-
- insertPoint = 0;
- selectionBegin = 0;
- selectionEnd = 0;
- insertPointX = 0;
- selectionBeginX = 0;
- selectionEndX = 0;
-
- scrollPointX = 0;
- setDirty();
- }
- void WNumberBox::apply() { start_func
- *((int*)setting) = strToInt(currentValue);
- }
- int WNumberBox::state() const { start_func
- return strToInt(currentValue);
- }
- int WNumberBox::state(int newValue) { start_func
- string newStr = intToStr(newValue);
- // (sets dirty)
- selectAll();
- pasteText(newStr);
- return strToInt(currentValue);
- }
- int WNumberBox::allowChange(const string& oldValue, string* newValue, int oldCursorPos, int* newCursorPos) const { start_func
- // Blank is specifically OK
- if (newValue->empty()) return 1;
- // + or - is OK if that range is enabled
- if ((newValue->size() == 1) && ((*newValue)[0] == '+') && (max > 0)) return 1;
- if ((newValue->size() == 1) && ((*newValue)[0] == '-') && (min < 0)) return 1;
- long current;
-
- // Make sure numeric
- try {
- current = strToIntErr(*newValue);
- }
- catch (int) {
- return 0;
- }
-
- // Any number from 1 to max is ok if max is positive
- if ((max > 0) && (current > 0) && (current <= max)) return 1;
- // Any number from -1 to min is ok if min is negative
- if ((min < 0) && (current < 0) && (current >= min)) return 1;
- // Zero is ok if zero is allowed
- if ((current == 0) && ((min <= 0) || (max >= 0))) return 1;
-
- return 0;
- }
|