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