380 lines
9.3 KiB
C++
380 lines
9.3 KiB
C++
|
|
||
|
|
||
|
#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; // 15
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
|
||
|
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(); //m_scrollbar->setValue(m_yoffset,0, yrange-ly, ly);
|
||
|
} else {
|
||
|
m_yoffset = 0;
|
||
|
m_scrollbar->hide(); //m_scrollbar->setValue(0,0, 0, 0);
|
||
|
}
|
||
|
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)
|
||
|
{
|
||
|
/* TDimension d = getSize();
|
||
|
int i = getItemCount()-1;
|
||
|
|
||
|
10 +
|
||
|
for(int y=10; y<d.ly && i>=0; y+=rowHeight, --i)
|
||
|
*/
|
||
|
|
||
|
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()) {
|
||
|
// if (!e.isShiftPressed())
|
||
|
unselectAll();
|
||
|
select(newSelected, !isSelected(newSelected));
|
||
|
}
|
||
|
} else
|
||
|
TWidget::keyDown(key, mod, pos);
|
||
|
}
|