ossp-pkg/as/as-gui/as_table.cpp
//
// OSSP asgui - Accounting system graphical user interface
// Copyright (c) 2002-2004 The OSSP Project (http://www.ossp.org/)
// Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
// Copyright (c) 2002-2004 Michael Schloh von Bennewitz <michael@schloh.com>
// Copyright (c) 2002-2004 Cable & Wireless Telecommunications Services GmbH
//
// This file is part of OSSP asgui, an accounting system graphical user
// interface which can be found at http://www.ossp.org/pkg/tool/asgui/.
//
// Permission to use, copy, modify, and distribute this software for
// any purpose with or without fee is hereby granted, provided that
// the above copyright notice and this permission notice appear in all
// copies.
//
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// as_table.cpp: ISO C++ implementation
//
#include <qheader.h>
#include "as_const.h"
#include "as_table.h"
// Implements an event filter for catching header double click events
bool TiTable::eventFilter(QObject *pObject, QEvent *pEvent)
{
if (pObject == horizontalHeader() && pEvent->type() == QEvent::MouseButtonDblClick)
return true;
if (pEvent->type() == QEvent::MouseButtonPress && // Ignore mid, right clicks
((QMouseEvent *)pEvent)->button() == QMouseEvent::RightButton ||
((QMouseEvent *)pEvent)->button() == QMouseEvent::MidButton)
return true;
else if (pEvent->type() == QEvent::KeyPress) {
if (((QKeyEvent *)pEvent)->key() == Qt::Key_Tab) { // Handle tab key
if (this->getEdition() >= 0) {
int nIter = 1; // Logic to skip invisible or read only columns
while (columnWidth((currentColumn() + nIter) % TITRAQ_IDXTAIL) <= 0
|| isColumnReadOnly((currentColumn() + nIter) % TITRAQ_IDXTAIL))
nIter++;
// Advance the column or both row and column possibly
int nColadvance = ((currentColumn() + nIter) % TITRAQ_IDXTAIL);
if ((currentColumn() + nIter) >= TITRAQ_IDXTAIL) {
this->clearSelection(true);
this->setCurrentCell(currentRow() + 1, nColadvance);
// this->repaint(false); // Really necessary?
}
else
this->setCurrentCell(currentRow(), nColadvance);
this->setReadOnly(false);
this->editCell(currentRow(), currentColumn());
this->setEdition(currentColumn());
}
return true; // Handle the tab key event and cancel its progress
}
else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Backtab) { // Handle shift tab key
if (this->getEdition() >= 0) {
int nIter = 1; // Logic to skip invisible or read only columns
while (columnWidth((currentColumn() - nIter + TITRAQ_IDXTAIL) % TITRAQ_IDXTAIL) <= 0
|| isColumnReadOnly((currentColumn() - nIter + TITRAQ_IDXTAIL) % TITRAQ_IDXTAIL))
nIter++;
// Advance the column or both row and column possibly
int nColadvance = (currentColumn() - nIter + TITRAQ_IDXTAIL) % TITRAQ_IDXTAIL;
if ((currentColumn() - nIter) < 0) {
this->clearSelection(true);
this->setCurrentCell(currentRow() - 1, nColadvance);
// this->repaint(false); // Really necessary?
}
else
this->setCurrentCell(currentRow(), nColadvance);
this->setReadOnly(false);
this->editCell(currentRow(), currentColumn());
this->setEdition(currentColumn());
}
return true; // Handle the shift tab key event and cancel its progress
}
else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Return && this->getEdition() >= 0) { // Return key
this->endEdit(currEditRow(), currEditCol(), true, false);
this->setEdition(); // Reset edition
return true; // Cancel progress
}
else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Enter && this->getEdition() >= 0) { // Enter key
this->endEdit(currEditRow(), currEditCol(), true, false);
this->setEdition(); // Reset edition
return true; // Cancel progress
}
else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Return) { // Return key without edition
this->setReadOnly(false); // Allow edition
this->editCell(currentRow(), currentColumn()); // Begin edition
this->setEdition(currentColumn()); // Store edition state
return true; // Cancel further progress
}
else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Enter) { // Enter key without edition
this->setReadOnly(false); // Allow edition
this->editCell(currentRow(), currentColumn()); // Begin edition
this->setEdition(currentColumn()); // Store edition state
return true; // Cancel further progress
}
else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Escape) // Handle escape key
this->setEdition();
else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Up && this->getEdition() >= 0) // Handle up key
return true; // Capture
else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Down && this->getEdition() >= 0) // Handle down key
return true; // Capture
// Forward incompletely handled key events
return QTable::eventFilter(pObject, pEvent);
}
else // Default behaviour is to pass the event onwards
return QTable::eventFilter(pObject, pEvent);
}
// Overridden member hack to allow externally connected control
// widgets to influence dirty or clean state of table data
void TiTable::setText(int nRow, int nCol, const QString &nText)
{
if (this->numRows() > 0) {
// If a cell was edited, emit a signal indicating so
// We can't rely on valueChanged for unknown reasons
if (nText != this->text(nRow, nCol) && nCol != TITRAQ_IDXLINE)
emit textEdited(nRow, nCol);
QTable::setText(nRow, nCol, nText);
}
}
// Overridden member hack to allow externally connected control
// widgets to influence dirty or clean state of table data
void TiTable::sortColumn(int nCol, bool bAscend, bool bWhole)
{
// Guard against a repeat sort behaviour
if (nCol == this->getSortcol() && bAscend == this->getSortdir())
this->setSortdir(!bAscend);
else
this->setSortdir(bAscend);
this->setSortcol(nCol);
QTable::sortColumn(nCol, this->getSortdir(), true);
// // Announce sorting policy with multiple selections
// QTableSelection Testsel = this->selection(this->currentSelection());
// if (Testsel.topRow() != Testsel.bottomRow())
// m_pStatbar->message(trUtf8("Multiple selections dropped when sorting"), 4000);
// Move and display the selection highlight
this->removeSelection(this->currentSelection());
this->selectRow(this->currentRow());
this->ensureCellVisible(this->currentRow(), 0);
// Write nonsaving line numbers for all rows
for (int nIter = this->numRows() - 1; nIter >= 0; nIter--)
this->setText(nIter, TITRAQ_IDXLINE, QString::number(nIter).rightJustify(4, QChar('0')));
}
// Overriden member to render edge rows differently according to a sort key
void TiTable::paintCell(QPainter *pPainter, int nRow, int nCol, const QRect &Recto, bool bSelect, const QColorGroup &Colgroup)
{
QColorGroup Cgroup(Colgroup);
int nRed, nGreen, nBlue;
nRed = m_pTiprefs->getNumber(TITRAQ_PREFLIGHTRED, TITRAQ_DEFLIGHTRED);
nGreen = m_pTiprefs->getNumber(TITRAQ_PREFLIGHTGREEN, TITRAQ_DEFLIGHTGREEN);
nBlue = m_pTiprefs->getNumber(TITRAQ_PREFLIGHTBLUE, TITRAQ_DEFLIGHTBLUE);
QColor Bright = QColor(nRed, nGreen, nBlue);
nRed = m_pTiprefs->getNumber(TITRAQ_PREFDARKRED, TITRAQ_DEFDARKRED);
nGreen = m_pTiprefs->getNumber(TITRAQ_PREFDARKGREEN, TITRAQ_DEFDARKGREEN);
nBlue = m_pTiprefs->getNumber(TITRAQ_PREFDARKBLUE, TITRAQ_DEFDARKBLUE);
QColor Dark = QColor(nRed, nGreen, nBlue);
// Alternate color for nonmatching sort keys
QString Cur = this->text(nRow, this->getSortcol());
QString Las = this->text(nRow - 1, this->getSortcol());
// A nice cascade of conditions providing a linewise base color test and set
// The algorythm:
// 1 Determine if the current row's index key differs from the former one
// 2a If they are the same, then current row should have the same color
// 2b If they are different, then current row should have an alt color
// 3 Store information about which color we chose for the current row
if (!Cur.isNull() && !Las.isNull() && Cur == Las) { // Set the base color conditionally
if (this->text(nRow - 1, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) == QChar(TITRAQ_BRIGHT)) {
Cgroup.setColor(QColorGroup::Base, Bright); // Bright
if (this->text(nRow, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) != QChar(TITRAQ_BRIGHT))
this->setText(nRow, TITRAQ_IDXSTATUS, this->text(nRow, TITRAQ_IDXSTATUS).replace(TITRAQ_IDXSTATCOLOR, sizeof(char), QChar(TITRAQ_BRIGHT)));
}
else {
Cgroup.setColor(QColorGroup::Base, Dark); // Dark
if (this->text(nRow, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) != QChar(TITRAQ_DARK))
this->setText(nRow, TITRAQ_IDXSTATUS, this->text(nRow, TITRAQ_IDXSTATUS).replace(TITRAQ_IDXSTATCOLOR, sizeof(char), QChar(TITRAQ_DARK)));
}
}
else {
if (this->text(nRow - 1, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) == QChar(TITRAQ_BRIGHT)) {
Cgroup.setColor(QColorGroup::Base, Dark); // Dark
if (this->text(nRow, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) != QChar(TITRAQ_DARK))
this->setText(nRow, TITRAQ_IDXSTATUS, this->text(nRow, TITRAQ_IDXSTATUS).replace(TITRAQ_IDXSTATCOLOR, sizeof(char), QChar(TITRAQ_DARK)));
}
else {
Cgroup.setColor(QColorGroup::Base, Bright); // Bright
if (this->text(nRow, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) != QChar(TITRAQ_BRIGHT))
this->setText(nRow, TITRAQ_IDXSTATUS, this->text(nRow, TITRAQ_IDXSTATUS).replace(TITRAQ_IDXSTATCOLOR, sizeof(char), QChar(TITRAQ_BRIGHT)));
}
}
QTable::paintCell(pPainter, nRow, nCol, Recto, bSelect, Cgroup);
};
// Blah
void TiTable::activateNextCell(void)
{
int nIter = 1; // Logic to skip invisible or read only columns
while (columnWidth((currentColumn() + nIter) % TITRAQ_IDXTAIL) <= 0
|| isColumnReadOnly((currentColumn() + nIter) % TITRAQ_IDXTAIL))
nIter++;
// Advance the column or both row and column possibly
int nColadvance = ((currentColumn() + nIter) % TITRAQ_IDXTAIL);
if ((currentColumn() + nIter) >= TITRAQ_IDXTAIL)
this->setCurrentCell(currentRow() + 1, nColadvance);
else
this->setCurrentCell(currentRow(), nColadvance);
this->setReadOnly(false);
this->editCell(currentRow(), currentColumn());
this->setEdition(currentColumn());
}
// Overriden member to properly handle read only attribute after edition
void TiTable::endEdit(int nRow, int nCol, bool bAccept, bool bReplace)
{
QTable::endEdit(nRow, nCol, bAccept, bReplace);
// Table read only attribute must be set to return to the normal
// row highlight and selection behaviour of AS. The reason it was
// reset in inplaceEdit() was to allow editing in the first place.
this->setReadOnly(true);
}