// filename: Textbox.h // author: John Flores // date: May, 2003. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #include "Textbox.h" Textbox::Textbox(int h, int w, int r, int c, Component *p) { win = newwin(h, w, r, c); keypad(win, true); height = h; width = w; numwidth = NUMWIDTH; textwidth = width - numwidth; parent = p; shownumbers = true; startcol = 0; currentcol = 0; startcolbkup = 0; currentcolbkup = 0; prevkeypress = 0; currentrow = 0; startrow = 0; findidx = String::NPOS; findlen = 0; } Textbox::~Textbox() { delwin(win); } void Textbox::notify(Component *notifier) { } void Textbox::togglenumbers() { shownumbers = !shownumbers; if (shownumbers) { numwidth = NUMWIDTH; textwidth = width - NUMWIDTH; while (currentcol > textwidth - 1) { startcol++; startcolbkup++; currentcol--; currentcolbkup--; } } else { numwidth = 0; textwidth = width; } } void Textbox::flush() { wrefresh(win); } void Textbox::display() { int row = 0; werase(win); for (stritr line = startline; line != text.end() && row < height; line++) { if (shownumbers) mvwprintw(win, row, 0, "%*d ", numwidth - 1, startrow + row + 1); if (startcol < int(line->length())) mvwaddnstr(win, row, numwidth, line->c_str() + startcol, textwidth); row++; } } int Textbox::edit() { if (text.isempty()) { text.insert(text.begin(), ""); gotop(); } int keypress = 0; bool firstkeypress = true; bool done = false; display(); wrefresh(win); do { prevkeypress = keypress; keypress = mvwgetch(win, currentrow, currentcol + numwidth); if (firstkeypress) { firstkeypress = false; parent->notify(this); } switch (keypress) { case key::CTRL_K: cut(); break; case key::CTRL_L: copy(); break; case key::CTRL_P: paste(); break; case key::HOME: gohome(); startcolbkup = startcol; currentcolbkup = currentcol; break; case key::END: goend(); startcolbkup = startcol; currentcolbkup = currentcol; break; case key::LEFT: goleft(); break; case key::RIGHT: goright(); break; case key::UP: goup(); break; case key::DOWN: godown(); break; case key::ENTER: keyenter(); break; case key::CTRL_U: case key::PAGEUP: gopageup(); break; case key::CTRL_D: case key::PAGEDOWN: gopagedown(); break; case key::TAB: keytab(); break; case key::DELETE_ASCII: case key::BACKSPACE: keybackspace(); break; case key::DELETE: keydelete(); break; default: if (isprint(keypress)) printkey(keypress); else done = true; } display(); } while (!done); return keypress; } bool Textbox::find(const char *s) { bool found = false; findline = currentline; do { if ((findidx = findline->find(s, strlen(s))) != String::NPOS) { findlen = strlen(s); found = true; } else { findline++; if (findline == text.end()) findline = text.begin(); } } while (!found && findline != currentline); if (found) { while (currentline != findline) { if (!godown()) gotop(); } startcol = 0; currentcol = findidx; while (currentcol > int(textwidth - strlen(s))) { currentcol--; startcol++; } display(); wattron(win, A_REVERSE); mvwaddch(win, currentrow, currentcol + numwidth, s[0]); wattroff(win, A_REVERSE); wrefresh(win); } return found; } bool Textbox::replace(const char *txtreplace) { bool found = false; if (findidx != String::NPOS) { found = true; findline->remove(findidx, findlen); findline->insert(findidx, txtreplace); } return found; } void Textbox::gotoline(int line) { if (line - 1 < int(startrow + currentrow)) { int num = (startrow + currentrow) - (line - 1); while (goup() && --num); } else if (line - 1 > int(startrow + currentrow)) { int num = (line - 1) - (startrow + currentrow); while (godown() && --num); } if (parent != NULL) parent->notify(this); } void Textbox::copy() { if (prevkeypress != key::CTRL_L) { while (!clipboard.isempty()) clipboard.remove(clipboard.begin()); } if (!text.isempty()) { clipboard.insert(clipboard.end(), *currentline); godown(); } } void Textbox::cut() { if (prevkeypress != key::CTRL_K) { while (!clipboard.isempty()) clipboard.remove(clipboard.begin()); } if (!text.isempty()) { clipboard.insert(clipboard.end(), *currentline); currentline = text.remove(currentline); if (text.isempty()) { text.insert(text.begin(), ""); gotop(); } else { if (currentline == text.end()) goup(); if (currentline == text.begin()) startline = currentline; if (startcol + currentcol > int(currentline->length())) goend(); } } } void Textbox::paste() { for (stritr line = clipboard.rbegin(); line != clipboard.rend(); line--) text.rinsert(currentline, *line); } bool Textbox::save(const char *filename) { ofstream fout(filename); #ifdef __GNUC__ if (fout.is_open()) { #else if (fout != NULL) { #endif for (stritr itr = text.begin(); itr != text.end(); itr++) fout << itr->c_str() << endl; return true; } return false; } bool Textbox::load(const char *filename) { ifstream fin(filename); #ifdef __GNUC__ if (fin.is_open()) { #else if (fin != NULL) { #endif while (!text.isempty()) text.remove(text.begin()); stritr line = text.insert(text.end(), ""); int idx = 0; char ch; while (!fin.eof()) { ch = fin.get(); if (ch == '\n' && fin.peek() != EOF) { line = text.insert(text.end(), ""); idx = 0; } else if (ch == '\t') { int tablen = TABSIZE - (idx % TABSIZE); line->insert(line->length(), " ", tablen); idx += tablen; } else if (isprint(ch) && ch != '\r') { line->insert(line->length(), ch); idx++; } } gotop(); return true; } return false; } inline void Textbox::gotop() { startrow = 0; currentrow = 0; startcol = startcolbkup; currentcol = currentcolbkup; startline = text.begin(); currentline = text.begin(); if (startcol + currentcol > int(currentline->length())) goend(); } inline void Textbox::gopageup() { startcol = startcolbkup; currentcol = currentcolbkup; for (int i = 0; i < height - 1 && startline != text.begin(); i++) { startrow--; startline--; currentline--; } if (startcol + currentcol > int(currentline->length())) goend(); } inline void Textbox::gopagedown() { startcol = startcolbkup; currentcol = currentcolbkup; for (int i = 0; i < height - 1 && currentline != text.rbegin(); i++) { startrow++; startline++; currentline++; } if (startcol + currentcol > int(currentline->length())) goend(); } inline void Textbox::printkey(char key) { currentline->insert(startcol + currentcol, key); goright(); } inline void Textbox::keydelete() { if (currentcol + startcol < int(currentline->length())) { currentline->remove(startcol + currentcol, 1); } else if (currentline != text.rbegin()) { stritr line = currentline; line++; currentline->insert(currentline->length(), *line); text.remove(line); } } inline void Textbox::keybackspace() { if (startcol + currentcol > 0) { currentline->remove(startcol + currentcol - 1, 1); goleft(); } else if (currentline != text.begin()) { stritr line = currentline; goup(); goend(); currentline->insert(currentline->length(), *line); text.remove(line); } currentcolbkup = currentcol; startcolbkup = startcol; } inline void Textbox::keytab() { int tablen = TABSIZE - ((startcol + currentcol) % TABSIZE); currentline->insert(startcol + currentcol, " ", tablen); while (tablen--) goright(); } inline void Textbox::keyenter() { text.rinsert(currentline, currentline->substr(startcol + currentcol, currentline->length() - (startcol + currentcol))); currentline->remove(startcol + currentcol, currentline->length() - (startcol + currentcol)); godown(); gohome(); currentcolbkup = currentcol; startcolbkup = startcol; } inline bool Textbox::godown() { if (currentline != text.rbegin()) { currentline++; currentrow++; if (currentrow >= height - 1) { for (int i = 0; startline != text.rbegin() && i < 8; i++) { currentrow--; startrow++; startline++; } } currentcol = currentcolbkup; startcol = startcolbkup; if (startcol + currentcol > int(currentline->length())) goend(); return true; } return false; } bool Textbox::goup() { if (currentline != text.begin()) { currentline--; currentrow--; if (currentrow <= 0) { for (int i = 0; startline != text.begin() && i < 8; i++) { currentrow++; startrow--; startline--; } } currentcol = currentcolbkup; startcol = startcolbkup; if (startcol + currentcol > int(currentline->length())) goend(); return true; } return false; } inline void Textbox::goleft() { if (startcol + currentcol > 0) { currentcol--; if (currentcol == 0) { for (int i = 0; startcol > 0 && i < 8; i++) { currentcol++; startcol--; } } } else if (goup()) { goend(); } startcolbkup = startcol; currentcolbkup = currentcol; } inline void Textbox::goright() { if (startcol + currentcol < int(currentline->length())) { currentcol++; if (currentcol >= textwidth - 1) { for (int i = 0; startcol < int(currentline->length()) && i < 8; i++) { currentcol--; startcol++; } } } else if (godown()) { gohome(); } startcolbkup = startcol; currentcolbkup = currentcol; } inline void Textbox::goend() { startcol = 0; currentcol = currentline->length(); while (currentcol > textwidth - 1) { currentcol--; startcol++; } } inline void Textbox::gohome() { startcol = 0; currentcol = 0; }