1 /* searchtextstorage.cc
2  * This file belongs to Worker, a file manager for UN*X/X11.
3  * Copyright (C) 2008-2015 Ralf Hoffmann.
4  * You can contact me at: ralf@boomerangsworld.de
5  *   or http://www.boomerangsworld.de/worker
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "searchtextstorage.hh"
23 #include "textstorage.h"
24 #include "textview.h"
25 #include <cctype>
26 
SearchTextStorage(TextStorage & ts)27 SearchTextStorage::SearchTextStorage( TextStorage &ts ) :
28     m_ts( ts ),
29     m_start_search( std::pair<int, int>( -1, -1 ) )
30 {
31     m_cur_match.line = -1;
32     m_cur_match.start_offset = -1;
33     m_cur_match.end_offset = -1;
34 }
35 
~SearchTextStorage()36 SearchTextStorage::~SearchTextStorage()
37 {
38 }
39 
search(const std::string & search_string)40 void SearchTextStorage::search( const std::string &search_string )
41 {
42     if ( search_string.length() < 1 ) {
43         m_cur_match.line = -1;
44         return;
45     }
46 
47     std::pair<int, int> start;
48     bool ignore_case = true;
49 
50     if ( search_string == m_last_search_string && m_cur_match.line >= 0 ) {
51         start = std::pair<int, int>( m_cur_match.line, m_cur_match.end_offset );
52         start.second++;
53     } else {
54         start = m_start_search;
55     }
56 
57     if ( start.first >= m_ts.getNrOfUnwrappedLines() )
58         start.first = -1;
59 
60     m_cur_match.line = -1;
61 
62     if ( start.first >= 0 ) {
63         std::string l;
64         if ( m_ts.getUnwrappedLine( start.first, 0, l ) == 0 ) {
65 
66             if ( start.second >= (int)l.length() ) {
67                 start.first++;
68                 start.second = 0;
69             }
70         }
71     }
72 
73     m_cur_match.line = -1;
74 
75     if ( start.first < 0 ) {
76         start.first = 0;
77         start.second = 0;
78     }
79 
80     for ( std::string::const_iterator it1 = search_string.begin();
81           it1 != search_string.end();
82           it1++ ) {
83         //TODO not utf8 safe
84         if ( std::isupper( *it1 ) ) {
85             ignore_case = false;
86             break;
87         }
88     }
89 
90     if ( start.first >= 0 ) {
91         for ( ; start.first < m_ts.getNrOfUnwrappedLines(); start.first++, start.second = 0 ) {
92             std::string l;
93             if ( m_ts.getUnwrappedLine( start.first, 0, l ) == 0 ) {
94                 int so = -1, eo = -1;
95 
96                 if ( start.second >= 0 && start.second < (int)l.length() ) {
97                     if ( m_cre.match( search_string.c_str(), l.c_str() + start.second, ignore_case, &so, &eo ) == true ) {
98                         m_cur_match.line = start.first;
99                         m_cur_match.start_offset = so + start.second;
100                         m_cur_match.end_offset = eo + start.second;
101                         break;
102                     }
103                 }
104             } else {
105                 break;
106             }
107         }
108         if ( m_cur_match.line == -1 ) {
109             // no match so start over next time
110             m_start_search.first = -1;
111             m_start_search.second = -1;
112         }
113     }
114 
115     m_last_search_string = search_string;
116 }
117 
searchBackwards(const std::string & search_string)118 void SearchTextStorage::searchBackwards( const std::string &search_string )
119 {
120     if ( search_string.length() < 1 ) {
121         m_cur_match.line = -1;
122         return;
123     }
124 
125     std::pair<int, int> start;
126     bool ignore_case = true;
127 
128     if ( search_string == m_last_search_string && m_cur_match.line >= 0 ) {
129         start = std::pair<int, int>( m_cur_match.line, m_cur_match.start_offset );
130 
131         prevPos( start );
132     } else {
133         start = m_start_search;
134     }
135 
136     if ( start.first >= m_ts.getNrOfUnwrappedLines() ) {
137         start.first = -1;
138     }
139 
140     m_cur_match.line = -1;
141 
142     m_cur_match.line = -1;
143 
144     if ( start.first < 0 ) {
145         start.first = 0;
146         start.second = 0;
147 
148         prevPos( start );
149     }
150 
151     for ( std::string::const_iterator it1 = search_string.begin();
152           it1 != search_string.end();
153           it1++ ) {
154         //TODO not utf8 safe
155         if ( std::isupper( *it1 ) ) {
156             ignore_case = false;
157             break;
158         }
159     }
160 
161     if ( start.first >= 0 ) {
162         for ( ; start.first >= 0; start.first--, start.second = -1 ) {
163             std::string l;
164             if ( m_ts.getUnwrappedLine( start.first, 0, l ) == 0 ) {
165                 if ( l.length() < 1 ) continue;
166 
167                 if ( start.second == -1 ) {
168                     start.second = l.length() - 1;
169                 }
170 
171                 int match_candidate_so = -1;
172                 int match_candidate_eo = -1;
173                 int pos = 0;
174 
175                 for (;;) {
176                     // search match, if lower than start.second, store as candidate
177                     // otherwise take possible candidate
178                     // repeat from match position + 1
179                     if ( pos >= (int)l.length() ) break;
180 
181                     int so = -1, eo = -1;
182 
183                     if ( m_cre.match( search_string.c_str(), l.c_str() + pos, ignore_case, &so, &eo ) == true ) {
184 
185                         if ( so + pos < start.second ) {
186                             match_candidate_so = so + pos;
187                             match_candidate_eo = eo + pos;
188 
189                             pos += so + 1;
190 
191                             continue;
192                         }
193                     }
194 
195                     break;
196                 }
197 
198                 if ( match_candidate_so >= 0 ) {
199                     m_cur_match.line = start.first;
200                     m_cur_match.start_offset = match_candidate_so;
201                     m_cur_match.end_offset = match_candidate_eo;
202                     break;
203                 }
204             } else {
205                 break;
206             }
207         }
208         if ( m_cur_match.line == -1 ) {
209             // no match so start over next time
210             m_start_search.first = -1;
211             m_start_search.second = -1;
212         }
213     }
214 
215     m_last_search_string = search_string;
216 }
217 
highlightCurMatch(TextView & tv)218 void SearchTextStorage::highlightCurMatch( TextView &tv )
219 {
220     if ( m_cur_match.line >= 0 ) {
221         tv.setSelectionStart( m_cur_match.line, m_cur_match.start_offset );
222         tv.setSelectionEnd( m_cur_match.line, m_cur_match.end_offset );
223     } else {
224         tv.clearSelection();
225     }
226 }
227 
setSearchStartLine(int line)228 void SearchTextStorage::setSearchStartLine( int line )
229 {
230     if ( line < 0 || line >= m_ts.getNrOfUnwrappedLines() ) return;
231 
232     m_start_search.first = line;
233     m_start_search.second = 0;
234     m_cur_match.line = -1;
235 }
236 
getCurMatchLine() const237 int SearchTextStorage::getCurMatchLine() const
238 {
239     return m_cur_match.line;
240 }
241 
prevPos(std::pair<int,int> & pos)242 int SearchTextStorage::prevPos( std::pair< int, int > &pos )
243 {
244     if ( pos.second > 0 ) {
245         pos.second--;
246         return 0;
247     }
248 
249     bool wrapped = false;
250 
251     if ( pos.first > 0 ) {
252         pos.first--;
253     } else {
254         pos.first = m_ts.getNrOfUnwrappedLines() - 1;
255         wrapped = true;
256     }
257 
258     if ( pos.first >= 0 ) {
259         std::string l;
260         if ( m_ts.getUnwrappedLine( pos.first, 0, l ) == 0 ) {
261             pos.second = l.length();
262         } else {
263             pos.first = -1;
264             pos.second = -1;
265             return -1;
266         }
267     }
268 
269     return wrapped == true ? 1 : 0;
270 }
271