// TextBox.cc for FbTk - fluxbox toolkit // Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net) // // 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. // $Id$ #include "TextBox.hh" #include "Font.hh" #include "EventManager.hh" #include "App.hh" #include "KeyUtil.hh" #ifdef HAVE_CCTYPE #include #else #include #endif #include #include namespace FbTk { TextBox::TextBox(int screen_num, const Font &font, const std::string &text): FbWindow(screen_num, 0, 0, 1, 1, ExposureMask | KeyPressMask | ButtonPressMask), m_font(&font), m_text(text), m_gc(0), m_cursor_pos(0), m_start_pos(0), m_end_pos(0) { FbTk::EventManager::instance()->add(*this, *this); } TextBox::TextBox(const FbWindow &parent, const Font &font, const std::string &text): FbWindow(parent, 0, 0, 1, 1, ExposureMask | KeyPressMask | ButtonPressMask), m_font(&font), m_text(text), m_gc(0), m_cursor_pos(0), m_start_pos(0), m_end_pos(0) { FbTk::EventManager::instance()->add(*this, *this); } TextBox::~TextBox() { } void TextBox::setText(const std::string &text) { m_text = text; m_start_pos = 0; cursorEnd(); adjustStartPos(); } void TextBox::setFont(const Font &font) { m_font = &font; } void TextBox::setGC(GC gc) { m_gc = gc; } void TextBox::setInputFocus() { XSetInputFocus(FbTk::App::instance()->display(), window(), RevertToParent, CurrentTime); } void TextBox::cursorHome() { m_start_pos = m_cursor_pos = 0; adjustEndPos(); } void TextBox::cursorEnd() { m_end_pos = text().size(); adjustStartPos(); m_cursor_pos = m_end_pos - m_start_pos; } void TextBox::cursorForward() { if (m_start_pos + cursorPosition() < m_end_pos) m_cursor_pos++; else if (m_end_pos < text().size()) { m_cursor_pos++; m_end_pos++; adjustStartPos(); } } void TextBox::cursorBackward() { if (cursorPosition()) m_cursor_pos--; else if (m_start_pos) { m_start_pos--; adjustEndPos(); } } void TextBox::backspace() { if (m_start_pos || cursorPosition()) { m_text.erase(m_start_pos + cursorPosition() - 1, 1); if (cursorPosition()) setCursorPosition(cursorPosition() - 1); else m_start_pos--; adjustEndPos(); } } void TextBox::deleteForward() { if (m_start_pos + m_cursor_pos < m_end_pos) { m_text.erase(m_start_pos + m_cursor_pos, 1); adjustEndPos(); } } void TextBox::insertText(const std::string &val) { m_text.insert(m_start_pos + cursorPosition(), val); m_cursor_pos += val.size(); m_end_pos += val.size(); if (m_start_pos + cursorPosition() < m_end_pos) adjustEndPos(); else adjustStartPos(); } void TextBox::killToEnd() { if (cursorPosition() >= 0 && cursorPosition() < static_cast(text().size())) { m_text.erase(cursorPosition()); setText(m_text); } } void TextBox::clear() { FbWindow::clear(); // center text by default int center_pos = (height() + font().ascent())/2; if (gc() == 0) setGC(DefaultGC(FbTk::App::instance()->display(), screenNumber())); font().drawText(*this, screenNumber(), gc(), text().c_str() + m_start_pos, m_end_pos - m_start_pos, 0, center_pos); // pos // draw cursor position int cursor_pos = font().textWidth(text().c_str() + m_start_pos, m_cursor_pos) + 1; drawLine(gc(), cursor_pos, center_pos, cursor_pos, center_pos - font().height()); } void TextBox::moveResize(int x, int y, unsigned int width, unsigned int height) { FbWindow::moveResize(x, y, width, height); clear(); } void TextBox::resize(unsigned int width, unsigned int height) { FbWindow::resize(width, height); clear(); } void TextBox::exposeEvent(XExposeEvent &event) { clear(); } void TextBox::buttonPressEvent(XButtonEvent &event) { setInputFocus(); if (event.window == window()) { std::string::size_type i; std::string::size_type click_pos = m_end_pos; int delta = width(); int tmp = 0; for(i = m_start_pos; i <= m_end_pos; i++) { tmp = abs(event.x - font().textWidth(m_text.c_str() + m_start_pos, i - m_start_pos)); if (tmp < delta) { delta = tmp; click_pos = i; } } m_cursor_pos = click_pos - m_start_pos; clear(); } } void TextBox::keyPressEvent(XKeyEvent &event) { // strip numlock and scrolllock mask event.state = KeyUtil::instance().cleanMods(event.state); KeySym ks; char keychar[1]; XLookupString(&event, keychar, 1, &ks, 0); // a modifier key by itself doesn't do anything if (IsModifierKey(ks)) return; if (event.state) { // handle keybindings with state if (event.state == ControlMask) { switch (ks) { case XK_b: cursorBackward(); break; case XK_f: cursorForward(); break; case XK_a: cursorHome(); break; case XK_e: cursorEnd(); break; case XK_d: deleteForward(); break; case XK_k: killToEnd(); break; case XK_c: cursorHome(); m_text = ""; m_start_pos = 0; m_cursor_pos = 0; m_end_pos = 0; break; } } else if (event.state == ShiftMask || event.state == 0x80) { // shif and altgr if (isprint(keychar[0])) { std::string val; val += keychar[0]; insertText(val); } } } else { // no state switch (ks) { case XK_BackSpace: backspace(); break; case XK_Home: cursorHome(); break; case XK_End: cursorEnd(); break; case XK_Left: cursorBackward(); break; case XK_Right: cursorForward(); break; case XK_Delete: deleteForward(); break; default: switch (ks) { case XK_KP_Insert: keychar[0] = '0'; break; case XK_KP_End: keychar[0] = '1'; break; case XK_KP_Down: keychar[0] = '2'; break; case XK_KP_Page_Down: keychar[0] = '3'; break; case XK_KP_Left: keychar[0] = '4'; break; case XK_KP_Begin: keychar[0] = '5'; break; case XK_KP_Right: keychar[0] = '6'; break; case XK_KP_Home: keychar[0] = '7'; break; case XK_KP_Up: keychar[0] = '8'; break; case XK_KP_Page_Up: keychar[0] = '9'; break; case XK_KP_Delete: keychar[0] = ','; break; }; if (isprint(keychar[0])) { std::string val; val += keychar[0]; insertText(val); } } } clear(); } void TextBox::setCursorPosition(int pos) { m_cursor_pos = pos < 0 ? 0 : pos; if (m_cursor_pos > text().size()) cursorEnd(); } void TextBox::adjustEndPos() { m_end_pos = text().size(); int text_width = font().textWidth(text().c_str() + m_start_pos, m_end_pos - m_start_pos); while (text_width > static_cast(width())) { m_end_pos--; text_width = font().textWidth(text().c_str() + m_start_pos, m_end_pos - m_start_pos); } } void TextBox::adjustStartPos() { int text_width = font().textWidth(text().c_str(), m_end_pos); if (text_width < static_cast(width())) return; int start_pos = 0; while (text_width > static_cast(width())) { start_pos++; text_width = font().textWidth(text().c_str() + start_pos, m_end_pos - start_pos); } // adjust cursorPosition() according relative to change to m_start_pos m_cursor_pos -= start_pos - m_start_pos; m_start_pos = start_pos; } }; // end namespace FbTk