1 /***************************************************************************
2 * Copyright (C) 2008-2009 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
27 # include <sys/select.h>
33 using namespace NCurses
;
35 void NCurses::InitScreen(GNUC_UNUSED
const char *window_title
, bool enable_colors
)
37 const int ColorsTable
[] =
39 COLOR_BLACK
, COLOR_RED
, COLOR_GREEN
, COLOR_YELLOW
,
40 COLOR_BLUE
, COLOR_MAGENTA
, COLOR_CYAN
, COLOR_WHITE
43 Xinitscr(1, const_cast<char **>(&window_title
));
47 if (has_colors() && enable_colors
)
56 # endif // USE_PDCURSES
58 for (int j
= 0; j
< 8; ++j
)
59 init_pair(num
++, ColorsTable
[j
], i
< 0 ? i
: ColorsTable
[i
]);
66 void NCurses::DestroyScreen()
72 Window::Window(size_t startx
,
76 const std::string
&title
,
87 itsBgColor(clDefault
),
89 itsBaseBgColor(clDefault
),
91 itsGetStringHelper(0),
97 itsUnderlineCounter(0),
99 itsAltCharsetCounter(0)
101 if (itsStartX
> size_t(COLS
)
102 || itsStartY
> size_t(LINES
)
103 || itsWidth
+itsStartX
> size_t(COLS
)
104 || itsHeight
+itsStartY
> size_t(LINES
))
105 FatalError("Constructed window is bigger than terminal size!");
107 if (itsBorder
!= brNone
)
109 itsWinBorder
= newpad(itsHeight
, itsWidth
);
110 wattron(itsWinBorder
, COLOR_PAIR(itsBorder
));
111 box(itsWinBorder
, 0, 0);
117 if (!itsTitle
.empty())
123 itsWindow
= newpad(itsHeight
, itsWidth
);
126 keypad(itsWindow
, 1);
129 Window::Window(const Window
&w
) : itsWindow(dupwin(w
.itsWindow
)),
130 itsWinBorder(dupwin(w
.itsWinBorder
)),
131 itsStartX(w
.itsStartX
),
132 itsStartY(w
.itsStartY
),
133 itsWidth(w
.itsWidth
),
134 itsHeight(w
.itsHeight
),
135 itsWindowTimeout(w
.itsWindowTimeout
),
136 itsColor(w
.itsColor
),
137 itsBgColor(w
.itsBgColor
),
138 itsBaseColor(w
.itsBaseColor
),
139 itsBaseBgColor(w
.itsBaseBgColor
),
140 itsBorder(w
.itsBorder
),
141 itsGetStringHelper(w
.itsGetStringHelper
),
144 itsTitle(w
.itsTitle
),
145 itsColors(w
.itsColors
),
146 itsHistory(w
.itsHistory
),
147 itsBoldCounter(w
.itsBoldCounter
),
148 itsUnderlineCounter(w
.itsUnderlineCounter
),
149 itsReverseCounter(w
.itsReverseCounter
),
150 itsAltCharsetCounter(w
.itsAltCharsetCounter
)
157 delwin(itsWinBorder
);
161 void Window::SetColor(Color fg
, Color bg
)
167 wattron(itsWindow
, COLOR_PAIR(bg
*8+fg
));
169 wattroff(itsWindow
, COLOR_PAIR(itsColor
));
174 void Window::SetBaseColor(Color fg
, Color bg
)
180 void Window::SetBorder(Border border
)
182 if (border
== brNone
&& itsBorder
!= brNone
)
184 delwin(itsWinBorder
);
189 Recreate(itsWidth
, itsHeight
);
191 else if (border
!= brNone
&& itsBorder
== brNone
)
193 itsWinBorder
= newpad(itsHeight
, itsWidth
);
194 wattron(itsWinBorder
, COLOR_PAIR(border
));
195 box(itsWinBorder
,0,0);
200 Recreate(itsWidth
, itsHeight
);
204 wattron(itsWinBorder
,COLOR_PAIR(border
));
205 box(itsWinBorder
, 0, 0);
210 void Window::SetTitle(const std::string
&new_title
)
212 if (itsTitle
== new_title
)
216 else if (!new_title
.empty() && itsTitle
.empty())
220 Recreate(itsWidth
, itsHeight
);
222 else if (new_title
.empty() && !itsTitle
.empty())
226 Recreate(itsWidth
, itsHeight
);
228 itsTitle
= new_title
;
231 void Window::CreateHistory()
234 itsHistory
= new std::deque
<std::wstring
>;
237 void Window::DeleteHistory()
243 void Window::Recreate(size_t width
, size_t height
)
246 itsWindow
= newpad(height
, width
);
247 SetTimeout(itsWindowTimeout
);
248 SetColor(itsColor
, itsBgColor
);
249 keypad(itsWindow
, 1);
252 void Window::MoveTo(size_t new_x
, size_t new_y
)
256 if (itsBorder
!= brNone
)
261 if (!itsTitle
.empty())
265 void Window::AdjustDimensions(size_t width
, size_t height
)
267 if (itsBorder
!= brNone
)
269 delwin(itsWinBorder
);
270 itsWinBorder
= newpad(height
, width
);
271 wattron(itsWinBorder
, COLOR_PAIR(itsBorder
));
272 box(itsWinBorder
, 0, 0);
276 if (!itsTitle
.empty())
282 void Window::Resize(size_t new_width
, size_t new_height
)
284 AdjustDimensions(new_width
, new_height
);
285 Recreate(itsWidth
, itsHeight
);
288 void Window::ShowBorder() const
290 if (itsBorder
!= brNone
)
293 prefresh(itsWinBorder
, 0, 0, GetStartY(), GetStartX(), itsStartY
+itsHeight
, itsStartX
+itsWidth
);
295 if (!itsTitle
.empty())
297 if (itsBorder
!= brNone
)
298 attron(COLOR_PAIR(itsBorder
));
300 attron(COLOR_PAIR(itsBaseColor
));
301 mvhline(itsStartY
-1, itsStartX
, 0, itsWidth
);
303 mvhline(itsStartY
-2, itsStartX
, 32, itsWidth
); // clear title line
304 mvaddstr(itsStartY
-2, itsStartX
, itsTitle
.c_str());
305 attroff(COLOR_PAIR(itsBorder
) | A_BOLD
);
310 void Window::Display()
316 void Window::Refresh()
318 prefresh(itsWindow
, 0, 0, itsStartY
, itsStartX
, itsStartY
+itsHeight
-1, itsStartX
+itsWidth
-1);
321 void Window::Clear(bool refresh
)
328 void Window::Hide(char ch
) const
330 for (size_t i
= 0; i
< GetHeight(); ++i
)
331 mvhline(i
+GetStartY(), GetStartX(), ch
, GetWidth());
335 void Window::Bold(bool bold_state
) const
337 (bold_state
? wattron
: wattroff
)(itsWindow
, A_BOLD
);
340 void Window::Underline(bool underline_state
) const
342 (underline_state
? wattron
: wattroff
)(itsWindow
, A_UNDERLINE
);
345 void Window::Reverse(bool reverse_state
) const
347 (reverse_state
? wattron
: wattroff
)(itsWindow
, A_REVERSE
);
350 void Window::AltCharset(bool altcharset_state
) const
352 (altcharset_state
? wattron
: wattroff
)(itsWindow
, A_ALTCHARSET
);
355 void Window::SetTimeout(int timeout
)
357 itsWindowTimeout
= timeout
;
358 wtimeout(itsWindow
, timeout
);
361 void Window::AddFDCallback(int fd
, void (*callback
)())
363 itsFDs
.push_back(std::make_pair(fd
, callback
));
366 void Window::ClearFDCallbacksList()
371 bool Window::FDCallbacksListEmpty() const
373 return itsFDs
.empty();
376 void Window::ReadKey(int &read_key
) const
378 // in pdcurses polling stdin doesn't work, so we can't poll
379 // both stdin and other file descriptors in one select. the
380 // workaround is to set the timeout of select to 0, poll
381 // other file descriptors and then wait for stdin input with
382 // the given timeout. unfortunately, this results in delays
383 // since ncmpcpp doesn't see that data arrived while waiting
384 // for input from stdin, but it seems there is no better option.
388 # if !defined(USE_PDCURSES)
389 FD_SET(STDIN_FILENO
, &fdset
);
390 timeval timeout
= { itsWindowTimeout
/1000, (itsWindowTimeout
%1000)*1000 };
392 timeval timeout
= { 0, 0 };
395 int fd_max
= STDIN_FILENO
;
396 for (FDCallbacks::const_iterator it
= itsFDs
.begin(); it
!= itsFDs
.end(); ++it
)
398 if (it
->first
> fd_max
)
400 FD_SET(it
->first
, &fdset
);
403 if (select(fd_max
+1, &fdset
, 0, 0, &timeout
) > 0)
405 # if !defined(USE_PDCURSES)
406 read_key
= FD_ISSET(STDIN_FILENO
, &fdset
) ? wgetch(itsWindow
) : ERR
;
407 # endif // !USE_PDCURSES
408 for (FDCallbacks::const_iterator it
= itsFDs
.begin(); it
!= itsFDs
.end(); ++it
)
409 if (FD_ISSET(it
->first
, &fdset
))
412 # if !defined(USE_PDCURSES)
416 read_key
= wgetch(itsWindow
);
420 void Window::ReadKey() const
425 std::string
Window::GetString(const std::string
&base
, size_t length
, size_t width
, bool encrypted
) const
428 size_t beginning
, maxbeginning
, minx
, x
, real_x
, y
, maxx
, real_maxx
;
430 getyx(itsWindow
, y
, x
);
431 minx
= real_maxx
= maxx
= real_x
= x
;
434 if (width
== size_t(-1))
435 width
= itsWidth
-x
-1;
439 std::wstring wbase
= ToWString(base
);
440 std::wstring
*tmp
= &wbase
;
441 size_t history_offset
= itsHistory
&& !encrypted
? itsHistory
->size() : -1;
446 bool block_scrolling
= 0;
448 // disable scrolling if wide chars are used
449 for (std::wstring::const_iterator it
= tmp
->begin(); it
!= tmp
->end(); ++it
)
450 if (wcwidth(*it
) > 1)
460 maxbeginning
= block_scrolling
? 0 : (tmp
->length() < width
? 0 : tmp
->length()-width
);
461 maxx
= minx
+ (Length(*tmp
) < width
? Length(*tmp
) : width
);
463 real_maxx
= minx
+ (tmp
->length() < width
? tmp
->length() : width
);
465 if (beginning
> maxbeginning
)
466 beginning
= maxbeginning
;
470 size_t real_real_maxx
= minx
;
471 size_t biggest_x
= minx
+width
;
473 if (block_scrolling
&& maxx
>= biggest_x
)
476 for (std::wstring::const_iterator it
= tmp
->begin(); i
< width
; ++it
, ++real_real_maxx
)
480 real_real_maxx
= real_maxx
;
482 real_x
= real_real_maxx
;
483 x
= block_scrolling
? (maxx
> biggest_x
? biggest_x
: maxx
) : maxx
;
484 beginning
= maxbeginning
;
488 mvwhline(itsWindow
, y
, minx
, 32, width
+1);
491 mvwprintw(itsWindow
, y
, minx
, "%ls", tmp
->substr(beginning
, width
+1).c_str());
493 mvwhline(itsWindow
, y
, minx
, '*', maxx
-minx
);
495 if (itsGetStringHelper
)
496 itsGetStringHelper(*tmp
);
498 wmove(itsWindow
, y
, x
);
499 prefresh(itsWindow
, 0, 0, itsStartY
, itsStartX
, itsStartY
+itsHeight
-1, itsStartX
+itsWidth
-1);
502 // these key codes are special and should be ignored
503 if ((input
< 10 || (input
> 10 && input
!= 21 && input
< 32))
505 && input
!= KEY_BACKSPACE
)
508 # endif // USE_PDCURSES
517 if (itsHistory
&& !encrypted
&& history_offset
> 0)
520 tmp
= &(*itsHistory
)[--history_offset
];
521 while (tmp
->empty() && history_offset
);
526 if (itsHistory
&& !encrypted
&& itsHistory
->size() > 0)
528 if (history_offset
< itsHistory
->size()-1)
531 tmp
= &(*itsHistory
)[++history_offset
];
532 while (tmp
->empty() && history_offset
< itsHistory
->size()-1);
534 else if (history_offset
== itsHistory
->size()-1)
547 x
+= wcwidth((*tmp
)[beginning
+real_x
-minx
-1]);
549 else if (beginning
< maxbeginning
)
553 case KEY_BACKSPACE
: case 127:
555 if (x
<= minx
&& !beginning
)
563 x
-= wcwidth((*tmp
)[beginning
+real_x
-minx
]);
565 else if (beginning
> 0)
567 if (input
!= KEY_BACKSPACE
&& input
!= 127)
568 break; // backspace = left & delete.
572 if ((real_x
-minx
)+beginning
== tmp
->length())
574 tmp
->erase(tmp
->begin()+(real_x
-minx
)+beginning
);
575 if (beginning
&& beginning
== maxbeginning
&& real_x
< maxx
)
597 real_maxx
= maxx
= real_x
= x
= minx
;
598 maxbeginning
= beginning
= 0;
602 if (tmp
->length() >= length
)
606 if (int(mbrtowc(&wc_in
, tmp_in
.c_str(), MB_CUR_MAX
, 0)) < 0)
609 if (wcwidth(wc_in
) > 1)
612 if ((real_x
-minx
)+beginning
>= tmp
->length())
614 tmp
->push_back(wc_in
);
625 tmp
->insert(tmp
->begin()+(real_x
-minx
)+beginning
, wc_in
);
631 else if (beginning
< maxbeginning
)
641 if (itsHistory
&& !encrypted
)
643 size_t old_size
= itsHistory
->size();
644 if (!tmp
->empty() && (itsHistory
->empty() || itsHistory
->back() != *tmp
))
645 itsHistory
->push_back(*tmp
);
646 if (old_size
> 1 && history_offset
< old_size
)
648 std::deque
<std::wstring
>::iterator it
= itsHistory
->begin()+history_offset
;
651 itsHistory
->erase(it
);
655 return ToString(*tmp
);
658 void Window::GetXY(int &x
, int &y
)
660 getyx(itsWindow
, y
, x
);
665 void Window::GotoXY(int x
, int y
)
667 wmove(itsWindow
, y
, x
);
672 int Window::X() const
677 int Window::Y() const
682 bool Window::hasCoords(int &x
, int &y
)
684 # ifndef USE_PDCURSES
685 return wmouse_trafo(itsWindow
, &y
, &x
, 0);
687 // wmouse_trafo is broken in pdcurses, use our own implementation
688 size_t u_x
= x
, u_y
= y
;
689 if (u_x
>= itsStartX
&& u_x
< itsStartX
+itsWidth
690 && u_y
>= itsStartY
&& u_y
< itsStartY
+itsHeight
)
700 size_t Window::GetWidth() const
702 if (itsBorder
!= brNone
)
708 size_t Window::GetHeight() const
710 size_t height
= itsHeight
;
711 if (itsBorder
!= brNone
)
713 if (!itsTitle
.empty())
718 size_t Window::GetStartX() const
720 if (itsBorder
!= brNone
)
726 size_t Window::GetStartY() const
728 size_t starty
= itsStartY
;
729 if (itsBorder
!= brNone
)
731 if (!itsTitle
.empty())
736 const std::string
&Window::GetTitle() const
741 Color
Window::GetColor() const
746 Border
Window::GetBorder() const
751 void Window::Scroll(Where where
)
754 scrollok(itsWindow
, 1);
761 wscrl(itsWindow
, -1);
764 wscrl(itsWindow
, itsWidth
);
767 wscrl(itsWindow
, -itsWidth
);
773 scrollok(itsWindow
, 0);
777 Window
&Window::operator<<(Colors colors
)
779 if (colors
.fg
== clEnd
|| colors
.bg
== clEnd
)
784 itsColors
.push(colors
);
785 SetColor(colors
.fg
, colors
.bg
);
789 Window
&Window::operator<<(Color color
)
794 while (!itsColors
.empty())
796 SetColor(itsBaseColor
, itsBaseBgColor
);
799 if (!itsColors
.empty())
801 if (!itsColors
.empty())
802 SetColor(itsColors
.top().fg
, itsColors
.top().bg
);
804 SetColor(itsBaseColor
, itsBaseBgColor
);
807 itsColors
.push(Colors(color
, clDefault
));
808 SetColor(itsColors
.top().fg
, itsColors
.top().bg
);
813 Window
&Window::operator<<(Format format
)
818 Bold((itsBoldCounter
= 0));
819 Reverse((itsReverseCounter
= 0));
820 AltCharset((itsAltCharsetCounter
= 0));
823 Bold(++itsBoldCounter
);
826 if (--itsBoldCounter
<= 0)
827 Bold((itsBoldCounter
= 0));
830 Underline(++itsUnderlineCounter
);
832 case fmtUnderlineEnd
:
833 if (--itsUnderlineCounter
<= 0)
834 Underline((itsUnderlineCounter
= 0));
837 Reverse(++itsReverseCounter
);
840 if (--itsReverseCounter
<= 0)
841 Reverse((itsReverseCounter
= 0));
844 AltCharset(++itsAltCharsetCounter
);
846 case fmtAltCharsetEnd
:
847 if (--itsAltCharsetCounter
<= 0)
848 AltCharset((itsAltCharsetCounter
= 0));
854 Window
&Window::operator<<(int (*f
)(WINDOW
*))
860 Window
&Window::operator<<(XY coords
)
862 GotoXY(coords
.x
, coords
.y
);
866 Window
&Window::operator<<(const char *s
)
868 wprintw(itsWindow
, "%s", s
);
872 Window
&Window::operator<<(char c
)
874 wprintw(itsWindow
, "%c", c
);
878 Window
&Window::operator<<(const wchar_t *ws
)
880 wprintw(itsWindow
, "%ls", ws
);
884 Window
&Window::operator<<(wchar_t wc
)
886 wprintw(itsWindow
, "%lc", wc
);
890 Window
&Window::operator<<(int i
)
892 wprintw(itsWindow
, "%d", i
);
896 Window
&Window::operator<<(double d
)
898 wprintw(itsWindow
, "%f", d
);
902 Window
&Window::operator<<(const std::string
&s
)
904 for (std::string::const_iterator it
= s
.begin(); it
!= s
.end(); ++it
)
905 wprintw(itsWindow
, "%c", *it
);
909 Window
&Window::operator<<(const std::wstring
&ws
)
911 for (std::wstring::const_iterator it
= ws
.begin(); it
!= ws
.end(); ++it
)
912 wprintw(itsWindow
, "%lc", *it
);
916 Window
&Window::operator<<(size_t s
)
918 wprintw(itsWindow
, "%zu", s
);
922 std::string
ToString(const std::wstring
&ws
)
926 for (size_t i
= 0; i
< ws
.length(); ++i
)
928 int n
= wcrtomb(s
, ws
[i
], 0);
935 std::wstring
ToWString(const std::string
&s
)
938 wchar_t *ws
= new wchar_t[s
.length()];
939 const char *c_s
= s
.c_str();
940 int n
= mbsrtowcs(ws
, &c_s
, s
.length(), 0);
942 result
.append(ws
, n
);
947 size_t Window::Length(const std::wstring
&ws
)
950 for (std::wstring::const_iterator it
= ws
.begin(); it
!= ws
.end(); ++it
)
951 length
+= wcwidth(*it
);