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 <cerrno>
29 #include <cstdlib>
30 #include <stdexcept>
31
32 #include "curswin.h"
33 #include "yacursex.h"
34
35 using namespace YACURS::INTERNAL;
36 //
37 // Private
38 //
39
40 //
41 // Protected
42 //
CursWin(WINDOW * win,COLOROBJ dc,bool subwin)43 CursWin::CursWin(WINDOW* win, COLOROBJ dc, bool subwin)
44 : _window{win},
45 _def_color{dc},
46 _box{false},
47 _subwin{subwin},
48 _horch{0},
49 _verch{0} {
50 if (win == 0)
51 throw std::invalid_argument(_("argument 'win' must not be 0"));
52
53 int x, y;
54 getbegyx(_window, y, x);
55 _area.x(x);
56 _area.y(y);
57
58 getmaxyx(_window, y, x);
59 _area.cols(x);
60 _area.rows(y);
61
62 _client_area = _area;
63
64 set_bg(_def_color);
65 set_color(_def_color);
66 }
67
68 //
69 // Public
70 //
CursWin(const Area & a,COLOROBJ c)71 CursWin::CursWin(const Area& a, COLOROBJ c)
72 : _window{0},
73 _def_color{c},
74 _box{false},
75 _subwin{false},
76 _horch{0},
77 _verch{0} {
78 if (a == Area::zero()) {
79 throw std::invalid_argument(_("Area must not be zero"));
80 }
81
82 _area = _client_area = a;
83
84 if ((_window = newwin(_area.rows(), _area.cols(), _area.y(), _area.x())) ==
85 0)
86 throw EXCEPTIONS::CursesException("newwin");
87
88 set_bg(_def_color);
89 set_color(_def_color);
90 }
91
CursWin(const CursWin & cw)92 CursWin::CursWin(const CursWin& cw)
93 : _window{dupwin(cw._window)},
94 _def_color{cw._def_color},
95 _area{cw._area},
96 _client_area{cw._client_area},
97 _box{cw._box},
98 _subwin{cw._subwin},
99 _horch{cw._horch},
100 _verch{cw._verch} {
101 if (_window == 0) throw EXCEPTIONS::CursesException("dupwin");
102 }
103
CursWin(CursWin && cw)104 CursWin::CursWin(CursWin&& cw)
105 : _window(cw._window),
106 _def_color(cw._def_color),
107 _area(std::move(cw._area)),
108 _client_area(std::move(cw._client_area)),
109 _box(cw._box),
110 _subwin(cw._subwin),
111 _horch(cw._horch),
112 _verch(cw._verch) {
113 cw._window = nullptr;
114 }
115
operator =(const CursWin & cw)116 CursWin& CursWin::operator=(const CursWin& cw) {
117 if (this == &cw) return *this;
118
119 if (_window != 0) {
120 if (delwin(_window) == ERR) throw EXCEPTIONS::CursesException("delwin");
121
122 _window = 0;
123 }
124
125 _window = cw._window;
126 _def_color = cw._def_color;
127
128 _box = cw._box;
129 _subwin = cw._subwin;
130 _area = cw._area;
131 _client_area = cw._client_area;
132
133 _horch = cw._horch;
134 _verch = cw._verch;
135
136 return *this;
137 }
138
operator =(CursWin && cw)139 CursWin& CursWin::operator=(CursWin&& cw) {
140 if (this == &cw) return *this;
141
142 _window = cw._window;
143 cw._window = nullptr;
144 _def_color = cw._def_color;
145
146 _box = cw._box;
147 _subwin = cw._subwin;
148 _area = std::move(cw._area);
149 _client_area = std::move(cw._client_area);
150
151 _horch = cw._horch;
152 _verch = cw._verch;
153
154 return *this;
155 }
156
~CursWin()157 CursWin::~CursWin() {
158 if (_window != 0) delwin(_window);
159 }
160
area() const161 const YACURS::Area& CursWin::area() const { return _area; }
162
client_area() const163 const YACURS::Area& CursWin::client_area() const { return _client_area; }
164
refresh(bool immediate)165 CursWin& CursWin::refresh(bool immediate) {
166 if (immediate) {
167 if (wrefresh(_window) == ERR)
168 throw EXCEPTIONS::CursesException("wrefresh");
169 } else {
170 if (wnoutrefresh(_window) == ERR)
171 throw EXCEPTIONS::CursesException("wnoutrefresh");
172 }
173 return *this;
174 }
175
issubwin() const176 bool CursWin::issubwin() const { return _subwin; }
177
has_box() const178 bool CursWin::has_box() const { return _box; }
179
box(chtype verch,chtype horch)180 CursWin& CursWin::box(chtype verch, chtype horch) {
181 _verch = verch;
182 _horch = horch;
183 // X/Open has trouble with colors and ACS_* characters, as used
184 // with borders. Therefore, we fall back to non ACS_*
185 // characters. Beware, NCurses also defines _XOPEN_CURSES,
186 // though.
187 #if !defined(_XOPEN_CURSES) || defined(NCURSES_VERSION)
188 if (::box(_window, _verch, _horch) == ERR)
189 throw EXCEPTIONS::CursesException("box");
190 #else
191 if (wborder(_window, _verch == 0 ? '|' : _verch, _verch == 0 ? '|' : _verch,
192 _horch == 0 ? '-' : _horch, _horch == 0 ? '-' : _horch, '+',
193 '+', '+', '+') == ERR)
194 throw EXCEPTIONS::CursesException("wborder");
195 #endif
196
197 _box = true;
198 _client_area = _area - Margin(1, 1, 1, 1);
199
200 return *this;
201 }
202
bkgd(chtype ch)203 CursWin& CursWin::bkgd(chtype ch) {
204 if (wbkgd(_window, ch) == ERR) throw EXCEPTIONS::CursesException("wbkgd");
205
206 return *this;
207 }
208
set_color(COLOROBJ c)209 CursWin& CursWin::set_color(COLOROBJ c) {
210 #if NCURSES_VERSION_PATCH < 20100313
211 wattrset(_window, Colors::color_pair(c));
212 #else
213 if (wattrset(_window, Colors::color_pair(c)) == ERR)
214 throw EXCEPTIONS::CursesException("wattrset");
215 #endif
216 return *this;
217 }
218
set_bg(COLOROBJ c)219 CursWin& CursWin::set_bg(COLOROBJ c) {
220 bkgd(' ' | Colors::color_pair(c));
221 return *this;
222 }
223
unset_box()224 CursWin& CursWin::unset_box() {
225 _client_area = _area;
226 _box = false;
227
228 return *this;
229 }
230
get_cursor() const231 YACURS::Coordinates CursWin::get_cursor() const {
232 int y, x;
233
234 getyx(_window, y, x);
235
236 return Coordinates(x, y);
237 }
238
move(const Coordinates & pos)239 CursWin& CursWin::move(const Coordinates& pos) {
240 if (wmove(_window, pos.y(), pos.x()) == ERR)
241 throw EXCEPTIONS::CursesException("wmove");
242
243 return *this;
244 }
245
clear()246 CursWin& CursWin::clear() {
247 if (wclear(_window) == ERR) throw EXCEPTIONS::CursesException("wclear");
248
249 // This has been introduced with the EVT_REDRAW event.
250 //
251 // After a erase, the box is gone, so we assume that when we have
252 // a box, it should stay.
253 if (_box) box(_verch, _horch);
254
255 return *this;
256 }
257
clrtobot()258 CursWin& CursWin::clrtobot() {
259 if (wclrtobot(_window) == ERR)
260 throw EXCEPTIONS::CursesException("wclrtobot");
261
262 return *this;
263 }
264
erase()265 CursWin& CursWin::erase() {
266 if (werase(_window) == ERR) throw EXCEPTIONS::CursesException("werase");
267
268 // After a erase, the box is gone, so we assume that when we have
269 // a box, it should stay.
270 if (_box) box(_verch, _horch);
271
272 return *this;
273 }
274
touch()275 CursWin& CursWin::touch() {
276 if (touchwin(_window) == ERR) throw EXCEPTIONS::CursesException("touchwin");
277
278 return *this;
279 }
280
untouch()281 CursWin& CursWin::untouch() {
282 if (untouchwin(_window) == ERR)
283 throw EXCEPTIONS::CursesException("untouchwin");
284
285 return *this;
286 }
287
is_touched() const288 bool CursWin::is_touched() const { return is_wintouched(_window) == TRUE; }
289
addstr(const CurStr & str)290 CursWin& CursWin::addstr(const CurStr& str) { return addnstr(str, -1); }
291
addstr(const std::string & str)292 CursWin& CursWin::addstr(const std::string& str) {
293 return addstr(CurStr(str, get_cursor()));
294 }
295
addstrx(const CurStr & str)296 CursWin& CursWin::addstrx(const CurStr& str) {
297 // space available to string
298 int16_t space = _client_area.cols() - str.position().x() + (_box ? 1 : 0);
299
300 if (space == 1) {
301 set_color(str.color());
302 if (_box) {
303 // mvaddch advances cursor, but does not move characters
304 // under cursor, which is ideal when placing character
305 // rigtht next to the border.
306 mvaddch(str.position(), '>');
307 } else {
308 // does not advance cursor, but moves everything under it
309 // to the right, which is ideal when having no box. If
310 // insch() would be used with box, the vert box char right
311 // next to the cursor is moved off the window.
312 mvinsch(str.position(), '>');
313 }
314 set_color(_def_color);
315 return *this;
316 }
317
318 if (space < 1) {
319 return *this;
320 }
321
322 set_color(str.color());
323 #ifdef YACURS_USE_WCHAR
324 // addnstr() will convert to wide char, we're only interested in
325 // length here.
326 size_t _mbslen = std::mbstowcs((wchar_t*)0, str.c_str(), 1);
327 if (_mbslen == (size_t)-1) throw EXCEPTIONS::SystemError(errno);
328
329 if (space < static_cast<int16_t>(_mbslen)) {
330 addnstr(str, space - 1).addch('>');
331 } else {
332 addstr(str);
333 }
334 #else
335 if (space < static_cast<int16_t>(str.length())) {
336 addnstr(str, space - 1).addch('>');
337 } else {
338 addstr(str);
339 }
340 #endif
341 set_color(_def_color);
342
343 return *this;
344 }
345
addstrx(const std::string & str)346 CursWin& CursWin::addstrx(const std::string& str) {
347 return addstrx(CurStr(str, get_cursor()));
348 }
349
addlinex(const CurStr & str)350 CursWin& CursWin::addlinex(const CurStr& str) {
351 CurStr tmp(str, Coordinates(_box ? 1 : 0, str.position().y()), str.color());
352
353 size_t mylen;
354
355 #ifdef YACURS_USE_WCHAR
356 mylen = std::mbstowcs((wchar_t*)0, str.c_str(), 0);
357 if (mylen == (size_t)-1) throw EXCEPTIONS::SystemError(errno);
358 #else
359 mylen = tmp.length();
360 #endif
361
362 if (static_cast<int16_t>(mylen + (_box ? 1 : 0)) <= _client_area.cols()) {
363 tmp.append(_client_area.cols() - mylen, ' ');
364 return addstrx(tmp);
365 }
366
367 return addstrx(tmp);
368 }
369
addlinex(const std::string & str)370 CursWin& CursWin::addlinex(const std::string& str) {
371 return addlinex(CurStr(str, get_cursor()));
372 }
373
addnstr(const CurStr & str,int n)374 CursWin& CursWin::addnstr(const CurStr& str, int n) {
375 set_color(str.color());
376
377 #ifdef YACURS_USE_WCHAR
378 size_t buflen = std::mbstowcs((wchar_t*)0, str.c_str(), 0);
379 if (buflen == (size_t)-1) throw EXCEPTIONS::SystemError(errno);
380
381 wchar_t* wbuffer = new wchar_t[buflen + 1];
382
383 size_t retval = std::mbstowcs(wbuffer, str.c_str(), buflen + 1);
384 assert(buflen == retval);
385 if (retval == (size_t)-1) {
386 delete[] wbuffer;
387 throw EXCEPTIONS::SystemError(errno);
388 }
389 wbuffer[retval] = L'\0';
390
391 if (mvwaddnwstr(_window, str.position().y(), str.position().x(), wbuffer,
392 n) == ERR &&
393 (str.position().x() + (n == -1 ? static_cast<int16_t>(retval) : n)) <
394 _client_area.cols()) {
395 delete[] wbuffer;
396 throw EXCEPTIONS::CursesException("mvwaddnwstr");
397 }
398
399 delete[] wbuffer;
400 #else
401 if (mymvwaddnstr(_window, str.position().y(), str.position().x(),
402 str.c_str(), n) == ERR &&
403 (str.position().x() + (n == -1 ? static_cast<int16_t>(str.length())
404 : n)) < _client_area.cols())
405 throw EXCEPTIONS::CursesException("mvwaddnstr");
406 #endif
407
408 set_color(_def_color);
409
410 return *this;
411 }
412
addnstr(const std::string & str,int n)413 CursWin& CursWin::addnstr(const std::string& str, int n) {
414 return addnstr(CurStr(str, get_cursor()), n);
415 }
416
addch(const chtype ch)417 CursWin& CursWin::addch(const chtype ch) {
418 Coordinates end(_area.cols() - 1, _area.rows() - 1);
419
420 if (get_cursor() == end) {
421 (void)waddch(_window, ch);
422 } else {
423 if (waddch(_window, ch) == ERR)
424 throw EXCEPTIONS::CursesException("waddch");
425 }
426
427 return *this;
428 }
429
mvaddch(const Coordinates & pos,const chtype ch)430 CursWin& CursWin::mvaddch(const Coordinates& pos, const chtype ch) {
431 Coordinates end(_area.cols() - 1, _area.rows() - 1);
432
433 if (pos == end) {
434 (void)mvwaddch(_window, pos.y(), pos.x(), ch);
435 } else {
436 if (mvwaddch(_window, pos.y(), pos.x(), ch) == ERR)
437 throw EXCEPTIONS::CursesException("mvwaddch");
438 }
439 return *this;
440 }
441
insch(const chtype ch)442 CursWin& CursWin::insch(const chtype ch) {
443 if (winsch(_window, ch) == ERR) throw EXCEPTIONS::CursesException("winsch");
444
445 return *this;
446 }
447
mvinsch(const Coordinates & pos,const chtype ch)448 CursWin& CursWin::mvinsch(const Coordinates& pos, const chtype ch) {
449 if (mvwinsch(_window, pos.y(), pos.x(), ch) == ERR)
450 throw EXCEPTIONS::CursesException("mvwinsch");
451
452 return *this;
453 }
454
mvdelch(const Coordinates & pos)455 CursWin& CursWin::mvdelch(const Coordinates& pos) {
456 if (mvwdelch(_window, pos.y(), pos.x()) == ERR)
457 throw EXCEPTIONS::CursesException("mvwdelch");
458
459 return *this;
460 }
461
delch()462 CursWin& CursWin::delch() {
463 if (wdelch(_window) == ERR) throw EXCEPTIONS::CursesException("wdelch");
464
465 return *this;
466 }
467
hrule(const Coordinates & ypos)468 CursWin& CursWin::hrule(const Coordinates& ypos) {
469 Coordinates where(ypos);
470
471 where.x(_box ? 1 : 0);
472 if (ypos == Coordinates::zero()) {
473 where.y(_box ? 1 : 0);
474 }
475
476 #if !defined(_XOPEN_CURSES) || defined(NCURSES_VERSION)
477 if (mvwhline(_window, where.y(), where.x(), 0, _client_area.cols()) == ERR)
478 throw EXCEPTIONS::CursesException("mvwhline");
479 #else
480 if (mvwhline(_window, where.y(), where.x(), '_', _client_area.cols()) ==
481 ERR)
482 throw EXCEPTIONS::CursesException("mvwhline");
483 #endif
484
485 return *this;
486 }
487
vrule(const Coordinates & xpos)488 CursWin& CursWin::vrule(const Coordinates& xpos) {
489 Coordinates where(xpos);
490
491 where.y(_box ? 1 : 0);
492 if (xpos == Coordinates::zero()) {
493 where.x(_box ? 1 : 0);
494 }
495
496 #if !defined(_XOPEN_CURSES) || defined(NCURSES_VERSION)
497 if (mvwvline(_window, where.y(), where.x(), 0, _client_area.rows()) == ERR)
498 throw EXCEPTIONS::CursesException("mvwvline");
499 #else
500 if (mvwvline(_window, where.y(), where.x(), '|', _client_area.rows()) ==
501 ERR)
502 throw EXCEPTIONS::CursesException("mvwvline");
503 #endif
504
505 return *this;
506 }
507
clearok(bool fl)508 CursWin& CursWin::clearok(bool fl) {
509 if (::clearok(_window, fl ? TRUE : FALSE) == ERR)
510 throw EXCEPTIONS::CursesException("clearok");
511
512 return *this;
513 }
514
scrollok(bool fl)515 CursWin& CursWin::scrollok(bool fl) {
516 if (::scrollok(_window, fl ? TRUE : FALSE) == ERR)
517 throw EXCEPTIONS::CursesException("scrollok");
518
519 return *this;
520 }
521
leaveok(bool fl)522 CursWin& CursWin::leaveok(bool fl) {
523 if (::leaveok(_window, fl ? TRUE : FALSE) == ERR)
524 throw EXCEPTIONS::CursesException("leaveok");
525
526 return *this;
527 }
528
derwin(const Area & a) const529 CursWin* CursWin::derwin(const Area& a) const {
530 WINDOW* _subwin = ::derwin(_window, a.rows(), a.cols(), a.y(), a.x());
531 if (_subwin == 0) throw EXCEPTIONS::CursesException("derwin");
532
533 return new CursWin(_subwin, _def_color, true);
534 }
535
subwin(const Area & a) const536 CursWin* CursWin::subwin(const Area& a) const {
537 WINDOW* _subwin = ::subwin(_window, a.rows(), a.cols(), a.y(), a.x());
538 if (_subwin == 0) throw EXCEPTIONS::CursesException("subwin");
539
540 return new CursWin(_subwin, _def_color, true);
541 }
542
operator +=(const CurStr & str)543 CursWin& CursWin::operator+=(const CurStr& str) { return addstrx(str); }
544
operator +=(const std::string & str)545 CursWin& CursWin::operator+=(const std::string& str) { return addstrx(str); }
546
operator <<(const CurStr & str)547 CursWin& CursWin::operator<<(const CurStr& str) { return addstr(str); }
548
operator <<(const std::string & str)549 CursWin& CursWin::operator<<(const std::string& str) { return addstr(str); }
550