You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmymoney/kmymoney2/widgets/kmymoneyselector.cpp

710 lines
21 KiB

/***************************************************************************
kmymoneyselector.cpp
-------------------
begin : Thu Jun 29 2006
copyright : (C) 2006 by Thomas Baumgart
email : Thomas Baumgart <ipwizard@users.sourceforge.net>
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
// ----------------------------------------------------------------------------
// QT Includes
#include <tqlayout.h>
#include <tqheader.h>
#include <tqtimer.h>
#include <tqstyle.h>
#include <tqregexp.h>
// ----------------------------------------------------------------------------
// KDE Includes
// ----------------------------------------------------------------------------
// Project Includes
#include <kmymoney/kmymoneyselector.h>
#include <kmymoney/kmymoneylistviewitem.h>
#include <kmymoney/kmymoneychecklistitem.h>
#include "../kmymoneyglobalsettings.h"
KMyMoneySelector::KMyMoneySelector(TQWidget *parent, const char *name, TQWidget::WFlags flags) :
TQWidget(parent, name, flags)
{
m_selMode = TQListView::Single;
m_listView = new KListView(this);
// don't show horizontal scroll bar
m_listView->setHScrollBarMode(TQScrollView::AlwaysOff);
m_listView->setSorting(-1);
if(parent) {
setFocusProxy(parent->focusProxy());
m_listView->setFocusProxy(parent->focusProxy());
}
m_listView->setAllColumnsShowFocus(true);
m_layout = new TQHBoxLayout( this, 0, 6);
m_listView->addColumn( "Hidden" );
m_listView->header()->hide();
m_listView->header()->setStretchEnabled(true, -1);
m_listView->header()->adjustHeaderSize();
m_layout->addWidget( m_listView );
// force init
m_selMode = TQListView::Multi;
setSelectionMode(TQListView::Single);
connect(m_listView, TQT_SIGNAL(rightButtonPressed(TQListViewItem* , const TQPoint&, int)), this, TQT_SLOT(slotListRightMouse(TQListViewItem*, const TQPoint&, int)));
}
KMyMoneySelector::~KMyMoneySelector()
{
}
void KMyMoneySelector::clear(void)
{
m_listView->clear();
m_visibleItem = 0;
}
void KMyMoneySelector::setSelectionMode(const TQListView::SelectionMode mode)
{
if(m_selMode != mode) {
m_selMode = mode;
clear();
// make sure, it's either Multi or Single
if(mode != TQListView::Multi) {
m_selMode = TQListView::Single;
connect(m_listView, TQT_SIGNAL(selectionChanged(void)), this, TQT_SIGNAL(stateChanged(void)));
connect(m_listView, TQT_SIGNAL(executed(TQListViewItem*)), this, TQT_SLOT(slotItemSelected(TQListViewItem*)));
} else {
disconnect(m_listView, TQT_SIGNAL(selectionChanged(void)), this, TQT_SIGNAL(stateChanged(void)));
disconnect(m_listView, TQT_SIGNAL(executed(TQListViewItem*)), this, TQT_SLOT(slotItemSelected(TQListViewItem*)));
}
}
TQWidget::update();
}
void KMyMoneySelector::slotItemSelected(TQListViewItem *item)
{
if(m_selMode == TQListView::Single) {
KMyMoneyListViewItem* l_item = dynamic_cast<KMyMoneyListViewItem*>(item);
if(l_item && l_item->isSelectable()) {
emit itemSelected(l_item->id());
}
}
}
TQListViewItem* KMyMoneySelector::newItem(const TQString& name, TQListViewItem* after, const TQString& key, const TQString& id, TQCheckListItem::Type type)
{
TQListViewItem* item;
if(after)
item = new KMyMoneyCheckListItem(m_listView, after, name, key, id, type);
else
item = new KMyMoneyCheckListItem(m_listView, name, key, id, type);
item->setSelectable(!id.isEmpty());
item->setOpen(true);
return item;
}
TQListViewItem* KMyMoneySelector::newItem(const TQString& name, const TQString& key, const TQString& id, TQCheckListItem::Type type)
{
return newItem(name, 0, key, id, type);
}
TQListViewItem* KMyMoneySelector::newTopItem(const TQString& name, const TQString& key, const TQString& id)
{
TQListViewItem* p;
if(m_selMode == TQListView::Multi) {
KMyMoneyCheckListItem* q = new KMyMoneyCheckListItem(m_listView, name, key, id);
connect(q, TQT_SIGNAL(stateChanged(bool)), this, TQT_SIGNAL(stateChanged(void)));
p = static_cast<TQListViewItem*> (q);
} else {
KMyMoneyListViewItem* q = new KMyMoneyListViewItem(m_listView, name, key, id);
p = static_cast<TQListViewItem*> (q);
}
return p;
}
TQListViewItem* KMyMoneySelector::newItem(TQListViewItem* parent, const TQString& name, const TQString& key, const TQString& id)
{
TQListViewItem* p;
if(m_selMode == TQListView::Multi) {
KMyMoneyCheckListItem* q = new KMyMoneyCheckListItem(parent, name, key, id);
connect(q, TQT_SIGNAL(stateChanged(bool)), this, TQT_SIGNAL(stateChanged(void)));
p = static_cast<TQListViewItem*> (q);
} else {
KMyMoneyListViewItem* q = new KMyMoneyListViewItem(parent, name, key, id);
p = static_cast<TQListViewItem*> (q);
}
return p;
}
void KMyMoneySelector::protectItem(const TQString& itemId, const bool protect)
{
TQListViewItemIterator it(m_listView, TQListViewItemIterator::Selectable);
TQListViewItem* it_v;
KMyMoneyListViewItem* it_l;
KMyMoneyCheckListItem* it_c;
// scan items
while((it_v = it.current()) != 0) {
it_l = dynamic_cast<KMyMoneyListViewItem*>(it_v);
if(it_l) {
if(it_l->id() == itemId) {
it_l->setSelectable(!protect);
break;
}
} else {
it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c) {
if(it_c->id() == itemId) {
it_c->setSelectable(!protect);
break;
}
}
}
++it;
}
}
TQListViewItem* KMyMoneySelector::item(const TQString& id) const
{
TQListViewItemIterator it(m_listView, TQListViewItemIterator::Selectable);
TQListViewItem* it_v;
KMyMoneyListViewItem* it_l;
KMyMoneyCheckListItem* it_c;
while((it_v = it.current()) != 0) {
it_l = dynamic_cast<KMyMoneyListViewItem*>(it_v);
if(it_l) {
if(it_l->id() == id)
break;
} else {
it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c->id() == id)
break;
}
++it;
}
return it_v;
}
int KMyMoneySelector::optimizedWidth(void) const
{
TQListViewItemIterator it(m_listView, TQListViewItemIterator::Selectable);
TQListViewItem* it_v;
KMyMoneyListViewItem* it_l;
KMyMoneyCheckListItem* it_c;
// scan items
int w = 0;
#ifndef KMM_DESIGNER
TQFontMetrics fm( KMyMoneyGlobalSettings::listCellFont());
#else
TQFontMetrics fm( font() );
#endif
while((it_v = it.current()) != 0) {
it_l = dynamic_cast<KMyMoneyListViewItem*>(it_v);
int nw = 0;
if(it_l) {
nw = it_l->width(fm, m_listView, 0);
} else {
it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c) {
nw = it_c->width(fm, m_listView, 0);
}
}
if(nw > w)
w = nw;
++it;
}
return w;
}
void KMyMoneySelector::setOptimizedWidth(void)
{
int w = optimizedWidth();
m_listView->setMinimumWidth(w+30);
m_listView->setMaximumWidth(w+30);
m_listView->setColumnWidth(0, w+28);
}
bool KMyMoneySelector::allItemsSelected(void) const
{
TQListViewItem* it_v;
if(m_selMode == TQListView::Single)
return false;
for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
if(it_v->rtti() == 1) {
TQCheckListItem* it_c = dynamic_cast<TQCheckListItem*>(it_v);
if(it_c->type() == TQCheckListItem::CheckBox) {
if(!(it_c->isOn() && allItemsSelected(it_v)))
return false;
} else {
if(!allItemsSelected(it_v))
return false;
}
}
}
return true;
}
bool KMyMoneySelector::allItemsSelected(const TQListViewItem *item) const
{
TQListViewItem* it_v;
for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
if(it_v->rtti() == 1) {
TQCheckListItem* it_c = static_cast<TQCheckListItem*>(it_v);
if(!(it_c->isOn() && allItemsSelected(it_v)))
return false;
}
}
return true;
}
void KMyMoneySelector::removeItem(const TQString& id)
{
TQListViewItem* it_v;
TQListViewItemIterator it;
it = TQListViewItemIterator(m_listView);
while((it_v = it.current()) != 0) {
if(it_v->rtti() == 1) {
KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c->type() == TQCheckListItem::CheckBox) {
if(id == it_c->id()) {
if(it_c->firstChild()) {
it_c->setSelectable(false);
} else {
delete it_c;
}
}
}
} else if(it_v->rtti() == 0) {
KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v);
if(id == it_c->id()) {
if(it_c->firstChild()) {
it_c->setSelectable(false);
} else {
delete it_c;
}
}
}
it++;
}
// get rid of top items that just lost the last children (e.g. Favorites)
it = TQListViewItemIterator(m_listView, TQListViewItemIterator::NotSelectable);
while((it_v = it.current()) != 0) {
if(it_v->rtti() == 1) {
KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c->childCount() == 0)
delete it_c;
}
it++;
}
return;
}
void KMyMoneySelector::selectAllItems(const bool state)
{
TQListViewItem* it_v;
for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
if(it_v->rtti() == 1) {
TQCheckListItem* it_c = dynamic_cast<TQCheckListItem*>(it_v);
if(it_c->type() == TQCheckListItem::CheckBox) {
it_c->setOn(state);
}
selectAllSubItems(it_v, state);
}
}
emit stateChanged();
}
void KMyMoneySelector::selectItems(const TQStringList& itemList, const bool state)
{
TQListViewItem* it_v;
for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
if(it_v->rtti() == 1) {
KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c->type() == TQCheckListItem::CheckBox && itemList.contains(it_c->id())) {
it_c->setOn(state);
}
selectSubItems(it_v, itemList, state);
}
}
emit stateChanged();
}
void KMyMoneySelector::selectSubItems(TQListViewItem* item, const TQStringList& itemList, const bool state)
{
TQListViewItem* it_v;
for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
if(it_v->rtti() == 1) {
KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c->type() == TQCheckListItem::CheckBox && itemList.contains(it_c->id())) {
it_c->setOn(state);
}
selectSubItems(it_v, itemList, state);
}
}
}
void KMyMoneySelector::selectAllSubItems(TQListViewItem* item, const bool state)
{
TQListViewItem* it_v;
for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
if(it_v->rtti() == 1) {
TQCheckListItem* it_c = dynamic_cast<TQCheckListItem*>(it_v);
if(it_c->type() == TQCheckListItem::CheckBox) {
it_c->setOn(state);
}
selectAllSubItems(it_v, state);
}
}
}
void KMyMoneySelector::selectedItems(TQStringList& list) const
{
TQListViewItem* it_v;
list.clear();
if(m_selMode == TQListView::Single) {
KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(m_listView->selectedItem());
if(it_c != 0)
list << it_c->id();
} else {
for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
if(it_v->rtti() == 1) {
KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c->type() == TQCheckListItem::CheckBox) {
if(it_c->isOn())
list << (*it_c).id();
}
selectedItems(list, it_v);
}
}
}
}
void KMyMoneySelector::selectedItems(TQStringList& list, TQListViewItem* item) const
{
TQListViewItem* it_v;
for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
if(it_v->rtti() == 1) {
KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c->type() == TQCheckListItem::CheckBox) {
if(it_c->isOn())
list << (*it_c).id();
selectedItems(list, it_v);
}
}
}
}
void KMyMoneySelector::itemList(TQStringList& list) const
{
TQListViewItemIterator it;
TQListViewItem* it_v;
it = TQListViewItemIterator(m_listView, TQListViewItemIterator::Selectable);
while((it_v = it.current()) != 0) {
{
if(it_v->rtti() == 1) {
KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c->type() == TQCheckListItem::CheckBox) {
list << it_c->id();
}
} else if(it_v->rtti() == 0) {
KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v);
list << it_c->id();
}
}
it++;
}
}
void KMyMoneySelector::setSelected(const TQString& id, const bool state)
{
TQListViewItemIterator it;
TQListViewItem* it_v;
TQListViewItem* it_visible = 0;
it = TQListViewItemIterator(m_listView, TQListViewItemIterator::Selectable);
while((it_v = it.current()) != 0) {
if(it_v->rtti() == 1) {
KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
TQ_CHECK_PTR(it_c);
if(it_c->type() == TQCheckListItem::CheckBox) {
if(it_c->id() == id) {
it_c->setOn(state);
m_listView->setSelected(it_v, true);
if(!it_visible)
it_visible = it_v;
}
}
} else if(it_v->rtti() == 0) {
KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v);
TQ_CHECK_PTR(it_c);
if(it_c->id() == id) {
m_listView->setSelected(it_v, true);
if(!it_visible)
it_visible = it_v;
ensureItemVisible(it_v);
return;
}
}
it++;
}
// make sure the first one found is visible
if(it_visible)
ensureItemVisible(it_visible);
}
void KMyMoneySelector::ensureItemVisible(const TQListViewItem *it_v)
{
// for some reason, I could only use the ensureItemVisible() method
// of TQListView successfully, after the widget was drawn on the screen.
// If called before it had no effect (if the item was not visible).
//
// The solution was to store the item we wanted to see in a local var
// and call TQListView::ensureItemVisible() about 10ms later in
// the slot slotShowSelected. (ipwizard, 12/29/2003)
m_visibleItem = it_v;
TQTimer::singleShot(100, this, TQT_SLOT(slotShowSelected()));
}
void KMyMoneySelector::slotShowSelected(void)
{
if(m_listView && m_visibleItem)
m_listView->ensureItemVisible(m_visibleItem);
}
int KMyMoneySelector::slotMakeCompletion(const TQString& _txt)
{
TQString txt(TQRegExp::escape(_txt));
if(KMyMoneyGlobalSettings::stringMatchFromStart() && this->isA("KMyMoneySelector") )
txt.prepend('^');
return slotMakeCompletion(TQRegExp(txt, false));
}
bool KMyMoneySelector::match(const TQRegExp& exp, TQListViewItem* item) const
{
return exp.search(item->text(0)) != -1;
}
int KMyMoneySelector::slotMakeCompletion(const TQRegExp& exp)
{
TQListViewItemIterator it(m_listView, TQListViewItemIterator::Selectable);
TQListViewItem* it_v;
// The logic used here seems to be awkward. The problem is, that
// TQListViewItem::setVisible works recursively on all it's children
// and grand-children.
//
// The way out of this is as follows: Make all items visible.
// Then go through the list again and perform the checks.
// If an item does not have any children (last leaf in the tree view)
// perform the check. Then check recursively on the parent of this
// leaf that it has no visible children. If that is the case, make the
// parent invisible and continue this check with it's parent.
while((it_v = it.current()) != 0) {
it_v->setVisible(true);
++it;
}
TQListViewItem* firstMatch = 0;
if(!exp.pattern().isEmpty()) {
it = TQListViewItemIterator(m_listView, TQListViewItemIterator::Selectable);
while((it_v = it.current()) != 0) {
if(it_v->firstChild() == 0) {
if(!match(exp, it_v)) {
// this is a node which does not contain the
// text and does not have children. So we can
// safely hide it. Then we check, if the parent
// has more children which are still visible. If
// none are found, the parent node is hidden also. We
// continue until the top of the tree or until we
// find a node that still has visible children.
bool hide = true;
while(hide) {
it_v->setVisible(false);
it_v = it_v->parent();
if(it_v && it_v->isSelectable()) {
hide = !match(exp, it_v);
TQListViewItem* child = it_v->firstChild();
for(; child && hide; child = child->nextSibling()) {
if(child->isVisible())
hide = false;
}
} else
hide = false;
}
} else if(!firstMatch) {
firstMatch = it_v;
}
++it;
} else if(match(exp, it_v)) {
if(!firstMatch) {
firstMatch = it_v;
}
// a node with children contains the text. We want
// to display all child nodes in this case, so we need
// to advance the iterator to the next sibling of the
// current node. This could well be the sibling of a
// parent or grandparent node.
TQListViewItem* curr = it_v;
TQListViewItem* item;
while((item = curr->nextSibling()) == 0) {
curr = curr->parent();
if(curr == 0)
break;
if(match(exp, curr))
firstMatch = curr;
}
do {
++it;
} while(it.current() && it.current() != item);
} else {
// It's a node with children that does not match. We don't
// change it's status here.
++it;
}
}
}
// make the first match the one that is selected
// if we have no match, make sure none is selected
if(m_selMode == TQListView::Single) {
if(firstMatch) {
m_listView->setSelected(firstMatch, true);
ensureItemVisible(firstMatch);
} else
m_listView->selectAll(false);
}
// Get the number of visible nodes for the return code
int cnt = 0;
it = TQListViewItemIterator(m_listView, TQListViewItemIterator::Selectable | TQListViewItemIterator::Visible);
while((it_v = it.current()) != 0) {
cnt++;
it++;
}
return cnt;
}
bool KMyMoneySelector::contains(const TQString& txt) const
{
TQListViewItemIterator it(m_listView, TQListViewItemIterator::Selectable);
TQListViewItem* it_v;
while((it_v = it.current()) != 0) {
if(it_v->rtti() == 1) {
KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c->text() == txt) {
return true;
}
} else if(it_v->rtti() == 0) {
KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v);
if(it_c->text(0) == txt) {
return true;
}
}
it++;
}
return false;
}
void KMyMoneySelector::slotListRightMouse(TQListViewItem* it_v, const TQPoint& pos, int /* col */)
{
if(it_v && (it_v->rtti() == 1)) {
KMyMoneyCheckListItem* it_c = static_cast<KMyMoneyCheckListItem*>(it_v);
if(it_c->type() == TQCheckListItem::CheckBox) {
// the following is copied from TQCheckListItem::activate() et al
int boxsize = m_listView->style().pixelMetric(TQStyle::PM_CheckListButtonSize, m_listView);
int align = m_listView->columnAlignment( 0 );
int marg = m_listView->itemMargin();
int y = 0;
if ( align & AlignVCenter )
y = ( ( height() - boxsize ) / 2 ) + marg;
else
y = (m_listView->fontMetrics().height() + 2 + marg - boxsize) / 2;
TQRect r( 0, y, boxsize-3, boxsize-3 );
// columns might have been swapped
r.moveBy( m_listView->header()->sectionPos( 0 ), 0 );
TQPoint topLeft = m_listView->itemRect(it_v).topLeft(); //### inefficient?
TQPoint p = m_listView->mapFromGlobal( pos ) - topLeft;
int xdepth = m_listView->treeStepSize() * (it_v->depth() + (m_listView->rootIsDecorated() ? 1 : 0))
+ m_listView->itemMargin();
xdepth += m_listView->header()->sectionPos( m_listView->header()->mapToSection( 0 ) );
p.rx() -= xdepth;
// copy ends around here
if ( r.contains( p ) ) {
// we get down here, if we have a right click onto the checkbox
selectAllSubItems(it_c, it_c->isOn());
}
}
}
}
TQStringList KMyMoneySelector::selectedItems(void) const
{
TQStringList list;
selectedItems(list);
return list;
}
TQStringList KMyMoneySelector::itemList(void) const
{
TQStringList list;
itemList(list);
return list;
}
#include "kmymoneyselector.moc"