Mercurial > dillo_port1.3
view dw/findtext.cc @ 1103:94b9265663f6
New utility function: a_Utf8_end_of_char()
author | corvid <corvid@lavabit.com> |
---|---|
date | Sun, 17 May 2009 12:04:13 -0400 |
parents | d7dbd3dcfa38 |
children | 74925c1746dd |
line wrap: on
line source
/* * Dillo Widget * * 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. * * 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/msg.h" namespace dw { namespace core { FindtextState::FindtextState () { key = NULL; nexttab = NULL; widget = NULL; iterator = NULL; hlIterator = NULL; } FindtextState::~FindtextState () { if (key) delete key; if (nexttab) delete[] nexttab; if (iterator) delete iterator; if (hlIterator) delete hlIterator; } void FindtextState::setWidget (Widget *widget) { this->widget = widget; // A widget change will restart the search. if (key) delete 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) delete 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); 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)) { // Highlighlighting 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); 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) delete 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 dw } // namespace core