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