move code responsible for screen resize to SIGWINCH handler
[ncmpcpp.git] / src / window.cpp
blob8fd0beaa6815a9ca78250cbb33589238cb061964
1 /***************************************************************************
2 * Copyright (C) 2008-2009 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
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. *
9 * *
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. *
14 * *
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 ***************************************************************************/
21 #include <cstring>
22 #include <cstdlib>
24 #ifdef WIN32
25 # include <winsock.h>
26 #else
27 # include <sys/select.h>
28 #endif
30 #include "error.h"
31 #include "window.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
42 # ifdef XCURSES
43 Xinitscr(1, const_cast<char **>(&window_title));
44 # else
45 initscr();
46 # endif // XCURSES
47 if (has_colors() && enable_colors)
49 start_color();
50 use_default_colors();
51 int num = 1;
52 # ifdef USE_PDCURSES
53 int i = 0;
54 # else
55 int i = -1;
56 # endif // USE_PDCURSES
57 for (; i < 8; ++i)
58 for (int j = 0; j < 8; ++j)
59 init_pair(num++, ColorsTable[j], i < 0 ? i : ColorsTable[i]);
61 noecho();
62 cbreak();
63 curs_set(0);
66 void NCurses::DestroyScreen()
68 curs_set(1);
69 endwin();
72 Window::Window(size_t startx,
73 size_t starty,
74 size_t width,
75 size_t height,
76 const std::string &title,
77 Color color,
78 Border border)
79 : itsWindow(0),
80 itsWinBorder(0),
81 itsStartX(startx),
82 itsStartY(starty),
83 itsWidth(width),
84 itsHeight(height),
85 itsWindowTimeout(-1),
86 itsColor(color),
87 itsBgColor(clDefault),
88 itsBaseColor(color),
89 itsBaseBgColor(clDefault),
90 itsBorder(border),
91 itsGetStringHelper(0),
92 itsX(0),
93 itsY(0),
94 itsTitle(title),
95 itsHistory(0),
96 itsBoldCounter(0),
97 itsUnderlineCounter(0),
98 itsReverseCounter(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);
112 itsStartX++;
113 itsStartY++;
114 itsWidth -= 2;
115 itsHeight -= 2;
117 if (!itsTitle.empty())
119 itsStartY += 2;
120 itsHeight -= 2;
123 itsWindow = newpad(itsHeight, itsWidth);
125 SetColor(itsColor);
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),
142 itsX(w.itsX),
143 itsY(w.itsY),
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)
154 Window::~Window()
156 delwin(itsWindow);
157 delwin(itsWinBorder);
158 delete itsHistory;
161 void Window::SetColor(Color fg, Color bg)
163 if (fg == clDefault)
164 fg = itsBaseColor;
166 if (fg != clDefault)
167 wattron(itsWindow, COLOR_PAIR(bg*8+fg));
168 else
169 wattroff(itsWindow, COLOR_PAIR(itsColor));
170 itsColor = fg;
171 itsBgColor = bg;
174 void Window::SetBaseColor(Color fg, Color bg)
176 itsBaseColor = fg;
177 itsBaseBgColor = bg;
180 void Window::SetBorder(Border border)
182 if (border == brNone && itsBorder != brNone)
184 delwin(itsWinBorder);
185 itsStartX--;
186 itsStartY--;
187 itsHeight += 2;
188 itsWidth += 2;
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);
196 itsStartX++;
197 itsStartY++;
198 itsHeight -= 2;
199 itsWidth -= 2;
200 Recreate(itsWidth, itsHeight);
202 else
204 wattron(itsWinBorder,COLOR_PAIR(border));
205 box(itsWinBorder, 0, 0);
207 itsBorder = border;
210 void Window::SetTitle(const std::string &new_title)
212 if (itsTitle == new_title)
214 return;
216 else if (!new_title.empty() && itsTitle.empty())
218 itsStartY += 2;
219 itsHeight -= 2;
220 Recreate(itsWidth, itsHeight);
222 else if (new_title.empty() && !itsTitle.empty())
224 itsStartY -= 2;
225 itsHeight += 2;
226 Recreate(itsWidth, itsHeight);
228 itsTitle = new_title;
231 void Window::CreateHistory()
233 if (!itsHistory)
234 itsHistory = new std::deque<std::wstring>;
237 void Window::DeleteHistory()
239 delete itsHistory;
240 itsHistory = 0;
243 void Window::Recreate(size_t width, size_t height)
245 delwin(itsWindow);
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)
254 itsStartX = new_x;
255 itsStartY = new_y;
256 if (itsBorder != brNone)
258 itsStartX++;
259 itsStartY++;
261 if (!itsTitle.empty())
262 itsStartY += 2;
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);
273 width -= 2;
274 height -= 2;
276 if (!itsTitle.empty())
277 height -= 2;
278 itsHeight = height;
279 itsWidth = width;
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)
292 refresh();
293 prefresh(itsWinBorder, 0, 0, GetStartY(), GetStartX(), itsStartY+itsHeight, itsStartX+itsWidth);
295 if (!itsTitle.empty())
297 if (itsBorder != brNone)
298 attron(COLOR_PAIR(itsBorder));
299 else
300 attron(COLOR_PAIR(itsBaseColor));
301 mvhline(itsStartY-1, itsStartX, 0, itsWidth);
302 attron(A_BOLD);
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);
307 refresh();
310 void Window::Display()
312 ShowBorder();
313 Refresh();
316 void Window::Refresh()
318 prefresh(itsWindow, 0, 0, itsStartY, itsStartX, itsStartY+itsHeight-1, itsStartX+itsWidth-1);
321 void Window::Clear(bool refresh)
323 werase(itsWindow);
324 if (refresh)
325 Window::Refresh();
328 void Window::Hide(char ch) const
330 for (size_t i = 0; i < GetHeight(); ++i)
331 mvhline(i+GetStartY(), GetStartX(), ch, GetWidth());
332 refresh();
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()
368 itsFDs.clear();
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.
386 fd_set fdset;
387 FD_ZERO(&fdset);
388 # if !defined(USE_PDCURSES)
389 FD_SET(STDIN_FILENO, &fdset);
390 timeval timeout = { itsWindowTimeout/1000, (itsWindowTimeout%1000)*1000 };
391 # else
392 timeval timeout = { 0, 0 };
393 # endif
395 int fd_max = STDIN_FILENO;
396 for (FDCallbacks::const_iterator it = itsFDs.begin(); it != itsFDs.end(); ++it)
398 if (it->first > fd_max)
399 fd_max = it->first;
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))
410 it->second();
412 # if !defined(USE_PDCURSES)
413 else
414 read_key = ERR;
415 # else
416 read_key = wgetch(itsWindow);
417 # endif
420 void Window::ReadKey() const
422 wgetch(itsWindow);
425 std::string Window::GetString(const std::string &base, size_t length, size_t width, bool encrypted) const
427 int input;
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;
433 width--;
434 if (width == size_t(-1))
435 width = itsWidth-x-1;
437 curs_set(1);
439 std::wstring wbase = ToWString(base);
440 std::wstring *tmp = &wbase;
441 size_t history_offset = itsHistory && !encrypted ? itsHistory->size() : -1;
443 std::string tmp_in;
444 wchar_t wc_in;
445 bool gotoend = 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)
451 block_scrolling = 1;
453 beginning = -1;
457 if (tmp->empty())
458 block_scrolling = 0;
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;
468 if (gotoend)
470 size_t real_real_maxx = minx;
471 size_t biggest_x = minx+width;
473 if (block_scrolling && maxx >= biggest_x)
475 size_t i = 0;
476 for (std::wstring::const_iterator it = tmp->begin(); i < width; ++it, ++real_real_maxx)
477 i += wcwidth(*it);
479 else
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;
485 gotoend = 0;
488 mvwhline(itsWindow, y, minx, 32, width+1);
490 if (!encrypted)
491 mvwprintw(itsWindow, y, minx, "%ls", tmp->substr(beginning, width+1).c_str());
492 else
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);
500 ReadKey(input);
502 // these key codes are special and should be ignored
503 if ((input < 10 || (input > 10 && input != 21 && input < 32))
504 # ifdef USE_PDCURSES
505 && input != KEY_BACKSPACE)
506 # else
508 # endif // USE_PDCURSES
509 continue;
511 switch (input)
513 case ERR:
514 case KEY_MOUSE:
515 break;
516 case KEY_UP:
517 if (itsHistory && !encrypted && history_offset > 0)
520 tmp = &(*itsHistory)[--history_offset];
521 while (tmp->empty() && history_offset);
522 gotoend = 1;
524 break;
525 case KEY_DOWN:
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)
536 tmp = &wbase;
537 history_offset++;
539 gotoend = 1;
541 break;
542 case KEY_RIGHT:
544 if (x < maxx)
546 real_x++;
547 x += wcwidth((*tmp)[beginning+real_x-minx-1]);
549 else if (beginning < maxbeginning)
550 beginning++;
551 break;
553 case KEY_BACKSPACE: case 127:
555 if (x <= minx && !beginning)
556 break;
558 case KEY_LEFT:
560 if (x > minx)
562 real_x--;
563 x -= wcwidth((*tmp)[beginning+real_x-minx]);
565 else if (beginning > 0)
566 beginning--;
567 if (input != KEY_BACKSPACE && input != 127)
568 break; // backspace = left & delete.
570 case KEY_DC:
572 if ((real_x-minx)+beginning == tmp->length())
573 break;
574 tmp->erase(tmp->begin()+(real_x-minx)+beginning);
575 if (beginning && beginning == maxbeginning && real_x < maxx)
577 real_x++;
578 x++;
580 break;
582 case KEY_HOME:
584 real_x = x = minx;
585 beginning = 0;
586 break;
588 case KEY_END:
590 gotoend = 1;
591 break;
593 case 10:
594 break;
595 case 21: // CTRL+U
596 tmp->clear();
597 real_maxx = maxx = real_x = x = minx;
598 maxbeginning = beginning = 0;
599 break;
600 default:
602 if (tmp->length() >= length)
603 break;
605 tmp_in += input;
606 if (int(mbrtowc(&wc_in, tmp_in.c_str(), MB_CUR_MAX, 0)) < 0)
607 break;
609 if (wcwidth(wc_in) > 1)
610 block_scrolling = 1;
612 if ((real_x-minx)+beginning >= tmp->length())
614 tmp->push_back(wc_in);
615 if (!beginning)
617 real_x++;
618 x += wcwidth(wc_in);
620 beginning++;
621 gotoend = 1;
623 else
625 tmp->insert(tmp->begin()+(real_x-minx)+beginning, wc_in);
626 if (x < maxx)
628 real_x++;
629 x += wcwidth(wc_in);
631 else if (beginning < maxbeginning)
632 beginning++;
634 tmp_in.clear();
638 while (input != 10);
639 curs_set(0);
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;
649 wbase = *it;
650 tmp = &wbase;
651 itsHistory->erase(it);
655 return ToString(*tmp);
658 void Window::GetXY(int &x, int &y)
660 getyx(itsWindow, y, x);
661 itsX = x;
662 itsY = y;
665 void Window::GotoXY(int x, int y)
667 wmove(itsWindow, y, x);
668 itsX = x;
669 itsY = y;
672 int Window::X() const
674 return itsX;
677 int Window::Y() const
679 return itsY;
682 bool Window::hasCoords(int &x, int &y)
684 # ifndef USE_PDCURSES
685 return wmouse_trafo(itsWindow, &y, &x, 0);
686 # else
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)
692 x -= itsStartX;
693 y -= itsStartY;
694 return true;
696 return false;
697 # endif
700 size_t Window::GetWidth() const
702 if (itsBorder != brNone)
703 return itsWidth+2;
704 else
705 return itsWidth;
708 size_t Window::GetHeight() const
710 size_t height = itsHeight;
711 if (itsBorder != brNone)
712 height += 2;
713 if (!itsTitle.empty())
714 height += 2;
715 return height;
718 size_t Window::GetStartX() const
720 if (itsBorder != brNone)
721 return itsStartX-1;
722 else
723 return itsStartX;
726 size_t Window::GetStartY() const
728 size_t starty = itsStartY;
729 if (itsBorder != brNone)
730 starty--;
731 if (!itsTitle.empty())
732 starty -= 2;
733 return starty;
736 const std::string &Window::GetTitle() const
738 return itsTitle;
741 Color Window::GetColor() const
743 return itsColor;
746 Border Window::GetBorder() const
748 return itsBorder;
751 void Window::Scroll(Where where)
753 idlok(itsWindow, 1);
754 scrollok(itsWindow, 1);
755 switch (where)
757 case wUp:
758 wscrl(itsWindow, 1);
759 break;
760 case wDown:
761 wscrl(itsWindow, -1);
762 break;
763 case wPageUp:
764 wscrl(itsWindow, itsWidth);
765 break;
766 case wPageDown:
767 wscrl(itsWindow, -itsWidth);
768 break;
769 default:
770 break;
772 idlok(itsWindow, 0);
773 scrollok(itsWindow, 0);
777 Window &Window::operator<<(Colors colors)
779 if (colors.fg == clEnd || colors.bg == clEnd)
781 *this << clEnd;
782 return *this;
784 itsColors.push(colors);
785 SetColor(colors.fg, colors.bg);
786 return *this;
789 Window &Window::operator<<(Color color)
791 switch (color)
793 case clDefault:
794 while (!itsColors.empty())
795 itsColors.pop();
796 SetColor(itsBaseColor, itsBaseBgColor);
797 break;
798 case clEnd:
799 if (!itsColors.empty())
800 itsColors.pop();
801 if (!itsColors.empty())
802 SetColor(itsColors.top().fg, itsColors.top().bg);
803 else
804 SetColor(itsBaseColor, itsBaseBgColor);
805 break;
806 default:
807 itsColors.push(Colors(color, clDefault));
808 SetColor(itsColors.top().fg, itsColors.top().bg);
810 return *this;
813 Window &Window::operator<<(Format format)
815 switch (format)
817 case fmtNone:
818 Bold((itsBoldCounter = 0));
819 Reverse((itsReverseCounter = 0));
820 AltCharset((itsAltCharsetCounter = 0));
821 break;
822 case fmtBold:
823 Bold(++itsBoldCounter);
824 break;
825 case fmtBoldEnd:
826 if (--itsBoldCounter <= 0)
827 Bold((itsBoldCounter = 0));
828 break;
829 case fmtUnderline:
830 Underline(++itsUnderlineCounter);
831 break;
832 case fmtUnderlineEnd:
833 if (--itsUnderlineCounter <= 0)
834 Underline((itsUnderlineCounter = 0));
835 break;
836 case fmtReverse:
837 Reverse(++itsReverseCounter);
838 break;
839 case fmtReverseEnd:
840 if (--itsReverseCounter <= 0)
841 Reverse((itsReverseCounter = 0));
842 break;
843 case fmtAltCharset:
844 AltCharset(++itsAltCharsetCounter);
845 break;
846 case fmtAltCharsetEnd:
847 if (--itsAltCharsetCounter <= 0)
848 AltCharset((itsAltCharsetCounter = 0));
849 break;
851 return *this;
854 Window &Window::operator<<(int (*f)(WINDOW *))
856 f(itsWindow);
857 return *this;
860 Window &Window::operator<<(XY coords)
862 GotoXY(coords.x, coords.y);
863 return *this;
866 Window &Window::operator<<(const char *s)
868 wprintw(itsWindow, "%s", s);
869 return *this;
872 Window &Window::operator<<(char c)
874 wprintw(itsWindow, "%c", c);
875 return *this;
878 Window &Window::operator<<(const wchar_t *ws)
880 wprintw(itsWindow, "%ls", ws);
881 return *this;
884 Window &Window::operator<<(wchar_t wc)
886 wprintw(itsWindow, "%lc", wc);
887 return *this;
890 Window &Window::operator<<(int i)
892 wprintw(itsWindow, "%d", i);
893 return *this;
896 Window &Window::operator<<(double d)
898 wprintw(itsWindow, "%f", d);
899 return *this;
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);
906 return *this;
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);
913 return *this;
916 Window &Window::operator<<(size_t s)
918 wprintw(itsWindow, "%zu", s);
919 return *this;
922 std::string ToString(const std::wstring &ws)
924 std::string result;
925 char s[MB_CUR_MAX];
926 for (size_t i = 0; i < ws.length(); ++i)
928 int n = wcrtomb(s, ws[i], 0);
929 if (n > 0)
930 result.append(s, n);
932 return result;
935 std::wstring ToWString(const std::string &s)
937 std::wstring result;
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);
941 if (n > 0)
942 result.append(ws, n);
943 delete [] ws;
944 return result;
947 size_t Window::Length(const std::wstring &ws)
949 size_t length = 0;
950 for (std::wstring::const_iterator it = ws.begin(); it != ws.end(); ++it)
951 length += wcwidth(*it);
952 return length;