123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734 |
- /* GCSx
- ** EDITBOX.CPP
- **
- ** Multi-line textbox window
- */
- /*****************************************************************************
- ** Copyright (C) 2003-2006 Janson
- **
- ** This program is free software; you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation; either version 2 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program; if not, write to the Free Software
- ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- *****************************************************************************/
- #include "all.h"
- const string EditBox::wordChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
- void EditBox::setDirtyAndBlink() { start_func
- insertBlink = 1;
- insertBlinkMs = SDL_GetTicks();
- setDirtyRange(insertion.vRow, insertion.vRow);
- }
- void EditBox::setDirtyRange(int first, int last) { start_func
- setDirty();
- if (vDirtyFirst == -1) {
- vDirtyFirst = first;
- vDirtyLast = last;
- }
- else {
- if (first < vDirtyFirst) vDirtyFirst = first;
- if (last > vDirtyLast) vDirtyLast = last;
- }
- }
- void EditBox::goLeft(TextPoint& point) { start_func
- --point.column;
- if (point.column < 0) {
- --point.vRow;
- if (point.vRow < 0) {
- // End of all text- cancel all movement
- ++point.vRow;
- ++point.column;
- }
- else point.column = (*findVLine(point.vRow)).length;
- }
- }
- void EditBox::goRight(TextPoint& point) { start_func
- ++point.column;
- if (point.column > (*findVLine(point.vRow)).length) {
- ++point.vRow;
- if (point.vRow >= vNumLines) {
- // End of all text- cancel all movement
- --point.vRow;
- --point.column;
- }
- else point.column = 0;
- }
- }
- void EditBox::vLineToDLine(TextPoint& point) { start_func
- list<VLine>::iterator VL = findVLine(point.vRow);
- assert(VL != lineMap.end());
- point.vRow = (*VL).dLine;
- point.column += (*VL).colStart;
- }
- void EditBox::dLineToVLine(TextPoint& point) { start_func
- list<VLine>::iterator VL = findDLine(point.vRow, &point.vRow);
- while (1) {
- assert(VL != lineMap.end());
- if (point.column - (*VL).colStart <= (*VL).length) break;
- ++point.vRow;
- ++VL;
- }
- point.column -= (*VL).colStart;
- if (point.column < 0) point.column = 0;
- point.pixelX = fontWidth((*(*VL).dLineI).substr((*VL).colStart, point.column), font);
- }
- void EditBox::vLineToDLineAllPoints() { start_func
- vLineToDLine(insertion);
- if (isSelect) {
- vLineToDLine(selectBegin);
- vLineToDLine(selectEnd);
- }
- }
- void EditBox::dLineToVLineAllPoints() { start_func
- dLineToVLine(insertion);
- if (isSelect) {
- dLineToVLine(selectBegin);
- dLineToVLine(selectEnd);
- }
- }
- void EditBox::resize(int newWidth, int newHeight, int newViewWidth, int newViewHeight, int fromParent) { start_func
- Window::resize(newWidth, newHeight, newViewWidth, newViewHeight, fromParent);
- // Always rewrap even if wordwrap off, if wrappedTo = -1
- if ((viewWidth != wrappedTo) && ((wordwrap) || (wrappedTo == -1))) {
- if (wrappedTo == -1) {
- insertion.vRow = 0;
- insertion.column = 0;
- insertion.pixelX = 0;
- isSelect = 0;
- fillLineMap();
- wrappedTo = viewWidth;
- }
- else {
- vLineToDLineAllPoints();
-
- fillLineMap();
- wrappedTo = viewWidth;
-
- dLineToVLineAllPoints();
- }
-
- setDirty();
- vDirtyFirst = 0;
- vDirtyLast = vNumLines - 1;
-
- Window::resize(GUI_EDITBOX_INNERMARGIN * 2 + GUI_EDITBOX_LEFTPAD + GUI_EDITBOX_RIGHTPAD + vLongestLength,
- GUI_EDITBOX_INNERMARGIN * 2 + vNumLines * lineHeight);
- }
- }
- int EditBox::addLineWrapped(int dLine, list<string>::iterator& iter, list<VLine>::iterator& insertAt) { start_func
- // Wrap at width of window
- int wrapAt = viewWidth - GUI_EDITBOX_INNERMARGIN * 2 - GUI_EDITBOX_LEFTPAD - GUI_EDITBOX_RIGHTPAD;
- int size = (*iter).size();
- int count = 0;
- int pos = 0;
- int skip;
-
- do {
- skip = 0;
-
- VLine newMap;
- newMap.dLine = dLine;
- newMap.dLineI = iter;
- newMap.colStart = pos;
- // Start with what's left
- newMap.length = size - pos;
-
- // Don't wrap 0 or 1 characters
- if ((wordwrap) && (newMap.length > 1)) {
- // Wrap text
- string str = newMap.getStr();
- // (always max out at MAX_LINELENGTH)
- int wrapMax = max(2, min(breakLineBefore(str, wrapAt), (int)MAX_LINELENGTH));
- // Any cut?
- if (wrapMax < newMap.length) {
- // Move back until character after wrap is space and before wrap is not
- int wrapMin = wrapMax;
- while ((wrapMin) && ((str[wrapMin] != ' ') || (str[wrapMin - 1] == ' '))) {
- --wrapMin;
- }
- // If we still have at least one character, wrap at word boundary found
- if (wrapMin > 0) wrapMax = wrapMin;
- }
- newMap.length = wrapMax;
-
- // Skip over all spaces
- while (str[wrapMax++] == ' ') ++skip;
- }
-
- // Skip over what we've used
- pos += newMap.length + skip;
-
- lineMap.insert(insertAt, newMap);
- int width = fontWidth(newMap.getStr(), font);
- if (width > vLongestLength) vLongestLength = width;
-
- ++count;
- // (loop back if anything left OR if we skipped spaces)
- } while ((pos < size) || (skip));
-
- return count;
- }
- void EditBox::fillLineMap() { start_func
- lineMap.clear();
-
- lastSpot = lineMap.end();
- lastSpotLine = -1;
- if (contents->empty())
- contents->push_back(blankString);
-
- dNumLines = 0;
- vNumLines = 0;
- vLongestLength = 0;
- list<string>::iterator end = contents->end();
- list<VLine>::iterator insertAt = lineMap.end();
- for (list<string>::iterator pos = contents->begin(); pos != end; ++pos) {
- vNumLines += addLineWrapped(dNumLines++, pos, insertAt);
- }
- }
- void EditBox::dumpLineMap() { start_func
- // @TODO: temporary function for debugging
- list<VLine>::iterator start = lineMap.begin();
- list<VLine>::iterator end = lineMap.end();
- int pos = 0;
- for (; start != end; ++start) {
- debugStdout("[%d] d%d c%d l%d | %s", pos++, (*start).dLine, (*start).colStart, (*start).length, (*start).getStr().c_str());
- }
- list<string>::iterator start2 = contents->begin();
- list<string>::iterator end2 = contents->end();
- pos = 0;
- for (; start2 != end2; ++start2) {
- debugStdout("(%d) %s", pos++, (*start2).c_str());
- }
- }
- void EditBox::eraseLineMap(int dLineFirst, int count, int* getVLineAt, int* getVLineCount) { start_func
- int vlNum;
- list<VLine>::iterator start = findDLine(dLineFirst, &vlNum);
- list<VLine>::iterator end = start;
-
- // Count elements being erased
- int vCount = 0;
- list<VLine>::iterator final = lineMap.end();
- for (; end != final; ++end) {
- if ((*end).dLine == dLineFirst + count) break;
- ++vCount;
- // (carry lastspot along for the ride)
- if ((lastSpotLine >= 0) && (lastSpot == end)) {
- ++lastSpot;
- if (lastSpot == final) lastSpotLine = -1;
- else ++lastSpotLine;
- }
- }
-
- // (final adjust to lastspot- safe if spot is -1)
- if (lastSpotLine >= vlNum) {
- assert(lastSpotLine >= vlNum + vCount);
- lastSpotLine -= vCount;
- }
- lineMap.erase(start, end);
- vNumLines -= vCount;
- dNumLines -= count;
-
- // Decrement dline references
- for (; end != final; ++end) {
- (*end).dLine -= count;
- }
-
- if (getVLineAt) *getVLineAt = vlNum;
- if (getVLineCount) *getVLineCount = vCount;
-
- #ifndef NDEBUG
- if (lastSpotLine >= 0) {
- vlNum = lastSpotLine;
- start = lastSpot;
- lastSpot = lineMap.end();
- lastSpotLine = -1;
- end = findVLine(vlNum);
- assert(start == end);
- }
- #endif
- }
- void EditBox::insertLineMap(int dLineAt, int count, int* getVLineAt, int* getVLineCount) { start_func
- // Position in VLines to add at
- int vlNum;
- list<VLine>::iterator insertAt = findDLine(dLineAt, &vlNum);
- if (vlNum == -1) vlNum = vNumLines;
-
- // Position in DLines to add from
- list<string>::iterator insertFrom;
- if (dLineAt == 0)
- insertFrom = contents->begin();
- else {
- list<VLine>::iterator insertAt2 = insertAt;
- --insertAt2;
- insertFrom = (*insertAt2).dLineI;
- ++insertFrom;
- }
-
- int vCount = 0;
- for (int did = 0; did < count; ++did) {
- vCount += addLineWrapped(dLineAt++, insertFrom, insertAt);
- ++insertFrom;
- ++dNumLines;
- }
-
- vNumLines += vCount;
- // (-1 safely ignored here)
- if (lastSpotLine >= vlNum) lastSpotLine += vCount;
-
- // Increment dline references
- list<VLine>::iterator end = lineMap.end();
- for (; insertAt != end; ++insertAt) {
- (*insertAt).dLine += count;
- }
-
- if (getVLineAt) *getVLineAt = vlNum;
- if (getVLineCount) *getVLineCount = vCount;
- }
- EditBox::EditBox(list<string>* storage, int myFont, int isReadOnly) : Window() { start_func
- readonly = isReadOnly;
- font = myFont;
- contents = storage;
- wordwrap = 1;
- autoIndent = 0;
- wrappedTo = -1;
-
- lastSpot = lineMap.end();
- lastSpotLine = -1;
-
- lineHeight = fontHeight(font);
- insertYOffset = 0;
- insertHeight = lineHeight;
- myFrame = NULL;
- myScroll = NULL;
- open = 0;
- haveFocus = 0;
- }
- void EditBox::prepOpen() { start_func
- // Simply "resizes", which forces init if not done yet
- resize(width, height);
- }
- EditBox::~EditBox() { start_func
- }
-
- int EditBox::event(int hasFocus, const SDL_Event* event) { start_func
- TextPoint point;
- list<VLine>::iterator pointVLine;
- string::size_type foundStr;
- string clipStorage;
- switch (event->type) {
- case SDL_CLOSE:
- open = 0;
- myFrame = NULL;
- myScroll = NULL;
- return 1;
-
- case SDL_MOUSEBUTTONDOWN:
- if (event->button.button == SDL_BUTTON_LEFT) {
- point = insertWhere(event->button.x, event->button.y);
- insertionState(point, SDL_GetModState() & KMOD_SHIFT);
- return 1;
- }
- break;
-
- case SDL_MOUSEBUTTONDBL:
- // Where clicked
- point = insertWhere(event->button.x, event->button.y);
- pointVLine = findVLine(point.vRow);
-
- // Find first word character to highlight
- --point.column;
- if (point.column < 0) point.column = 0;
- foundStr = (*pointVLine).getStr().find_last_not_of(wordChars, point.column);
- if (foundStr == string::npos) point.column = 0;
- else point.column = foundStr + 1;
- insertionState(point, 0);
-
- // Find last character to highlight
- foundStr = (*pointVLine).getStr().find_first_not_of(wordChars, point.column);
- if (foundStr == string::npos) point.column = (*pointVLine).length;
- else point.column = foundStr;
- insertionState(point, 1);
- return 1;
-
- case SDL_MOUSEMOTION:
- if (event->motion.state & SDL_BUTTON_LMASK) {
- point = insertWhere((Sint16)event->motion.x, (Sint16)event->motion.y);
- insertionState(point, 1);
- return 1;
- }
- break;
-
- case SDL_MOUSEFOCUS:
- if (event->user.code & 1) {
- selectMouse(MOUSE_BEAM);
- }
- else {
- selectMouse(MOUSE_NORMAL);
- }
- return 1;
- case SDL_INPUTFOCUS:
- if (event->user.code & 1) {
- if (!haveFocus) {
- haveFocus = 1;
- if (isSelect) setDirtyRange(selectBegin.vRow, selectEnd.vRow);
- setDirtyAndBlink();
- }
- }
- else {
- if (haveFocus) {
- haveFocus = 0;
- if (isSelect) setDirtyRange(selectBegin.vRow, selectEnd.vRow);
- else setDirtyRange(insertion.vRow, insertion.vRow);
- }
- }
- 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();
- setDirtyRange(insertion.vRow, insertion.vRow);
- }
- 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()) {
- if (!readonly) pasteText(blankString);
- clipboardCopy(clipStorage);
- }
- return 1;
- case EDIT_PASTE:
- if ((!readonly) && (canConvertClipboard(CLIPBOARD_TEXT_MULTI))) {
- clipboardPasteTextMulti(clipStorage);
- pasteText(clipStorage);
- }
- return 1;
- case EDIT_SELECTALL:
- selectAll();
- return 1;
- }
- break;
- case SDL_KEYDOWN: {
- int drag = 0;
- Sint32 key;
- 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) || (readonly)) break;
- keyDelete();
- return 1;
- case SDLK_BACKSPACE:
- if ((drag) || (readonly)) break;
- keyBackspace();
- return 1;
-
- case combineKey(SDLK_DELETE, KMOD_CTRL):
- if ((drag) || (readonly)) break;
- // If no selection, delete to end of line
- if (!isSelect) {
- point = insertion;
- pointVLine = findVLine(point.vRow);
- point.column = (*pointVLine).length;
- insertionState(point, 1);
- }
-
- // Delete current selection
- keyDelete();
- return 1;
-
- case SDLK_RIGHT:
- point = insertion;
- goRight(point);
- insertionState(point, drag);
- return 1;
- case SDLK_DOWN:
- point = insertion;
- ++point.vRow;
- if (point.vRow < vNumLines) {
- pointVLine = findVLine(point.vRow);
- if (point.column > (*pointVLine).length) point.column = (*pointVLine).length;
- // @TODO: currently goes to same column on next row- NOT
- // same pixel position; also, remember "virtual" column
- // to use for all up/down movement
- insertionState(point, drag);
- }
- return 1;
- case SDLK_LEFT:
- point = insertion;
- goLeft(point);
- insertionState(point, drag);
- return 1;
- case SDLK_UP:
- point = insertion;
- --point.vRow;
- if (point.vRow >= 0) {
- pointVLine = findVLine(point.vRow);
- if (point.column > (*pointVLine).length) point.column = (*pointVLine).length;
- // @TODO: currently goes to same column on previous row- NOT
- // same pixel position; also, remember "virtual" column
- // to use for all up/down movement
- insertionState(point, drag);
- }
- return 1;
- case SDLK_END:
- point = insertion;
- pointVLine = findVLine(point.vRow);
- point.column = (*pointVLine).length;
- insertionState(point, drag);
- return 1;
- case SDLK_HOME:
- point = insertion;
- point.column = 0;
- insertionState(point, drag);
- return 1;
- case combineKey(SDLK_UP, KMOD_CTRL):
- if (myFrame) {
- myFrame->scrollBy(0, lineHeight);
- }
- if (myScroll) {
- myScroll->scrollBy(0, lineHeight);
- }
- return 1;
- case combineKey(SDLK_DOWN, KMOD_CTRL):
- if (myFrame) {
- myFrame->scrollBy(0, -lineHeight);
- }
- if (myScroll) {
- myScroll->scrollBy(0, -lineHeight);
- }
- return 1;
- case SDLK_PAGEUP:
- point = insertion;
- if (point.vRow > 0) {
- point.vRow -= max(1, (viewHeight / lineHeight) - 1);
- pointVLine = findVLine(point.vRow);
- if (point.column > (*pointVLine).length) point.column = (*pointVLine).length;
- // @TODO: currently goes to same column on target row- NOT
- // same pixel position; also, remember "virtual" column
- // to use for all up/down movement
- insertionState(point, drag);
- }
- return 1;
- case SDLK_PAGEDOWN:
- point = insertion;
- if (point.vRow < vNumLines - 1) {
- point.vRow += max(1, (viewHeight / lineHeight) - 1);
- pointVLine = findVLine(point.vRow);
- if (point.column > (*pointVLine).length) point.column = (*pointVLine).length;
- // @TODO: currently goes to same column on target row- NOT
- // same pixel position; also, remember "virtual" column
- // to use for all up/down movement
- insertionState(point, drag);
- }
- return 1;
- case combineKey(SDLK_PAGEUP, KMOD_CTRL):
- if (myFrame) {
- myFrame->scrollBy(0, max(1, (viewHeight / lineHeight) - 1) * lineHeight);
- }
- if (myScroll) {
- myScroll->scrollBy(0, max(1, (viewHeight / lineHeight) - 1) * lineHeight);
- }
- return 1;
- case combineKey(SDLK_PAGEDOWN, KMOD_CTRL):
- if (myFrame) {
- myFrame->scrollBy(0, max(1, (viewHeight / lineHeight) - 1) * -lineHeight);
- }
- if (myScroll) {
- myScroll->scrollBy(0, max(1, (viewHeight / lineHeight) - 1) * -lineHeight);
- }
- return 1;
- case combineKey(SDLK_HOME, KMOD_CTRL):
- point.vRow = 0;
- point.column = 0;
- insertionState(point, drag);
- return 1;
- case combineKey(SDLK_END, KMOD_CTRL):
- point.vRow = vNumLines - 1;
- pointVLine = findVLine(point.vRow);
- point.column = (*pointVLine).length;
- insertionState(point, drag);
- return 1;
- case combineKey(SDLK_RIGHT, KMOD_CTRL):
- // Current position
- point = insertion;
- pointVLine = findVLine(point.vRow);
-
- // Last character? Next line
- if (point.column == (*pointVLine).length) {
- // Last character of last line?
- if (point.vRow == vNumLines - 1) return 1;
- goRight(point);
- pointVLine = findVLine(point.vRow);
- foundStr = 0;
- }
- else {
- // Next not word character
- foundStr = (*pointVLine).getStr().find_first_not_of(wordChars, point.column);
- if (foundStr == string::npos) foundStr = (*pointVLine).length;
- }
-
- // Next word character
- foundStr = (*pointVLine).getStr().find_first_of(wordChars, foundStr);
- if (foundStr == string::npos) foundStr = (*pointVLine).length;
-
- point.column = foundStr;
- insertionState(point, drag);
- return 1;
-
- case combineKey(SDLK_LEFT, KMOD_CTRL):
- // Current position
- point = insertion;
- pointVLine = findVLine(point.vRow);
-
- if (point.column == 0) {
- point.column = 0;
- goLeft(point);
- }
- else {
- // Previous word character then not word character
- foundStr = (*pointVLine).getStr().find_last_of(wordChars, point.column - 1);
- if (foundStr == string::npos) {
- // Last character, previous line
- point.column = 0;
- goLeft(point);
- }
- else {
- foundStr = (*pointVLine).getStr().find_last_not_of(wordChars, foundStr);
- if (foundStr == string::npos) foundStr = 0;
- else if (foundStr > 0) ++foundStr;
- point.column = foundStr;
- }
- }
-
- insertionState(point, drag);
- return 1;
- case SDLK_TAB:
- if (readonly) break;
-
- if (isSelect) {
- // indent/unindent block if a selection
- int dStart = (*findVLine(selectBegin.vRow)).dLine;
- int dEnd = (*findVLine(selectEnd.vRow)).dLine;
- if (drag)
- unindent(dStart, dEnd - dStart + 1);
- else
- indent(dStart, dEnd - dStart + 1);
- }
- else {
- if (drag) {
- // Go left to first multiple of TAB_SIZE as long as only spaces in our way
- string line = (*findVLine(insertion.vRow)).getStr();
- if (line[insertion.column - 1] == ' ') keyBackspace();
- while ((insertion.column % TAB_SIZE) && (line[insertion.column - 1] == ' ')) {
- keyBackspace();
- }
- }
- else {
- // Insert spaces until multiple of TAB_SIZE
- pasteText(" ");
- while (insertion.column % TAB_SIZE) {
- pasteText(" ");
- }
- }
- }
-
- return 1;
-
- case SDLK_RETURN:
- case SDLK_KP_ENTER: {
- if (readonly) break;
- // (get line now, for auto-indent later)
- string line = *(*findVLine(insertion.vRow)).dLineI;
- string toInsert("\n");
- pasteText(toInsert);
-
- // Auto-indent to match previous line
- string::size_type pos = line.find_first_not_of(' ', 0);
- if (pos == string::npos) pos = line.size();
-
- // Adjust indent by counting ([{ vs }])
- if (autoIndent) {
- int adjust = 0;
- for (int c = line.size() - 1; c >= 0; --c) {\
- if ((line[c] == '(') || (line[c] == '[') || (line[c] == '{'))
- ++adjust;
- else if ((line[c] == ')') || (line[c] == ']') || (line[c] == '}'))
- --adjust;
- }
- if (adjust > 0)
- pos += TAB_SIZE;
- }
- if (pos > 0) {
- string spaces(pos, ' ');
- pasteText(spaces);
- }
-
- return 1;
- }
-
- default:
- if (readonly) break;
- 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;
- }
- string toInsert(1, key);
- pasteText(toInsert);
-
- if ((autoIndent) && ((key == '}') || (key == ']'))) {
- // Previous line must exist
- list<VLine>::iterator curV = findVLine(insertion.vRow);
- if ((*curV).dLine > 0) {
- // Indent on current
- list<string>::iterator dl = (*curV).dLineI;
- string line = *dl;
- string::size_type pos = line.find_first_not_of(' ', 0);
-
- // Indent on previous must not be greater
- // @TODO: also, if same indent and previous had the extra {, don't unindent
- --dl;
- line = *dl;
- if (line.find_first_not_of(' ', 0) <= pos) {
- // Unindent curent line
- unindent((*curV).dLine, 1);
- }
- }
- }
-
- return 1;
- }
- break;
- }
- break;
- }
- }
-
- return 0;
- }
- void EditBox::load() { start_func
- if (open) {
- // Force reinit
- wrappedTo = -1;
- prepOpen();
- }
- }
- void EditBox::apply() { start_func
- // (nothing)
- }
- list<EditBox::VLine>::iterator EditBox::findDLine(int dLine, int* getVLineNum) { start_func
- // Out of range?
- if ((dLine < 0) || (dLine >= dNumLines)) {
- if (getVLineNum) *getVLineNum = -1;
- return lineMap.end();
- }
-
- // Special case
- if (dLine == 0) {
- if (getVLineNum) *getVLineNum = 0;
- return lineMap.begin();
- }
- // Starts at beginning, end, or lastSpot, whichever is closest
- // Beginning
- int startLine = 0;
- int vLine = 0;
- list<VLine>::iterator startIter = lineMap.begin();
-
- // Is end closer?
- if (dNumLines - dLine < dLine) {
- vLine = vNumLines;
- startLine = dNumLines;
- startIter = lineMap.end();
- }
-
- // Is lastSpot closer?
- if (lastSpotLine >= 0) {
- if (abs((*lastSpot).dLine - dLine) < abs(startLine - dLine)) {
- vLine = lastSpotLine;
- startLine = (*lastSpot).dLine;
- startIter = lastSpot;
- }
- }
-
- // Iterate! Backwards first, stop at PREVIOUS dline
- // (safe because we special-cased line 0 previously)
- while (startLine >= dLine) {
- --startIter;
- --vLine;
- startLine = (*startIter).dLine;
- }
- // Forwards? (at least once)
- // This way we know we're at the first instance of a given dline
- while (startLine < dLine) {
- ++startIter;
- ++vLine;
- startLine = (*startIter).dLine;
- }
- if (getVLineNum) *getVLineNum = vLine;
-
- return startIter;
- }
- list<EditBox::VLine>::iterator EditBox::findVLine(int line) { start_func
- // Out of range?
- if ((line < 0) || (line >= vNumLines)) return lineMap.end();
- // Starts at beginning, end, or lastSpot, whichever is closest
- // Beginning
- int startLine = 0;
- list<VLine>::iterator startIter = lineMap.begin();
-
- // Is end closer?
- if (vNumLines - line < line) {
- startLine = vNumLines;
- startIter = lineMap.end();
- }
-
- // Is lastSpot closer?
- if (lastSpotLine >= 0) {
- if (abs(lastSpotLine - line) < abs(startLine - line)) {
- startLine = lastSpotLine;
- startIter = lastSpot;
- }
- }
-
- // Iterate! Forwards?
- while (startLine < line) {
- ++startLine;
- ++startIter;
- }
- // Backwards?
- while (startLine > line) {
- --startLine;
- --startIter;
- }
-
- // Retain for next time
- if (startLine != lastSpotLine) {
- lastSpotLine = startLine;
- lastSpot = startIter;
- }
-
- return startIter;
- }
- void EditBox::paintText(const string& text, const string& entireLine, int posInLine, int x, int y, SDL_Surface* destSurface, int isSelected) const { start_func
- drawText(text, guiRGB[isSelected ? COLOR_TEXTBOX : COLOR_TEXT], x, y, destSurface, font);
- }
- void EditBox::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
- assert(destSurface);
- if (visible) {
- // If dirty, redraw range or all
- if (dirty) {
- // Always redraw entire width
- Rect toDraw;
- getRect(toDraw);
- toDraw.x += xOffset;
- toDraw.y += yOffset;
- // Total dirty- as is
- if (!totalDirty) {
- // Not total dirty- do only dirty lines
- toDraw.y += vDirtyFirst * lineHeight + GUI_EDITBOX_INNERMARGIN;
- toDraw.h = (vDirtyLast - vDirtyFirst + 1) * lineHeight;
- }
- dirty = totalDirty = 0;
- vDirtyFirst = vDirtyLast = -1;
-
- // Add our redraw into display rect and clip
- boundRects(toDisplay, toDraw);
- intersectRects(toDisplay, clipArea);
- }
-
- // Anything to draw?
- if (toDisplay.w) {
- SDL_SetClipRect(destSurface, &toDisplay);
-
- xOffset += x;
- yOffset += y;
- // This widget attempts to only redraw lines within the
- // dirtied area.
- // Background
- SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_TEXTBOX]);
-
- // Draw all lines we can see
- int pos = (toDisplay.y - yOffset - GUI_EDITBOX_INNERMARGIN) / lineHeight;
- if (pos < 0) pos = 0;
- if (pos >= vNumLines) pos = vNumLines - 1;
- // (add in height - 1 so it rounds up)
- int last = (toDisplay.y + toDisplay.h - yOffset - GUI_EDITBOX_INNERMARGIN + lineHeight - 1) / lineHeight;
- if (last < 0) last = 0;
- if (last >= vNumLines) last = vNumLines - 1;
-
- // Need an iterator pointing at first line
- list<VLine>::iterator spot = findVLine(pos);
-
- for (; pos <= last; ++pos, ++spot) {
- VLine& vl = *spot;
- int drewText = 0;
-
- if (haveFocus) {
- if ((isSelect) && (pos >= selectBegin.vRow) && (pos <= selectEnd.vRow)) {
- int beginX = pos == selectBegin.vRow ? selectBegin.pixelX : 0;
- int endX = pos == selectEnd.vRow ? selectEnd.pixelX : vLongestLength;
- // (can have an "invisible" selection at end or beginning of a row)
- if (endX > beginX) {
- int beginCol = pos == selectBegin.vRow ? selectBegin.column : 0;
- int endCol = pos == selectEnd.vRow ? selectEnd.column : vl.length;
-
- if ((beginCol > 0) || (endCol < vl.length)) {
- // Paint all text, to show unselected area
- paintText(vl.getStr(), *vl.dLineI, vl.colStart,
- xOffset + GUI_EDITBOX_INNERMARGIN + GUI_EDITBOX_LEFTPAD,
- lineHeight * pos + yOffset + GUI_EDITBOX_INNERMARGIN, destSurface);
- }
-
- drawGradient(xOffset + GUI_EDITBOX_INNERMARGIN + GUI_EDITBOX_LEFTPAD + beginX,
- lineHeight * pos + yOffset + GUI_EDITBOX_INNERMARGIN,
- endX - beginX, lineHeight,
- guiRGB[COLOR_SELECTION1], guiRGB[COLOR_SELECTION2], destSurface);
-
- // Overwrite selected text
- paintText((*vl.dLineI).substr(vl.colStart + beginCol, endCol - beginCol),
- *vl.dLineI, vl.colStart + beginCol,
- xOffset + GUI_EDITBOX_INNERMARGIN + GUI_EDITBOX_LEFTPAD + beginX,
- lineHeight * pos + yOffset + GUI_EDITBOX_INNERMARGIN, destSurface, 1);
-
- drewText = 1;
- }
- }
- }
-
- // Paint text if didn't do during selection
- if (!drewText) {
- paintText(vl.getStr(), *vl.dLineI, vl.colStart,
- xOffset + GUI_EDITBOX_INNERMARGIN + GUI_EDITBOX_LEFTPAD,
- lineHeight * pos + yOffset + GUI_EDITBOX_INNERMARGIN, destSurface);
- }
-
- if ((haveFocus) && (insertBlink) && (insertion.vRow == pos)) {
- // Insertion point
- drawRect(xOffset + GUI_EDITBOX_INNERMARGIN + GUI_EDITBOX_LEFTPAD + insertion.pixelX - 1,
- lineHeight * pos + yOffset + GUI_EDITBOX_INNERMARGIN + insertYOffset,
- GUI_EDITBOX_INSERTPOINTWIDTH, insertHeight,
- guiPacked[COLOR_CURSOR], destSurface);
- }
- }
- }
- }
- }
- Window::CommandSupport EditBox::supportsCommand(int code) const { start_func
- switch (code) {
- case EDIT_COPY:
- case EDIT_CUT:
- if (isSelect) return Window::COMMAND_ENABLE;
- return Window::COMMAND_DISABLE;
-
- case EDIT_PASTE:
- if (canConvertClipboard(CLIPBOARD_TEXT_MULTI)) return Window::COMMAND_ENABLE;
- return Window::COMMAND_DISABLE;
- case EDIT_SELECTALL:
- return Window::COMMAND_ENABLE;
- }
-
- return Window::COMMAND_HIDE;
- }
- void EditBox::addTo(Dialog* dialog, int showWidth, int showLines) { start_func
- assert(!myFrame);
- assert(!myScroll);
- assert(dialog);
- // With this set, when we get a resize() (from our parents, below) we'll
- // initialize everything automatically
- wrappedTo = -1;
- // Set to standard size
- if (showWidth <= 0) {
- string standard(GUI_EDITBOX_DEFAULTWIDTH, 'X');
- showWidth = fontWidth(standard, font);
- }
- if (showLines <= 0) {
- showLines = GUI_EDITBOX_DEFAULTLINES;
- }
- myScroll = new WidgetScroll(WidgetScroll::FRAMETYPE_BEVEL, this, showWidth, showLines * lineHeight);
- dialog->addWidget(myScroll);
-
- // Just in case parents didn't resize us, this triggers initialization
- prepOpen();
- }
- EditBox::TextPoint EditBox::insertWhere(int x, int y) { start_func
- // Determine row
- int row = (y - GUI_EDITBOX_INNERMARGIN) / lineHeight;
- if (row < 0) row = 0;
- if (row >= vNumLines) row = vNumLines - 1;
- return insertWhereRow(x, row);
- }
- EditBox::TextPoint EditBox::insertWhereRow(int x, int row) { start_func
- TextPoint result;
- result.vRow = row;
- // Adjust by insertion point width for a bit of a fudge
- x -= GUI_EDITBOX_INNERMARGIN + GUI_EDITBOX_LEFTPAD - GUI_EDITBOX_INSERTPOINTWIDTH;
- result.column = breakLineBefore((*findVLine(row)).getStr(), x);
- return result;
- }
- int EditBox::breakLineBefore(const string& line, int xPixel) { start_func
- // (special case of 0 pixel would automatically drop to 0 during algorithm,
- // but less-than-0 needs to be covered anyways)
- if (xPixel <= 0) return 0;
- // Start at end of line (special case)
- int widthPixels = fontWidth(line, font);
- int widthChars = line.size();
- if (widthPixels <= xPixel) return widthChars;
-
- // 1 = too long, -1 = too short
- int lastResult = 1;
-
- while (1) {
- // Amount we're off in pixels (negative = move left)
- int changePixels = xPixel - widthPixels;
- if (!changePixels) changePixels = 1;
- // Estimated change in characters
- int changeChars = changePixels * widthChars / widthPixels;
- // Change must be non-zero
- if (!changeChars) changeChars = changePixels > 0 ? 1 : -1;
- widthChars += changeChars;
- // Result (1 too long, -1 too short)
- widthPixels = fontWidth(line.substr(0, widthChars), font);
- int thisResult = widthPixels <= xPixel ? -1 : 1;
- // We're done when the result is "too short", previous result was "too long",
- // and we just moved a single character back
- if ((thisResult < lastResult) && (changeChars == -1)) return widthChars;
- lastResult = thisResult;
- }
- }
-
- void EditBox::keyBackspace() { start_func
- if (!isSelect) {
- TextPoint point = insertion;
-
- // If only spaces to our left and non-space to our right, unindent
- string line = (*findVLine(point.vRow)).getStr();
- string::size_type pos = line.find_first_not_of(' ', 0);
- if ((point.column > 0) && (((int)pos == point.column) || ((pos == string::npos) && (point.column == (int)line.size())))) {
- // Go left to first multiple of TAB_SIZE
- goLeft(point);
- while (point.column % TAB_SIZE) {
- goLeft(point);
- }
- }
- else {
- // Go left one character
- goLeft(point);
- }
- insertionState(point, 1);
- }
- pasteText(blankString);
- }
- void EditBox::keyDelete() { start_func
- if (!isSelect) {
- TextPoint point = insertion;
-
- // If right-most character, delete all spaces at beginning of next line as well
- string line = (*findVLine(point.vRow)).getStr();
- if (point.column == (int)line.size()) {
- // Go right until a non-space or end of next line
- goRight(point);
- string line = (*findVLine(point.vRow)).getStr();
- int max = line.size();
- while ((point.column < max) && (line[point.column] == ' ')) {
- goRight(point);
- }
- }
- else {
- // Go right one character
- goRight(point);
- }
- insertionState(point, 1);
- }
- pasteText(blankString);
- }
- void EditBox::modifyDLine(int dLine) { start_func
- int erased, added, modLine;
-
- eraseLineMap(dLine, 1, &modLine, &erased);
- insertLineMap(dLine, 1, NULL, &added);
- int change = erased - added;
- if ((change) && (insertion.vRow >= dLine)) {
- // Scroll to ensure same view
- if (myFrame) {
- myFrame->scrollBy(0, change * lineHeight);
- }
- if (myScroll) {
- myScroll->scrollBy(0, change * lineHeight);
- }
- }
- // Resize
- resize(GUI_EDITBOX_INNERMARGIN * 2 + GUI_EDITBOX_LEFTPAD + GUI_EDITBOX_RIGHTPAD + vLongestLength,
- GUI_EDITBOX_INNERMARGIN * 2 + vNumLines * lineHeight);
-
- if (added == erased)
- setDirtyRange(modLine, modLine + added - 1);
- else
- setDirtyRange(modLine, vNumLines - 1);
- }
- void EditBox::doIndent(int dLine, int count, int function) { start_func
- // Clip range
- if (dLine < 0) {
- count += dLine;
- dLine = 0;
- }
- if (dLine + count > dNumLines)
- count = dNumLines - dLine;
- if (count <= 0)
- return;
-
- contentModifiedPre(EDITBOX_MODIFY_LINE, dLine, count);
- contentModifiedPre(EDITBOX_MODIFY_DONE, 0, 0);
- vLineToDLineAllPoints();
-
- list<VLine>::iterator vl = findDLine(dLine);
- assert(vl != lineMap.end());
- list<string>::iterator dl = (*vl).dLineI;
- int dlNum = dLine;
- debugStdout("start");
- do {
- assert(dl != contents->end());
- string& mod = *dl;
-
- int trim;
- if (function) {
- // Indent
- mod = string(TAB_SIZE, ' ') + mod;
- trim = TAB_SIZE;
- }
- else {
- // Unindent
- trim = mod.find_first_not_of(' ', 0);
- if (trim == (int)string::npos)
- trim = mod.size();
- trim = min((int)TAB_SIZE, trim);
- mod = mod.substr(trim, string::npos);
- trim = -trim;
- }
-
- modifyDLine(dlNum);
-
- // (affects dline position before converting back to vlines)
- if (insertion.vRow == dlNum) insertion.column = max(0, insertion.column + trim);
- if (selectBegin.vRow == dlNum) selectBegin.column = max(0, selectBegin.column + trim);
- if (selectEnd.vRow == dlNum) selectEnd.column = max(0, selectEnd.column + trim);
-
- ++dl;
- ++dlNum;
- } while (--count);
-
- dLineToVLineAllPoints();
- contentModifiedPost(EDITBOX_MODIFY_LINE, dLine, count);
- contentModifiedPost(EDITBOX_MODIFY_DONE, 0, 0);
- }
- void EditBox::indent(int dLine, int count) { start_func
- doIndent(dLine, count, 1);
- }
- void EditBox::unindent(int dLine, int count) { start_func
- doIndent(dLine, count, 0);
- }
- void EditBox::getCursorData(TextPoint& ins, TextPoint& selBegin, TextPoint& selEnd) const { start_func
- ins = insertion;
- if (isSelect) {
- selBegin = selectBegin;
- selEnd = selectEnd;
- }
- else {
- selBegin = selEnd = ins;
- }
- }
- void EditBox::setCursorData(TextPoint& ins, TextPoint& selBegin, TextPoint& selEnd) { start_func
- if ((selBegin.vRow != selEnd.vRow) || (selBegin.column != selEnd.column)) {
- if ((selBegin.vRow == ins.vRow) && (selBegin.column == ins.column)) {
- insertionState(selEnd, 0);
- insertionState(selBegin, 1);
- }
- else if ((selEnd.vRow == ins.vRow) && (selEnd.column == ins.column)) {
- insertionState(selBegin, 0);
- insertionState(selEnd, 1);
- }
- else {
- insertionState(ins, 0);
- }
- }
- else {
- insertionState(ins, 0);
- }
- }
-
- const EditBox::TextPoint& EditBox::insertionState() const { start_func
- return insertion;
- }
- const EditBox::TextPoint& EditBox::insertionState(TextPoint& newPos, int drag) { start_func
- // Clip
- if (newPos.vRow < 0) newPos.vRow = 0;
- if (newPos.vRow >= vNumLines) newPos.vRow = vNumLines - 1;
- VLine& vl = *findVLine(newPos.vRow);
- if (newPos.column < 0) newPos.column = 0;
- if (newPos.column > vl.length) newPos.column = vl.length;
- // First, find correct pixelX
- newPos.pixelX = fontWidth((*vl.dLineI).substr(vl.colStart, newPos.column), font);
-
- // Drag?
- if (drag) {
- int vDirtyFirst;
- int vDirtyLast;
-
- // No current selection?
- if (!isSelect) {
- selectBegin = selectEnd = insertion;
- }
-
- // If selection beginning matches insert point
- if ((selectBegin.vRow == insertion.vRow) && (selectBegin.column == insertion.column)) {
- // Drag beginning point
- vDirtyFirst = selectBegin.vRow;
- vDirtyLast = newPos.vRow;
- selectBegin = insertion = newPos;
- }
- else {
- // Drag end point
- vDirtyFirst = selectEnd.vRow;
- vDirtyLast = newPos.vRow;
- selectEnd = insertion = newPos;
- }
-
- // No selection?
- if ((selectBegin.vRow == selectEnd.vRow) && (selectBegin.column == selectEnd.column)) {
- isSelect = 0;
- }
- else {
- isSelect = 1;
-
- // Ensure in proper order
- if ((selectEnd.vRow < selectBegin.vRow) ||
- ((selectEnd.vRow == selectBegin.vRow) &&
- (selectEnd.column < selectBegin.column))) {
- swap(selectBegin, selectEnd);
- }
- }
-
- // Dirty
- if (vDirtyFirst > vDirtyLast) swap(vDirtyFirst, vDirtyLast);
- setDirtyRange(vDirtyFirst, vDirtyLast);
- }
- else {
- // Dirty previous
- if (isSelect) setDirtyRange(selectBegin.vRow, selectEnd.vRow);
- else setDirtyRange(insertion.vRow, insertion.vRow);
- // Reset selection
- isSelect = 0;
- // Insertion point
- insertion = newPos;
- }
-
- // Scroll
- int scrollLeft = insertion.pixelX + GUI_EDITBOX_INNERMARGIN + GUI_EDITBOX_LEFTPAD - 1 - GUI_EDITBOX_SCROLLAHEAD;
- if (scrollLeft < 0) scrollLeft = 0;
- if (myFrame) {
- myFrame->scrollToView(scrollLeft,
- insertion.vRow * lineHeight + GUI_EDITBOX_INNERMARGIN,
- GUI_EDITBOX_INSERTPOINTWIDTH + GUI_EDITBOX_SCROLLAHEAD * 2, lineHeight);
- }
- if (myScroll) {
- myScroll->scrollToView(scrollLeft,
- insertion.vRow * lineHeight + GUI_EDITBOX_INNERMARGIN,
- GUI_EDITBOX_INSERTPOINTWIDTH + GUI_EDITBOX_SCROLLAHEAD * 2, lineHeight);
- }
- // (dirties current row)
- setDirtyAndBlink();
- return insertion;
- }
- void EditBox::selectAll() { start_func
- // First point
- TextPoint point;
- point.vRow = 0;
- point.column = 0;
- insertionState(point, 0);
-
- // Last point
- point.vRow = vNumLines - 1;
- point.column = (*findVLine(point.vRow)).length;
- insertionState(point, 1);
- }
- void EditBox::contentModifyHelper(int vLine, int vCount) { start_func
- // vcount can be positive (inserted lines) or negative (removed lines)
- // in addition, any line can have been modified 'in place'
- TextPoint newInsertion = insertion;
- TextPoint newBegin = selectBegin;
- TextPoint newEnd = selectEnd;
-
- // Insertion
- if (vCount > 0) {
- if (newInsertion.vRow >= vLine) {
- newInsertion.vRow += vCount;
- // Scroll to ensure same view
- if (myFrame) {
- myFrame->scrollBy(0, -vCount * lineHeight);
- }
- if (myScroll) {
- myScroll->scrollBy(0, -vCount * lineHeight);
- }
- }
- if (isSelect) {
- if (newBegin.vRow >= vLine) {
- newBegin.vRow += vCount;
- }
- if (newEnd.vRow >= vLine) {
- newEnd.vRow += vCount;
- }
- }
- }
-
- // Deletion
- else if (vCount < 0) {
- if (newInsertion.vRow >= vLine) {
- if (newInsertion.vRow >= vLine - vCount) {
- // past affected area
- newInsertion.vRow += vCount;
- // Scroll to ensure same view
- if (myFrame) {
- myFrame->scrollBy(0, -vCount * lineHeight);
- }
- if (myScroll) {
- myScroll->scrollBy(0, -vCount * lineHeight);
- }
- }
- else {
- // within affected area
- newInsertion.vRow = vLine;
- newInsertion.column = 0;
- if (newInsertion.vRow >= vNumLines) {
- newInsertion.vRow = vNumLines - 1;
- newInsertion.column = 99999;
- }
- }
- }
- if (isSelect) {
- if (newBegin.vRow >= vLine) {
- if (newBegin.vRow >= vLine - vCount) {
- // past affected area
- newBegin.vRow += vCount;
- }
- else {
- // within affected area
- newBegin.vRow = vLine;
- newBegin.column = 0;
- if (newBegin.vRow >= vNumLines) {
- newBegin.vRow = vNumLines - 1;
- newBegin.column = MAX_LINELENGTH + 1;
- }
- }
- }
- if (newEnd.vRow >= vLine) {
- if (newEnd.vRow >= vLine - vCount) {
- // past affected area
- newEnd.vRow += vCount;
- }
- else {
- // within affected area
- newEnd.vRow = vLine;
- newEnd.column = 0;
- if (newEnd.vRow >= vNumLines) {
- newEnd.vRow = vNumLines - 1;
- newEnd.column = MAX_LINELENGTH + 1;
- }
- }
- }
- }
- }
-
- // Verify columns
- int len = (*findVLine(newInsertion.vRow)).length;
- if (len < newInsertion.column) newInsertion.column = len;
- if (isSelect) {
- len = (*findVLine(newBegin.vRow)).length;
- if (len < newBegin.column) newBegin.column = len;
- len = (*findVLine(newEnd.vRow)).length;
- if (len < newEnd.column) newEnd.column = len;
- }
-
- // Resize
- resize(GUI_EDITBOX_INNERMARGIN * 2 + GUI_EDITBOX_LEFTPAD + GUI_EDITBOX_RIGHTPAD + vLongestLength,
- GUI_EDITBOX_INNERMARGIN * 2 + vNumLines * lineHeight);
-
- // Set new positions
- if (isSelect) {
- if ((newBegin.column == newInsertion.column) && (newBegin.vRow == newInsertion.vRow))
- insertionState(newEnd, 0);
- else
- insertionState(newBegin, 0);
- insertionState(newInsertion, 1);
- }
- else
- insertionState(newInsertion, 0);
- }
- void EditBox::contentModify(int dLine, int countLines) { start_func
- int modLine, erased, added;
- eraseLineMap(dLine, countLines, &modLine, &erased);
- insertLineMap(dLine, countLines, &modLine, &added);
- contentModifyHelper(modLine + min(added, erased), added - erased);
- if (added == erased)
- setDirtyRange(modLine, modLine + added - 1);
- else
- setDirtyRange(modLine, vNumLines - 1);
- }
- void EditBox::contentInsert(int dLine, int countLines) { start_func
- int modLine, added;
- insertLineMap(dLine, countLines, &modLine, &added);
- contentModifyHelper(modLine, added);
- setDirtyRange(modLine, vNumLines - 1);
- }
- void EditBox::contentDelete(int dLine, int countLines) { start_func
- int modLine, erased;
- eraseLineMap(dLine, countLines, &modLine, &erased);
- contentModifyHelper(modLine, -erased);
- setDirtyRange(modLine, vNumLines - 1);
- }
- void EditBox::pasteText(const string& text) { start_func
- // Most of our work is done on dlines, not vlines
- VLine* insertionVL = &*findVLine(insertion.vRow);
- VLine* beginVL = &*findVLine(isSelect ? selectBegin.vRow : insertion.vRow);
- VLine* endVL = &*findVLine(isSelect ? selectEnd.vRow : insertion.vRow);
- // Count lines being inserted
- string::size_type spos = 0;
- int insertedDLines = 0;
- while ((spos = text.find_first_of('\n', spos)) != string::npos) {
- ++insertedDLines;
- ++spos;
- }
- // Start by alerting to incoming modifications
- int r1 = -1, r2 = 0, i1 = -1, m1;
- if (beginVL->dLine != endVL->dLine) contentModifiedPre(EDITBOX_REMOVE_LINES, r1 = beginVL->dLine + 1, r2 = endVL->dLine - beginVL->dLine);
- if (insertedDLines) contentModifiedPre(EDITBOX_INSERT_LINES, i1 = beginVL->dLine + 1, insertedDLines);
- contentModifiedPre(EDITBOX_MODIFY_LINE, m1 = beginVL->dLine, 1);
- contentModifiedPre(EDITBOX_MODIFY_DONE, 0, 0);
- // First, clear selection, if any
- if (isSelect) {
- list<string>::iterator pos = beginVL->dLineI;
- list<string>::iterator end = endVL->dLineI;
- // One row?
- if (pos == end) {
- (*pos).erase(beginVL->colStart + selectBegin.column, endVL->colStart + selectEnd.column - beginVL->colStart - selectBegin.column);
- }
- else {
- list<string>::iterator first = pos;
- // To end of first line
- (*pos).erase(beginVL->colStart + selectBegin.column, (*pos).size());
- // Lines in between
- ++pos;
- list<string>::iterator next = pos;
- for (; pos != end; pos = next) {
- ++next;
- contents->erase(pos);
- }
- // Last line- start to middle
- (*pos).erase(0, endVL->colStart + selectEnd.column);
- // Combine first and last lines
- // Combine
- *first += *pos;
- contents->erase(pos);
- }
- // selectBegin is still entirely valid
- insertion = selectBegin;
- insertionVL = beginVL;
- isSelect = 0;
- }
- // Insert
- spos = 0;
- string::size_type prev = spos;
- int insertAt = insertionVL->colStart + insertion.column;
- list<string>::iterator insertStr = insertionVL->dLineI;
- // Split at every \n
- int inserted = 0;
- while ((spos = text.find_first_of('\n', spos)) != string::npos) {
- // Insert new line containing text after insertion point
- list<string>::iterator nextStr = insertStr;
- ++nextStr;
- contents->insert(nextStr, (*insertStr).substr(insertAt, string::npos));
- // Delete text we moved forward
- (*insertStr).erase(insertAt, string::npos);
-
- // Insert text
- (*insertStr).insert(insertAt, text.substr(prev, spos - prev));
- // Move insertion point to beginning of next (newly added) line
- insertAt = 0;
- ++insertStr;
- ++spos;
- prev = spos;
- ++inserted;
- }
- // One final portion to insert
- int lenToInsert = text.substr(prev, string::npos).size();
- (*insertStr).insert(insertAt, text.substr(prev, lenToInsert));
- // (will disappear after calling erase)
- int iVLdL = insertionVL->dLine;
- // Handles modifying both numlines and linemap
- int eCount;
- eraseLineMap(beginVL->dLine, endVL->dLine - beginVL->dLine + 1, NULL, &eCount);
- // Handles longest length, modifying both numlines and linemap
- int iStart, iCount;
- insertLineMap(iVLdL, inserted + 1, &iStart, &iCount);
- // Resize
- resize(GUI_EDITBOX_INNERMARGIN * 2 + GUI_EDITBOX_LEFTPAD + GUI_EDITBOX_RIGHTPAD + vLongestLength,
- GUI_EDITBOX_INNERMARGIN * 2 + vNumLines * lineHeight);
-
- // Move insertion point
- TextPoint newinsert = insertion;
- newinsert.column = insertAt + lenToInsert;
- newinsert.vRow = iStart + iCount - 1;
- insertionVL = &*findVLine(newinsert.vRow);
- while (newinsert.column < insertionVL->colStart) {
- insertionVL = &*findVLine(--newinsert.vRow);
- }
- newinsert.column -= insertionVL->colStart;
- insertionState(newinsert);
- // Alert to finished modifications
- if (r1 >= 0) contentModifiedPost(EDITBOX_REMOVE_LINES, r1, r2);
- if (i1 >= 0) contentModifiedPost(EDITBOX_INSERT_LINES, i1, insertedDLines);
- contentModifiedPost(EDITBOX_MODIFY_LINE, m1, 1);
- contentModifiedPost(EDITBOX_MODIFY_DONE, 0, 0);
- // dirty
- if (eCount == iCount)
- setDirtyRange(iStart, iStart + eCount - 1);
- else
- setDirtyRange(iStart, vNumLines - 1);
- }
- void EditBox::copyText(string& buffer) { start_func
- if (isSelect) {
- VLine* beginVL = &*findVLine(selectBegin.vRow);
- VLine* endVL = &*findVLine(selectEnd.vRow);
- list<string>::iterator pos = beginVL->dLineI;
- list<string>::iterator end = endVL->dLineI;
-
- // One row?
- if (pos == end) {
- buffer = (*pos).substr(beginVL->colStart + selectBegin.column, endVL->colStart + selectEnd.column - beginVL->colStart - selectBegin.column);
- }
- else {
- // To end of first line
- buffer = (*pos).substr(beginVL->colStart + selectBegin.column, string::npos);
-
- // Lines in between
- for (++pos; pos != end; ++pos) {
- buffer += "\n";
- buffer += *pos;
- }
-
- // Last line- start to middle
- buffer += "\n";
- buffer += (*pos).substr(0, endVL->colStart + selectEnd.column);
- }
- }
- else {
- buffer = blankString;
- }
- }
- Window::WindowType EditBox::windowType() const { start_func
- return WINDOW_CLIENT;
- }
- void EditBox::contentModifiedPre(ContentChangeType type, int firstRow, int numRows) { start_func
- // (default does nothing)
- }
- void EditBox::contentModifiedPost(ContentChangeType type, int firstRow, int numRows) { start_func
- // (default does nothing)
- }
- void EditBox::updateTitlebar() { start_func
- if (myFrame) {
- myFrame->setTitle("(no title)");
- }
- }
- FrameWindow* EditBox::runWindowed() { start_func
- assert(!myScroll);
- // Prevent duplication
- if (myFrame) {
- desktop->bringToTop(myFrame);
- return myFrame;
- }
- // With this set, when we get a resize() (from our parents, below) we'll
- // initialize everything automatically
- wrappedTo = -1;
- // We remember the frame pointer even though it'll delete itself
- myFrame = new FrameWindow(blankString, FrameWindow::RESIZING_NORMAL, FrameWindow::FRAMETYPE_BEVEL_TEXT, this);
- open = 1;
-
- // Cascade
- myFrame->show(FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CASCADE);
- // Just in case parents didn't resize us, this triggers initialization
- prepOpen();
- // Scroll to cursor
- insertionState(insertion, isSelect);
- updateTitlebar();
- return myFrame;
- }
|