| |
| |
| #include "textlist.h" |
| #include "tw/colors.h" |
| #include "tw/event.h" |
| #include "tw/scrollbar.h" |
| #include "tw/keycodes.h" |
| |
| #include <vector> |
| #include <map> |
| #include <string> |
| |
| using namespace std; |
| using namespace TwConsts; |
| |
| namespace { |
| const int rowHeight = 20; |
| }; |
| |
| |
| |
| class TTextList::Data { |
| public: |
| Data(TWidget *w) |
| : m_w(w) |
| , m_selAction(0) |
| , m_dblClickAction(0) |
| , m_scrollbar(new TScrollbar(w)) |
| , m_yoffset(0) {} |
| |
| ~Data() { |
| if (m_selAction) delete m_selAction; |
| |
| if (m_dblClickAction) delete m_dblClickAction; |
| |
| std::map<string, TTextListItem *>::iterator it = m_items.begin(); |
| for (; it != m_items.end(); ++it) delete it->second; |
| } |
| |
| int posToItem(const TPoint &p); |
| void updateScrollBarStatus() { |
| if (!m_scrollbar) return; |
| unsigned int ly = m_w->getLy(); |
| |
| if ((m_items.size() * rowHeight) > ly) { |
| m_scrollbar->setValue(m_yoffset, 0, (m_items.size() + 1) * rowHeight - ly, |
| ly); |
| m_scrollbar->show(); |
| |
| } else { |
| m_yoffset = 0; |
| m_scrollbar->hide(); |
| } |
| m_scrollbar->invalidate(); |
| } |
| |
| TWidget *m_w; |
| std::map<string, TTextListItem *> m_items; |
| vector<string> m_selectedItems; |
| |
| TGenericTextListAction *m_selAction; |
| TGenericTextListAction *m_dblClickAction; |
| TScrollbar *m_scrollbar; |
| int m_yoffset; |
| }; |
| |
| |
| |
| int TTextList::Data::posToItem(const TPoint &p) { |
| |
| |
| |
| |
| |
| |
| |
| int y = m_w->getSize().ly - p.y + m_yoffset; |
| int item = y / rowHeight; |
| return item; |
| } |
| |
| |
| |
| TTextListItem::TTextListItem(const string &id, const string &caption) |
| : m_id(id), m_caption(caption) {} |
| |
| |
| |
| TTextList::TTextList(TWidget *parent, string name) |
| : TWidget(parent, name), m_data(0) { |
| m_data = new Data(this); |
| m_data->m_scrollbar->setAction( |
| new TScrollbarAction<TTextList>(this, &TTextList::scrollTo)); |
| } |
| |
| |
| |
| TTextList::~TTextList() { delete m_data; } |
| |
| |
| |
| void TTextList::addItem(TTextListItem *item) { |
| std::map<string, TTextListItem *>::iterator it = |
| m_data->m_items.find(item->getId()); |
| |
| if (it == m_data->m_items.end()) { |
| m_data->m_items.insert(make_pair(item->getId(), item)); |
| m_data->updateScrollBarStatus(); |
| } |
| } |
| |
| |
| |
| void TTextList::removeItem(const string &itemId) { |
| std::map<string, TTextListItem *>::iterator it = m_data->m_items.find(itemId); |
| if (it != m_data->m_items.end()) { |
| m_data->m_items.erase(it); |
| m_data->updateScrollBarStatus(); |
| } |
| } |
| |
| |
| |
| void TTextList::clearAll() { |
| m_data->m_items.clear(); |
| m_data->m_selectedItems.clear(); |
| invalidate(); |
| m_data->updateScrollBarStatus(); |
| } |
| |
| |
| |
| int TTextList::getItemCount() const { return m_data->m_items.size(); } |
| |
| |
| |
| TTextListItem *TTextList::getItem(int i) const { |
| if (i >= 0 && i < (int)m_data->m_items.size()) { |
| std::map<string, TTextListItem *>::iterator it = m_data->m_items.begin(); |
| advance(it, i); |
| return it->second; |
| } |
| return 0; |
| } |
| |
| |
| |
| int TTextList::itemToIndex(const string &itemId) { |
| std::map<string, TTextListItem *>::iterator it = m_data->m_items.find(itemId); |
| |
| if (it == m_data->m_items.end()) |
| return -1; |
| else |
| return distance(m_data->m_items.begin(), it); |
| } |
| |
| |
| |
| int TTextList::getSelectedItemCount() const { |
| return m_data->m_selectedItems.size(); |
| } |
| |
| |
| |
| TTextListItem *TTextList::getSelectedItem(int i) const { |
| if (i < 0 || (int)m_data->m_selectedItems.size() <= i) return 0; |
| |
| string itemId = m_data->m_selectedItems[i]; |
| std::map<string, TTextListItem *>::iterator it = m_data->m_items.find(itemId); |
| if (it != m_data->m_items.end()) |
| return it->second; |
| else |
| return 0; |
| } |
| |
| |
| |
| string TTextList::getSelectedItemId(int i) const { |
| if ((int)m_data->m_selectedItems.size() <= i) return ""; |
| assert(i >= 0 && i < (int)m_data->m_selectedItems.size()); |
| return m_data->m_selectedItems[i]; |
| } |
| |
| |
| void TTextList::select(int i, bool on) { |
| assert(i >= 0 && i < (int)m_data->m_items.size()); |
| |
| std::map<string, TTextListItem *>::iterator it = m_data->m_items.begin(); |
| advance(it, i); |
| |
| string id = it->first; |
| vector<string>::iterator it2 = |
| find(m_data->m_selectedItems.begin(), m_data->m_selectedItems.end(), id); |
| |
| if (on) { |
| if (it2 == m_data->m_selectedItems.end()) |
| m_data->m_selectedItems.push_back(id); |
| } else { |
| if (it2 != m_data->m_selectedItems.end()) |
| m_data->m_selectedItems.erase(it2); |
| } |
| |
| if (m_data->m_selAction) m_data->m_selAction->sendCommand(i); |
| |
| invalidate(); |
| } |
| |
| |
| |
| void TTextList::select(const string &itemId, bool on) { |
| std::map<string, TTextListItem *>::iterator it = m_data->m_items.find(itemId); |
| |
| if (it != m_data->m_items.end()) { |
| int i = distance(m_data->m_items.begin(), it); |
| select(i, on); |
| } |
| } |
| |
| |
| |
| void TTextList::unselectAll() { |
| m_data->m_selectedItems.clear(); |
| invalidate(); |
| } |
| |
| |
| |
| bool TTextList::isSelected(int i) const { |
| assert(i >= 0 && i < (int)m_data->m_items.size()); |
| |
| std::map<string, TTextListItem *>::iterator it = m_data->m_items.begin(); |
| advance(it, i); |
| |
| string id = it->first; |
| |
| vector<string>::iterator it2 = |
| find(m_data->m_selectedItems.begin(), m_data->m_selectedItems.end(), id); |
| |
| return it2 != m_data->m_selectedItems.end(); |
| } |
| |
| |
| |
| bool TTextList::isSelected(const string &item) const { |
| assert(false); |
| return false; |
| } |
| |
| |
| |
| void TTextList::scrollTo(int y) { |
| y = (y / rowHeight) * rowHeight; |
| if (m_data->m_yoffset == y) return; |
| m_data->m_yoffset = y; |
| m_data->updateScrollBarStatus(); |
| invalidate(); |
| } |
| |
| |
| |
| void TTextList::draw() { |
| drawRect(TRect(TPoint(0, 0), getSize())); |
| TDimension d = getSize(); |
| |
| int y = d.ly - 1 - rowHeight; |
| for (int i = m_data->m_yoffset / rowHeight; i < getItemCount() && y >= 0; |
| ++i, y -= rowHeight) { |
| if (isSelected(i)) { |
| setColor(Blue, 2); |
| fillRect(2, y - 2, getSize().lx - 1 - 2, y + rowHeight - 1 - 2); |
| } |
| |
| setColor(Black); |
| drawText(TPoint(10, y), getItem(i)->getCaption()); |
| } |
| } |
| |
| |
| |
| void TTextList::setSelAction(TGenericTextListAction *action) { |
| if (m_data->m_selAction) delete m_data->m_selAction; |
| |
| m_data->m_selAction = action; |
| } |
| |
| |
| |
| void TTextList::setDblClickAction(TGenericTextListAction *action) { |
| if (m_data->m_dblClickAction) delete m_data->m_dblClickAction; |
| |
| m_data->m_dblClickAction = action; |
| } |
| |
| |
| |
| void TTextList::configureNotify(const TDimension &d) { |
| m_data->m_scrollbar->setGeometry(d.lx - 20, 1, d.lx - 2, d.ly - 2); |
| m_data->updateScrollBarStatus(); |
| } |
| |
| |
| |
| void TTextList::leftButtonDown(const TMouseEvent &e) { |
| int i = m_data->posToItem(e.m_pos); |
| if (i >= 0 && i < (int)m_data->m_items.size()) { |
| if (!e.isShiftPressed()) unselectAll(); |
| select(i, !isSelected(i)); |
| } |
| } |
| |
| |
| |
| void TTextList::leftButtonDoubleClick(const TMouseEvent &e) { |
| int i = m_data->posToItem(e.m_pos); |
| if (i >= 0 && i < (int)m_data->m_items.size()) { |
| if (m_data->m_dblClickAction) m_data->m_dblClickAction->sendCommand(i); |
| } |
| } |
| |
| |
| |
| void TTextList::keyDown(int key, unsigned long mod, const TPoint &pos) { |
| if ((key == TK_UpArrow) || (key == TK_DownArrow)) { |
| int lastSelected = -1; |
| for (int i = 0; i < getItemCount(); ++i) |
| if (isSelected(i)) lastSelected = i; |
| if (lastSelected == -1) return; |
| int newSelected = (key == TK_UpArrow) ? lastSelected - 1 : lastSelected + 1; |
| |
| if (newSelected >= 0 && newSelected < (int)m_data->m_items.size()) { |
| |
| unselectAll(); |
| select(newSelected, !isSelected(newSelected)); |
| } |
| } else |
| TWidget::keyDown(key, mod, pos); |
| } |
| |