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