1 //
2 // This file is part of libyacurs.
3 // Copyright (C) 2013 Rafael Ostertag
4 //
5 // This program is free software: you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation, either version 3 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see
17 // <http://www.gnu.org/licenses/>.
18 //
19 //
20 // $Id$
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "gettext.h"
27
28 #include <cassert>
29 #include <cerrno>
30 #include <stdexcept>
31
32 #include "cursorbuf.h"
33 #include "yacursex.h"
34
35 using namespace YACURS::INTERNAL;
36 //
37 // Private
38 //
39
40 //
41 // Protected
42 //
43
44 //
45 // Public
46 //
CursorBuffer(const std::wstring & buffer,tsz_t max_size)47 CursorBuffer::CursorBuffer(const std::wstring& buffer, tsz_t max_size)
48 : _buffer{buffer.length() > max_size ? buffer.substr(0, max_size) : buffer},
49 _mbs_cache{},
50 _mbs_cache_valid{false},
51 _changed{false},
52 _curs_pos{0},
53 _offset{0},
54 _max_size{max_size} {}
55
CursorBuffer(const std::string & buffer,tsz_t max_size)56 CursorBuffer::CursorBuffer(const std::string& buffer, tsz_t max_size)
57 : _mbs_cache{},
58 _mbs_cache_valid{false},
59 _changed{false},
60 _curs_pos{0},
61 _offset{0},
62 _max_size{max_size} {
63 wchar_t* tmpbuf = new wchar_t[buffer.length() + 1];
64
65 size_t len = buffer.length() > _max_size ? _max_size : buffer.length();
66
67 size_t retval = std::mbstowcs(tmpbuf, buffer.c_str(), len + 1);
68
69 if (retval == len + 1) tmpbuf[len] = L'\0';
70
71 if (retval == (size_t)-1) {
72 delete[] tmpbuf;
73 throw EXCEPTIONS::SystemError(errno);
74 }
75
76 _buffer = tmpbuf;
77
78 delete[] tmpbuf;
79 }
80
CursorBuffer(const CursorBuffer & cb)81 CursorBuffer::CursorBuffer(const CursorBuffer& cb)
82 : _buffer{cb._buffer},
83 _mbs_cache{cb._mbs_cache},
84 _mbs_cache_valid{cb._mbs_cache_valid},
85 _changed{cb._changed},
86 _curs_pos{cb._curs_pos},
87 _offset{cb._offset},
88 _max_size{cb._max_size} {}
89
CursorBuffer(CursorBuffer && cb)90 CursorBuffer::CursorBuffer(CursorBuffer&& cb)
91 : _buffer{std::move(cb._buffer)},
92 _mbs_cache{cb._mbs_cache},
93 _mbs_cache_valid{cb._mbs_cache_valid},
94 _changed{cb._changed},
95 _curs_pos{cb._curs_pos},
96 _offset{cb._offset},
97 _max_size{cb._max_size} {}
98
operator =(const CursorBuffer & cb)99 CursorBuffer& CursorBuffer::operator=(const CursorBuffer& cb) {
100 if (&cb == this) return *this;
101
102 _buffer = cb._buffer;
103 _mbs_cache = cb._mbs_cache;
104 _mbs_cache_valid = cb._mbs_cache_valid;
105 _changed = cb._changed;
106 _curs_pos = cb._curs_pos;
107 _offset = cb._offset;
108 _max_size = cb._max_size;
109
110 return *this;
111 }
112
operator =(CursorBuffer && cb)113 CursorBuffer& CursorBuffer::operator=(CursorBuffer&& cb) {
114 if (&cb == this) return *this;
115
116 _buffer = std::move(cb._buffer);
117 _mbs_cache = cb._mbs_cache;
118 _mbs_cache_valid = cb._mbs_cache_valid;
119 _changed = cb._changed;
120 _curs_pos = cb._curs_pos;
121 _offset = cb._offset;
122 _max_size = cb._max_size;
123
124 return *this;
125 }
126
set(const std::wstring & buffer)127 void CursorBuffer::set(const std::wstring& buffer) {
128 // Do nothing if the string is the same as the one we already have
129 if (buffer == _buffer) return;
130
131 _curs_pos = 0;
132 _offset = 0;
133 if (buffer.length() > _max_size)
134 _buffer = buffer.substr(0, _max_size);
135 else
136 _buffer = buffer;
137
138 _mbs_cache_valid = false;
139 _changed = false;
140 }
141
set(const std::string & buffer)142 void CursorBuffer::set(const std::string& buffer) {
143 wchar_t* tmpbuf = new wchar_t[buffer.length() + 1];
144
145 size_t retval = std::mbstowcs(tmpbuf, buffer.c_str(), buffer.length() + 1);
146
147 if (retval == buffer.length() + 1) tmpbuf[buffer.length()] = L'\0';
148
149 if (retval == (size_t)-1) {
150 delete[] tmpbuf;
151 throw EXCEPTIONS::SystemError(errno);
152 }
153
154 set(tmpbuf);
155
156 delete[] tmpbuf;
157 }
158
length() const159 CursorBuffer::tsz_t CursorBuffer::length() const { return _buffer.length(); }
160
max_size(CursorBuffer::tsz_t max_size)161 void CursorBuffer::max_size(CursorBuffer::tsz_t max_size) {
162 _max_size = max_size;
163
164 // Re-set buffer, so it will be truncated.
165 if (_buffer.length() > max_size) {
166 set(_buffer.substr(0, max_size));
167 // set() does not set _changed state. Since we know, the
168 // string has been changed, we set it here.
169 _changed = true;
170
171 // reset scrolling information
172 _curs_pos = 0;
173 _offset = 0;
174 }
175 }
176
max_size() const177 CursorBuffer::tsz_t CursorBuffer::max_size() const { return _max_size; }
178
get_cursor() const179 CursorBuffer::tsz_t CursorBuffer::get_cursor() const { return _curs_pos; }
180
clear()181 void CursorBuffer::clear() {
182 _buffer.clear();
183 _changed = true;
184 _curs_pos = 0;
185 _mbs_cache_valid = false;
186 }
187
clear_eol()188 void CursorBuffer::clear_eol() {
189 if (_buffer.empty()) return;
190 tsz_t __vcurs_pos = _offset + _curs_pos;
191 _buffer = _buffer.erase(__vcurs_pos, _buffer.length() - __vcurs_pos);
192
193 _mbs_cache_valid = false;
194 _changed = true;
195 }
196
clear_sol()197 void CursorBuffer::clear_sol() {
198 if (_buffer.empty() || _curs_pos == 0) return;
199 tsz_t __vcurs_pos = _offset + _curs_pos;
200 _buffer = _buffer.erase(0, __vcurs_pos);
201
202 _curs_pos = 0;
203 _offset = 0;
204
205 _mbs_cache_valid = false;
206 _changed = true;
207 }
208
backspace()209 void CursorBuffer::backspace() {
210 if (_curs_pos == 0 || _buffer.empty()) return;
211
212 _curs_pos--;
213
214 tsz_t __vcurs_pos = _offset + _curs_pos;
215
216 _buffer = _buffer.erase(__vcurs_pos, 1);
217
218 _mbs_cache_valid = false;
219 _changed = true;
220 }
221
del()222 void CursorBuffer::del() {
223 tsz_t __vcurs_pos = _offset + _curs_pos;
224 if (__vcurs_pos >= _buffer.length() || _buffer.empty()) return;
225
226 _buffer = _buffer.erase(__vcurs_pos, 1);
227
228 _mbs_cache_valid = false;
229 _changed = true;
230 }
231
home()232 void CursorBuffer::home() {
233 _curs_pos = 0;
234 _offset = 0;
235 }
236
end()237 void CursorBuffer::end() {
238 if (_buffer.empty()) return;
239 _curs_pos = _buffer.length();
240 }
241
back_step()242 void CursorBuffer::back_step() {
243 if (_curs_pos == 0 || _buffer.empty()) return;
244
245 _curs_pos--;
246 }
247
forward_step()248 void CursorBuffer::forward_step() {
249 if (_buffer.empty() || _offset + _curs_pos >= _buffer.length()) return;
250
251 _curs_pos++;
252 }
253
insert(std::wstring::value_type c)254 void CursorBuffer::insert(std::wstring::value_type c) {
255 // do not overrun the max size
256 if (_buffer.length() >= _max_size) return;
257
258 _mbs_cache_valid = false;
259 _changed = true;
260
261 tsz_t __vcurs_pos = _offset + _curs_pos;
262
263 if (__vcurs_pos == _buffer.length())
264 _buffer.push_back(c);
265 else
266 _buffer.insert(__vcurs_pos, 1, c);
267
268 _curs_pos++;
269 }
270
271 /**
272 *
273 * Not sure fi *len is correct, but *curs_pos should fit...
274 *
275 */
info(int16_t _size,int16_t * len,int16_t * curs_pos) const276 void CursorBuffer::info(int16_t _size, int16_t* len, int16_t* curs_pos) const {
277 if (_size < 2) throw std::out_of_range(_("_size must not be <2"));
278
279 if (curs_pos) *curs_pos = _curs_pos;
280
281 if (len) {
282 if (_buffer.empty())
283 *len = 0;
284 else
285 *len =
286 static_cast<uint16_t>(((_offset + _size > _buffer.length() - 1)
287 ? _buffer.length() - _offset
288 : _size) -
289 _offset);
290 }
291 }
292
293 /**
294 * @author Markus Neumann
295 *
296 * @internal
297 *
298 * This implementation shows propper scrolling.
299 *
300 */
wstring(int16_t _size,int16_t * curs_pos)301 std::wstring CursorBuffer::wstring(int16_t _size, int16_t* curs_pos) {
302 if (_size < 2) throw std::out_of_range(_("_size must not be <2"));
303
304 /**
305 *
306 * _offset needs to be changed, if _curs_pos exceeds window limit.
307 *
308 */
309 if (_curs_pos >= _size) {
310 _offset = _offset + _curs_pos - _size + 1;
311 _curs_pos = _size - 1;
312 }
313 /**
314 *
315 * display always 1char on the left, to see what you delete.
316 *
317 */
318 if (_curs_pos <= 0) {
319 if (_offset > 0) {
320 _offset = _offset + _curs_pos - 1;
321 _curs_pos = 1;
322 }
323 }
324
325 if (curs_pos) *curs_pos = _curs_pos;
326
327 return _buffer.substr(_offset, (_offset + _size > _buffer.length() - 1)
328 ? _buffer.length() - _offset
329 : _size);
330 }
331
string(int16_t _size,int16_t * curs_pos)332 std::string CursorBuffer::string(int16_t _size, int16_t* curs_pos) {
333 std::wstring tmp_wbuf = wstring(_size, curs_pos);
334
335 size_t mbr_len = std::wcstombs(0, tmp_wbuf.c_str(), 0);
336 char* tmp_mb_buff = new char[mbr_len + 1];
337
338 if (std::wcstombs(tmp_mb_buff, tmp_wbuf.c_str(), mbr_len + 1) ==
339 (size_t)-1) {
340 delete[] tmp_mb_buff;
341 throw EXCEPTIONS::SystemError(errno);
342 }
343
344 std::string retval(tmp_mb_buff);
345 delete[] tmp_mb_buff;
346
347 return retval;
348 }
349
wstring() const350 const std::wstring& CursorBuffer::wstring() const { return _buffer; }
351
string() const352 const std::string& CursorBuffer::string() const {
353 if (_mbs_cache_valid) return _mbs_cache;
354
355 size_t mbr_len = std::wcstombs(0, _buffer.c_str(), 0);
356 char* tmp_mb_buff = new char[mbr_len + 1];
357
358 if (std::wcstombs(tmp_mb_buff, _buffer.c_str(), mbr_len + 1) ==
359 (size_t)-1) {
360 delete[] tmp_mb_buff;
361 throw EXCEPTIONS::SystemError(errno);
362 }
363
364 _mbs_cache = tmp_mb_buff;
365 _mbs_cache_valid = true;
366
367 delete[] tmp_mb_buff;
368
369 return _mbs_cache;
370 }
371
changed() const372 bool CursorBuffer::changed() const { return _changed; }
373