diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 |
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 |
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /curses/cursesfield.C | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2 | |
Initial checkin
Imported from subversion report, converted to git. Updated all paths in
scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'curses/cursesfield.C')
| -rw-r--r-- | curses/cursesfield.C | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/curses/cursesfield.C b/curses/cursesfield.C new file mode 100644 index 0000000..498ab67 --- /dev/null +++ b/curses/cursesfield.C @@ -0,0 +1,689 @@ +/* +** Copyright 2002-2011, Double Precision Inc. +** +** See COPYING for distribution information. +*/ + +#include "curses_config.h" +#include "cursesfield.H" +#include "curseskeyhandler.H" +#include "cursesmoronize.H" +#include "widechar.H" +#include <algorithm> +#include <iostream> + +std::vector<unicode_char> CursesField::yesKeys, CursesField::noKeys; +unicode_char CursesField::yankKey='\x19'; +unicode_char CursesField::clrEolKey='\x0B'; + +std::list<CursesFlowedLine> CursesField::cutBuffer; // for cut/paste + +CursesField::CursesField(CursesContainer *parent, + size_t widthArg, + size_t maxlengthArg, + std::string initValue) + : Curses(parent), width(widthArg), maxlength(maxlengthArg), + shiftoffset(0), + selectpos(0), passwordChar(0), yesnoField(false), + isUnderlined(true) +{ + if (yesKeys.size() == 0) + mail::iconvert::convert(std::string("yY"), + unicode_default_chset(), + yesKeys); + + if (noKeys.size() == 0) + mail::iconvert::convert(std::string("nN"), + unicode_default_chset(), + yesKeys); + + std::vector<unicode_char> utext; + + mail::iconvert::convert(initValue, unicode_default_chset(), utext); + + text.set_contents(utext, std::vector<unicode_char>()); + + selectpos=text.before_insert.graphemes.size(); +} + +void CursesField::setAttribute(Curses::CursesAttr attr) +{ + attribute=attr; + draw(); +} + +CursesField::~CursesField() +{ + if (optionHelp.size() > 0) + CursesKeyHandler::handlerListModified=true; +} + +void CursesField::setOptionHelp(const std::vector< std::pair<std::string, + std::string> > + &help) +{ + CursesKeyHandler::handlerListModified=true; + optionHelp=help; +} + +void CursesField::setRow(int row) +{ + erase(); + Curses::setRow(row); + draw(); +} + +void CursesField::setCol(int col) +{ + erase(); + Curses::setCol(col); + draw(); +} + +void CursesField::setText(std::string textArg) +{ + std::vector<unicode_char> utext; + + mail::iconvert::convert(textArg, unicode_default_chset(), utext); + + text.set_contents(utext, std::vector<unicode_char>()); + + selectpos=text.before_insert.graphemes.size(); + shiftoffset=0; + draw(); +} + +std::string CursesField::getText() const +{ + std::vector<unicode_char> b, a; + + text.get_contents(b, a); + + b.insert(b.end(), a.begin(), a.end()); + + return mail::iconvert::convert(b, unicode_default_chset()); +} + +int CursesField::getWidth() const +{ + return width; +} + +void CursesField::setWidth(int w) +{ + if (w > 0) + width=w; + draw(); +} + +int CursesField::getHeight() const +{ + return 1; +} + +void CursesField::getbeforeafter(widecharbuf &wbefore, widecharbuf &wafter) +{ + std::vector<unicode_char> before, after; + text.get_contents(before, after); + + wbefore.init_unicode(before.begin(), before.end()); + wafter.init_unicode(after.begin(), after.end()); +} + +void CursesField::draw() +{ + widecharbuf wbefore, wafter; + + getbeforeafter(wbefore, wafter); + + wafter.expandtabs(wbefore.expandtabs(0)); + + size_t a=wbefore.graphemes.size(); + size_t b=selectpos; + + if (a > b) + { + size_t c=a; a=b; b=c; + } + + if (a < shiftoffset) + a=shiftoffset; + + if (b < shiftoffset) + b=shiftoffset; + + a -= shiftoffset; + b -= shiftoffset; + + wbefore += wafter; + + std::vector<unicode_char> ubeforesel, usel, uaftersel; + size_t usel_pos=0, uaftersel_pos=0; + + size_t p=shiftoffset; + size_t col=0; + + size_t virtual_col=0; + + for (size_t i=0; i<p && i < wbefore.graphemes.size(); ++i) + virtual_col += wbefore.graphemes[i].wcwidth(virtual_col); + + std::vector<unicode_char> unicodes; + + while (p < wbefore.graphemes.size() && col < width) + { + const widecharbuf::grapheme_t &grapheme=wbefore.graphemes[p]; + + size_t grapheme_width=grapheme.wcwidth(virtual_col); + + if (grapheme_width + col > width) + break; + + std::vector<unicode_char> *vp; + + if (a) + { + vp= &ubeforesel; + --a; + --b; + usel_pos += grapheme_width; + uaftersel_pos += grapheme_width; + } + else if (b) + { + vp= &usel; + --b; + uaftersel_pos += grapheme_width; + } + else + vp= &uaftersel; + + unicodes.clear(); + + if (passwordChar) + vp->insert(vp->end(), grapheme_width, passwordChar); + else + vp->insert(vp->end(), grapheme.uptr, + grapheme.uptr+grapheme.cnt); + + col += grapheme_width; + virtual_col += grapheme_width; + ++p; + } + + uaftersel.insert(uaftersel.end(), width-col, ' '); + + bool underlined=isUnderlined; + + { + Curses::CursesAttr aa=attribute; + + if (ubeforesel.size()) + writeText(ubeforesel, 0, 0, + aa.setUnderline(underlined)); + } + + { + Curses::CursesAttr aa=attribute; + + if (usel.size()) + writeText(usel, 0, usel_pos, + aa.setReverse().setUnderline(underlined)); + } + + { + Curses::CursesAttr aa=attribute; + + if (uaftersel.size()) + writeText(uaftersel, 0, uaftersel_pos, + aa.setUnderline(underlined)); + } +} + +void CursesField::erase() +{ + std::vector<unicode_char> v; + + v.insert(v.end(), width, ' '); + + writeText(v, 0, 0, CursesAttr()); +} + +bool CursesField::isFocusable() +{ + return true; +} + +void CursesField::focusGained() +{ + text.to_before(); + + selectpos=0; + shiftoffset=0; + draw(); +} + +void CursesField::focusLost() +{ + text.to_after(); + selectpos=shiftoffset=0; + draw(); +} + +int CursesField::getCursorPosition(int &row, int &col) +{ + // Automatically scroll field to keep the cursor visible + + row=0; + + size_t save_shiftoffset=shiftoffset; + + widecharbuf wbefore, wafter; + + col=text.adjust_shift_pos(shiftoffset, width, wbefore, wafter); + + if (save_shiftoffset != shiftoffset) + draw(); + + Curses::getCursorPosition(row, col); + + return selectpos == wbefore.graphemes.size(); +} + +// Set selection position to current position + +void CursesField::setselectpos() +{ + text.insert_to_before(); + + widecharbuf wbefore, wafter; + + getbeforeafter(wbefore, wafter); + + selectpos=wbefore.graphemes.size(); +} + +bool CursesField::processKeyInFocus(const Key &key) +{ + if (key == key.HOME) + { + text.to_after(); + shiftoffset=0; + selectpos=0; + draw(); + return true; + } + + if (key == key.SHIFTHOME) + { + text.to_after(); + shiftoffset=0; + draw(); + return true; + } + + if (key == key.END) + { + text.to_before(); + setselectpos(); + draw(); + return true; + } + + if (key == key.SHIFTEND) + { + setselectpos(); + text.to_before(); + draw(); + return true; + } + + if (key == key.LEFT) + { + left(); + draw(); + return true; + } + + if (key == key.SHIFTLEFT) + { + size_t savepos=selectpos; + left(); + selectpos=savepos; + draw(); + return true; + } + + if (key == key.RIGHT) + { + right(); + draw(); + return true; + } + + if (key == key.SHIFTRIGHT) + { + size_t savepos=selectpos; + right(); + + selectpos=savepos; + draw(); + return true; + } + + if ((key.plain() && key.ukey == '\t') || + key == key.DOWN || key == key.UP || + key == key.SHIFTDOWN || key == key.SHIFTUP || key == key.ENTER || + key == key.PGDN || key == key.SHIFTPGDN || + key == key.PGUP || key == key.SHIFTPGUP) + { + setselectpos(); + draw(); + return Curses::processKeyInFocus(key); + } + + std::vector<unicode_char> cut_text; + + if (text.inserted.empty() && key != key.SHIFT) + text.contents_cut(selectpos, cut_text); + + if (!cut_text.empty()) + { + if (!(key.plain() && key.ukey == yankKey)) + { + cutBuffer.clear(); + cutBuffer.push_back(CursesFlowedLine(mail::iconvert + ::convert(cut_text, + "utf-8"), + false)); + } + + selectpos=text.before_insert.graphemes.size(); + draw(); + + if (key == key.BACKSPACE || key == key.DEL) + return true; + } + + if (key == key.BACKSPACE) + { + text.insert_to_before(); + + if (text.before_insert.graphemes.size() > 0) + { + text.contents_cut(text.before_insert + .graphemes.size()-1, cut_text); + setselectpos(); + } + draw(); + return true; + } + + if (key == key.DEL) + { + text.insert_to_before(); + + if (text.after_insert.graphemes.size() > 0) + { + text.contents_cut(text.before_insert + .graphemes.size()+1, cut_text); + setselectpos(); + } + draw(); + return true; + } + + if (!key.plain()) + { + return Curses::processKeyInFocus(key); + } + + unicode_char k=key.ukey; + + if (k == yankKey && !yesnoField && + optionField.size() == 0 && !cutBuffer.empty()) + ; // Will paste later + else if (k < ' ') + { + return Curses::processKeyInFocus(key); + } + + bool doYesNoField=true; + + if (optionField.size() > 0) + { + text.inserted.push_back(k); + + std::vector<unicode_char> before, after; + + text.get_contents(before, after); + + before.insert(before.end(), after.begin(), after.end()); + + if (before.empty()) + return true; + + text.inserted.pop_back(); + + std::vector<unicode_char>::iterator p= + find(optionField.begin(), + optionField.end(), *before.begin()); + + + if (p == optionField.end()) + { + if (!yesnoField) + return true; + } + else doYesNoField=false; + + } + + if (doYesNoField && yesnoField) + { + text.inserted.push_back(k); + + std::vector<unicode_char> before, after; + + text.get_contents(before, after); + + before.insert(before.end(), after.begin(), after.end()); + + if (before.empty()) + return true; + + std::vector<unicode_char>::iterator p= + find(yesKeys.begin(), + yesKeys.end(), *before.begin()); + + if (p != yesKeys.end()) + { + before.clear(); + after.clear(); + before.push_back('Y'); + text.set_contents(before, after); + this->processKeyInFocus(Key(Key::ENTER)); + // Automatic enter + return true; + } + + p=find(noKeys.begin(), noKeys.end(), *before.begin()); + + if (p != noKeys.end()) + { + before.clear(); + after.clear(); + before.push_back('N'); + text.set_contents(before, after); + this->processKeyInFocus(Key(Key::ENTER)); + // Automatic enter + return true; + } + + text.inserted.pop_back(); + } + + if (k == yankKey) + { + std::vector<unicode_char> ucut; + + if (!cutBuffer.empty()) + mail::iconvert::convert(cutBuffer.front().text, + "utf-8", + ucut); + + text.insert_to_before(); + + std::vector<unicode_char> before, after; + text.get_contents(before, after); + + before.insert(before.end(), ucut.begin(), ucut.end()); + + widecharbuf wbefore, wafter; + + wbefore.init_unicode(before.begin(), before.end()); + wafter.init_unicode(after.begin(), after.end()); + + size_t wbefore_w=wbefore.wcwidth(0); + size_t wafter_w=wafter.wcwidth(wbefore_w); + + if (wbefore_w + wafter_w > maxlength) + beepError(); + else + text.set_contents(before, after); + } + else + { + text.inserted.push_back(k); + + widecharbuf wbefore, wafter; + + getbeforeafter(wbefore, wafter); + + size_t wbefore_w=wbefore.wcwidth(0); + size_t wafter_w=wafter.wcwidth(wbefore_w); + + if (wbefore_w + wafter_w > maxlength) + { + beepError(); + text.inserted.pop_back(); + } + } + + { + widecharbuf wbefore, wafter; + + getbeforeafter(wbefore, wafter); + + selectpos=wbefore.graphemes.size(); + } + + draw(); + + if (yesnoField || optionField.size() > 0) + this->processKeyInFocus(Key(Key::ENTER)); // Automatic enter + else if (CursesMoronize::enabled && !passwordChar && + !text.inserted.empty()) + { + size_t i; + unicode_char m_buf[CursesMoronize::max_keycode_len+1]; + std::vector<unicode_char>::iterator b(text.inserted.begin()), + e(text.inserted.end()), p=e; + + for (i=0; i<sizeof(m_buf)/sizeof(m_buf[0])-1; ++i) + { + if (b == p) + break; + + --p; + + m_buf[i]= *p; + } + + m_buf[i]=0; + + if (m_buf[0] && CursesMoronize::enabled) + { + std::vector<unicode_char> repl_c; + + i=CursesMoronize::moronize(m_buf, repl_c); + + if (i > 0) + { + while (i > 0) + { + processKeyInFocus(Curses::Key + (Curses::Key::BACKSPACE)); + --i; + } + + std::string s(mail::iconvert::convert + (repl_c, unicode_default_chset())); + + std::vector<wchar_t> wc; + + towidechar(s.begin(), s.end(), wc); + + for (std::vector<wchar_t>::iterator + b(wc.begin()), e(wc.end()); b != e; + ++b) + { + processKeyInFocus(Curses::Key(*b)); + } + } + } + } + + return true; +} + +void CursesField::left() +{ + text.insert_to_before(); + + if (text.before_insert.graphemes.size() > 0) + { + std::vector<unicode_char> cut_text; + + text.contents_cut(text.before_insert.graphemes.size()-1, + cut_text); + + std::vector<unicode_char> before, after; + text.get_contents(before, after); + + after.insert(after.begin(), cut_text.begin(), cut_text.end()); + + text.set_contents(before, after); + } + setselectpos(); + draw(); +} + +void CursesField::right() +{ + text.insert_to_before(); + + if (text.after_insert.graphemes.size() > 0) + { + std::vector<unicode_char> cut_text; + + text.contents_cut(text.before_insert.graphemes.size()+1, + cut_text); + + std::vector<unicode_char> before, after; + text.get_contents(before, after); + + before.insert(before.end(), cut_text.begin(), cut_text.end()); + + text.set_contents(before, after); + } + setselectpos(); + draw(); +} + +void CursesField::setCursorPos(size_t o) +{ + text.to_position(o); + setselectpos(); + draw(); +} |
