Fri, 28 Nov 2008 14:20:00 +0100
Added tag ASGUI_0.7.7 for changeset d64aaa7d146f
michael@1 | 1 | // |
michael@1 | 2 | // OSSP asgui - Accounting system graphical user interface |
michael@1 | 3 | // Copyright (c) 2002-2004 The OSSP Project (http://www.ossp.org/) |
michael@1 | 4 | // Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com> |
michael@1 | 5 | // Copyright (c) 2002-2004 Michael Schloh von Bennewitz <michael@schloh.com> |
michael@1 | 6 | // Copyright (c) 2002-2004 Cable & Wireless Telecommunications Services GmbH |
michael@1 | 7 | // |
michael@1 | 8 | // This file is part of OSSP asgui, an accounting system graphical user |
michael@1 | 9 | // interface which can be found at http://www.ossp.org/pkg/tool/asgui/. |
michael@1 | 10 | // |
michael@1 | 11 | // Permission to use, copy, modify, and distribute this software for |
michael@1 | 12 | // any purpose with or without fee is hereby granted, provided that |
michael@1 | 13 | // the above copyright notice and this permission notice appear in all |
michael@1 | 14 | // copies. |
michael@1 | 15 | // |
michael@1 | 16 | // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
michael@1 | 17 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
michael@1 | 18 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
michael@1 | 19 | // IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR |
michael@1 | 20 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@1 | 21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@1 | 22 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
michael@1 | 23 | // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
michael@1 | 24 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
michael@1 | 25 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
michael@1 | 26 | // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
michael@1 | 27 | // SUCH DAMAGE. |
michael@1 | 28 | // |
michael@1 | 29 | // as_table.cpp: ISO C++ implementation |
michael@1 | 30 | // |
michael@1 | 31 | |
michael@1 | 32 | #include <qheader.h> |
michael@1 | 33 | |
michael@1 | 34 | #include "as_const.h" |
michael@1 | 35 | #include "as_table.h" |
michael@1 | 36 | |
michael@1 | 37 | |
michael@1 | 38 | // Implements an event filter for catching header double click events |
michael@1 | 39 | bool TiTable::eventFilter(QObject *pObject, QEvent *pEvent) |
michael@1 | 40 | { |
michael@1 | 41 | if (pObject == horizontalHeader() && pEvent->type() == QEvent::MouseButtonDblClick) |
michael@1 | 42 | return true; |
michael@1 | 43 | if (pEvent->type() == QEvent::MouseButtonPress && // Ignore mid, right clicks |
michael@1 | 44 | ((QMouseEvent *)pEvent)->button() == QMouseEvent::RightButton || |
michael@1 | 45 | ((QMouseEvent *)pEvent)->button() == QMouseEvent::MidButton) |
michael@1 | 46 | return true; |
michael@1 | 47 | else if (pEvent->type() == QEvent::KeyPress) { |
michael@1 | 48 | if (((QKeyEvent *)pEvent)->key() == Qt::Key_Tab) { // Handle tab key |
michael@1 | 49 | if (this->getEdition() >= 0) { |
michael@1 | 50 | int nIter = 1; // Logic to skip invisible or read only columns |
michael@1 | 51 | while (columnWidth((currentColumn() + nIter) % TITRAQ_IDXTAIL) <= 0 |
michael@1 | 52 | || isColumnReadOnly((currentColumn() + nIter) % TITRAQ_IDXTAIL)) |
michael@1 | 53 | nIter++; |
michael@1 | 54 | |
michael@1 | 55 | // Advance the column or both row and column possibly |
michael@1 | 56 | int nColadvance = ((currentColumn() + nIter) % TITRAQ_IDXTAIL); |
michael@1 | 57 | if ((currentColumn() + nIter) >= TITRAQ_IDXTAIL) { |
michael@1 | 58 | this->clearSelection(true); |
michael@1 | 59 | this->setCurrentCell(currentRow() + 1, nColadvance); |
michael@1 | 60 | // this->repaint(false); // Really necessary? |
michael@1 | 61 | } |
michael@1 | 62 | else |
michael@1 | 63 | this->setCurrentCell(currentRow(), nColadvance); |
michael@1 | 64 | this->setReadOnly(false); |
michael@1 | 65 | this->editCell(currentRow(), currentColumn()); |
michael@1 | 66 | this->setEdition(currentColumn()); |
michael@1 | 67 | } |
michael@1 | 68 | return true; // Handle the tab key event and cancel its progress |
michael@1 | 69 | } |
michael@1 | 70 | else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Backtab) { // Handle shift tab key |
michael@1 | 71 | if (this->getEdition() >= 0) { |
michael@1 | 72 | int nIter = 1; // Logic to skip invisible or read only columns |
michael@1 | 73 | while (columnWidth((currentColumn() - nIter + TITRAQ_IDXTAIL) % TITRAQ_IDXTAIL) <= 0 |
michael@1 | 74 | || isColumnReadOnly((currentColumn() - nIter + TITRAQ_IDXTAIL) % TITRAQ_IDXTAIL)) |
michael@1 | 75 | nIter++; |
michael@1 | 76 | |
michael@1 | 77 | // Advance the column or both row and column possibly |
michael@1 | 78 | int nColadvance = (currentColumn() - nIter + TITRAQ_IDXTAIL) % TITRAQ_IDXTAIL; |
michael@1 | 79 | if ((currentColumn() - nIter) < 0) { |
michael@1 | 80 | this->clearSelection(true); |
michael@1 | 81 | this->setCurrentCell(currentRow() - 1, nColadvance); |
michael@1 | 82 | // this->repaint(false); // Really necessary? |
michael@1 | 83 | } |
michael@1 | 84 | else |
michael@1 | 85 | this->setCurrentCell(currentRow(), nColadvance); |
michael@1 | 86 | this->setReadOnly(false); |
michael@1 | 87 | this->editCell(currentRow(), currentColumn()); |
michael@1 | 88 | this->setEdition(currentColumn()); |
michael@1 | 89 | } |
michael@1 | 90 | return true; // Handle the shift tab key event and cancel its progress |
michael@1 | 91 | } |
michael@1 | 92 | else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Return && this->getEdition() >= 0) { // Return key |
michael@1 | 93 | this->endEdit(currEditRow(), currEditCol(), true, false); |
michael@1 | 94 | this->setEdition(); // Reset edition |
michael@1 | 95 | return true; // Cancel progress |
michael@1 | 96 | } |
michael@1 | 97 | else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Enter && this->getEdition() >= 0) { // Enter key |
michael@1 | 98 | this->endEdit(currEditRow(), currEditCol(), true, false); |
michael@1 | 99 | this->setEdition(); // Reset edition |
michael@1 | 100 | return true; // Cancel progress |
michael@1 | 101 | } |
michael@1 | 102 | else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Return) { // Return key without edition |
michael@1 | 103 | this->setReadOnly(false); // Allow edition |
michael@1 | 104 | this->editCell(currentRow(), currentColumn()); // Begin edition |
michael@1 | 105 | this->setEdition(currentColumn()); // Store edition state |
michael@1 | 106 | return true; // Cancel further progress |
michael@1 | 107 | } |
michael@1 | 108 | else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Enter) { // Enter key without edition |
michael@1 | 109 | this->setReadOnly(false); // Allow edition |
michael@1 | 110 | this->editCell(currentRow(), currentColumn()); // Begin edition |
michael@1 | 111 | this->setEdition(currentColumn()); // Store edition state |
michael@1 | 112 | return true; // Cancel further progress |
michael@1 | 113 | } |
michael@1 | 114 | else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Escape) // Handle escape key |
michael@1 | 115 | this->setEdition(); |
michael@1 | 116 | else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Up && this->getEdition() >= 0) // Handle up key |
michael@1 | 117 | return true; // Capture |
michael@1 | 118 | else if (((QKeyEvent *)pEvent)->key() == Qt::Key_Down && this->getEdition() >= 0) // Handle down key |
michael@1 | 119 | return true; // Capture |
michael@1 | 120 | |
michael@1 | 121 | // Forward incompletely handled key events |
michael@1 | 122 | return QTable::eventFilter(pObject, pEvent); |
michael@1 | 123 | } |
michael@1 | 124 | else // Default behaviour is to pass the event onwards |
michael@1 | 125 | return QTable::eventFilter(pObject, pEvent); |
michael@1 | 126 | } |
michael@1 | 127 | |
michael@1 | 128 | // Overridden member hack to allow externally connected control |
michael@1 | 129 | // widgets to influence dirty or clean state of table data |
michael@1 | 130 | void TiTable::setText(int nRow, int nCol, const QString &nText) |
michael@1 | 131 | { |
michael@1 | 132 | if (this->numRows() > 0) { |
michael@1 | 133 | // If a cell was edited, emit a signal indicating so |
michael@1 | 134 | // We can't rely on valueChanged for unknown reasons |
michael@1 | 135 | if (nText != this->text(nRow, nCol) && nCol != TITRAQ_IDXLINE) |
michael@1 | 136 | emit textEdited(nRow, nCol); |
michael@1 | 137 | |
michael@1 | 138 | QTable::setText(nRow, nCol, nText); |
michael@1 | 139 | } |
michael@1 | 140 | } |
michael@1 | 141 | |
michael@1 | 142 | // Overridden member hack to allow externally connected control |
michael@1 | 143 | // widgets to influence dirty or clean state of table data |
michael@1 | 144 | void TiTable::sortColumn(int nCol, bool bAscend, bool bWhole) |
michael@1 | 145 | { |
michael@1 | 146 | // Guard against a repeat sort behaviour |
michael@1 | 147 | if (nCol == this->getSortcol() && bAscend == this->getSortdir()) |
michael@1 | 148 | this->setSortdir(!bAscend); |
michael@1 | 149 | else |
michael@1 | 150 | this->setSortdir(bAscend); |
michael@1 | 151 | |
michael@1 | 152 | this->setSortcol(nCol); |
michael@1 | 153 | QTable::sortColumn(nCol, this->getSortdir(), true); |
michael@1 | 154 | |
michael@1 | 155 | // // Announce sorting policy with multiple selections |
michael@1 | 156 | // QTableSelection Testsel = this->selection(this->currentSelection()); |
michael@1 | 157 | // if (Testsel.topRow() != Testsel.bottomRow()) |
michael@1 | 158 | // m_pStatbar->message(trUtf8("Multiple selections dropped when sorting"), 4000); |
michael@1 | 159 | |
michael@1 | 160 | // Move and display the selection highlight |
michael@1 | 161 | this->removeSelection(this->currentSelection()); |
michael@1 | 162 | this->selectRow(this->currentRow()); |
michael@1 | 163 | this->ensureCellVisible(this->currentRow(), 0); |
michael@1 | 164 | |
michael@1 | 165 | // Write nonsaving line numbers for all rows |
michael@1 | 166 | for (int nIter = this->numRows() - 1; nIter >= 0; nIter--) |
michael@1 | 167 | this->setText(nIter, TITRAQ_IDXLINE, QString::number(nIter).rightJustify(4, QChar('0'))); |
michael@1 | 168 | } |
michael@1 | 169 | |
michael@1 | 170 | // Overriden member to render edge rows differently according to a sort key |
michael@1 | 171 | void TiTable::paintCell(QPainter *pPainter, int nRow, int nCol, const QRect &Recto, bool bSelect, const QColorGroup &Colgroup) |
michael@1 | 172 | { |
michael@1 | 173 | QColorGroup Cgroup(Colgroup); |
michael@1 | 174 | int nRed, nGreen, nBlue; |
michael@1 | 175 | |
michael@1 | 176 | nRed = m_pTiprefs->getNumber(TITRAQ_PREFLIGHTRED, TITRAQ_DEFLIGHTRED); |
michael@1 | 177 | nGreen = m_pTiprefs->getNumber(TITRAQ_PREFLIGHTGREEN, TITRAQ_DEFLIGHTGREEN); |
michael@1 | 178 | nBlue = m_pTiprefs->getNumber(TITRAQ_PREFLIGHTBLUE, TITRAQ_DEFLIGHTBLUE); |
michael@1 | 179 | QColor Bright = QColor(nRed, nGreen, nBlue); |
michael@1 | 180 | nRed = m_pTiprefs->getNumber(TITRAQ_PREFDARKRED, TITRAQ_DEFDARKRED); |
michael@1 | 181 | nGreen = m_pTiprefs->getNumber(TITRAQ_PREFDARKGREEN, TITRAQ_DEFDARKGREEN); |
michael@1 | 182 | nBlue = m_pTiprefs->getNumber(TITRAQ_PREFDARKBLUE, TITRAQ_DEFDARKBLUE); |
michael@1 | 183 | QColor Dark = QColor(nRed, nGreen, nBlue); |
michael@1 | 184 | |
michael@1 | 185 | // Alternate color for nonmatching sort keys |
michael@1 | 186 | QString Cur = this->text(nRow, this->getSortcol()); |
michael@1 | 187 | QString Las = this->text(nRow - 1, this->getSortcol()); |
michael@1 | 188 | |
michael@1 | 189 | // A nice cascade of conditions providing a linewise base color test and set |
michael@1 | 190 | // The algorythm: |
michael@1 | 191 | // 1 Determine if the current row's index key differs from the former one |
michael@1 | 192 | // 2a If they are the same, then current row should have the same color |
michael@1 | 193 | // 2b If they are different, then current row should have an alt color |
michael@1 | 194 | // 3 Store information about which color we chose for the current row |
michael@1 | 195 | if (!Cur.isNull() && !Las.isNull() && Cur == Las) { // Set the base color conditionally |
michael@1 | 196 | if (this->text(nRow - 1, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) == QChar(TITRAQ_BRIGHT)) { |
michael@1 | 197 | Cgroup.setColor(QColorGroup::Base, Bright); // Bright |
michael@1 | 198 | if (this->text(nRow, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) != QChar(TITRAQ_BRIGHT)) |
michael@1 | 199 | this->setText(nRow, TITRAQ_IDXSTATUS, this->text(nRow, TITRAQ_IDXSTATUS).replace(TITRAQ_IDXSTATCOLOR, sizeof(char), QChar(TITRAQ_BRIGHT))); |
michael@1 | 200 | } |
michael@1 | 201 | else { |
michael@1 | 202 | Cgroup.setColor(QColorGroup::Base, Dark); // Dark |
michael@1 | 203 | if (this->text(nRow, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) != QChar(TITRAQ_DARK)) |
michael@1 | 204 | this->setText(nRow, TITRAQ_IDXSTATUS, this->text(nRow, TITRAQ_IDXSTATUS).replace(TITRAQ_IDXSTATCOLOR, sizeof(char), QChar(TITRAQ_DARK))); |
michael@1 | 205 | } |
michael@1 | 206 | } |
michael@1 | 207 | else { |
michael@1 | 208 | if (this->text(nRow - 1, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) == QChar(TITRAQ_BRIGHT)) { |
michael@1 | 209 | Cgroup.setColor(QColorGroup::Base, Dark); // Dark |
michael@1 | 210 | if (this->text(nRow, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) != QChar(TITRAQ_DARK)) |
michael@1 | 211 | this->setText(nRow, TITRAQ_IDXSTATUS, this->text(nRow, TITRAQ_IDXSTATUS).replace(TITRAQ_IDXSTATCOLOR, sizeof(char), QChar(TITRAQ_DARK))); |
michael@1 | 212 | } |
michael@1 | 213 | else { |
michael@1 | 214 | Cgroup.setColor(QColorGroup::Base, Bright); // Bright |
michael@1 | 215 | if (this->text(nRow, TITRAQ_IDXSTATUS).at(TITRAQ_IDXSTATCOLOR) != QChar(TITRAQ_BRIGHT)) |
michael@1 | 216 | this->setText(nRow, TITRAQ_IDXSTATUS, this->text(nRow, TITRAQ_IDXSTATUS).replace(TITRAQ_IDXSTATCOLOR, sizeof(char), QChar(TITRAQ_BRIGHT))); |
michael@1 | 217 | } |
michael@1 | 218 | } |
michael@1 | 219 | |
michael@1 | 220 | QTable::paintCell(pPainter, nRow, nCol, Recto, bSelect, Cgroup); |
michael@1 | 221 | }; |
michael@1 | 222 | |
michael@1 | 223 | // Blah |
michael@1 | 224 | void TiTable::activateNextCell(void) |
michael@1 | 225 | { |
michael@1 | 226 | int nIter = 1; // Logic to skip invisible or read only columns |
michael@1 | 227 | while (columnWidth((currentColumn() + nIter) % TITRAQ_IDXTAIL) <= 0 |
michael@1 | 228 | || isColumnReadOnly((currentColumn() + nIter) % TITRAQ_IDXTAIL)) |
michael@1 | 229 | nIter++; |
michael@1 | 230 | |
michael@1 | 231 | // Advance the column or both row and column possibly |
michael@1 | 232 | int nColadvance = ((currentColumn() + nIter) % TITRAQ_IDXTAIL); |
michael@1 | 233 | if ((currentColumn() + nIter) >= TITRAQ_IDXTAIL) |
michael@1 | 234 | this->setCurrentCell(currentRow() + 1, nColadvance); |
michael@1 | 235 | else |
michael@1 | 236 | this->setCurrentCell(currentRow(), nColadvance); |
michael@1 | 237 | this->setReadOnly(false); |
michael@1 | 238 | this->editCell(currentRow(), currentColumn()); |
michael@1 | 239 | this->setEdition(currentColumn()); |
michael@1 | 240 | } |
michael@1 | 241 | |
michael@1 | 242 | // Overriden member to properly handle read only attribute after edition |
michael@1 | 243 | void TiTable::endEdit(int nRow, int nCol, bool bAccept, bool bReplace) |
michael@1 | 244 | { |
michael@1 | 245 | QTable::endEdit(nRow, nCol, bAccept, bReplace); |
michael@1 | 246 | |
michael@1 | 247 | // Table read only attribute must be set to return to the normal |
michael@1 | 248 | // row highlight and selection behaviour of AS. The reason it was |
michael@1 | 249 | // reset in inplaceEdit() was to allow editing in the first place. |
michael@1 | 250 | this->setReadOnly(true); |
michael@1 | 251 | } |