diff options
Diffstat (limited to 'dw/findtext.cc')
-rw-r--r-- | dw/findtext.cc | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/dw/findtext.cc b/dw/findtext.cc new file mode 100644 index 0000000..57c83c5 --- /dev/null +++ b/dw/findtext.cc @@ -0,0 +1,307 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + + +#include "core.hh" +#include "../lout/debug.hh" +#include "../lout/msg.h" + +namespace dw { +namespace core { + +FindtextState::FindtextState () +{ + DBG_OBJ_CREATE ("dw::core::FindtextState"); + + key = NULL; + nexttab = NULL; + widget = NULL; + iterator = NULL; + hlIterator = NULL; +} + +FindtextState::~FindtextState () +{ + if (key) + free(key); + if (nexttab) + delete[] nexttab; + if (iterator) + delete iterator; + if (hlIterator) + delete hlIterator; + + DBG_OBJ_DELETE (); +} + +void FindtextState::setWidget (Widget *widget) +{ + this->widget = widget; + + // A widget change will restart the search. + if (key) + free(key); + key = NULL; + if (nexttab) + delete[] nexttab; + nexttab = NULL; + + if (iterator) + delete iterator; + iterator = NULL; + if (hlIterator) + delete hlIterator; + hlIterator = NULL; +} + +FindtextState::Result FindtextState::search (const char *key, bool caseSens, + bool backwards) +{ + if (!widget || *key == 0) // empty keys are not found + return NOT_FOUND; + + bool wasHighlighted = unhighlight (); + bool newKey; + + // If the key (or the widget) changes (including case sensitivity), + // the search is started from the beginning. + if (this->key == NULL || this->caseSens != caseSens || + strcmp (this->key, key) != 0) { + newKey = true; + if (this->key) + free(this->key); + this->key = strdup (key); + this->caseSens = caseSens; + + if (nexttab) + delete[] nexttab; + nexttab = createNexttab (key, caseSens, backwards); + + if (iterator) + delete iterator; + iterator = new CharIterator (widget, true); + + if (backwards) { + /* Go to end */ + while (iterator->next () ) ; + iterator->prev (); //We don't want to be at CharIterator::END. + } else { + iterator->next (); + } + } else + newKey = false; + + bool firstTrial = !wasHighlighted || newKey; + + if (search0 (backwards, firstTrial)) { + // Highlighting is done with a clone. + hlIterator = iterator->cloneCharIterator (); + for (int i = 0; key[i]; i++) + hlIterator->next (); + CharIterator::highlight (iterator, hlIterator, HIGHLIGHT_FINDTEXT); + CharIterator::scrollTo (iterator, hlIterator, + HPOS_INTO_VIEW, VPOS_CENTER); + + // The search will continue from the word after the found position. + iterator->next (); + return SUCCESS; + } else { + if (firstTrial) { + return NOT_FOUND; + } else { + // Nothing found anymore, reset the state for the next trial. + delete iterator; + iterator = new CharIterator (widget, true); + if (backwards) { + /* Go to end */ + while (iterator->next ()) ; + iterator->prev (); //We don't want to be at CharIterator::END. + } else { + iterator->next (); + } + // We expect a success. + Result result2 = search (key, caseSens, backwards); + assert (result2 == SUCCESS); + return RESTART; + } + } +} + +/** + * \brief This method is called when the user closes the "find text" dialog. + */ +void FindtextState::resetSearch () +{ + unhighlight (); + + if (key) + free(key); + key = NULL; +} + +/* + * Return a new string: with the reverse of the original. + */ +const char* FindtextState::rev(const char *str) +{ + if (!str) + return NULL; + + int len = strlen(str); + char *nstr = new char[len+1]; + for (int i = 0; i < len; ++i) + nstr[i] = str[len-1 -i]; + nstr[len] = 0; + + return nstr; +} + +int *FindtextState::createNexttab (const char *needle, bool caseSens, + bool backwards) +{ + const char* key; + + key = (backwards) ? rev(needle) : needle; + int i = 0; + int j = -1; + int l = strlen (key); + int *nexttab = new int[l + 1]; // + 1 is necessary for l == 1 case + nexttab[0] = -1; + + do { + if (j == -1 || charsEqual (key[i], key[j], caseSens)) { + i++; + j++; + nexttab[i] = j; + //_MSG ("nexttab[%d] = %d\n", i, j); + } else + j = nexttab[j]; + } while (i < l - 1); + + if (backwards) + delete [] key; + + return nexttab; +} + +/** + * \brief Unhighlight, and return whether a region was highlighted. + */ +bool FindtextState::unhighlight () +{ + if (hlIterator) { + CharIterator *start = hlIterator->cloneCharIterator (); + for (int i = 0; key[i]; i++) + start->prev (); + + CharIterator::unhighlight (start, hlIterator, HIGHLIGHT_FINDTEXT); + delete start; + delete hlIterator; + hlIterator = NULL; + + return true; + } else + return false; +} + +bool FindtextState::search0 (bool backwards, bool firstTrial) +{ + if (iterator->getChar () == CharIterator::END) + return false; + + bool ret = false; + const char* searchKey = (backwards) ? rev(key) : key; + int j = 0; + bool nextit = true; + int l = strlen (key); + + if (backwards && !firstTrial) { + _MSG("Having to do."); + /* Position correctly */ + /* In order to achieve good results (i.e: find a word that ends within + * the previously searched word's limit) we have to position the + * iterator in the semilast character of the previously searched word. + * + * Since we know that if a word was found before it was exactly the + * same word as the one we are searching for now, we can apply the + * following expression: + * + * Where l=length of the key and n=num of positions to move: + * + * n = l - 3 + * + * If n is negative, we have to move backwards, but if it is + * positive, we have to move forward. So, when l>=4, we start moving + * the iterator forward. */ + + if (l==1) { + iterator->prev(); + iterator->prev(); + } else if (l==2) { + iterator->prev(); + } else if (l>=4) { + for (int i=0; i<l-3; i++) { + iterator->next(); + } + } + + } else if (backwards && l==1) { + /* Particular case where we can't find the last character */ + iterator->next(); + } + + do { + if (j == -1 || charsEqual (iterator->getChar(),searchKey[j],caseSens)) { + j++; + nextit = backwards ? iterator->prev () : iterator->next (); + } else + j = nexttab[j]; + } while (nextit && j < l); + + if (j >= l) { + if (backwards) { + //This is the location of the key + iterator->next(); + } else { + // Go back to where the key was found. + for (int i = 0; i < l; i++) + iterator->prev (); + } + ret = true; + } + + if (backwards) + delete [] searchKey; + + return ret; +} + +} // namespace core +} // namespace dw |