1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt3Support module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qglobal.h"
43 #if defined(Q_CC_BOR)
44 // needed for qsort() because of a std namespace problem on Borland
45 #include "qplatformdefs.h"
46 #endif
47
48 #include "q3table.h"
49
50
51 #include <qpainter.h>
52 #include <qlineedit.h>
53 #include <qcursor.h>
54 #include <qapplication.h>
55 #include <qtimer.h>
56 #include <qicon.h>
57 #include <q3combobox.h>
58 #include <qstyleoption.h>
59 #include <qcheckbox.h>
60 #include <q3dragobject.h>
61 #include <qevent.h>
62 #include <q3listbox.h>
63 #include <qstyle.h>
64 #include <q3datatable.h>
65 #include <qvalidator.h>
66 #include <q3button.h>
67
68 #include <stdlib.h>
69 #include <limits.h>
70
71 QT_BEGIN_NAMESPACE
72
73 using namespace Qt;
74
75 class Q3HeaderData;
76 extern bool qt_get_null_label_bit(Q3HeaderData *data, int section);
77 extern void qt_set_null_label_bit(Q3HeaderData *data, int section, bool b);
78
79 static bool qt_update_cell_widget = true;
80 static bool qt_table_clipper_enabled = true;
81 #ifndef QT_INTERNAL_TABLE
82 Q_COMPAT_EXPORT
83 #endif
qt_set_table_clipper_enabled(bool enabled)84 void qt_set_table_clipper_enabled(bool enabled)
85 {
86 qt_table_clipper_enabled = enabled;
87 }
88
89 class Q_COMPAT_EXPORT Q3TableHeader : public Q3Header
90 {
91 friend class Q3Table;
92 Q_OBJECT
93
94 public:
95 enum SectionState {
96 Normal,
97 Bold,
98 Selected
99 };
100
101 Q3TableHeader(int, Q3Table *t, QWidget* parent=0, const char* name=0);
~Q3TableHeader()102 ~Q3TableHeader() {};
103 void addLabel(const QString &s, int size);
104 void setLabel(int section, const QString & s, int size = -1);
105 void setLabel(int section, const QIconSet & iconset, const QString & s,
106 int size = -1);
107
108 void setLabels(const QStringList & labels);
109
110 void removeLabel(int section);
111
112 void setSectionState(int s, SectionState state);
113 void setSectionStateToAll(SectionState state);
114 SectionState sectionState(int s) const;
115
116 int sectionSize(int section) const;
117 int sectionPos(int section) const;
118 int sectionAt(int section) const;
119
120 void setSectionStretchable(int s, bool b);
121 bool isSectionStretchable(int s) const;
122
123 void updateCache();
124
125 signals:
126 void sectionSizeChanged(int s);
127
128 protected:
129 void paintEvent(QPaintEvent *e);
130 void paintSection(QPainter *p, int index, const QRect& fr);
131 void mousePressEvent(QMouseEvent *e);
132 void mouseMoveEvent(QMouseEvent *e);
133 void mouseReleaseEvent(QMouseEvent *e);
134 void mouseDoubleClickEvent(QMouseEvent *e);
135 void resizeEvent(QResizeEvent *e);
136
137 private slots:
138 void doAutoScroll();
139 void sectionWidthChanged(int col, int os, int ns);
140 void indexChanged(int sec, int oldIdx, int newIdx);
141 void updateStretches();
142 void updateWidgetStretches();
143
144 private:
145 void updateSelections();
146 void saveStates();
147 void setCaching(bool b);
148 void swapSections(int oldIdx, int newIdx, bool swapTable = true);
149 bool doSelection(QMouseEvent *e);
150 void sectionLabelChanged(int section);
151 void resizeArrays(int n);
152
153 private:
154 Q3MemArray<int> states, oldStates;
155 Q3MemArray<bool> stretchable;
156 Q3MemArray<int> sectionSizes, sectionPoses;
157 bool mousePressed;
158 int pressPos, startPos, endPos;
159 Q3Table *table;
160 QTimer *autoScrollTimer;
161 QWidget *line1, *line2;
162 bool caching;
163 int resizedSection;
164 bool isResizing;
165 int numStretches;
166 QTimer *stretchTimer, *widgetStretchTimer;
167 Q3TableHeaderPrivate *d;
168
169 Q_DISABLE_COPY(Q3TableHeader)
170 };
171
172 #ifdef _WS_QWS_
173 # define NO_LINE_WIDGET
174 #endif
175
176
177
178 struct Q3TablePrivate
179 {
Q3TablePrivateQ3TablePrivate180 Q3TablePrivate() : hasRowSpan(false), hasColSpan(false),
181 inMenuMode(false), redirectMouseEvent(false)
182 {
183 hiddenRows.setAutoDelete(true);
184 hiddenCols.setAutoDelete(true);
185 }
186 uint hasRowSpan : 1;
187 uint hasColSpan : 1;
188 uint inMenuMode : 1;
189 uint redirectMouseEvent : 1;
190 Q3IntDict<int> hiddenRows, hiddenCols;
191 QTimer *geomTimer;
192 int lastVisRow;
193 int lastVisCol;
194 };
195
196 struct Q3TableHeaderPrivate
197 {
198 #ifdef NO_LINE_WIDGET
199 int oldLinePos;
200 #endif
201 };
202
isRowSelection(Q3Table::SelectionMode selMode)203 static bool isRowSelection(Q3Table::SelectionMode selMode)
204 {
205 return selMode == Q3Table::SingleRow || selMode == Q3Table::MultiRow;
206 }
207
208 /*!
209 \class Q3TableSelection
210 \brief The Q3TableSelection class provides access to a selected area in a
211 Q3Table.
212
213 \compat
214
215 The selection is a rectangular set of cells in a Q3Table. One of
216 the rectangle's cells is called the anchor cell; this is the cell
217 that was selected first. The init() function sets the anchor and
218 the selection rectangle to exactly this cell; the expandTo()
219 function expands the selection rectangle to include additional
220 cells.
221
222 There are various access functions to find out about the area:
223 anchorRow() and anchorCol() return the anchor's position;
224 leftCol(), rightCol(), topRow() and bottomRow() return the
225 rectangle's four edges. All four are part of the selection.
226
227 A newly created Q3TableSelection is inactive -- isActive() returns
228 false. You must use init() and expandTo() to activate it.
229
230 \sa Q3Table Q3Table::addSelection() Q3Table::selection()
231 Q3Table::selectCells() Q3Table::selectRow() Q3Table::selectColumn()
232 */
233
234 /*!
235 Creates an inactive selection. Use init() and expandTo() to
236 activate it.
237 */
238
Q3TableSelection()239 Q3TableSelection::Q3TableSelection()
240 : active(false), inited(false), tRow(-1), lCol(-1),
241 bRow(-1), rCol(-1), aRow(-1), aCol(-1)
242 {
243 }
244
245 /*!
246 Creates an active selection, starting at \a start_row and \a
247 start_col, ending at \a end_row and \a end_col.
248 */
249
Q3TableSelection(int start_row,int start_col,int end_row,int end_col)250 Q3TableSelection::Q3TableSelection(int start_row, int start_col, int end_row, int end_col)
251 : active(false), inited(false), tRow(-1), lCol(-1),
252 bRow(-1), rCol(-1), aRow(-1), aCol(-1)
253 {
254 init(start_row, start_col);
255 expandTo(end_row, end_col);
256 }
257
258 /*!
259 Sets the selection anchor to cell \a row, \a col and the selection
260 to only contain this cell. The selection is not active until
261 expandTo() is called.
262
263 To extend the selection to include additional cells, call
264 expandTo().
265
266 \sa isActive()
267 */
268
init(int row,int col)269 void Q3TableSelection::init(int row, int col)
270 {
271 aCol = lCol = rCol = col;
272 aRow = tRow = bRow = row;
273 active = false;
274 inited = true;
275 }
276
277 /*!
278 Expands the selection to include cell \a row, \a col. The new
279 selection rectangle is the bounding rectangle of \a row, \a col
280 and the previous selection rectangle. After calling this function
281 the selection is active.
282
283 If you haven't called init(), this function does nothing.
284
285 \sa init() isActive()
286 */
287
expandTo(int row,int col)288 void Q3TableSelection::expandTo(int row, int col)
289 {
290 if (!inited)
291 return;
292 active = true;
293
294 if (row < aRow) {
295 tRow = row;
296 bRow = aRow;
297 } else {
298 tRow = aRow;
299 bRow = row;
300 }
301
302 if (col < aCol) {
303 lCol = col;
304 rCol = aCol;
305 } else {
306 lCol = aCol;
307 rCol = col;
308 }
309 }
310
311 /*!
312 Returns true if \a s includes the same cells as the selection;
313 otherwise returns false.
314 */
315
operator ==(const Q3TableSelection & s) const316 bool Q3TableSelection::operator==(const Q3TableSelection &s) const
317 {
318 return (s.active == active &&
319 s.tRow == tRow && s.bRow == bRow &&
320 s.lCol == lCol && s.rCol == rCol);
321 }
322
323 /*!
324 \fn bool Q3TableSelection::operator!=(const Q3TableSelection &s) const
325
326 Returns true if \a s does not include the same cells as the
327 selection; otherwise returns false.
328 */
329
330
331 /*!
332 \fn int Q3TableSelection::topRow() const
333
334 Returns the top row of the selection.
335
336 \sa bottomRow() leftCol() rightCol()
337 */
338
339 /*!
340 \fn int Q3TableSelection::bottomRow() const
341
342 Returns the bottom row of the selection.
343
344 \sa topRow() leftCol() rightCol()
345 */
346
347 /*!
348 \fn int Q3TableSelection::leftCol() const
349
350 Returns the left column of the selection.
351
352 \sa topRow() bottomRow() rightCol()
353 */
354
355 /*!
356 \fn int Q3TableSelection::rightCol() const
357
358 Returns the right column of the selection.
359
360 \sa topRow() bottomRow() leftCol()
361 */
362
363 /*!
364 \fn int Q3TableSelection::anchorRow() const
365
366 Returns the anchor row of the selection.
367
368 \sa anchorCol() expandTo()
369 */
370
371 /*!
372 \fn int Q3TableSelection::anchorCol() const
373
374 Returns the anchor column of the selection.
375
376 \sa anchorRow() expandTo()
377 */
378
379 /*!
380 \fn int Q3TableSelection::numRows() const
381
382 Returns the number of rows in the selection.
383
384 \sa numCols()
385 */
numRows() const386 int Q3TableSelection::numRows() const
387 {
388 return (tRow < 0) ? 0 : bRow - tRow + 1;
389 }
390
391 /*!
392 Returns the number of columns in the selection.
393
394 \sa numRows()
395 */
numCols() const396 int Q3TableSelection::numCols() const
397 {
398 return (lCol < 0) ? 0 : rCol - lCol + 1;
399 }
400
401 /*!
402 \fn bool Q3TableSelection::isActive() const
403
404 Returns whether the selection is active or not. A selection is
405 active after init() \e and expandTo() have been called.
406 */
407
408 /*!
409 \fn bool Q3TableSelection::isEmpty() const
410
411 Returns whether the selection is empty or not.
412
413 \sa numRows(), numCols()
414 */
415
416 /*!
417 \class Q3TableItem
418 \brief The Q3TableItem class provides the cell content for Q3Table cells.
419
420 \compat
421
422 For many applications Q3TableItems are ideal for presenting and
423 editing the contents of Q3Table cells. In situations where you need
424 to create very large tables you may prefer an alternative approach
425 to using Q3TableItems: see the notes on large tables.
426
427 A Q3TableItem contains a cell's data, by default, a string and a
428 pixmap. The table item also holds the cell's display size and how
429 the data should be aligned. The table item specifies the cell's
430 \l EditType and the editor used for in-place editing (by default a
431 QLineEdit). If you want checkboxes use \l{Q3CheckTableItem}, and if
432 you want comboboxes use \l{Q3ComboTableItem}. The \l EditType (set
433 in the constructor) determines whether the cell's contents may be
434 edited.
435
436 If a pixmap is specified it is displayed to the left of any text.
437 You can change the text or pixmap with setText() and setPixmap()
438 respectively. For text you can use setWordWrap().
439
440 When sorting table items the key() function is used; by default
441 this returns the table item's text(). Reimplement key() to
442 customize how your table items will sort.
443
444 Table items are inserted into a table using Q3Table::setItem(). If
445 you insert an item into a cell that already contains a table item
446 the original item will be deleted.
447
448 Example:
449 \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 0
450
451 You can move a table item from one cell to another, in the same or
452 a different table, using Q3Table::takeItem() and Q3Table::setItem()
453 but see also Q3Table::swapCells().
454
455 Table items can be deleted with delete in the standard way; the
456 table and cell will be updated accordingly.
457
458 Note, that if you have a table item that is not currently in a table
459 then anything you do to that item other than insert it into a table
460 will result in undefined behaviour.
461
462 Reimplement createEditor() and setContentFromEditor() if you want
463 to use your own widget instead of a QLineEdit for editing cell
464 contents. Reimplement paint() if you want to display custom
465 content.
466
467 It is important to ensure that your custom widget can accept the
468 keyboard focus, so that the user can use the tab key to navigate the
469 table as normal. Therefore, if the widget returned by createEditor()
470 does not itself accept the keyboard focus, it is necessary to
471 nominate a child widget to do so on its behalf. For example, a
472 QHBox with two child QLineEdit widgets may use one of them to
473 accept the keyboard focus:
474
475 \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 1
476
477 By default, table items may be replaced by new Q3TableItems
478 during the lifetime of a Q3Table. Therefore, if you create your
479 own subclass of Q3TableItem, and you want to ensure that
480 this does not happen, you must call setReplaceable(false)
481 in the constructor of your subclass.
482
483 \img qtableitems.png Table Items
484
485 \sa Q3CheckTableItem Q3ComboTableItem
486
487 */
488
489 /*!
490 \fn Q3Table *Q3TableItem::table() const
491
492 Returns the Q3Table the table item belongs to.
493
494 \sa Q3Table::setItem() Q3TableItem()
495 */
496
497 /*!
498 \enum Q3TableItem::EditType
499
500 \target wheneditable
501 This enum is used to define whether a cell is editable or
502 read-only (in conjunction with other settings), and how the cell
503 should be displayed.
504
505 \value Always
506 The cell always \e looks editable.
507
508 Using this EditType ensures that the editor created with
509 createEditor() (by default a QLineEdit) is always visible. This
510 has implications for the alignment of the content: the default
511 editor aligns everything (even numbers) to the left whilst
512 numerical values in the cell are by default aligned to the right.
513
514 If a cell with the edit type \c Always looks misaligned you could
515 reimplement createEditor() for these items.
516
517 \value WhenCurrent
518 The cell \e looks editable only when it has keyboard focus (see
519 Q3Table::setCurrentCell()).
520
521 \value OnTyping
522 The cell \e looks editable only when the user types in it or
523 double-clicks it. It resembles the \c WhenCurrent functionality
524 but is, perhaps, nicer.
525
526 The \c OnTyping edit type is the default when Q3TableItem objects
527 are created by the convenience functions Q3Table::setText() and
528 Q3Table::setPixmap().
529
530 \value Never The cell is not editable.
531
532 The cell is actually editable only if Q3Table::isRowReadOnly() is
533 false for its row, Q3Table::isColumnReadOnly() is false for its
534 column, and Q3Table::isReadOnly() is false.
535
536 Q3ComboTableItems have an isEditable() property. This property is
537 used to indicate whether the user may enter their own text or are
538 restricted to choosing one of the choices in the list.
539 Q3ComboTableItems may be interacted with only if they are editable
540 in accordance with their EditType as described above.
541
542 */
543
544 /*!
545 Creates a table item that is a child of table \a table with no
546 text. The item has the \l EditType \a et.
547
548 The table item will use a QLineEdit for its editor, will not
549 word-wrap and will occupy a single cell. Insert the table item
550 into a table with Q3Table::setItem().
551
552 The table takes ownership of the table item, so a table item
553 should not be inserted into more than one table at a time.
554 */
555
Q3TableItem(Q3Table * table,EditType et)556 Q3TableItem::Q3TableItem(Q3Table *table, EditType et)
557 : txt(), pix(), t(table), edType(et), wordwrap(false),
558 tcha(true), rw(-1), cl(-1), rowspan(1), colspan(1)
559 {
560 enabled = true;
561 }
562
563 /*!
564 Creates a table item that is a child of table \a table with text
565 \a text. The item has the \l EditType \a et.
566
567 The table item will use a QLineEdit for its editor, will not
568 word-wrap and will occupy a single cell. Insert the table item
569 into a table with Q3Table::setItem().
570
571 The table takes ownership of the table item, so a table item
572 should not be inserted into more than one table at a time.
573 */
574
Q3TableItem(Q3Table * table,EditType et,const QString & text)575 Q3TableItem::Q3TableItem(Q3Table *table, EditType et, const QString &text)
576 : txt(text), pix(), t(table), edType(et), wordwrap(false),
577 tcha(true), rw(-1), cl(-1), rowspan(1), colspan(1)
578 {
579 enabled = true;
580 }
581
582 /*!
583 Creates a table item that is a child of table \a table with text
584 \a text and pixmap \a p. The item has the \l EditType \a et.
585
586 The table item will display the pixmap to the left of the text. It
587 will use a QLineEdit for editing the text, will not word-wrap and
588 will occupy a single cell. Insert the table item into a table with
589 Q3Table::setItem().
590
591 The table takes ownership of the table item, so a table item
592 should not be inserted in more than one table at a time.
593 */
594
Q3TableItem(Q3Table * table,EditType et,const QString & text,const QPixmap & p)595 Q3TableItem::Q3TableItem(Q3Table *table, EditType et,
596 const QString &text, const QPixmap &p)
597 : txt(text), pix(p), t(table), edType(et), wordwrap(false),
598 tcha(true), rw(-1), cl(-1), rowspan(1), colspan(1)
599 {
600 enabled = true;
601 }
602
603 /*!
604 The destructor deletes this item and frees all allocated
605 resources.
606
607 If the table item is in a table (i.e. was inserted with
608 setItem()), it will be removed from the table and the cell it
609 occupied.
610 */
611
~Q3TableItem()612 Q3TableItem::~Q3TableItem()
613 {
614 if (table())
615 table()->takeItem(this);
616 }
617
618 int Q3TableItem::RTTI = 0;
619
620 /*!
621 Returns the Run Time Type Identification value for this table item
622 which for Q3TableItems is 0.
623
624 When you create subclasses based on Q3TableItem make sure that each
625 subclass returns a unique rtti() value. It is advisable to use
626 values greater than 1000, preferably large random numbers, to
627 allow for extensions to this class.
628
629 \sa Q3CheckTableItem::rtti() Q3ComboTableItem::rtti()
630 */
631
rtti() const632 int Q3TableItem::rtti() const
633 {
634 return RTTI;
635 }
636
637 /*!
638 Returns the table item's pixmap or a null pixmap if no pixmap has
639 been set.
640
641 \sa setPixmap() text()
642 */
643
pixmap() const644 QPixmap Q3TableItem::pixmap() const
645 {
646 return pix;
647 }
648
649
650 /*!
651 Returns the text of the table item or an empty string if there is
652 no text.
653
654 To ensure that the current value of the editor is returned,
655 setContentFromEditor() is called:
656 \list 1
657 \i if the editMode() is \c Always, or
658 \i if editMode() is \e not \c Always but the editor of the cell is
659 active and the editor is not a QLineEdit.
660 \endlist
661
662 This means that text() returns the original text value of the item
663 if the editor is a line edit, until the user commits an edit (e.g.
664 by pressing Enter or Tab) in which case the new text is returned.
665 For other editors (e.g. a combobox) setContentFromEditor() is
666 always called so the currently display value is the one returned.
667
668 \sa setText() pixmap()
669 */
670
text() const671 QString Q3TableItem::text() const
672 {
673 QWidget *w = table()->cellWidget(rw, cl);
674 if (w && (edType == Always ||
675 rtti() == Q3ComboTableItem::RTTI ||
676 rtti() == Q3CheckTableItem::RTTI))
677 ((Q3TableItem*)this)->setContentFromEditor(w);
678 return txt;
679 }
680
681 /*!
682 Sets pixmap \a p to be this item's pixmap.
683
684 Note that setPixmap() does not update the cell the table item
685 belongs to. Use Q3Table::updateCell() to repaint the cell's
686 contents.
687
688 For \l{Q3ComboTableItem}s and \l{Q3CheckTableItem}s this function
689 has no visible effect.
690
691 \sa Q3Table::setPixmap() pixmap() setText()
692 */
693
setPixmap(const QPixmap & p)694 void Q3TableItem::setPixmap(const QPixmap &p)
695 {
696 pix = p;
697 }
698
699 /*!
700 Changes the table item's text to \a str.
701
702 Note that setText() does not update the cell the table item
703 belongs to. Use Q3Table::updateCell() to repaint the cell's
704 contents.
705
706 \sa Q3Table::setText() text() setPixmap() Q3Table::updateCell()
707 */
708
setText(const QString & str)709 void Q3TableItem::setText(const QString &str)
710 {
711 txt = str;
712 }
713
714 /*!
715 This virtual function is used to paint the contents of an item
716 using the painter \a p in the rectangular area \a cr using the
717 color group \a cg.
718
719 If \a selected is true the cell is displayed in a way that
720 indicates that it is highlighted.
721
722 You don't usually need to use this function but if you want to
723 draw custom content in a cell you will need to reimplement it.
724
725 The painter passed to this function is translated so that 0, 0
726 is the top-left corner of the item that is being painted.
727
728 Note that the painter is not clipped by default in order to get
729 maximum efficiency. If you want clipping, use
730
731 \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 2
732
733 */
734
paint(QPainter * p,const QColorGroup & cg,const QRect & cr,bool selected)735 void Q3TableItem::paint(QPainter *p, const QColorGroup &cg,
736 const QRect &cr, bool selected)
737 {
738 p->fillRect(0, 0, cr.width(), cr.height(),
739 selected ? cg.brush(QColorGroup::Highlight)
740 : cg.brush(QColorGroup::Base));
741
742 int w = cr.width();
743 int h = cr.height();
744
745 int x = 0;
746 if (!pix.isNull()) {
747 p->drawPixmap(0, (cr.height() - pix.height()) / 2, pix);
748 x = pix.width() + 2;
749 }
750
751 if (selected)
752 p->setPen(cg.highlightedText());
753 else
754 p->setPen(cg.text());
755 p->drawText(x + 2, 0, w - x - 4, h,
756 wordwrap ? (alignment() | WordBreak) : alignment(), text());
757 }
758
759 /*!
760 This virtual function creates an editor which the user can
761 interact with to edit the cell's contents. The default
762 implementation creates a QLineEdit.
763
764 If the function returns 0, the cell is read-only.
765
766 The returned widget should preferably be invisible, ideally with
767 Q3Table::viewport() as parent.
768
769 If you reimplement this function you'll almost certainly need to
770 reimplement setContentFromEditor(), and may need to reimplement
771 sizeHint().
772
773 \sa Q3Table::createEditor() setContentFromEditor() Q3Table::viewport() setReplaceable()
774 */
775
createEditor() const776 QWidget *Q3TableItem::createEditor() const
777 {
778 QLineEdit *e = new QLineEdit(table()->viewport(), "qt_tableeditor");
779 e->setFrame(false);
780 e->setText(text());
781 return e;
782 }
783
784 /*!
785 Whenever the content of a cell has been edited by the editor \a w,
786 Q3Table calls this virtual function to copy the new values into the
787 Q3TableItem.
788
789 If you reimplement createEditor() and return something that is not
790 a QLineEdit you will need to reimplement this function.
791
792 \sa Q3Table::setCellContentFromEditor()
793 */
794
setContentFromEditor(QWidget * w)795 void Q3TableItem::setContentFromEditor(QWidget *w)
796 {
797 QLineEdit *le = qobject_cast<QLineEdit*>(w);
798 if (le) {
799 QString input = le->text();
800 if (le->validator())
801 le->validator()->fixup(input);
802 setText(input);
803 }
804 }
805
806 /*!
807 The alignment function returns how the text contents of the cell
808 are aligned when drawn. The default implementation aligns numbers
809 to the right and any other text to the left.
810
811 \sa Qt::Alignment
812 */
813
814 // ed: For consistency reasons a setAlignment() should be provided
815 // as well.
816
alignment() const817 int Q3TableItem::alignment() const
818 {
819 bool num;
820 bool ok1 = false, ok2 = false;
821 (void)text().toInt(&ok1);
822 if (!ok1)
823 (void)text().toDouble(&ok2); // ### should be .-aligned
824 num = ok1 || ok2;
825
826 return (num ? AlignRight : AlignLeft) | AlignVCenter;
827 }
828
829 /*!
830 If \a b is true, the cell's text will be wrapped over multiple
831 lines, when necessary, to fit the width of the cell; otherwise the
832 text will be written as a single line.
833
834 \sa wordWrap() Q3Table::adjustColumn() Q3Table::setColumnStretchable()
835 */
836
setWordWrap(bool b)837 void Q3TableItem::setWordWrap(bool b)
838 {
839 wordwrap = b;
840 }
841
842 /*!
843 Returns true if word wrap is enabled for the cell; otherwise
844 returns false.
845
846 \sa setWordWrap()
847 */
848
wordWrap() const849 bool Q3TableItem::wordWrap() const
850 {
851 return wordwrap;
852 }
853
854 /*! \internal */
855
updateEditor(int oldRow,int oldCol)856 void Q3TableItem::updateEditor(int oldRow, int oldCol)
857 {
858 if (edType != Always)
859 return;
860 if (oldRow != -1 && oldCol != -1)
861 table()->clearCellWidget(oldRow, oldCol);
862 if (rw != -1 && cl != -1)
863 table()->setCellWidget(rw, cl, createEditor());
864 }
865
866 /*!
867 Returns the table item's edit type.
868
869 This is set when the table item is constructed.
870
871 \sa EditType Q3TableItem()
872 */
873
editType() const874 Q3TableItem::EditType Q3TableItem::editType() const
875 {
876 return edType;
877 }
878
879 /*!
880 If \a b is true it is acceptable to replace the contents of the
881 cell with the contents of another Q3TableItem. If \a b is false the
882 contents of the cell may not be replaced by the contents of
883 another table item. Table items that span more than one cell may
884 not have their contents replaced by another table item.
885
886 (This differs from \l EditType because EditType is concerned with
887 whether the \e user is able to change the contents of a cell.)
888
889 \sa isReplaceable()
890 */
891
setReplaceable(bool b)892 void Q3TableItem::setReplaceable(bool b)
893 {
894 tcha = b;
895 }
896
897 /*!
898 This function returns whether the contents of the cell may be
899 replaced with the contents of another table item. Regardless of
900 this setting, table items that span more than one cell may not
901 have their contents replaced by another table item.
902
903 (This differs from \l EditType because EditType is concerned with
904 whether the \e user is able to change the contents of a cell.)
905
906 \sa setReplaceable() EditType
907 */
908
isReplaceable() const909 bool Q3TableItem::isReplaceable() const
910 {
911 if (rowspan > 1 || colspan > 1)
912 return false;
913 return tcha;
914 }
915
916 /*!
917 This virtual function returns the key that should be used for
918 sorting. The default implementation returns the text() of the
919 relevant item.
920
921 \sa Q3Table::setSorting()
922 */
923
key() const924 QString Q3TableItem::key() const
925 {
926 return text();
927 }
928
929 /*!
930 This virtual function returns the size a cell needs to show its
931 entire content.
932
933 If you subclass Q3TableItem you will often need to reimplement this
934 function.
935 */
936
sizeHint() const937 QSize Q3TableItem::sizeHint() const
938 {
939 QSize strutSize = QApplication::globalStrut();
940 if (edType == Always && table()->cellWidget(rw, cl))
941 return table()->cellWidget(rw, cl)->sizeHint().expandedTo(strutSize);
942
943 QSize s;
944 int x = 0;
945 if (!pix.isNull()) {
946 s = pix.size();
947 s.setWidth(s.width() + 2);
948 x = pix.width() + 2;
949 }
950
951 QString t = text();
952 if (!wordwrap && t.find(QLatin1Char('\n')) == -1)
953 return QSize(s.width() + table()->fontMetrics().width(text()) + 10,
954 QMAX(s.height(), table()->fontMetrics().height())).expandedTo(strutSize);
955
956 QRect r = table()->fontMetrics().boundingRect(x + 2, 0, table()->columnWidth(col()) - x - 4, 0,
957 wordwrap ? (alignment() | WordBreak) : alignment(),
958 text());
959 r.setWidth(QMAX(r.width() + 10, table()->columnWidth(col())));
960 return QSize(r.width(), QMAX(s.height(), r.height())).expandedTo(strutSize);
961 }
962
963 /*!
964 Changes the extent of the Q3TableItem so that it spans multiple
965 cells covering \a rs rows and \a cs columns. The top left cell is
966 the original cell.
967
968 \warning This function only works if the item has already been
969 inserted into the table using e.g. Q3Table::setItem(). This
970 function also checks to make sure if \a rs and \a cs are within
971 the bounds of the table and returns without changing the span if
972 they are not. In addition swapping, inserting or removing rows and
973 columns that cross Q3TableItems spanning more than one cell is not
974 supported.
975
976 \sa rowSpan() colSpan()
977 */
978
setSpan(int rs,int cs)979 void Q3TableItem::setSpan(int rs, int cs)
980 {
981 if (rs == rowspan && cs == colspan)
982 return;
983
984 if (!table()->d->hasRowSpan)
985 table()->d->hasRowSpan = rs > 1;
986 if (!table()->d->hasColSpan)
987 table()->d->hasColSpan = cs > 1;
988 // return if we are thinking too big...
989 if (rw + rs > table()->numRows())
990 return;
991
992 if (cl + cs > table()->numCols())
993 return;
994
995 if (rw == -1 || cl == -1)
996 return;
997
998 int rrow = rw;
999 int rcol = cl;
1000 if (rowspan > 1 || colspan > 1) {
1001 Q3Table* t = table();
1002 t->takeItem(this);
1003 t->setItem(rrow, rcol, this);
1004 }
1005
1006 rowspan = rs;
1007 colspan = cs;
1008
1009 for (int r = 0; r < rowspan; ++r) {
1010 for (int c = 0; c < colspan; ++c) {
1011 if (r == 0 && c == 0)
1012 continue;
1013 qt_update_cell_widget = false;
1014 table()->setItem(r + rw, c + cl, this);
1015 qt_update_cell_widget = true;
1016 rw = rrow;
1017 cl = rcol;
1018 }
1019 }
1020
1021 table()->updateCell(rw, cl);
1022 QWidget *w = table()->cellWidget(rw, cl);
1023 if (w)
1024 w->resize(table()->cellGeometry(rw, cl).size());
1025 }
1026
1027 /*!
1028 Returns the row span of the table item, usually 1.
1029
1030 \sa setSpan() colSpan()
1031 */
1032
rowSpan() const1033 int Q3TableItem::rowSpan() const
1034 {
1035 return rowspan;
1036 }
1037
1038 /*!
1039 Returns the column span of the table item, usually 1.
1040
1041 \sa setSpan() rowSpan()
1042 */
1043
colSpan() const1044 int Q3TableItem::colSpan() const
1045 {
1046 return colspan;
1047 }
1048
1049 /*!
1050 Sets row \a r as the table item's row. Usually you do not need to
1051 call this function.
1052
1053 If the cell spans multiple rows, this function sets the top row
1054 and retains the height of the multi-cell table item.
1055
1056 \sa row() setCol() rowSpan()
1057 */
1058
setRow(int r)1059 void Q3TableItem::setRow(int r)
1060 {
1061 rw = r;
1062 }
1063
1064 /*!
1065 Sets column \a c as the table item's column. Usually you will not
1066 need to call this function.
1067
1068 If the cell spans multiple columns, this function sets the
1069 left-most column and retains the width of the multi-cell table
1070 item.
1071
1072 \sa col() setRow() colSpan()
1073 */
1074
setCol(int c)1075 void Q3TableItem::setCol(int c)
1076 {
1077 cl = c;
1078 }
1079
1080 /*!
1081 Returns the row where the table item is located. If the cell spans
1082 multiple rows, this function returns the top-most row.
1083
1084 \sa col() setRow()
1085 */
1086
row() const1087 int Q3TableItem::row() const
1088 {
1089 return rw;
1090 }
1091
1092 /*!
1093 Returns the column where the table item is located. If the cell
1094 spans multiple columns, this function returns the left-most
1095 column.
1096
1097 \sa row() setCol()
1098 */
1099
col() const1100 int Q3TableItem::col() const
1101 {
1102 return cl;
1103 }
1104
1105 /*!
1106 If \a b is true, the table item is enabled; if \a b is false the
1107 table item is disabled.
1108
1109 A disabled item doesn't respond to user interaction.
1110
1111 \sa isEnabled()
1112 */
1113
setEnabled(bool b)1114 void Q3TableItem::setEnabled(bool b)
1115 {
1116 if (b == (bool)enabled)
1117 return;
1118 enabled = b;
1119 table()->updateCell(row(), col());
1120 }
1121
1122 /*!
1123 Returns true if the table item is enabled; otherwise returns false.
1124
1125 \sa setEnabled()
1126 */
1127
isEnabled() const1128 bool Q3TableItem::isEnabled() const
1129 {
1130 return (bool)enabled;
1131 }
1132
1133 /*!
1134 \class Q3ComboTableItem
1135 \brief The Q3ComboTableItem class provides a means of using
1136 comboboxes in Q3Tables.
1137
1138 \compat
1139
1140 A Q3ComboTableItem is a table item which looks and behaves like a
1141 combobox. The advantage of using Q3ComboTableItems rather than real
1142 comboboxes is that a Q3ComboTableItem uses far less resources than
1143 real comboboxes in \l{Q3Table}s. When the cell has the focus it
1144 displays a real combobox which the user can interact with. When
1145 the cell does not have the focus the cell \e looks like a
1146 combobox. Only text items (i.e. no pixmaps) may be used in
1147 Q3ComboTableItems.
1148
1149 Q3ComboTableItem items have the edit type \c WhenCurrent (see
1150 \l{EditType}). The Q3ComboTableItem's list of items is provided by
1151 a QStringList passed to the constructor.
1152
1153 The list of items may be changed using setStringList(). The
1154 current item can be set with setCurrentItem() and retrieved with
1155 currentItem(). The text of the current item can be obtained with
1156 currentText(), and the text of a particular item can be retrieved
1157 with text().
1158
1159 If isEditable() is true the Q3ComboTableItem will permit the user
1160 to either choose an existing list item, or create a new list item
1161 by entering their own text; otherwise the user may only choose one
1162 of the existing list items.
1163
1164 To populate a table cell with a Q3ComboTableItem use
1165 Q3Table::setItem().
1166
1167 Q3ComboTableItems may be deleted with Q3Table::clearCell().
1168
1169 Q3ComboTableItems can be distinguished from \l{Q3TableItem}s and
1170 \l{Q3CheckTableItem}s using their Run Time Type Identification
1171 number (see rtti()).
1172
1173 \img qtableitems.png Table Items
1174
1175 \sa Q3CheckTableItem Q3TableItem Q3ComboBox
1176 */
1177
1178 Q3ComboBox *Q3ComboTableItem::fakeCombo = 0;
1179 QWidget *Q3ComboTableItem::fakeComboWidget = 0;
1180 int Q3ComboTableItem::fakeRef = 0;
1181
1182 /*!
1183 Creates a combo table item for the table \a table. The combobox's
1184 list of items is passed in the \a list argument. If \a editable is
1185 true the user may type in new list items; if \a editable is false
1186 the user may only select from the list of items provided.
1187
1188 By default Q3ComboTableItems cannot be replaced by other table
1189 items since isReplaceable() returns false by default.
1190
1191 \sa Q3Table::clearCell() EditType
1192 */
1193
Q3ComboTableItem(Q3Table * table,const QStringList & list,bool editable)1194 Q3ComboTableItem::Q3ComboTableItem(Q3Table *table, const QStringList &list, bool editable)
1195 : Q3TableItem(table, WhenCurrent, QLatin1String("")), entries(list), current(0), edit(editable)
1196 {
1197 setReplaceable(false);
1198 if (!Q3ComboTableItem::fakeCombo) {
1199 Q3ComboTableItem::fakeComboWidget = new QWidget(0, 0);
1200 Q3ComboTableItem::fakeCombo = new Q3ComboBox(false, Q3ComboTableItem::fakeComboWidget, 0);
1201 Q3ComboTableItem::fakeCombo->hide();
1202 }
1203 ++Q3ComboTableItem::fakeRef;
1204 if (entries.count())
1205 setText(entries.at(current));
1206 }
1207
1208 /*!
1209 Q3ComboTableItem destructor.
1210 */
~Q3ComboTableItem()1211 Q3ComboTableItem::~Q3ComboTableItem()
1212 {
1213 if (--Q3ComboTableItem::fakeRef <= 0) {
1214 delete Q3ComboTableItem::fakeComboWidget;
1215 Q3ComboTableItem::fakeComboWidget = 0;
1216 Q3ComboTableItem::fakeCombo = 0;
1217 }
1218 }
1219
1220 /*!
1221 Sets the list items of this Q3ComboTableItem to the strings in the
1222 string list \a l.
1223 */
1224
setStringList(const QStringList & l)1225 void Q3ComboTableItem::setStringList(const QStringList &l)
1226 {
1227 entries = l;
1228 current = 0;
1229 if (entries.count())
1230 setText(entries.at(current));
1231 if (table()->cellWidget(row(), col())) {
1232 cb->clear();
1233 cb->insertStringList(entries);
1234 }
1235 table()->updateCell(row(), col());
1236 }
1237
1238 /*! \reimp */
1239
createEditor() const1240 QWidget *Q3ComboTableItem::createEditor() const
1241 {
1242 // create an editor - a combobox in our case
1243 ((Q3ComboTableItem*)this)->cb = new Q3ComboBox(edit, table()->viewport(), "qt_editor_cb");
1244 cb->insertStringList(entries);
1245 cb->setCurrentItem(current);
1246 QObject::connect(cb, SIGNAL(activated(int)), table(), SLOT(doValueChanged()));
1247 return cb;
1248 }
1249
1250 /*! \reimp */
1251
setContentFromEditor(QWidget * w)1252 void Q3ComboTableItem::setContentFromEditor(QWidget *w)
1253 {
1254 Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
1255 if (cb) {
1256 entries.clear();
1257 for (int i = 0; i < cb->count(); ++i)
1258 entries << cb->text(i);
1259 current = cb->currentItem();
1260 setText(cb->currentText());
1261 }
1262 }
1263
1264 /*! \reimp */
1265
paint(QPainter * p,const QColorGroup & cg,const QRect & cr,bool selected)1266 void Q3ComboTableItem::paint(QPainter *p, const QColorGroup &cg,
1267 const QRect &cr, bool selected)
1268 {
1269 fakeCombo->resize(cr.width(), cr.height());
1270
1271 QPalette pal2(cg);
1272 if (selected) {
1273 pal2.setBrush(QPalette::Base, cg.QPalette::brush(QPalette::Highlight));
1274 pal2.setColor(QPalette::Text, cg.highlightedText());
1275 }
1276
1277 QStyle::State flags = QStyle::State_None;
1278 if(isEnabled() && table()->isEnabled())
1279 flags |= QStyle::State_Enabled;
1280 // Since we still have the "fakeCombo" may as well use it in this case.
1281 QStyleOptionComboBox opt;
1282 opt.initFrom(table());
1283 opt.rect = fakeCombo->rect();
1284 opt.palette = pal2;
1285 opt.state &= ~QStyle::State_HasFocus;
1286 opt.state &= ~QStyle::State_MouseOver;
1287 opt.state |= flags;
1288 opt.subControls = QStyle::SC_All;
1289 opt.activeSubControls = QStyle::SC_None;
1290 opt.editable = fakeCombo->editable();
1291 table()->style()->drawComplexControl(QStyle::CC_ComboBox, &opt, p, fakeCombo);
1292
1293 p->save();
1294 QRect textR = table()->style()->subControlRect(QStyle::CC_ComboBox, &opt,
1295 QStyle::SC_ComboBoxEditField, fakeCombo);
1296 int align = alignment(); // alignment() changes entries
1297 p->drawText(textR, wordWrap() ? (align | Qt::WordBreak) : align, entries.value(current));
1298 p->restore();
1299 }
1300
1301 /*!
1302 Sets the list item \a i to be the combo table item's current list
1303 item.
1304
1305 \sa currentItem()
1306 */
1307
setCurrentItem(int i)1308 void Q3ComboTableItem::setCurrentItem(int i)
1309 {
1310 QWidget *w = table()->cellWidget(row(), col());
1311 Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
1312 if (cb) {
1313 cb->setCurrentItem(i);
1314 current = cb->currentItem();
1315 setText(cb->currentText());
1316 } else {
1317 if (i < 0 || i >= entries.count())
1318 return;
1319 current = i;
1320 setText(entries.at(i));
1321 table()->updateCell(row(), col());
1322 }
1323 }
1324
1325 /*!
1326 \overload
1327
1328 Sets the list item whose text is \a s to be the combo table item's
1329 current list item. Does nothing if no list item has the text \a s.
1330
1331 \sa currentItem()
1332 */
1333
setCurrentItem(const QString & s)1334 void Q3ComboTableItem::setCurrentItem(const QString &s)
1335 {
1336 int i = entries.findIndex(s);
1337 if (i != -1)
1338 setCurrentItem(i);
1339 }
1340
1341 /*!
1342 Returns the index of the combo table item's current list item.
1343
1344 \sa setCurrentItem()
1345 */
1346
currentItem() const1347 int Q3ComboTableItem::currentItem() const
1348 {
1349 QWidget *w = table()->cellWidget(row(), col());
1350 Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
1351 if (cb)
1352 return cb->currentItem();
1353 return current;
1354 }
1355
1356 /*!
1357 Returns the text of the combo table item's current list item.
1358
1359 \sa currentItem() text()
1360 */
1361
currentText() const1362 QString Q3ComboTableItem::currentText() const
1363 {
1364 QWidget *w = table()->cellWidget(row(), col());
1365 Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
1366 if (cb)
1367 return cb->currentText();
1368 return entries.value(current);
1369 }
1370
1371 /*!
1372 Returns the total number of list items in the combo table item.
1373 */
1374
count() const1375 int Q3ComboTableItem::count() const
1376 {
1377 QWidget *w = table()->cellWidget(row(), col());
1378 Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
1379 if (cb)
1380 return cb->count();
1381 return (int)entries.count();
1382 }
1383
1384 /*!
1385 Returns the text of the combo's list item at index \a i.
1386
1387 \sa currentText()
1388 */
1389
text(int i) const1390 QString Q3ComboTableItem::text(int i) const
1391 {
1392 QWidget *w = table()->cellWidget(row(), col());
1393 Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
1394 if (cb)
1395 return cb->text(i);
1396 return entries.value(i);
1397 }
1398
1399 /*!
1400 If \a b is true the combo table item can be edited, i.e. the user
1401 may enter a new text item themselves. If \a b is false the user may
1402 may only choose one of the existing items.
1403
1404 \sa isEditable()
1405 */
1406
setEditable(bool b)1407 void Q3ComboTableItem::setEditable(bool b)
1408 {
1409 edit = b;
1410 }
1411
1412 /*!
1413 Returns true if the user can add their own list items to the
1414 combobox's list of items; otherwise returns false.
1415
1416 \sa setEditable()
1417 */
1418
isEditable() const1419 bool Q3ComboTableItem::isEditable() const
1420 {
1421 return edit;
1422 }
1423
1424 int Q3ComboTableItem::RTTI = 1;
1425
1426 /*!
1427 \fn int Q3ComboTableItem::rtti() const
1428
1429 Returns 1.
1430
1431 Make your derived classes return their own values for rtti()to
1432 distinguish between different table item subclasses. You should
1433 use values greater than 1000, preferably a large random number, to
1434 allow for extensions to this class.
1435
1436
1437 \sa Q3TableItem::rtti()
1438 */
1439
rtti() const1440 int Q3ComboTableItem::rtti() const
1441 {
1442 return RTTI;
1443 }
1444
1445 /*! \reimp */
1446
sizeHint() const1447 QSize Q3ComboTableItem::sizeHint() const
1448 {
1449 fakeCombo->insertItem(currentText());
1450 fakeCombo->setCurrentItem(fakeCombo->count() - 1);
1451 QSize sh = fakeCombo->sizeHint();
1452 fakeCombo->removeItem(fakeCombo->count() - 1);
1453 return sh.expandedTo(QApplication::globalStrut());
1454 }
1455
1456 /*!
1457 \fn QString Q3ComboTableItem::text() const
1458
1459 Returns the text of the table item or an empty string if there is
1460 no text.
1461
1462 \sa Q3TableItem::text()
1463 */
1464
1465 /*!
1466 \class Q3CheckTableItem
1467 \brief The Q3CheckTableItem class provides checkboxes in Q3Tables.
1468
1469 \compat
1470
1471 A Q3CheckTableItem is a table item which looks and behaves like a
1472 checkbox. The advantage of using Q3CheckTableItems rather than real
1473 checkboxes is that a Q3CheckTableItem uses far less resources than
1474 a real checkbox would in a \l{Q3Table}. When the cell has the focus
1475 it displays a real checkbox which the user can interact with. When
1476 the cell does not have the focus the cell \e looks like a
1477 checkbox. Pixmaps may not be used in Q3CheckTableItems.
1478
1479 Q3CheckTableItem items have the edit type \c WhenCurrent (see
1480 \l{EditType}).
1481
1482 To change the checkbox's label use setText(). The checkbox can be
1483 checked and unchecked with setChecked() and its state retrieved
1484 using isChecked().
1485
1486 To populate a table cell with a Q3CheckTableItem use
1487 Q3Table::setItem().
1488
1489 Q3CheckTableItems can be distinguished from \l{Q3TableItem}s and
1490 \l{Q3ComboTableItem}s using their Run Time Type Identification
1491 (rtti) value.
1492
1493 \img qtableitems.png Table Items
1494
1495 \sa rtti() EditType Q3ComboTableItem Q3TableItem QCheckBox
1496 */
1497
1498 /*!
1499 Creates a Q3CheckTableItem with an \l{EditType} of \c WhenCurrent
1500 as a child of \a table. The checkbox is initially unchecked and
1501 its label is set to the string \a txt.
1502 */
1503
Q3CheckTableItem(Q3Table * table,const QString & txt)1504 Q3CheckTableItem::Q3CheckTableItem(Q3Table *table, const QString &txt)
1505 : Q3TableItem(table, WhenCurrent, txt), checked(false)
1506 {
1507 }
1508
1509 /*! \reimp */
1510
setText(const QString & t)1511 void Q3CheckTableItem::setText(const QString &t)
1512 {
1513 Q3TableItem::setText(t);
1514 QWidget *w = table()->cellWidget(row(), col());
1515 QCheckBox *cb = qobject_cast<QCheckBox*>(w);
1516 if (cb)
1517 cb->setText(t);
1518 }
1519
1520
1521 /*! \reimp */
1522
createEditor() const1523 QWidget *Q3CheckTableItem::createEditor() const
1524 {
1525 // create an editor - a combobox in our case
1526 ((Q3CheckTableItem*)this)->cb = new QCheckBox(table()->viewport(), "qt_editor_checkbox");
1527 cb->setChecked(checked);
1528 cb->setText(text());
1529 cb->setBackgroundColor(table()->viewport()->backgroundColor());
1530 cb->setAutoFillBackground(true);
1531 QObject::connect(cb, SIGNAL(toggled(bool)), table(), SLOT(doValueChanged()));
1532 return cb;
1533 }
1534
1535 /*! \reimp */
1536
setContentFromEditor(QWidget * w)1537 void Q3CheckTableItem::setContentFromEditor(QWidget *w)
1538 {
1539 QCheckBox *cb = qobject_cast<QCheckBox*>(w);
1540 if (cb)
1541 checked = cb->isChecked();
1542 }
1543
1544 /*! \reimp */
1545
paint(QPainter * p,const QColorGroup & cg,const QRect & cr,bool selected)1546 void Q3CheckTableItem::paint(QPainter *p, const QColorGroup &cg,
1547 const QRect &cr, bool selected)
1548 {
1549 QPalette pal = cg;
1550
1551 p->fillRect(0, 0, cr.width(), cr.height(),
1552 selected ? pal.brush(QPalette::Highlight)
1553 : pal.brush(QPalette::Base));
1554
1555 QSize sz = QSize(table()->style()->pixelMetric(QStyle::PM_IndicatorWidth),
1556 table()->style()->pixelMetric(QStyle::PM_IndicatorHeight));
1557 QPalette pal2(pal);
1558 pal2.setBrush(QPalette::Window, pal.brush(QPalette::Base));
1559 QStyleOptionButton opt;
1560 opt.initFrom(table());
1561 opt.rect.setRect(0, (cr.height() - sz.height()) / 2, sz.width(), sz.height());
1562 opt.palette = pal2;
1563 opt.state &= ~QStyle::State_HasFocus;
1564 opt.state &= ~QStyle::State_MouseOver;
1565 if(isEnabled())
1566 opt.state |= QStyle::State_Enabled;
1567 if (checked)
1568 opt.state |= QStyle::State_On;
1569 else
1570 opt.state |= QStyle::State_Off;
1571 if (isEnabled() && table()->isEnabled())
1572 opt.state |= QStyle::State_Enabled;
1573 table()->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, p, table());
1574 if (selected)
1575 p->setPen(pal.highlightedText().color());
1576 else
1577 p->setPen(pal.text().color());
1578 opt.rect.setRect(0, 0, cr.width(), cr.height());
1579 QRect textRect = table()->style()->subElementRect(QStyle::SE_CheckBoxContents, &opt, table());
1580 p->drawText(textRect, wordWrap() ? (alignment() | Qt::WordBreak) : alignment(), text());
1581 }
1582
1583 /*!
1584 If \a b is true the checkbox is checked; if \a b is false the
1585 checkbox is unchecked.
1586
1587 \sa isChecked()
1588 */
1589
setChecked(bool b)1590 void Q3CheckTableItem::setChecked(bool b)
1591 {
1592 checked = b;
1593 table()->updateCell(row(), col());
1594 QWidget *w = table()->cellWidget(row(), col());
1595 QCheckBox *cb = qobject_cast<QCheckBox*>(w);
1596 if (cb)
1597 cb->setChecked(b);
1598 }
1599
1600 /*!
1601 Returns true if the checkbox table item is checked; otherwise
1602 returns false.
1603
1604 \sa setChecked()
1605 */
1606
isChecked() const1607 bool Q3CheckTableItem::isChecked() const
1608 {
1609 // #### why was this next line here. It must not be here, as
1610 // #### people want to call isChecked() from within paintCell()
1611 // #### and end up in an infinite loop that way
1612 // table()->updateCell(row(), col());
1613 QWidget *w = table()->cellWidget(row(), col());
1614 QCheckBox *cb = qobject_cast<QCheckBox*>(w);
1615 if (cb)
1616 return cb->isChecked();
1617 return checked;
1618 }
1619
1620 int Q3CheckTableItem::RTTI = 2;
1621
1622 /*!
1623 \fn int Q3CheckTableItem::rtti() const
1624
1625 Returns 2.
1626
1627 Make your derived classes return their own values for rtti()to
1628 distinguish between different table item subclasses. You should
1629 use values greater than 1000, preferably a large random number, to
1630 allow for extensions to this class.
1631
1632 \sa Q3TableItem::rtti()
1633 */
1634
rtti() const1635 int Q3CheckTableItem::rtti() const
1636 {
1637 return RTTI;
1638 }
1639
1640 /*! \reimp */
1641
sizeHint() const1642 QSize Q3CheckTableItem::sizeHint() const
1643 {
1644 QSize sz = QSize(table()->style()->pixelMetric(QStyle::PM_IndicatorWidth),
1645 table()->style()->pixelMetric(QStyle::PM_IndicatorHeight));
1646 sz.setWidth(sz.width() + 6);
1647 QSize sh(Q3TableItem::sizeHint());
1648 return QSize(sh.width() + sz.width(), QMAX(sh.height(), sz.height())).
1649 expandedTo(QApplication::globalStrut());
1650 }
1651
1652 /*!
1653 \class Q3Table
1654 \brief The Q3Table class provides a flexible editable table widget.
1655
1656 \compat
1657
1658 Q3Table is easy to use, although it does have a large API because
1659 of the comprehensive functionality that it provides. Q3Table
1660 includes functions for manipulating \link #headers
1661 headers\endlink, \link #columnsrows rows and columns\endlink,
1662 \link #cells cells\endlink and \link #selections
1663 selections\endlink. Q3Table also provides in-place editing and
1664 drag and drop, as well as a useful set of
1665 \link #signals signals\endlink. Q3Table efficiently supports very
1666 large tables, for example, tables one million by one million cells
1667 are perfectly possible. Q3Table is economical with memory, using
1668 none for unused cells.
1669
1670 \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 3
1671
1672 The first line constructs the table specifying its size in rows
1673 and columns. We then insert a pixmap and some text into the \e
1674 same \link #cells cell\endlink, with the pixmap appearing to the
1675 left of the text. Q3Table cells can be populated with
1676 \l{Q3TableItem}s, \l{Q3ComboTableItem}s or by \l{Q3CheckTableItem}s.
1677 By default a vertical header appears at the left of the table
1678 showing row numbers and a horizontal header appears at the top of
1679 the table showing column numbers. (The numbers displayed start at
1680 1, although row and column numbers within Q3Table begin at 0.)
1681
1682 If you want to use mouse tracking call setMouseTracking(true) on
1683 the \e viewport.
1684
1685 \img qtableitems.png Table Items
1686
1687 \target headers
1688 \section1 Headers
1689
1690 Q3Table supports a header column, e.g. to display row numbers, and
1691 a header row, e.g to display column titles. To set row or column
1692 labels use Q3Header::setLabel() on the pointers returned by
1693 verticalHeader() and horizontalHeader() respectively. The vertical
1694 header is displayed within the table's left margin whose width is
1695 set with setLeftMargin(). The horizontal header is displayed
1696 within the table's top margin whose height is set with
1697 setTopMargin(). The table's grid can be switched off with
1698 setShowGrid(). If you want to hide a horizontal header call
1699 hide(), and call setTopMargin(0) so that the area the header
1700 would have occupied is reduced to zero size.
1701
1702 Header labels are indexed via their section numbers. Note that the
1703 default behavior of Q3Header regarding section numbers is overridden
1704 for Q3Table. See the explanation below in the Rows and Columns
1705 section in the discussion of moving columns and rows.
1706
1707 \target columnsrows
1708 \section1 Rows and Columns
1709
1710 Row and column sizes are set with setRowHeight() and
1711 setColumnWidth(). If you want a row high enough to show the
1712 tallest item in its entirety, use adjustRow(). Similarly, to make
1713 a column wide enough to show the widest item use adjustColumn().
1714 If you want the row height and column width to adjust
1715 automatically as the height and width of the table changes use
1716 setRowStretchable() and setColumnStretchable().
1717
1718 Rows and columns can be hidden and shown with hideRow(),
1719 hideColumn(), showRow() and showColumn(). New rows and columns are
1720 inserted using insertRows() and insertColumns(). Additional rows
1721 and columns are added at the bottom (rows) or right (columns) if
1722 you set setNumRows() or setNumCols() to be larger than numRows()
1723 or numCols(). Existing rows and columns are removed with
1724 removeRow() and removeColumn(). Multiple rows and columns can be
1725 removed with removeRows() and removeColumns().
1726
1727 Rows and columns can be set to be movable using
1728 rowMovingEnabled() and columnMovingEnabled(). The user can drag
1729 them to reorder them holding down the Ctrl key and dragging the
1730 mouse. For performance reasons, the default behavior of Q3Header
1731 section numbers is overridden by Q3Table. Currently in Q3Table, when
1732 a row or column is dragged and reordered, the section number is
1733 also changed to its new position. Therefore, there is no
1734 difference between the section and the index fields in Q3Header.
1735 The Q3Table Q3Header classes do not provide a mechanism for indexing
1736 independently of the user interface ordering.
1737
1738 The table can be sorted using sortColumn(). Users can sort a
1739 column by clicking its header if setSorting() is set to true. Rows
1740 can be swapped with swapRows(), columns with swapColumns() and
1741 cells with swapCells().
1742
1743 For editable tables (see setReadOnly()) you can set the read-only
1744 property of individual rows and columns with setRowReadOnly() and
1745 setColumnReadOnly(). (Whether a cell is editable or read-only
1746 depends on these settings and the cell's Q3TableItem.
1747
1748 The row and column which have the focus are returned by
1749 currentRow() and currentColumn() respectively.
1750
1751 Although many Q3Table functions operate in terms of rows and
1752 columns the indexOf() function returns a single integer
1753 identifying a particular cell.
1754
1755 \target cells
1756 \section1 Cells
1757
1758 All of a Q3Table's cells are empty when the table is constructed.
1759
1760 There are two approaches to populating the table's cells. The
1761 first and simplest approach is to use Q3TableItems or Q3TableItem
1762 subclasses. The second approach doesn't use Q3TableItems at all
1763 which is useful for very large sparse tables but requires you to
1764 reimplement a number of functions. We'll look at each approach in
1765 turn.
1766
1767 To put a string in a cell use setText(). This function will create
1768 a new Q3TableItem for the cell if one doesn't already exist, and
1769 displays the text in it. By default the table item's widget will
1770 be a QLineEdit. A pixmap may be put in a cell with setPixmap(),
1771 which also creates a table item if required. A cell may contain \e
1772 both a pixmap and text; the pixmap is displayed to the left of the
1773 text. Another approach is to construct a Q3TableItem or Q3TableItem
1774 subclass, set its properties, then insert it into a cell with
1775 setItem().
1776
1777 If you want cells which contain comboboxes use the Q3ComboTableItem
1778 class. Similarly if you require cells containing checkboxes use
1779 the Q3CheckTableItem class. These table items look and behave just
1780 like the combobox or checkbox widgets but consume far less memory.
1781
1782 Q3Table takes ownership of its Q3TableItems and will delete them
1783 when the table itself is destroyed. You can take ownership of a
1784 table item using takeItem() which you use to move a cell's
1785 contents from one cell to another, either within the same table,
1786 or from one table to another. (See also, swapCells()).
1787
1788 In-place editing of the text in Q3TableItems, and the values in
1789 Q3ComboTableItems and Q3CheckTableItems works automatically. Cells
1790 may be editable or read-only, see Q3TableItem::EditType. If you
1791 want fine control over editing see beginEdit() and endEdit().
1792
1793 The contents of a cell can be retrieved as a Q3TableItem using
1794 item(), or as a string with text() or as a pixmap (if there is
1795 one) with pixmap(). A cell's bounding rectangle is given by
1796 cellGeometry(). Use updateCell() to repaint a cell, for example to
1797 clear away a cell's visual representation after it has been
1798 deleted with clearCell(). The table can be forced to scroll to
1799 show a particular cell with ensureCellVisible(). The isSelected()
1800 function indicates if a cell is selected.
1801
1802 It is possible to use your own widget as a cell's widget using
1803 setCellWidget(), but subclassing Q3TableItem might be a simpler
1804 approach. The cell's widget (if there is one) can be removed with
1805 clearCellWidget().
1806
1807 \keyword notes on large tables
1808 \target bigtables
1809 \section2 Large tables
1810
1811 For large, sparse, tables using Q3TableItems or other widgets is
1812 inefficient. The solution is to \e draw the cell as it should
1813 appear and to create and destroy cell editors on demand.
1814
1815 This approach requires that you reimplement various functions.
1816 Reimplement paintCell() to display your data, and createEditor()
1817 and setCellContentFromEditor() to support in-place editing. It
1818 is very important to reimplement resizeData() to have no
1819 functionality, to prevent Q3Table from attempting to create a huge
1820 array. You will also need to reimplement item(), setItem(),
1821 takeItem(), clearCell(), and insertWidget(), cellWidget() and
1822 clearCellWidget(). In almost every circumstance (for sorting,
1823 removing and inserting columns and rows, etc.), you also need
1824 to reimplement swapRows(), swapCells() and swapColumns(), including
1825 header handling.
1826
1827 If you represent active cells with a dictionary of Q3TableItems and
1828 QWidgets, i.e. only store references to cells that are actually
1829 used, many of the functions can be implemented with a single line
1830 of code.
1831
1832 For more information on cells see the Q3TableItem documenation.
1833
1834 \target selections
1835 \section1 Selections
1836
1837 Q3Table's support single selection, multi-selection (multiple
1838 cells) or no selection. The selection mode is set with
1839 setSelectionMode(). Use isSelected() to determine if a particular
1840 cell is selected, and isRowSelected() and isColumnSelected() to
1841 see if a row or column is selected.
1842
1843 Q3Table's support many simultaneous selections. You can
1844 programmatically select cells with addSelection(). The number of
1845 selections is given by numSelections(). The current selection is
1846 returned by currentSelection(). You can remove a selection with
1847 removeSelection() and remove all selections with
1848 clearSelection(). Selections are Q3TableSelection objects.
1849
1850 To easily add a new selection use selectCells(), selectRow() or
1851 selectColumn().
1852
1853 Alternatively, use addSelection() to add new selections using
1854 Q3TableSelection objects. The advantage of using Q3TableSelection
1855 objects is that you can call Q3TableSelection::expandTo() to resize
1856 the selection and can query and compare them.
1857
1858 The number of selections is given by numSelections(). The current
1859 selection is returned by currentSelection(). You can remove a
1860 selection with removeSelection() and remove all selections with
1861 clearSelection().
1862
1863 \target signals
1864 \section1 Signals
1865
1866 When the user clicks a cell the currentChanged() signal is
1867 emitted. You can also connect to the lower level clicked(),
1868 doubleClicked() and pressed() signals. If the user changes the
1869 selection the selectionChanged() signal is emitted; similarly if
1870 the user changes a cell's value the valueChanged() signal is
1871 emitted. If the user right-clicks (or presses the appropriate
1872 platform-specific key sequence) the contextMenuRequested() signal
1873 is emitted. If the user drops a drag and drop object the dropped()
1874 signal is emitted with the drop event.
1875 */
1876
1877 /*!
1878 \fn void Q3Table::currentChanged(int row, int col)
1879
1880 This signal is emitted when the current cell has changed to \a
1881 row, \a col.
1882 */
1883
1884 /*!
1885 \fn void Q3Table::valueChanged(int row, int col)
1886
1887 This signal is emitted when the user changed the value in the cell
1888 at \a row, \a col.
1889 */
1890
1891 /*!
1892 \fn int Q3Table::currentRow() const
1893
1894 Returns the current row.
1895
1896 \sa currentColumn()
1897 */
1898
1899 /*!
1900 \fn int Q3Table::currentColumn() const
1901
1902 Returns the current column.
1903
1904 \sa currentRow()
1905 */
1906
1907 /*!
1908 \enum Q3Table::EditMode
1909
1910 \value NotEditing No cell is currently being edited.
1911
1912 \value Editing A cell is currently being edited. The editor was
1913 initialised with the cell's contents.
1914
1915 \value Replacing A cell is currently being edited. The editor was
1916 not initialised with the cell's contents.
1917 */
1918
1919 /*!
1920 \enum Q3Table::SelectionMode
1921
1922 \value NoSelection No cell can be selected by the user.
1923
1924 \value Single The user may only select a single range of cells.
1925
1926 \value Multi The user may select multiple ranges of cells.
1927
1928 \value SingleRow The user may select one row at once.
1929
1930 \value MultiRow The user may select multiple rows.
1931 */
1932
1933 /*!
1934 \enum Q3Table::FocusStyle
1935
1936 Specifies how the current cell (focus cell) is drawn.
1937
1938 \value FollowStyle The current cell is drawn according to the
1939 current style and the cell's background is also drawn selected, if
1940 the current cell is within a selection
1941
1942 \value SpreadSheet The current cell is drawn as in a spreadsheet.
1943 This means, it is signified by a black rectangle around the cell,
1944 and the background of the current cell is always drawn with the
1945 widget's base color - even when selected.
1946
1947 */
1948
1949 /*!
1950 \fn void Q3Table::clicked(int row, int col, int button, const QPoint &mousePos)
1951
1952 This signal is emitted when mouse button \a button is clicked. The
1953 cell where the event took place is at \a row, \a col, and the
1954 mouse's position is in \a mousePos.
1955
1956 \sa Qt::MouseButton
1957 */
1958
1959 /*!
1960 \fn void Q3Table::doubleClicked(int row, int col, int button, const QPoint &mousePos)
1961
1962 This signal is emitted when mouse button \a button is
1963 double-clicked. The cell where the event took place is at \a row,
1964 \a col, and the mouse's position is in \a mousePos.
1965
1966 \sa Qt::MouseButton
1967 */
1968
1969 /*!
1970 \fn void Q3Table::pressed(int row, int col, int button, const QPoint &mousePos)
1971
1972 This signal is emitted when mouse button \a button is pressed. The
1973 cell where the event took place is at \a row, \a col, and the
1974 mouse's position is in \a mousePos.
1975
1976 \sa Qt::MouseButton
1977 */
1978
1979 /*!
1980 \fn void Q3Table::selectionChanged()
1981
1982 This signal is emitted whenever a selection changes.
1983
1984 \sa Q3TableSelection
1985 */
1986
1987 /*!
1988 \fn void Q3Table::contextMenuRequested(int row, int col, const QPoint & pos)
1989
1990 This signal is emitted when the user invokes a context menu with
1991 the right mouse button (or with a system-specific keypress). The
1992 cell where the event took place is at \a row, \a col. \a pos is
1993 the position where the context menu will appear in the global
1994 coordinate system. This signal is always emitted, even if the
1995 contents of the cell are disabled.
1996 */
1997
1998 /*!
1999 Creates an empty table object called \a name as a child of \a
2000 parent.
2001
2002 Call setNumRows() and setNumCols() to set the table size before
2003 populating the table if you're using Q3TableItems.
2004 */
2005
Q3Table(QWidget * parent,const char * name)2006 Q3Table::Q3Table(QWidget *parent, const char *name)
2007 : Q3ScrollView(parent, name, WNoAutoErase | WStaticContents),
2008 leftHeader(0), topHeader(0),
2009 currentSel(0), lastSortCol(-1), sGrid(true), mRows(false), mCols(false),
2010 asc(true), doSort(true), readOnly(false)
2011 {
2012 init(0, 0);
2013 }
2014
2015 /*!
2016 Constructs an empty table called \a name with \a numRows rows and
2017 \a numCols columns. The table is a child of \a parent.
2018
2019 If you're using \l{Q3TableItem}s to populate the table's cells, you
2020 can create Q3TableItem, Q3ComboTableItem and Q3CheckTableItem items
2021 and insert them into the table using setItem(). (See the notes on
2022 large tables for an alternative to using Q3TableItems.)
2023 */
2024
Q3Table(int numRows,int numCols,QWidget * parent,const char * name)2025 Q3Table::Q3Table(int numRows, int numCols, QWidget *parent, const char *name)
2026 : Q3ScrollView(parent, name, WNoAutoErase | WStaticContents),
2027 leftHeader(0), topHeader(0),
2028 currentSel(0), lastSortCol(-1), sGrid(true), mRows(false), mCols(false),
2029 asc(true), doSort(true), readOnly(false)
2030 {
2031 init(numRows, numCols);
2032 }
2033
2034 /*! \internal
2035 */
2036
init(int rows,int cols)2037 void Q3Table::init(int rows, int cols)
2038 {
2039 #ifndef QT_NO_DRAGANDDROP
2040 setDragAutoScroll(false);
2041 #endif
2042 d = new Q3TablePrivate;
2043 d->geomTimer = new QTimer(this);
2044 d->lastVisCol = 0;
2045 d->lastVisRow = 0;
2046 connect(d->geomTimer, SIGNAL(timeout()), this, SLOT(updateGeometriesSlot()));
2047 shouldClearSelection = false;
2048 dEnabled = false;
2049 roRows.setAutoDelete(true);
2050 roCols.setAutoDelete(true);
2051 setSorting(false);
2052
2053 unused = true; // It's unused, ain't it? :)
2054
2055 selMode = Multi;
2056
2057 contents.setAutoDelete(true);
2058 widgets.setAutoDelete(true);
2059
2060 // Enable clipper and set background mode
2061 enableClipper(qt_table_clipper_enabled);
2062
2063 viewport()->setFocusProxy(this);
2064 viewport()->setFocusPolicy(Qt::WheelFocus);
2065 setFocusPolicy(Qt::WheelFocus);
2066
2067 viewport()->setBackgroundMode(PaletteBase);
2068 setBackgroundMode(PaletteBackground, PaletteBase);
2069 setResizePolicy(Manual);
2070 selections.setAutoDelete(true);
2071
2072 // Create headers
2073 leftHeader = new Q3TableHeader(rows, this, this, "left table header");
2074 leftHeader->setOrientation(Vertical);
2075 leftHeader->setTracking(true);
2076 leftHeader->setMovingEnabled(true);
2077 topHeader = new Q3TableHeader(cols, this, this, "right table header");
2078 topHeader->setOrientation(Horizontal);
2079 topHeader->setTracking(true);
2080 topHeader->setMovingEnabled(true);
2081 if (QApplication::reverseLayout())
2082 setMargins(0, fontMetrics().height() + 4, 30, 0);
2083 else
2084 setMargins(30, fontMetrics().height() + 4, 0, 0);
2085
2086 topHeader->setUpdatesEnabled(false);
2087 leftHeader->setUpdatesEnabled(false);
2088 // Initialize headers
2089 int i = 0;
2090 for (i = 0; i < numCols(); ++i)
2091 topHeader->resizeSection(i, QMAX(100, QApplication::globalStrut().height()));
2092 for (i = 0; i < numRows(); ++i)
2093 leftHeader->resizeSection(i, QMAX(20, QApplication::globalStrut().width()));
2094 topHeader->setUpdatesEnabled(true);
2095 leftHeader->setUpdatesEnabled(true);
2096
2097 // Prepare for contents
2098 contents.setAutoDelete(false);
2099
2100 // Connect header, table and scroll bars
2101 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
2102 topHeader, SLOT(setOffset(int)));
2103 connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
2104 leftHeader, SLOT(setOffset(int)));
2105 connect(topHeader, SIGNAL(sectionSizeChanged(int)),
2106 this, SLOT(columnWidthChanged(int)));
2107 connect(topHeader, SIGNAL(indexChange(int,int,int)),
2108 this, SLOT(columnIndexChanged(int,int,int)));
2109 connect(topHeader, SIGNAL(sectionClicked(int)),
2110 this, SLOT(columnClicked(int)));
2111 connect(leftHeader, SIGNAL(sectionSizeChanged(int)),
2112 this, SLOT(rowHeightChanged(int)));
2113 connect(leftHeader, SIGNAL(indexChange(int,int,int)),
2114 this, SLOT(rowIndexChanged(int,int,int)));
2115
2116 // Initialize variables
2117 autoScrollTimer = new QTimer(this);
2118 connect(autoScrollTimer, SIGNAL(timeout()),
2119 this, SLOT(doAutoScroll()));
2120 curRow = curCol = 0;
2121 topHeader->setSectionState(curCol, Q3TableHeader::Bold);
2122 leftHeader->setSectionState(curRow, Q3TableHeader::Bold);
2123 edMode = NotEditing;
2124 editRow = editCol = -1;
2125
2126 drawActiveSelection = true;
2127
2128 installEventFilter(this);
2129
2130 focusStl = SpreadSheet;
2131
2132 was_visible = false;
2133
2134 // initial size
2135 resize(640, 480);
2136 }
2137
2138 /*!
2139 Releases all the resources used by the Q3Table object,
2140 including all \l{Q3TableItem}s and their widgets.
2141 */
2142
~Q3Table()2143 Q3Table::~Q3Table()
2144 {
2145 setUpdatesEnabled(false);
2146 contents.setAutoDelete(true);
2147 contents.clear();
2148 widgets.clear();
2149
2150 delete d;
2151 }
2152
setReadOnly(bool b)2153 void Q3Table::setReadOnly(bool b)
2154 {
2155 readOnly = b;
2156
2157 Q3TableItem *i = item(curRow, curCol);
2158 if (readOnly && isEditing()) {
2159 endEdit(editRow, editCol, true, false);
2160 } else if (!readOnly && i && (i->editType() == Q3TableItem::WhenCurrent
2161 || i->editType() == Q3TableItem::Always)) {
2162 editCell(curRow, curCol);
2163 }
2164 }
2165
2166 /*!
2167 If \a ro is true, row \a row is set to be read-only; otherwise the
2168 row is set to be editable.
2169
2170 Whether a cell in this row is editable or read-only depends on the
2171 cell's EditType, and this setting.
2172
2173 \sa isRowReadOnly() setColumnReadOnly() setReadOnly()
2174 */
2175
setRowReadOnly(int row,bool ro)2176 void Q3Table::setRowReadOnly(int row, bool ro)
2177 {
2178 if (ro)
2179 roRows.replace(row, new int(0));
2180 else
2181 roRows.remove(row);
2182
2183 if (curRow == row) {
2184 Q3TableItem *i = item(curRow, curCol);
2185 if (ro && isEditing()) {
2186 endEdit(editRow, editCol, true, false);
2187 } else if (!ro && i && (i->editType() == Q3TableItem::WhenCurrent
2188 || i->editType() == Q3TableItem::Always)) {
2189 editCell(curRow, curCol);
2190 }
2191 }
2192 }
2193
2194 /*!
2195 If \a ro is true, column \a col is set to be read-only; otherwise
2196 the column is set to be editable.
2197
2198 Whether a cell in this column is editable or read-only depends on
2199 the cell's EditType, and this setting.
2200
2201 \sa isColumnReadOnly() setRowReadOnly() setReadOnly()
2202
2203 */
2204
setColumnReadOnly(int col,bool ro)2205 void Q3Table::setColumnReadOnly(int col, bool ro)
2206 {
2207 if (ro)
2208 roCols.replace(col, new int(0));
2209 else
2210 roCols.remove(col);
2211
2212 if (curCol == col) {
2213 Q3TableItem *i = item(curRow, curCol);
2214 if (ro && isEditing()) {
2215 endEdit(editRow, editCol, true, false);
2216 } else if (!ro && i && (i->editType() == Q3TableItem::WhenCurrent
2217 || i->editType() == Q3TableItem::Always)) {
2218 editCell(curRow, curCol);
2219 }
2220 }
2221 }
2222
2223 /*!
2224 \property Q3Table::readOnly
2225 \brief whether the table is read-only
2226
2227 Whether a cell in the table is editable or read-only depends on
2228 the cell's \link Q3TableItem::EditType EditType\endlink, and this setting.
2229
2230 \sa QWidget::enabled setColumnReadOnly() setRowReadOnly()
2231 */
2232
isReadOnly() const2233 bool Q3Table::isReadOnly() const
2234 {
2235 return readOnly;
2236 }
2237
2238 /*!
2239 Returns true if row \a row is read-only; otherwise returns false.
2240
2241 Whether a cell in this row is editable or read-only depends on the
2242 cell's \link Q3TableItem::EditType EditType\endlink, and this
2243 setting.
2244
2245 \sa setRowReadOnly() isColumnReadOnly()
2246 */
2247
isRowReadOnly(int row) const2248 bool Q3Table::isRowReadOnly(int row) const
2249 {
2250 return (roRows.find(row) != 0);
2251 }
2252
2253 /*!
2254 Returns true if column \a col is read-only; otherwise returns
2255 false.
2256
2257 Whether a cell in this column is editable or read-only depends on
2258 the cell's EditType, and this setting.
2259
2260 \sa setColumnReadOnly() isRowReadOnly()
2261 */
2262
isColumnReadOnly(int col) const2263 bool Q3Table::isColumnReadOnly(int col) const
2264 {
2265 return (roCols.find(col) != 0);
2266 }
2267
setSelectionMode(SelectionMode mode)2268 void Q3Table::setSelectionMode(SelectionMode mode)
2269 {
2270 if (mode == selMode)
2271 return;
2272 selMode = mode;
2273 clearSelection();
2274 if (isRowSelection(selMode) && numRows() > 0 && numCols() > 0) {
2275 currentSel = new Q3TableSelection();
2276 selections.append(currentSel);
2277 currentSel->init(curRow, 0);
2278 currentSel->expandTo(curRow, numCols() - 1);
2279 repaintSelections(0, currentSel);
2280 }
2281 }
2282
2283 /*!
2284 \property Q3Table::selectionMode
2285 \brief the current selection mode
2286
2287 The default mode is \c Multi which allows the user to select
2288 multiple ranges of cells.
2289 */
2290
selectionMode() const2291 Q3Table::SelectionMode Q3Table::selectionMode() const
2292 {
2293 return selMode;
2294 }
2295
2296 /*!
2297 \property Q3Table::focusStyle
2298 \brief how the current (focus) cell is drawn
2299
2300 The default style is \c SpreadSheet.
2301
2302 \sa Q3Table::FocusStyle
2303 */
2304
setFocusStyle(FocusStyle fs)2305 void Q3Table::setFocusStyle(FocusStyle fs)
2306 {
2307 focusStl = fs;
2308 updateCell(curRow, curCol);
2309 }
2310
focusStyle() const2311 Q3Table::FocusStyle Q3Table::focusStyle() const
2312 {
2313 return focusStl;
2314 }
2315
2316 /*!
2317 This functions updates all the header states to be in sync with
2318 the current selections. This should be called after
2319 programmatically changing, adding or removing selections, so that
2320 the headers are updated.
2321 */
2322
updateHeaderStates()2323 void Q3Table::updateHeaderStates()
2324 {
2325 horizontalHeader()->setUpdatesEnabled(false);
2326 verticalHeader()->setUpdatesEnabled(false);
2327
2328 ((Q3TableHeader*)verticalHeader())->setSectionStateToAll(Q3TableHeader::Normal);
2329 ((Q3TableHeader*)horizontalHeader())->setSectionStateToAll(Q3TableHeader::Normal);
2330
2331 Q3PtrListIterator<Q3TableSelection> it(selections);
2332 Q3TableSelection *s;
2333 while ((s = it.current()) != 0) {
2334 ++it;
2335 if (s->isActive()) {
2336 if (s->leftCol() == 0 &&
2337 s->rightCol() == numCols() - 1) {
2338 for (int i = 0; i < s->bottomRow() - s->topRow() + 1; ++i)
2339 leftHeader->setSectionState(s->topRow() + i, Q3TableHeader::Selected);
2340 }
2341 if (s->topRow() == 0 &&
2342 s->bottomRow() == numRows() - 1) {
2343 for (int i = 0; i < s->rightCol() - s->leftCol() + 1; ++i)
2344 topHeader->setSectionState(s->leftCol() + i, Q3TableHeader::Selected);
2345 }
2346 }
2347 }
2348
2349 horizontalHeader()->setUpdatesEnabled(true);
2350 verticalHeader()->setUpdatesEnabled(true);
2351 horizontalHeader()->repaint(false);
2352 verticalHeader()->repaint(false);
2353 }
2354
2355 /*!
2356 Returns the table's top Q3Header.
2357
2358 This header contains the column labels.
2359
2360 To modify a column label use Q3Header::setLabel().
2361
2362 \sa verticalHeader() setTopMargin() Q3Header
2363 */
2364
horizontalHeader() const2365 Q3Header *Q3Table::horizontalHeader() const
2366 {
2367 return (Q3Header*)topHeader;
2368 }
2369
2370 /*!
2371 Returns the table's vertical Q3Header.
2372
2373 This header contains the row labels.
2374
2375 \sa horizontalHeader() setLeftMargin() Q3Header
2376 */
2377
verticalHeader() const2378 Q3Header *Q3Table::verticalHeader() const
2379 {
2380 return (Q3Header*)leftHeader;
2381 }
2382
setShowGrid(bool b)2383 void Q3Table::setShowGrid(bool b)
2384 {
2385 if (sGrid == b)
2386 return;
2387 sGrid = b;
2388 updateContents();
2389 }
2390
2391 /*!
2392 \property Q3Table::showGrid
2393 \brief whether the table's grid is displayed
2394
2395 The grid is shown by default.
2396 */
2397
showGrid() const2398 bool Q3Table::showGrid() const
2399 {
2400 return sGrid;
2401 }
2402
2403 /*!
2404 \property Q3Table::columnMovingEnabled
2405 \brief whether columns can be moved by the user
2406
2407 The default is false. Columns are moved by dragging whilst holding
2408 down the Ctrl key.
2409
2410 \sa rowMovingEnabled
2411 */
2412
setColumnMovingEnabled(bool b)2413 void Q3Table::setColumnMovingEnabled(bool b)
2414 {
2415 mCols = b;
2416 }
2417
columnMovingEnabled() const2418 bool Q3Table::columnMovingEnabled() const
2419 {
2420 return mCols;
2421 }
2422
2423 /*!
2424 \property Q3Table::rowMovingEnabled
2425 \brief whether rows can be moved by the user
2426
2427 The default is false. Rows are moved by dragging whilst holding
2428 down the Ctrl key.
2429
2430
2431 \sa columnMovingEnabled
2432 */
2433
setRowMovingEnabled(bool b)2434 void Q3Table::setRowMovingEnabled(bool b)
2435 {
2436 mRows = b;
2437 }
2438
rowMovingEnabled() const2439 bool Q3Table::rowMovingEnabled() const
2440 {
2441 return mRows;
2442 }
2443
2444 /*!
2445 This is called when Q3Table's internal array needs to be resized to
2446 \a len elements.
2447
2448 If you don't use Q3TableItems you should reimplement this as an
2449 empty method to avoid wasting memory. See the notes on large
2450 tables for further details.
2451 */
2452
resizeData(int len)2453 void Q3Table::resizeData(int len)
2454 {
2455 contents.resize(len);
2456 widgets.resize(len);
2457 }
2458
2459 /*!
2460 Swaps the data in \a row1 and \a row2.
2461
2462 This function is used to swap the positions of two rows. It is
2463 called when the user changes the order of rows (see
2464 setRowMovingEnabled()), and when rows are sorted.
2465
2466 If you don't use \l{Q3TableItem}s and want your users to be able to
2467 swap rows, e.g. for sorting, you will need to reimplement this
2468 function. (See the notes on large tables.)
2469
2470 If \a swapHeader is true, the rows' header contents is also
2471 swapped.
2472
2473 This function will not update the Q3Table, you will have to do
2474 this manually, e.g. by calling updateContents().
2475
2476 \sa swapColumns() swapCells()
2477 */
2478
swapRows(int row1,int row2,bool swapHeader)2479 void Q3Table::swapRows(int row1, int row2, bool swapHeader)
2480 {
2481 if (swapHeader)
2482 leftHeader->swapSections(row1, row2, false);
2483
2484 Q3PtrVector<Q3TableItem> tmpContents;
2485 tmpContents.resize(numCols());
2486 Q3PtrVector<QWidget> tmpWidgets;
2487 tmpWidgets.resize(numCols());
2488 int i;
2489
2490 contents.setAutoDelete(false);
2491 widgets.setAutoDelete(false);
2492 for (i = 0; i < numCols(); ++i) {
2493 Q3TableItem *i1, *i2;
2494 i1 = item(row1, i);
2495 i2 = item(row2, i);
2496 if (i1 || i2) {
2497 tmpContents.insert(i, i1);
2498 contents.remove(indexOf(row1, i));
2499 contents.insert(indexOf(row1, i), i2);
2500 contents.remove(indexOf(row2, i));
2501 contents.insert(indexOf(row2, i), tmpContents[ i ]);
2502 if (contents[ indexOf(row1, i) ])
2503 contents[ indexOf(row1, i) ]->setRow(row1);
2504 if (contents[ indexOf(row2, i) ])
2505 contents[ indexOf(row2, i) ]->setRow(row2);
2506 }
2507
2508 QWidget *w1, *w2;
2509 w1 = cellWidget(row1, i);
2510 w2 = cellWidget(row2, i);
2511 if (w1 || w2) {
2512 tmpWidgets.insert(i, w1);
2513 widgets.remove(indexOf(row1, i));
2514 widgets.insert(indexOf(row1, i), w2);
2515 widgets.remove(indexOf(row2, i));
2516 widgets.insert(indexOf(row2, i), tmpWidgets[ i ]);
2517 }
2518 }
2519 contents.setAutoDelete(false);
2520 widgets.setAutoDelete(true);
2521
2522 updateRowWidgets(row1);
2523 updateRowWidgets(row2);
2524 if (curRow == row1)
2525 curRow = row2;
2526 else if (curRow == row2)
2527 curRow = row1;
2528 if (editRow == row1)
2529 editRow = row2;
2530 else if (editRow == row2)
2531 editRow = row1;
2532 }
2533
2534 /*!
2535 Sets the left margin to be \a m pixels wide.
2536
2537 The verticalHeader(), which displays row labels, occupies this
2538 margin.
2539
2540 In an Arabic or Hebrew localization, the verticalHeader() will
2541 appear on the right side of the table, and this call will set the
2542 right margin.
2543
2544 \sa leftMargin() setTopMargin() verticalHeader()
2545 */
2546
setLeftMargin(int m)2547 void Q3Table::setLeftMargin(int m)
2548 {
2549 if (QApplication::reverseLayout())
2550 setMargins(leftMargin(), topMargin(), m, bottomMargin());
2551 else
2552 setMargins(m, topMargin(), rightMargin(), bottomMargin());
2553 updateGeometries();
2554 }
2555
2556 /*!
2557 Sets the top margin to be \a m pixels high.
2558
2559 The horizontalHeader(), which displays column labels, occupies
2560 this margin.
2561
2562 \sa topMargin() setLeftMargin()
2563 */
2564
setTopMargin(int m)2565 void Q3Table::setTopMargin(int m)
2566 {
2567 setMargins(leftMargin(), m, rightMargin(), bottomMargin());
2568 updateGeometries();
2569 }
2570
2571 /*!
2572 Swaps the data in \a col1 with \a col2.
2573
2574 This function is used to swap the positions of two columns. It is
2575 called when the user changes the order of columns (see
2576 setColumnMovingEnabled(), and when columns are sorted.
2577
2578 If you don't use \l{Q3TableItem}s and want your users to be able to
2579 swap columns you will need to reimplement this function. (See the
2580 notes on large tables.)
2581
2582 If \a swapHeader is true, the columns' header contents is also
2583 swapped.
2584
2585 \sa swapCells()
2586 */
2587
swapColumns(int col1,int col2,bool swapHeader)2588 void Q3Table::swapColumns(int col1, int col2, bool swapHeader)
2589 {
2590 if (swapHeader)
2591 topHeader->swapSections(col1, col2, false);
2592
2593 Q3PtrVector<Q3TableItem> tmpContents;
2594 tmpContents.resize(numRows());
2595 Q3PtrVector<QWidget> tmpWidgets;
2596 tmpWidgets.resize(numRows());
2597 int i;
2598
2599 contents.setAutoDelete(false);
2600 widgets.setAutoDelete(false);
2601 for (i = 0; i < numRows(); ++i) {
2602 Q3TableItem *i1, *i2;
2603 i1 = item(i, col1);
2604 i2 = item(i, col2);
2605 if (i1 || i2) {
2606 tmpContents.insert(i, i1);
2607 contents.remove(indexOf(i, col1));
2608 contents.insert(indexOf(i, col1), i2);
2609 contents.remove(indexOf(i, col2));
2610 contents.insert(indexOf(i, col2), tmpContents[ i ]);
2611 if (contents[ indexOf(i, col1) ])
2612 contents[ indexOf(i, col1) ]->setCol(col1);
2613 if (contents[ indexOf(i, col2) ])
2614 contents[ indexOf(i, col2) ]->setCol(col2);
2615 }
2616
2617 QWidget *w1, *w2;
2618 w1 = cellWidget(i, col1);
2619 w2 = cellWidget(i, col2);
2620 if (w1 || w2) {
2621 tmpWidgets.insert(i, w1);
2622 widgets.remove(indexOf(i, col1));
2623 widgets.insert(indexOf(i, col1), w2);
2624 widgets.remove(indexOf(i, col2));
2625 widgets.insert(indexOf(i, col2), tmpWidgets[ i ]);
2626 }
2627 }
2628 contents.setAutoDelete(false);
2629 widgets.setAutoDelete(true);
2630
2631 columnWidthChanged(col1);
2632 columnWidthChanged(col2);
2633 if (curCol == col1)
2634 curCol = col2;
2635 else if (curCol == col2)
2636 curCol = col1;
2637 if (editCol == col1)
2638 editCol = col2;
2639 else if (editCol == col2)
2640 editCol = col1;
2641 }
2642
2643 /*!
2644 Swaps the contents of the cell at \a row1, \a col1 with the
2645 contents of the cell at \a row2, \a col2.
2646
2647 This function is also called when the table is sorted.
2648
2649 If you don't use \l{Q3TableItem}s and want your users to be able to
2650 swap cells, you will need to reimplement this function. (See the
2651 notes on large tables.)
2652
2653 \sa swapColumns() swapRows()
2654 */
2655
swapCells(int row1,int col1,int row2,int col2)2656 void Q3Table::swapCells(int row1, int col1, int row2, int col2)
2657 {
2658 contents.setAutoDelete(false);
2659 widgets.setAutoDelete(false);
2660 Q3TableItem *i1, *i2;
2661 i1 = item(row1, col1);
2662 i2 = item(row2, col2);
2663 if (i1 || i2) {
2664 Q3TableItem *tmp = i1;
2665 contents.remove(indexOf(row1, col1));
2666 contents.insert(indexOf(row1, col1), i2);
2667 contents.remove(indexOf(row2, col2));
2668 contents.insert(indexOf(row2, col2), tmp);
2669 if (contents[ indexOf(row1, col1) ]) {
2670 contents[ indexOf(row1, col1) ]->setRow(row1);
2671 contents[ indexOf(row1, col1) ]->setCol(col1);
2672 }
2673 if (contents[ indexOf(row2, col2) ]) {
2674 contents[ indexOf(row2, col2) ]->setRow(row2);
2675 contents[ indexOf(row2, col2) ]->setCol(col2);
2676 }
2677 }
2678
2679 QWidget *w1, *w2;
2680 w1 = cellWidget(row1, col1);
2681 w2 = cellWidget(row2, col2);
2682 if (w1 || w2) {
2683 QWidget *tmp = w1;
2684 widgets.remove(indexOf(row1, col1));
2685 widgets.insert(indexOf(row1, col1), w2);
2686 widgets.remove(indexOf(row2, col2));
2687 widgets.insert(indexOf(row2, col2), tmp);
2688 }
2689
2690 updateRowWidgets(row1);
2691 updateRowWidgets(row2);
2692 updateColWidgets(col1);
2693 updateColWidgets(col2);
2694 contents.setAutoDelete(false);
2695 widgets.setAutoDelete(true);
2696 }
2697
is_child_of(QWidget * child,QWidget * parent)2698 static bool is_child_of(QWidget *child, QWidget *parent)
2699 {
2700 while (child) {
2701 if (child == parent)
2702 return true;
2703 child = child->parentWidget();
2704 }
2705 return false;
2706 }
2707
2708 /*!
2709 Draws the table contents on the painter \a p. This function is
2710 optimized so that it only draws the cells inside the \a cw pixels
2711 wide and \a ch pixels high clipping rectangle at position \a cx,
2712 \a cy.
2713
2714 Additionally, drawContents() highlights the current cell.
2715 */
2716
drawContents(QPainter * p,int cx,int cy,int cw,int ch)2717 void Q3Table::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
2718 {
2719 int colfirst = columnAt(cx);
2720 int collast = columnAt(cx + cw);
2721 int rowfirst = rowAt(cy);
2722 int rowlast = rowAt(cy + ch);
2723
2724 if (rowfirst == -1 || colfirst == -1) {
2725 paintEmptyArea(p, cx, cy, cw, ch);
2726 return;
2727 }
2728
2729 drawActiveSelection = hasFocus() || viewport()->hasFocus() || d->inMenuMode
2730 || is_child_of(qApp->focusWidget(), viewport())
2731 || !style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this);
2732 if (rowlast == -1)
2733 rowlast = numRows() - 1;
2734 if (collast == -1)
2735 collast = numCols() - 1;
2736
2737 bool currentInSelection = false;
2738
2739 Q3PtrListIterator<Q3TableSelection> it( selections );
2740 Q3TableSelection *s;
2741 while ( ( s = it.current() ) != 0 ) {
2742 ++it;
2743 if (s->isActive() &&
2744 curRow >= s->topRow() &&
2745 curRow <= s->bottomRow() &&
2746 curCol >= s->leftCol() &&
2747 curCol <= s->rightCol()) {
2748 currentInSelection = s->topRow() != curRow || s->bottomRow() != curRow || s->leftCol() != curCol || s->rightCol() != curCol;
2749 break;
2750 }
2751 }
2752
2753 // Go through the rows
2754 for (int r = rowfirst; r <= rowlast; ++r) {
2755 // get row position and height
2756 int rowp = rowPos(r);
2757 int rowh = rowHeight(r);
2758
2759 // Go through the columns in row r
2760 // if we know from where to where, go through [colfirst, collast],
2761 // else go through all of them
2762 for (int c = colfirst; c <= collast; ++c) {
2763 // get position and width of column c
2764 int colp, colw;
2765 colp = columnPos(c);
2766 colw = columnWidth(c);
2767 int oldrp = rowp;
2768 int oldrh = rowh;
2769
2770 Q3TableItem *itm = item(r, c);
2771 if (itm &&
2772 (itm->colSpan() > 1 || itm->rowSpan() > 1)) {
2773 bool goon = (r == itm->row() && c == itm->col())
2774 || (r == rowfirst && c == itm->col())
2775 || (r == itm->row() && c == colfirst);
2776 if (!goon)
2777 continue;
2778 rowp = rowPos(itm->row());
2779 rowh = 0;
2780 int i;
2781 for (i = 0; i < itm->rowSpan(); ++i)
2782 rowh += rowHeight(i + itm->row());
2783 colp = columnPos(itm->col());
2784 colw = 0;
2785 for (i = 0; i < itm->colSpan(); ++i)
2786 colw += columnWidth(i + itm->col());
2787 }
2788
2789 // Translate painter and draw the cell
2790 p->translate(colp, rowp);
2791 bool selected = isSelected(r, c);
2792 if (focusStl != FollowStyle && selected && !currentInSelection &&
2793 r == curRow && c == curCol )
2794 selected = false;
2795 paintCell(p, r, c, QRect(colp, rowp, colw, rowh), selected);
2796 p->translate(-colp, -rowp);
2797
2798 rowp = oldrp;
2799 rowh = oldrh;
2800
2801 QWidget *w = cellWidget(r, c);
2802 QRect cg(cellGeometry(r, c));
2803 if (w && w->geometry() != QRect(contentsToViewport(cg.topLeft()), cg.size() - QSize(1, 1))) {
2804 moveChild(w, colp, rowp);
2805 w->resize(cg.size() - QSize(1, 1));
2806 }
2807 }
2808 }
2809 d->lastVisCol = collast;
2810 d->lastVisRow = rowlast;
2811
2812 // draw indication of current cell
2813 QRect focusRect = cellGeometry(curRow, curCol);
2814 p->translate(focusRect.x(), focusRect.y());
2815 paintFocus(p, focusRect);
2816 p->translate(-focusRect.x(), -focusRect.y());
2817
2818 // Paint empty rects
2819 paintEmptyArea(p, cx, cy, cw, ch);
2820
2821 drawActiveSelection = true;
2822 }
2823
2824 /*!
2825 \reimp
2826
2827 (Implemented to get rid of a compiler warning.)
2828 */
2829
drawContents(QPainter *)2830 void Q3Table::drawContents(QPainter *)
2831 {
2832 }
2833
2834 /*!
2835 Returns the geometry of cell \a row, \a col in the cell's
2836 coordinate system. This is a convenience function useful in
2837 paintCell(). It is equivalent to QRect(QPoint(0,0), cellGeometry(
2838 row, col).size());
2839
2840 \sa cellGeometry()
2841 */
2842
cellRect(int row,int col) const2843 QRect Q3Table::cellRect(int row, int col) const
2844 {
2845 return QRect(QPoint(0,0), cellGeometry(row, col).size());
2846 }
2847
2848 /*!
2849 \overload
2850
2851 Use the other paintCell() function. This function is only included
2852 for backwards compatibility.
2853 */
2854
paintCell(QPainter * p,int row,int col,const QRect & cr,bool selected)2855 void Q3Table::paintCell(QPainter* p, int row, int col,
2856 const QRect &cr, bool selected)
2857 {
2858 if (cr.width() == 0 || cr.height() == 0)
2859 return;
2860 #if defined(Q_WS_WIN)
2861 const QColorGroup &cg = (!drawActiveSelection && style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus) ? palette().inactive() : colorGroup());
2862 #else
2863 const QColorGroup &cg = colorGroup();
2864 #endif
2865
2866 Q3TableItem *itm = item(row, col);
2867 QColorGroup cg2(cg);
2868 if (itm && !itm->isEnabled())
2869 cg2 = palette().disabled();
2870
2871 paintCell(p, row, col, cr, selected, cg2);
2872 }
2873
2874 /*!
2875 Paints the cell at \a row, \a col on the painter \a p. The painter
2876 has already been translated to the cell's origin. \a cr describes
2877 the cell coordinates in the content coordinate system.
2878
2879 If \a selected is true the cell is highlighted.
2880
2881 \a cg is the colorgroup which should be used to draw the cell
2882 content.
2883
2884 If you want to draw custom cell content, for example right-aligned
2885 text, you must either reimplement paintCell(), or subclass
2886 Q3TableItem and reimplement Q3TableItem::paint() to do the custom
2887 drawing.
2888
2889 If you're using a Q3TableItem subclass, for example, to store a
2890 data structure, then reimplementing Q3TableItem::paint() may be the
2891 best approach. For data you want to draw immediately, e.g. data
2892 retrieved from a database, it is probably best to reimplement
2893 paintCell(). Note that if you reimplement paintCell(), i.e. don't
2894 use \l{Q3TableItem}s, you must reimplement other functions: see the
2895 notes on large tables.
2896
2897 Note that the painter is not clipped by default in order to get
2898 maximum efficiency. If you want clipping, use code like this:
2899
2900 \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 4
2901 */
2902
paintCell(QPainter * p,int row,int col,const QRect & cr,bool selected,const QColorGroup & cg)2903 void Q3Table::paintCell(QPainter *p, int row, int col,
2904 const QRect &cr, bool selected, const QColorGroup &cg)
2905 {
2906 if (focusStl == SpreadSheet && selected &&
2907 row == curRow &&
2908 col == curCol && (hasFocus() || viewport()->hasFocus()))
2909 selected = false;
2910
2911 QPalette pal = cg;
2912 int w = cr.width();
2913 int h = cr.height();
2914 int x2 = w - 1;
2915 int y2 = h - 1;
2916
2917
2918 Q3TableItem *itm = item(row, col);
2919 if (itm) {
2920 p->save();
2921 itm->paint(p, pal, cr, selected);
2922 p->restore();
2923 } else {
2924 p->fillRect(0, 0, w, h, selected ? pal.brush(QPalette::Highlight) : pal.brush(QPalette::Base));
2925 }
2926
2927 if (sGrid) {
2928 // Draw our lines
2929 QPen pen(p->pen());
2930 int gridColor = style()->styleHint(QStyle::SH_Table_GridLineColor, 0, this);
2931 if (gridColor != -1) {
2932 if (palette() != pal)
2933 p->setPen(pal.mid().color());
2934 else
2935 p->setPen((QRgb)gridColor);
2936 } else {
2937 p->setPen(pal.mid().color());
2938 }
2939 p->drawLine(x2, 0, x2, y2);
2940 p->drawLine(0, y2, x2, y2);
2941 p->setPen(pen);
2942 }
2943 }
2944
2945 /*!
2946 Draws the focus rectangle of the current cell (see currentRow(),
2947 currentColumn()).
2948
2949 The painter \a p is already translated to the cell's origin, while
2950 \a cr specifies the cell's geometry in content coordinates.
2951 */
2952
paintFocus(QPainter * p,const QRect & cr)2953 void Q3Table::paintFocus(QPainter *p, const QRect &cr)
2954 {
2955 if (!hasFocus() && !viewport()->hasFocus())
2956 return;
2957 QRect focusRect(0, 0, cr.width(), cr.height());
2958 if (focusStyle() == SpreadSheet) {
2959 p->setPen(QPen(Qt::black, 1));
2960 p->setBrush(Qt::NoBrush);
2961 p->drawRect(focusRect.x(), focusRect.y(), focusRect.width() - 1, focusRect.height() - 1);
2962 p->drawRect(focusRect.x() - 1, focusRect.y() - 1, focusRect.width() + 1, focusRect.height() + 1);
2963 } else {
2964 QStyleOptionFocusRect opt;
2965 opt.init(this);
2966 opt.rect = focusRect;
2967 opt.palette = palette();
2968 opt.state |= QStyle::State_KeyboardFocusChange;
2969 if (isSelected(curRow, curCol, false)) {
2970 opt.state |= QStyle::State_FocusAtBorder;
2971 opt.backgroundColor = palette().highlight().color();
2972 } else {
2973 opt.state |= QStyle::State_None;
2974 opt.backgroundColor = palette().base().color();
2975 }
2976 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, this);
2977 }
2978 }
2979
2980 /*!
2981 This function fills the \a cw pixels wide and \a ch pixels high
2982 rectangle starting at position \a cx, \a cy with the background
2983 color using the painter \a p.
2984
2985 paintEmptyArea() is invoked by drawContents() to erase or fill
2986 unused areas.
2987 */
2988
paintEmptyArea(QPainter * p,int cx,int cy,int cw,int ch)2989 void Q3Table::paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch)
2990 {
2991 // Regions work with shorts, so avoid an overflow and adjust the
2992 // table size to the visible size
2993 QSize ts(tableSize());
2994 ts.setWidth(QMIN(ts.width(), visibleWidth()));
2995 ts.setHeight(QMIN(ts.height(), visibleHeight()));
2996
2997 // Region of the rect we should draw, calculated in viewport
2998 // coordinates, as a region can't handle bigger coordinates
2999 contentsToViewport2(cx, cy, cx, cy);
3000 QRegion reg(QRect(cx, cy, cw, ch));
3001
3002 // Subtract the table from it
3003 reg = reg.subtracted(QRect(QPoint(0, 0), ts));
3004
3005 // And draw the rectangles (transformed inc contents coordinates as needed)
3006 Q3MemArray<QRect> r = reg.rects();
3007 for (int i = 0; i < (int)r.count(); ++i)
3008 p->fillRect(QRect(viewportToContents2(r[i].topLeft()),r[i].size()), viewport()->backgroundBrush());
3009 }
3010
3011 /*!
3012 Returns the Q3TableItem representing the contents of the cell at \a
3013 row, \a col.
3014
3015 If \a row or \a col are out of range or no content has been set
3016 for this cell, item() returns 0.
3017
3018 If you don't use \l{Q3TableItem}s you may need to reimplement this
3019 function: see the notes on large tables.
3020
3021 \sa setItem()
3022 */
3023
item(int row,int col) const3024 Q3TableItem *Q3Table::item(int row, int col) const
3025 {
3026 if (row < 0 || col < 0 || row > numRows() - 1 ||
3027 col > numCols() - 1 || row * col >= (int)contents.size())
3028 return 0;
3029
3030 return contents[ indexOf(row, col) ]; // contents array lookup
3031 }
3032
3033 /*!
3034 Inserts the table item \a item into the table at row \a row,
3035 column \a col, and repaints the cell. If a table item already
3036 exists in this cell it is deleted and replaced with \a item. The
3037 table takes ownership of the table item.
3038
3039 If you don't use \l{Q3TableItem}s you may need to reimplement this
3040 function: see the notes on large tables.
3041
3042 \sa item() takeItem()
3043 */
3044
setItem(int row,int col,Q3TableItem * item)3045 void Q3Table::setItem(int row, int col, Q3TableItem *item)
3046 {
3047 if (!item)
3048 return;
3049
3050 if ((int)contents.size() != numRows() * numCols())
3051 resizeData(numRows() * numCols());
3052
3053 int orow = item->row();
3054 int ocol = item->col();
3055 clearCell(row, col);
3056
3057 contents.insert(indexOf(row, col), item);
3058 item->setRow(row);
3059 item->setCol(col);
3060 item->t = this;
3061 updateCell(row, col);
3062 if (qt_update_cell_widget)
3063 item->updateEditor(orow, ocol);
3064
3065 if (row == curRow && col == curCol && item->editType() == Q3TableItem::WhenCurrent) {
3066 if (beginEdit(row, col, false))
3067 setEditMode(Editing, row, col);
3068 }
3069 }
3070
3071 /*!
3072 Removes the Q3TableItem at \a row, \a col.
3073
3074 If you don't use \l{Q3TableItem}s you may need to reimplement this
3075 function: see the notes on large tables.
3076 */
3077
clearCell(int row,int col)3078 void Q3Table::clearCell(int row, int col)
3079 {
3080 if ((int)contents.size() != numRows() * numCols())
3081 resizeData(numRows() * numCols());
3082 clearCellWidget(row, col);
3083 contents.setAutoDelete(true);
3084 contents.remove(indexOf(row, col));
3085 contents.setAutoDelete(false);
3086 }
3087
3088 /*!
3089 Sets the text in the cell at \a row, \a col to \a text.
3090
3091 If the cell does not contain a table item a Q3TableItem is created
3092 with an \link Q3TableItem::EditType EditType\endlink of \c OnTyping,
3093 otherwise the existing table item's text (if any) is replaced with
3094 \a text.
3095
3096 \sa text() setPixmap() setItem() Q3TableItem::setText()
3097 */
3098
setText(int row,int col,const QString & text)3099 void Q3Table::setText(int row, int col, const QString &text)
3100 {
3101 Q3TableItem *itm = item(row, col);
3102 if (itm) {
3103 itm->setText(text);
3104 itm->updateEditor(row, col);
3105 updateCell(row, col);
3106 } else {
3107 Q3TableItem *i = new Q3TableItem(this, Q3TableItem::OnTyping,
3108 text, QPixmap());
3109 setItem(row, col, i);
3110 }
3111 }
3112
3113 /*!
3114 Sets the pixmap in the cell at \a row, \a col to \a pix.
3115
3116 If the cell does not contain a table item a Q3TableItem is created
3117 with an \link Q3TableItem::EditType EditType\endlink of \c OnTyping,
3118 otherwise the existing table item's pixmap (if any) is replaced
3119 with \a pix.
3120
3121 Note that \l{Q3ComboTableItem}s and \l{Q3CheckTableItem}s don't show
3122 pixmaps.
3123
3124 \sa pixmap() setText() setItem() Q3TableItem::setPixmap()
3125 */
3126
setPixmap(int row,int col,const QPixmap & pix)3127 void Q3Table::setPixmap(int row, int col, const QPixmap &pix)
3128 {
3129 Q3TableItem *itm = item(row, col);
3130 if (itm) {
3131 itm->setPixmap(pix);
3132 updateCell(row, col);
3133 } else {
3134 Q3TableItem *i = new Q3TableItem(this, Q3TableItem::OnTyping,
3135 QString(), pix);
3136 setItem(row, col, i);
3137 }
3138 }
3139
3140 /*!
3141 Returns the text in the cell at \a row, \a col, or an empty string
3142 if the relevant item does not exist or has no text.
3143
3144 \sa setText() setPixmap()
3145 */
3146
text(int row,int col) const3147 QString Q3Table::text(int row, int col) const
3148 {
3149 Q3TableItem *itm = item(row, col);
3150 if (itm)
3151 return itm->text();
3152 return QString();
3153 }
3154
3155 /*!
3156 Returns the pixmap set for the cell at \a row, \a col, or a
3157 null-pixmap if the cell contains no pixmap.
3158
3159 \sa setPixmap()
3160 */
3161
pixmap(int row,int col) const3162 QPixmap Q3Table::pixmap(int row, int col) const
3163 {
3164 Q3TableItem *itm = item(row, col);
3165 if (itm)
3166 return itm->pixmap();
3167 return QPixmap();
3168 }
3169
3170 /*!
3171 Moves the focus to the cell at \a row, \a col.
3172
3173 \sa currentRow() currentColumn()
3174 */
3175
setCurrentCell(int row,int col)3176 void Q3Table::setCurrentCell(int row, int col)
3177 {
3178 setCurrentCell(row, col, true, true);
3179 }
3180
3181 // need to use a define, as leftMargin() is protected
3182 #define VERTICALMARGIN \
3183 (QApplication::reverseLayout() ? \
3184 rightMargin() \
3185 : \
3186 leftMargin() \
3187 )
3188
3189 /*!
3190 \reimp
3191 */
inputMethodQuery(Qt::InputMethodQuery query) const3192 QVariant Q3Table::inputMethodQuery(Qt::InputMethodQuery query) const
3193 {
3194 if (query == Qt::ImMicroFocus)
3195 return QRect(columnPos(curCol) + leftMargin() - contentsX(), rowPos(curRow) + topMargin() - contentsY(),
3196 columnWidth(curCol), rowHeight(curRow));
3197 return QWidget::inputMethodQuery(query);
3198
3199 }
3200
3201 /*! \internal */
3202
setCurrentCell(int row,int col,bool updateSelections,bool ensureVisible)3203 void Q3Table::setCurrentCell(int row, int col, bool updateSelections, bool ensureVisible)
3204 {
3205 Q3TableItem *oldItem = item(curRow, curCol);
3206
3207 if (row > numRows() - 1)
3208 row = numRows() - 1;
3209 if (col > numCols() - 1)
3210 col = numCols() - 1;
3211
3212 if (curRow == row && curCol == col)
3213 return;
3214
3215
3216 Q3TableItem *itm = oldItem;
3217 if (itm && itm->editType() != Q3TableItem::Always && itm->editType() != Q3TableItem::Never)
3218 endEdit(curRow, curCol, true, false);
3219 int oldRow = curRow;
3220 int oldCol = curCol;
3221 curRow = row;
3222 curCol = col;
3223 repaintCell(oldRow, oldCol);
3224 repaintCell(curRow, curCol);
3225 if (ensureVisible)
3226 ensureCellVisible(curRow, curCol);
3227 emit currentChanged(row, col);
3228
3229 if (oldCol != curCol) {
3230 if (!isColumnSelected(oldCol))
3231 topHeader->setSectionState(oldCol, Q3TableHeader::Normal);
3232 else if (isRowSelection(selectionMode()))
3233 topHeader->setSectionState(oldCol, Q3TableHeader::Selected);
3234 topHeader->setSectionState(curCol, isColumnSelected(curCol, true) ?
3235 Q3TableHeader::Selected : Q3TableHeader::Bold);
3236 }
3237
3238 if (oldRow != curRow) {
3239 if (!isRowSelected(oldRow))
3240 leftHeader->setSectionState(oldRow, Q3TableHeader::Normal);
3241 leftHeader->setSectionState(curRow, isRowSelected(curRow, true) ?
3242 Q3TableHeader::Selected : Q3TableHeader::Bold);
3243 }
3244
3245 itm = item(curRow, curCol);
3246
3247
3248 if (cellWidget(oldRow, oldCol) &&
3249 cellWidget(oldRow, oldCol)->hasFocus())
3250 viewport()->setFocus();
3251
3252 if (itm && itm->editType() == Q3TableItem::WhenCurrent) {
3253 if (beginEdit(curRow, curCol, false))
3254 setEditMode(Editing, row, col);
3255 } else if (itm && itm->editType() == Q3TableItem::Always) {
3256 if (cellWidget(itm->row(), itm->col()))
3257 cellWidget(itm->row(), itm->col())->setFocus();
3258 }
3259
3260 if (updateSelections && isRowSelection(selectionMode()) &&
3261 !isSelected(curRow, curCol, false)) {
3262 if (selectionMode() == Q3Table::SingleRow)
3263 clearSelection();
3264 currentSel = new Q3TableSelection();
3265 selections.append(currentSel);
3266 currentSel->init(curRow, 0);
3267 currentSel->expandTo(curRow, numCols() - 1);
3268 repaintSelections(0, currentSel);
3269 }
3270 }
3271
3272 /*!
3273 Scrolls the table until the cell at \a row, \a col becomes
3274 visible.
3275 */
3276
ensureCellVisible(int row,int col)3277 void Q3Table::ensureCellVisible(int row, int col)
3278 {
3279 if (!updatesEnabled() || !viewport()->updatesEnabled())
3280 return;
3281 int cw = columnWidth(col);
3282 int rh = rowHeight(row);
3283 if (cw < visibleWidth())
3284 ensureVisible(columnPos(col) + cw / 2, rowPos(row) + rh / 2, cw / 2, rh / 2);
3285 else
3286 ensureVisible(columnPos(col), rowPos(row) + rh / 2, 0, rh / 2);
3287 }
3288
3289 /*!
3290 Returns true if the cell at \a row, \a col is selected; otherwise
3291 returns false.
3292
3293 \sa isRowSelected() isColumnSelected()
3294 */
3295
isSelected(int row,int col) const3296 bool Q3Table::isSelected(int row, int col) const
3297 {
3298 return isSelected(row, col, true);
3299 }
3300
3301 /*! \internal */
3302
isSelected(int row,int col,bool includeCurrent) const3303 bool Q3Table::isSelected(int row, int col, bool includeCurrent) const
3304 {
3305 Q3PtrListIterator<Q3TableSelection> it(selections);
3306 Q3TableSelection *s;
3307 while ((s = it.current()) != 0) {
3308 ++it;
3309 if (s->isActive() &&
3310 row >= s->topRow() &&
3311 row <= s->bottomRow() &&
3312 col >= s->leftCol() &&
3313 col <= s->rightCol())
3314 return true;
3315 if (includeCurrent && row == currentRow() && col == currentColumn())
3316 return true;
3317 }
3318 return false;
3319 }
3320
3321 /*!
3322 Returns true if row \a row is selected; otherwise returns false.
3323
3324 If \a full is false (the default), 'row is selected' means that at
3325 least one cell in the row is selected. If \a full is true, then 'row
3326 is selected' means every cell in the row is selected.
3327
3328 \sa isColumnSelected() isSelected()
3329 */
3330
isRowSelected(int row,bool full) const3331 bool Q3Table::isRowSelected(int row, bool full) const
3332 {
3333 if (!full) {
3334 Q3PtrListIterator<Q3TableSelection> it(selections);
3335 Q3TableSelection *s;
3336 while ((s = it.current()) != 0) {
3337 ++it;
3338 if (s->isActive() &&
3339 row >= s->topRow() &&
3340 row <= s->bottomRow())
3341 return true;
3342 if (row == currentRow())
3343 return true;
3344 }
3345 } else {
3346 Q3PtrListIterator<Q3TableSelection> it(selections);
3347 Q3TableSelection *s;
3348 while ((s = it.current()) != 0) {
3349 ++it;
3350 if (s->isActive() &&
3351 row >= s->topRow() &&
3352 row <= s->bottomRow() &&
3353 s->leftCol() == 0 &&
3354 s->rightCol() == numCols() - 1)
3355 return true;
3356 }
3357 }
3358 return false;
3359 }
3360
3361 /*!
3362 Returns true if column \a col is selected; otherwise returns false.
3363
3364 If \a full is false (the default), 'column is selected' means that
3365 at least one cell in the column is selected. If \a full is true,
3366 then 'column is selected' means every cell in the column is
3367 selected.
3368
3369 \sa isRowSelected() isSelected()
3370 */
3371
isColumnSelected(int col,bool full) const3372 bool Q3Table::isColumnSelected(int col, bool full) const
3373 {
3374 if (!full) {
3375 Q3PtrListIterator<Q3TableSelection> it(selections);
3376 Q3TableSelection *s;
3377 while ((s = it.current()) != 0) {
3378 ++it;
3379 if (s->isActive() &&
3380 col >= s->leftCol() &&
3381 col <= s->rightCol())
3382 return true;
3383 if (col == currentColumn())
3384 return true;
3385 }
3386 } else {
3387 Q3PtrListIterator<Q3TableSelection> it(selections);
3388 Q3TableSelection *s;
3389 while ((s = it.current()) != 0) {
3390 ++it;
3391 if (s->isActive() &&
3392 col >= s->leftCol() &&
3393 col <= s->rightCol() &&
3394 s->topRow() == 0 &&
3395 s->bottomRow() == numRows() - 1)
3396 return true;
3397 }
3398 }
3399 return false;
3400 }
3401
3402 /*!
3403 \property Q3Table::numSelections
3404 \brief The number of selections.
3405
3406 \sa currentSelection()
3407 */
3408
numSelections() const3409 int Q3Table::numSelections() const
3410 {
3411 return selections.count();
3412 }
3413
3414 /*!
3415 Returns selection number \a num, or an inactive Q3TableSelection if \a
3416 num is out of range (see Q3TableSelection::isActive()).
3417 */
3418
selection(int num) const3419 Q3TableSelection Q3Table::selection(int num) const
3420 {
3421 if (num < 0 || num >= (int)selections.count())
3422 return Q3TableSelection();
3423
3424 Q3TableSelection *s = ((Q3Table*)this)->selections.at(num);
3425 return *s;
3426 }
3427
3428 /*!
3429 Adds a selection described by \a s to the table and returns its
3430 number or -1 if the selection is invalid.
3431
3432 Remember to call Q3TableSelection::init() and
3433 Q3TableSelection::expandTo() to make the selection valid (see also
3434 Q3TableSelection::isActive(), or use the
3435 Q3TableSelection(int,int,int,int) constructor).
3436
3437 \sa numSelections() removeSelection() clearSelection()
3438 */
3439
addSelection(const Q3TableSelection & s)3440 int Q3Table::addSelection(const Q3TableSelection &s)
3441 {
3442 if (!s.isActive())
3443 return -1;
3444
3445 const int maxr = numRows()-1;
3446 const int maxc = numCols()-1;
3447 currentSel = new Q3TableSelection(QMIN(s.anchorRow(), maxr), QMIN(s.anchorCol(), maxc),
3448 QMIN(s.bottomRow(), maxr), QMIN(s.rightCol(), maxc));
3449
3450 selections.append(currentSel);
3451
3452 repaintSelections(0, currentSel, true, true);
3453
3454 emit selectionChanged();
3455
3456 return selections.count() - 1;
3457 }
3458
3459 /*!
3460 If the table has a selection, \a s, this selection is removed from
3461 the table.
3462
3463 \sa addSelection() numSelections()
3464 */
3465
removeSelection(const Q3TableSelection & s)3466 void Q3Table::removeSelection(const Q3TableSelection &s)
3467 {
3468 selections.setAutoDelete(false);
3469 for (Q3TableSelection *sel = selections.first(); sel; sel = selections.next()) {
3470 if (s == *sel) {
3471 selections.removeRef(sel);
3472 repaintSelections(sel, 0, true, true);
3473 if (sel == currentSel)
3474 currentSel = 0;
3475 delete sel;
3476 }
3477 }
3478 selections.setAutoDelete(true);
3479 emit selectionChanged();
3480 }
3481
3482 /*!
3483 \overload
3484
3485 Removes selection number \a num from the table.
3486
3487 \sa numSelections() addSelection() clearSelection()
3488 */
3489
removeSelection(int num)3490 void Q3Table::removeSelection(int num)
3491 {
3492 if (num < 0 || num >= (int)selections.count())
3493 return;
3494
3495 Q3TableSelection *s = selections.at(num);
3496 if (s == currentSel)
3497 currentSel = 0;
3498 selections.removeRef(s);
3499 repaintContents(false);
3500 }
3501
3502 /*!
3503 Returns the number of the current selection or -1 if there is no
3504 current selection.
3505
3506 \sa numSelections()
3507 */
3508
currentSelection() const3509 int Q3Table::currentSelection() const
3510 {
3511 if (!currentSel)
3512 return -1;
3513 return ((Q3Table*)this)->selections.findRef(currentSel);
3514 }
3515
3516 /*! Selects the range starting at \a start_row and \a start_col and
3517 ending at \a end_row and \a end_col.
3518
3519 \sa Q3TableSelection
3520 */
3521
selectCells(int start_row,int start_col,int end_row,int end_col)3522 void Q3Table::selectCells(int start_row, int start_col, int end_row, int end_col)
3523 {
3524 const int maxr = numRows()-1;
3525 const int maxc = numCols()-1;
3526
3527 start_row = QMIN(maxr, QMAX(0, start_row));
3528 start_col = QMIN(maxc, QMAX(0, start_col));
3529 end_row = QMIN(maxr, end_row);
3530 end_col = QMIN(maxc, end_col);
3531 Q3TableSelection sel(start_row, start_col, end_row, end_col);
3532 addSelection(sel);
3533 }
3534
3535 /*! Selects the row \a row.
3536
3537 \sa Q3TableSelection
3538 */
3539
selectRow(int row)3540 void Q3Table::selectRow(int row)
3541 {
3542 row = QMIN(numRows()-1, row);
3543 if (row < 0)
3544 return;
3545 if (selectionMode() == SingleRow) {
3546 setCurrentCell(row, currentColumn());
3547 } else {
3548 Q3TableSelection sel(row, 0, row, numCols() - 1);
3549 addSelection(sel);
3550 }
3551 }
3552
3553 /*! Selects the column \a col.
3554
3555 \sa Q3TableSelection
3556 */
3557
selectColumn(int col)3558 void Q3Table::selectColumn(int col)
3559 {
3560 col = QMIN(numCols()-1, col);
3561 if (col < 0)
3562 return;
3563 Q3TableSelection sel(0, col, numRows() - 1, col);
3564 addSelection(sel);
3565 }
3566
3567 /*! \reimp
3568 */
contentsMousePressEvent(QMouseEvent * e)3569 void Q3Table::contentsMousePressEvent(QMouseEvent* e)
3570 {
3571 contentsMousePressEventEx(e);
3572 }
3573
contentsMousePressEventEx(QMouseEvent * e)3574 void Q3Table::contentsMousePressEventEx(QMouseEvent* e)
3575 {
3576 shouldClearSelection = false;
3577 if (isEditing()) {
3578 if (!cellGeometry(editRow, editCol).contains(e->pos())) {
3579 endEdit(editRow, editCol, true, edMode != Editing);
3580 } else {
3581 e->ignore();
3582 return;
3583 }
3584 }
3585
3586 d->redirectMouseEvent = false;
3587
3588 int tmpRow = rowAt(e->pos().y());
3589 int tmpCol = columnAt(e->pos().x());
3590 pressedRow = tmpRow;
3591 pressedCol = tmpCol;
3592 fixRow(tmpRow, e->pos().y());
3593 fixCol(tmpCol, e->pos().x());
3594 startDragCol = -1;
3595 startDragRow = -1;
3596
3597 if (isSelected(tmpRow, tmpCol)) {
3598 startDragCol = tmpCol;
3599 startDragRow = tmpRow;
3600 dragStartPos = e->pos();
3601 }
3602
3603 Q3TableItem *itm = item(pressedRow, pressedCol);
3604 if (itm && !itm->isEnabled()) {
3605 emit pressed(tmpRow, tmpCol, e->button(), e->pos());
3606 return;
3607 }
3608
3609 if ((e->state() & ShiftButton) == ShiftButton) {
3610 int oldRow = curRow;
3611 int oldCol = curCol;
3612 setCurrentCell(tmpRow, tmpCol, selMode == SingleRow, true);
3613 if (selMode != NoSelection && selMode != SingleRow) {
3614 if (!currentSel) {
3615 currentSel = new Q3TableSelection();
3616 selections.append(currentSel);
3617 if (!isRowSelection(selectionMode()))
3618 currentSel->init(oldRow, oldCol);
3619 else
3620 currentSel->init(oldRow, 0);
3621 }
3622 Q3TableSelection oldSelection = *currentSel;
3623 if (!isRowSelection(selectionMode()))
3624 currentSel->expandTo(tmpRow, tmpCol);
3625 else
3626 currentSel->expandTo(tmpRow, numCols() - 1);
3627 repaintSelections(&oldSelection, currentSel);
3628 emit selectionChanged();
3629 }
3630 } else if ((e->state() & ControlButton) == ControlButton) {
3631 setCurrentCell(tmpRow, tmpCol, false, true);
3632 if (selMode != NoSelection) {
3633 if (selMode == Single || (selMode == SingleRow && !isSelected(tmpRow, tmpCol, false)))
3634 clearSelection();
3635 if (!(selMode == SingleRow && isSelected(tmpRow, tmpCol, false))) {
3636 currentSel = new Q3TableSelection();
3637 selections.append(currentSel);
3638 if (!isRowSelection(selectionMode())) {
3639 currentSel->init(tmpRow, tmpCol);
3640 currentSel->expandTo(tmpRow, tmpCol);
3641 } else {
3642 currentSel->init(tmpRow, 0);
3643 currentSel->expandTo(tmpRow, numCols() - 1);
3644 repaintSelections(0, currentSel);
3645 }
3646 emit selectionChanged();
3647 }
3648 }
3649 } else {
3650 setCurrentCell(tmpRow, tmpCol, false, true);
3651 Q3TableItem *itm = item(tmpRow, tmpCol);
3652 if (itm && itm->editType() == Q3TableItem::WhenCurrent) {
3653 QWidget *w = cellWidget(tmpRow, tmpCol);
3654 if (qobject_cast<Q3ComboBox*>(w) || qobject_cast<QAbstractButton*>(w)) {
3655 QMouseEvent ev(e->type(), w->mapFromGlobal(e->globalPos()),
3656 e->globalPos(), e->button(), e->state());
3657 QApplication::sendPostedEvents(w, 0);
3658 QApplication::sendEvent(w, &ev);
3659 d->redirectMouseEvent = true;
3660 }
3661 }
3662 if (isSelected(tmpRow, tmpCol, false)) {
3663 shouldClearSelection = true;
3664 } else {
3665 bool b = signalsBlocked();
3666 if (selMode != NoSelection)
3667 blockSignals(true);
3668 clearSelection();
3669 blockSignals(b);
3670 if (selMode != NoSelection) {
3671 currentSel = new Q3TableSelection();
3672 selections.append(currentSel);
3673 if (!isRowSelection(selectionMode())) {
3674 currentSel->init(tmpRow, tmpCol);
3675 currentSel->expandTo(tmpRow, tmpCol);
3676 } else {
3677 currentSel->init(tmpRow, 0);
3678 currentSel->expandTo(tmpRow, numCols() - 1);
3679 repaintSelections(0, currentSel);
3680 }
3681 emit selectionChanged();
3682 }
3683 }
3684 }
3685
3686 emit pressed(tmpRow, tmpCol, e->button(), e->pos());
3687 }
3688
3689 /*! \reimp
3690 */
3691
contentsMouseDoubleClickEvent(QMouseEvent * e)3692 void Q3Table::contentsMouseDoubleClickEvent(QMouseEvent *e)
3693 {
3694 if (e->button() != LeftButton)
3695 return;
3696 if (!isRowSelection(selectionMode()))
3697 clearSelection();
3698 int tmpRow = rowAt(e->pos().y());
3699 int tmpCol = columnAt(e->pos().x());
3700 Q3TableItem *itm = item(tmpRow, tmpCol);
3701 if (itm && !itm->isEnabled())
3702 return;
3703 if (tmpRow != -1 && tmpCol != -1) {
3704 if (beginEdit(tmpRow, tmpCol, false))
3705 setEditMode(Editing, tmpRow, tmpCol);
3706 }
3707
3708 emit doubleClicked(tmpRow, tmpCol, e->button(), e->pos());
3709 }
3710
3711 /*!
3712 Sets the current edit mode to \a mode, the current edit row to \a
3713 row and the current edit column to \a col.
3714
3715 \sa EditMode
3716 */
3717
setEditMode(EditMode mode,int row,int col)3718 void Q3Table::setEditMode(EditMode mode, int row, int col)
3719 {
3720 edMode = mode;
3721 editRow = row;
3722 editCol = col;
3723 }
3724
3725
3726 /*! \reimp
3727 */
3728
contentsMouseMoveEvent(QMouseEvent * e)3729 void Q3Table::contentsMouseMoveEvent(QMouseEvent *e)
3730 {
3731 if ((e->state() & MouseButtonMask) == NoButton)
3732 return;
3733 int tmpRow = rowAt(e->pos().y());
3734 int tmpCol = columnAt(e->pos().x());
3735 fixRow(tmpRow, e->pos().y());
3736 fixCol(tmpCol, e->pos().x());
3737
3738 #ifndef QT_NO_DRAGANDDROP
3739 if (dragEnabled() && startDragRow != -1 && startDragCol != -1) {
3740 if (QPoint(dragStartPos - e->pos()).manhattanLength() > QApplication::startDragDistance())
3741 startDrag();
3742 return;
3743 }
3744 #endif
3745 if (selectionMode() == MultiRow && (e->state() & ControlButton) == ControlButton)
3746 shouldClearSelection = false;
3747
3748 if (shouldClearSelection) {
3749 clearSelection();
3750 if (selMode != NoSelection) {
3751 currentSel = new Q3TableSelection();
3752 selections.append(currentSel);
3753 if (!isRowSelection(selectionMode()))
3754 currentSel->init(tmpRow, tmpCol);
3755 else
3756 currentSel->init(tmpRow, 0);
3757 emit selectionChanged();
3758 }
3759 shouldClearSelection = false;
3760 }
3761
3762 QPoint pos = mapFromGlobal(e->globalPos());
3763 pos -= QPoint(leftHeader->width(), topHeader->height());
3764 autoScrollTimer->stop();
3765 doAutoScroll();
3766 if (pos.x() < 0 || pos.x() > visibleWidth() || pos.y() < 0 || pos.y() > visibleHeight())
3767 autoScrollTimer->start(100, true);
3768 }
3769
3770 /*! \internal
3771 */
3772
doValueChanged()3773 void Q3Table::doValueChanged()
3774 {
3775 emit valueChanged(editRow, editCol);
3776 }
3777
3778 /*! \internal
3779 */
3780
doAutoScroll()3781 void Q3Table::doAutoScroll()
3782 {
3783 QPoint pos = QCursor::pos();
3784 pos = mapFromGlobal(pos);
3785 pos -= QPoint(leftHeader->width(), topHeader->height());
3786
3787 int tmpRow = curRow;
3788 int tmpCol = curCol;
3789 if (pos.y() < 0)
3790 tmpRow--;
3791 else if (pos.y() > visibleHeight())
3792 tmpRow++;
3793 if (pos.x() < 0)
3794 tmpCol--;
3795 else if (pos.x() > visibleWidth())
3796 tmpCol++;
3797
3798 pos += QPoint(contentsX(), contentsY());
3799 if (tmpRow == curRow)
3800 tmpRow = rowAt(pos.y());
3801 if (tmpCol == curCol)
3802 tmpCol = columnAt(pos.x());
3803 pos -= QPoint(contentsX(), contentsY());
3804
3805 fixRow(tmpRow, pos.y());
3806 fixCol(tmpCol, pos.x());
3807
3808 if (tmpRow < 0 || tmpRow > numRows() - 1)
3809 tmpRow = currentRow();
3810 if (tmpCol < 0 || tmpCol > numCols() - 1)
3811 tmpCol = currentColumn();
3812
3813 ensureCellVisible(tmpRow, tmpCol);
3814
3815 if (currentSel && selMode != NoSelection) {
3816 Q3TableSelection oldSelection = *currentSel;
3817 bool useOld = true;
3818 if (selMode != SingleRow) {
3819 if (!isRowSelection(selectionMode())) {
3820 currentSel->expandTo(tmpRow, tmpCol);
3821 } else {
3822 currentSel->expandTo(tmpRow, numCols() - 1);
3823 }
3824 } else {
3825 bool currentInSelection = tmpRow == curRow && isSelected(tmpRow, tmpCol);
3826 if (!currentInSelection) {
3827 useOld = false;
3828 clearSelection();
3829 currentSel = new Q3TableSelection();
3830 selections.append(currentSel);
3831 currentSel->init(tmpRow, 0);
3832 currentSel->expandTo(tmpRow, numCols() - 1);
3833 repaintSelections(0, currentSel);
3834 } else {
3835 currentSel->expandTo(tmpRow, numCols() - 1);
3836 }
3837 }
3838 setCurrentCell(tmpRow, tmpCol, false, true);
3839 repaintSelections(useOld ? &oldSelection : 0, currentSel);
3840 if (currentSel && oldSelection != *currentSel)
3841 emit selectionChanged();
3842 } else {
3843 setCurrentCell(tmpRow, tmpCol, false, true);
3844 }
3845
3846 if (pos.x() < 0 || pos.x() > visibleWidth() || pos.y() < 0 || pos.y() > visibleHeight())
3847 autoScrollTimer->start(100, true);
3848 }
3849
3850 /*! \reimp
3851 */
3852
contentsMouseReleaseEvent(QMouseEvent * e)3853 void Q3Table::contentsMouseReleaseEvent(QMouseEvent *e)
3854 {
3855 if (pressedRow == curRow && pressedCol == curCol)
3856 emit clicked(curRow, curCol, e->button(), e->pos());
3857
3858 if (e->button() != LeftButton)
3859 return;
3860 if (shouldClearSelection) {
3861 int tmpRow = rowAt(e->pos().y());
3862 int tmpCol = columnAt(e->pos().x());
3863 fixRow(tmpRow, e->pos().y());
3864 fixCol(tmpCol, e->pos().x());
3865 clearSelection();
3866 if (selMode != NoSelection) {
3867 currentSel = new Q3TableSelection();
3868 selections.append(currentSel);
3869 if (!isRowSelection(selectionMode())) {
3870 currentSel->init(tmpRow, tmpCol);
3871 } else {
3872 currentSel->init(tmpRow, 0);
3873 currentSel->expandTo(tmpRow, numCols() - 1);
3874 repaintSelections(0, currentSel);
3875 }
3876 emit selectionChanged();
3877 }
3878 shouldClearSelection = false;
3879 }
3880 autoScrollTimer->stop();
3881
3882 if (d->redirectMouseEvent && pressedRow == curRow && pressedCol == curCol &&
3883 item(pressedRow, pressedCol) && item(pressedRow, pressedCol)->editType() ==
3884 Q3TableItem::WhenCurrent) {
3885 QWidget *w = cellWidget(pressedRow, pressedCol);
3886 if (w) {
3887 QMouseEvent ev(e->type(), w->mapFromGlobal(e->globalPos()),
3888 e->globalPos(), e->button(), e->state());
3889 QApplication::sendPostedEvents(w, 0);
3890 bool old = w->testAttribute(Qt::WA_NoMousePropagation);
3891 w->setAttribute(Qt::WA_NoMousePropagation, true);
3892 QApplication::sendEvent(w, &ev);
3893 w->setAttribute(Qt::WA_NoMousePropagation, old);
3894 }
3895 }
3896 }
3897
3898 /*!
3899 \reimp
3900 */
3901
contentsContextMenuEvent(QContextMenuEvent * e)3902 void Q3Table::contentsContextMenuEvent(QContextMenuEvent *e)
3903 {
3904 if (!receivers(SIGNAL(contextMenuRequested(int,int,QPoint)))) {
3905 e->ignore();
3906 return;
3907 }
3908 if (e->reason() == QContextMenuEvent::Keyboard) {
3909 QRect r = cellGeometry(curRow, curCol);
3910 emit contextMenuRequested(curRow, curCol, viewport()->mapToGlobal(contentsToViewport(r.center())));
3911 } else {
3912 int tmpRow = rowAt(e->pos().y());
3913 int tmpCol = columnAt(e->pos().x());
3914 emit contextMenuRequested(tmpRow, tmpCol, e->globalPos());
3915 }
3916 }
3917
3918
3919 /*! \reimp
3920 */
3921
eventFilter(QObject * o,QEvent * e)3922 bool Q3Table::eventFilter(QObject *o, QEvent *e)
3923 {
3924 switch (e->type()) {
3925 case QEvent::KeyPress: {
3926 Q3TableItem *itm = item(curRow, curCol);
3927 QWidget *editorWidget = cellWidget(editRow, editCol);
3928
3929 if (isEditing() && editorWidget && o == editorWidget) {
3930 itm = item(editRow, editCol);
3931 QKeyEvent *ke = (QKeyEvent*)e;
3932 if (ke->key() == Key_Escape) {
3933 if (!itm || itm->editType() == Q3TableItem::OnTyping)
3934 endEdit(editRow, editCol, false, edMode != Editing);
3935 return true;
3936 }
3937
3938 if ((ke->state() == NoButton || ke->state() == Keypad)
3939 && (ke->key() == Key_Return || ke->key() == Key_Enter)) {
3940 if (!itm || itm->editType() == Q3TableItem::OnTyping)
3941 endEdit(editRow, editCol, true, edMode != Editing);
3942 activateNextCell();
3943 return true;
3944 }
3945
3946 if (ke->key() == Key_Tab || ke->key() == Key_BackTab) {
3947 if (ke->state() & Qt::ControlButton)
3948 return false;
3949 if (!itm || itm->editType() == Q3TableItem::OnTyping)
3950 endEdit(editRow, editCol, true, edMode != Editing);
3951 if ((ke->key() == Key_Tab) && !(ke->state() & ShiftButton)) {
3952 if (currentColumn() >= numCols() - 1)
3953 return true;
3954 int cc = QMIN(numCols() - 1, currentColumn() + 1);
3955 while (cc < numCols()) {
3956 Q3TableItem *i = item(currentRow(), cc);
3957 if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled()))
3958 break;
3959 ++cc;
3960 }
3961 setCurrentCell(currentRow(), cc);
3962 } else { // Key_BackTab
3963 if (currentColumn() == 0)
3964 return true;
3965 int cc = QMAX(0, currentColumn() - 1);
3966 while (cc >= 0) {
3967 Q3TableItem *i = item(currentRow(), cc);
3968 if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled()))
3969 break;
3970 --cc;
3971 }
3972 setCurrentCell(currentRow(), cc);
3973 }
3974 itm = item(curRow, curCol);
3975 if (beginEdit(curRow, curCol, false))
3976 setEditMode(Editing, curRow, curCol);
3977 return true;
3978 }
3979
3980 if ((edMode == Replacing ||
3981 (itm && itm->editType() == Q3TableItem::WhenCurrent)) &&
3982 (ke->key() == Key_Up || ke->key() == Key_Prior ||
3983 ke->key() == Key_Home || ke->key() == Key_Down ||
3984 ke->key() == Key_Next || ke->key() == Key_End ||
3985 ke->key() == Key_Left || ke->key() == Key_Right)) {
3986 if (!itm || itm->editType() == Q3TableItem::OnTyping) {
3987 endEdit(editRow, editCol, true, edMode != Editing);
3988 }
3989 keyPressEvent(ke);
3990 return true;
3991 }
3992 } else {
3993 QObjectList l = viewport()->queryList("QWidget");
3994 if (l.contains(o)) {
3995 QKeyEvent *ke = (QKeyEvent*)e;
3996 if ((ke->state() & ControlButton) == ControlButton ||
3997 (ke->key() != Key_Left && ke->key() != Key_Right &&
3998 ke->key() != Key_Up && ke->key() != Key_Down &&
3999 ke->key() != Key_Prior && ke->key() != Key_Next &&
4000 ke->key() != Key_Home && ke->key() != Key_End))
4001 return false;
4002 keyPressEvent((QKeyEvent*)e);
4003 return true;
4004 }
4005 }
4006
4007 } break;
4008 case QEvent::FocusOut: {
4009 QWidget *editorWidget = cellWidget(editRow, editCol);
4010 if (isEditing() && editorWidget && o == editorWidget && ((QFocusEvent*)e)->reason() != Qt::PopupFocusReason) {
4011 // if the editor is the parent of the new focus widget, do nothing
4012 QWidget *w = QApplication::focusWidget();
4013 while (w) {
4014 w = w->parentWidget();
4015 if (w == editorWidget)
4016 break;
4017 }
4018 if (w)
4019 break;
4020 // otherwise, end editing
4021 Q3TableItem *itm = item(editRow, editCol);
4022 if (!itm || itm->editType() == Q3TableItem::OnTyping) {
4023 endEdit(editRow, editCol, true, edMode != Editing);
4024 return true;
4025 }
4026 }
4027 break;
4028 }
4029 #ifndef QT_NO_WHEELEVENT
4030 case QEvent::Wheel:
4031 if (o == this || o == viewport()) {
4032 QWheelEvent* we = (QWheelEvent*)e;
4033 scrollBy(0, -we->delta());
4034 we->accept();
4035 return true;
4036 }
4037 #endif
4038 default:
4039 break;
4040 }
4041
4042 return Q3ScrollView::eventFilter(o, e);
4043 }
4044
fixCell(int & row,int & col,int key)4045 void Q3Table::fixCell(int &row, int &col, int key)
4046 {
4047 if (rowHeight(row) > 0 && columnWidth(col) > 0)
4048 return;
4049 if (rowHeight(row) <= 0) {
4050 if (key == Key_Down ||
4051 key == Key_Next ||
4052 key == Key_End) {
4053 while (row < numRows() && rowHeight(row) <= 0)
4054 row++;
4055 if (rowHeight(row) <= 0)
4056 row = curRow;
4057 } else if (key == Key_Up ||
4058 key == Key_Prior ||
4059 key == Key_Home)
4060 while (row >= 0 && rowHeight(row) <= 0)
4061 row--;
4062 if (rowHeight(row) <= 0)
4063 row = curRow;
4064 } else if (columnWidth(col) <= 0) {
4065 if (key == Key_Left) {
4066 while (col >= 0 && columnWidth(col) <= 0)
4067 col--;
4068 if (columnWidth(col) <= 0)
4069 col = curCol;
4070 } else if (key == Key_Right) {
4071 while (col < numCols() && columnWidth(col) <= 0)
4072 col++;
4073 if (columnWidth(col) <= 0)
4074 col = curCol;
4075 }
4076 }
4077 }
4078
4079 /*! \reimp
4080 */
4081
keyPressEvent(QKeyEvent * e)4082 void Q3Table::keyPressEvent(QKeyEvent* e)
4083 {
4084 if (isEditing() && item(editRow, editCol) &&
4085 item(editRow, editCol)->editType() == Q3TableItem::OnTyping)
4086 return;
4087
4088 int tmpRow = curRow;
4089 int tmpCol = curCol;
4090 int oldRow = tmpRow;
4091 int oldCol = tmpCol;
4092
4093 bool navigationKey = false;
4094 int r;
4095 switch (e->key()) {
4096 case Key_Left:
4097 tmpCol = QMAX(0, tmpCol - 1);
4098 navigationKey = true;
4099 break;
4100 case Key_Right:
4101 tmpCol = QMIN(numCols() - 1, tmpCol + 1);
4102 navigationKey = true;
4103 break;
4104 case Key_Up:
4105 tmpRow = QMAX(0, tmpRow - 1);
4106 navigationKey = true;
4107 break;
4108 case Key_Down:
4109 tmpRow = QMIN(numRows() - 1, tmpRow + 1);
4110 navigationKey = true;
4111 break;
4112 case Key_Prior:
4113 r = QMAX(0, rowAt(rowPos(tmpRow) - visibleHeight()));
4114 if (r < tmpRow || tmpRow < 0)
4115 tmpRow = r;
4116 navigationKey = true;
4117 break;
4118 case Key_Next:
4119 r = QMIN(numRows() - 1, rowAt(rowPos(tmpRow) + visibleHeight()));
4120 if (r > tmpRow)
4121 tmpRow = r;
4122 else
4123 tmpRow = numRows() - 1;
4124 navigationKey = true;
4125 break;
4126 case Key_Home:
4127 tmpRow = 0;
4128 navigationKey = true;
4129 break;
4130 case Key_End:
4131 tmpRow = numRows() - 1;
4132 navigationKey = true;
4133 break;
4134 case Key_F2:
4135 if (beginEdit(tmpRow, tmpCol, false))
4136 setEditMode(Editing, tmpRow, tmpCol);
4137 break;
4138 case Key_Enter: case Key_Return:
4139 activateNextCell();
4140 return;
4141 case Key_Tab: case Key_BackTab:
4142 if ((e->key() == Key_Tab) && !(e->state() & ShiftButton)) {
4143 if (currentColumn() >= numCols() - 1)
4144 return;
4145 int cc = QMIN(numCols() - 1, currentColumn() + 1);
4146 while (cc < numCols()) {
4147 Q3TableItem *i = item(currentRow(), cc);
4148 if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled()))
4149 break;
4150 ++cc;
4151 }
4152 setCurrentCell(currentRow(), cc);
4153 } else { // Key_BackTab
4154 if (currentColumn() == 0)
4155 return;
4156 int cc = QMAX(0, currentColumn() - 1);
4157 while (cc >= 0) {
4158 Q3TableItem *i = item(currentRow(), cc);
4159 if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled()))
4160 break;
4161 --cc;
4162 }
4163 setCurrentCell(currentRow(), cc);
4164 }
4165 return;
4166 case Key_Escape:
4167 e->ignore();
4168 return;
4169 default: // ... or start in-place editing
4170 if (e->text()[ 0 ].isPrint()) {
4171 Q3TableItem *itm = item(tmpRow, tmpCol);
4172 if (!itm || itm->editType() == Q3TableItem::OnTyping) {
4173 QWidget *w = beginEdit(tmpRow, tmpCol,
4174 itm ? itm->isReplaceable() : true);
4175 if (w) {
4176 setEditMode((!itm || (itm && itm->isReplaceable())
4177 ? Replacing : Editing), tmpRow, tmpCol);
4178 QApplication::sendEvent(w, e);
4179 return;
4180 }
4181 }
4182 }
4183 e->ignore();
4184 return;
4185 }
4186
4187 if (navigationKey) {
4188 fixCell(tmpRow, tmpCol, e->key());
4189 if ((e->state() & ShiftButton) == ShiftButton &&
4190 selMode != NoSelection && selMode != SingleRow) {
4191 bool justCreated = false;
4192 setCurrentCell(tmpRow, tmpCol, false, true);
4193 if (!currentSel) {
4194 justCreated = true;
4195 currentSel = new Q3TableSelection();
4196 selections.append(currentSel);
4197 if (!isRowSelection(selectionMode()))
4198 currentSel->init(oldRow, oldCol);
4199 else
4200 currentSel->init(oldRow < 0 ? 0 : oldRow, 0);
4201 }
4202 Q3TableSelection oldSelection = *currentSel;
4203 if (!isRowSelection(selectionMode()))
4204 currentSel->expandTo(tmpRow, tmpCol);
4205 else
4206 currentSel->expandTo(tmpRow, numCols() - 1);
4207 repaintSelections(justCreated ? 0 : &oldSelection, currentSel);
4208 emit selectionChanged();
4209 } else {
4210 setCurrentCell(tmpRow, tmpCol, false, true);
4211 if (!isRowSelection(selectionMode())) {
4212 clearSelection();
4213 } else {
4214 bool currentInSelection = tmpRow == oldRow && isSelected(tmpRow, tmpCol, false);
4215 if (!currentInSelection) {
4216 bool hasOldSel = false;
4217 Q3TableSelection oldSelection;
4218 if (selectionMode() == MultiRow) {
4219 bool b = signalsBlocked();
4220 blockSignals(true);
4221 clearSelection();
4222 blockSignals(b);
4223 } else {
4224 if (currentSel) {
4225 oldSelection = *currentSel;
4226 hasOldSel = true;
4227 selections.removeRef(currentSel);
4228 leftHeader->setSectionState(oldSelection.topRow(), Q3TableHeader::Normal);
4229 }
4230 }
4231 currentSel = new Q3TableSelection();
4232 selections.append(currentSel);
4233 currentSel->init(tmpRow, 0);
4234 currentSel->expandTo(tmpRow, numCols() - 1);
4235 repaintSelections(hasOldSel ? &oldSelection : 0, currentSel, !hasOldSel);
4236 emit selectionChanged();
4237 }
4238 }
4239 }
4240 } else {
4241 setCurrentCell(tmpRow, tmpCol, false, true);
4242 }
4243 }
4244
4245 /*! \reimp
4246 */
4247
focusInEvent(QFocusEvent *)4248 void Q3Table::focusInEvent(QFocusEvent*)
4249 {
4250 d->inMenuMode = false;
4251 QWidget *editorWidget = cellWidget(editRow, editCol);
4252 updateCell(curRow, curCol);
4253 if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this))
4254 repaintSelections();
4255 if (isEditing() && editorWidget)
4256 editorWidget->setFocus();
4257
4258 }
4259
4260
4261 /*! \reimp
4262 */
4263
focusOutEvent(QFocusEvent * e)4264 void Q3Table::focusOutEvent(QFocusEvent *e)
4265 {
4266 updateCell(curRow, curCol);
4267 if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) {
4268 d->inMenuMode =
4269 e->reason() == Qt::PopupFocusReason ||
4270 (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar"));
4271 if (!d->inMenuMode)
4272 repaintSelections();
4273 }
4274 }
4275
4276 /*! \reimp
4277 */
4278
sizeHint() const4279 QSize Q3Table::sizeHint() const
4280 {
4281 if (cachedSizeHint().isValid())
4282 return cachedSizeHint();
4283
4284 constPolish();
4285
4286 QSize s = tableSize();
4287 QSize sh;
4288 if (s.width() < 500 && s.height() < 500) {
4289 sh = QSize(tableSize().width() + VERTICALMARGIN + 5,
4290 tableSize().height() + topMargin() + 5);
4291 } else {
4292 sh = Q3ScrollView::sizeHint();
4293 if (!topHeader->isHidden())
4294 sh.setHeight(sh.height() + topHeader->height());
4295 if (!leftHeader->isHidden())
4296 sh.setWidth(sh.width() + leftHeader->width());
4297 }
4298 setCachedSizeHint(sh);
4299 return sh;
4300 }
4301
4302 /*! \reimp
4303 */
4304
viewportResizeEvent(QResizeEvent * e)4305 void Q3Table::viewportResizeEvent(QResizeEvent *e)
4306 {
4307 Q3ScrollView::viewportResizeEvent(e);
4308 updateGeometries();
4309 }
4310
4311 /*! \reimp
4312 */
4313
showEvent(QShowEvent * e)4314 void Q3Table::showEvent(QShowEvent *e)
4315 {
4316 Q3ScrollView::showEvent(e);
4317 QRect r(cellGeometry(numRows() - 1, numCols() - 1));
4318 resizeContents(r.right() + 1, r.bottom() + 1);
4319 updateGeometries();
4320 }
4321
4322 /*! \reimp
4323 */
4324
paintEvent(QPaintEvent * e)4325 void Q3Table::paintEvent(QPaintEvent *e)
4326 {
4327 QRect topLeftCorner = QStyle::visualRect(layoutDirection(), rect(), QRect(frameWidth(), frameWidth(), VERTICALMARGIN, topMargin()));
4328 erase(topLeftCorner); // erase instead of widget on top
4329 Q3ScrollView::paintEvent(e);
4330
4331 #ifdef Q_OS_WINCE
4332 QPainter p(this);
4333 p.drawLine(topLeftCorner.bottomLeft(), topLeftCorner.bottomRight());
4334 p.drawLine(topLeftCorner.bottomRight(), topLeftCorner.topRight());
4335 #endif
4336 }
4337
4338 static bool inUpdateCell = false;
4339
4340 /*!
4341 Repaints the cell at \a row, \a col.
4342 */
4343
updateCell(int row,int col)4344 void Q3Table::updateCell(int row, int col)
4345 {
4346 if (inUpdateCell || row < 0 || col < 0)
4347 return;
4348 inUpdateCell = true;
4349 QRect cg = cellGeometry(row, col);
4350 QRect r(contentsToViewport(QPoint(cg.x() - 2, cg.y() - 2)),
4351 QSize(cg.width() + 4, cg.height() + 4));
4352 viewport()->update(r);
4353 inUpdateCell = false;
4354 }
4355
repaintCell(int row,int col)4356 void Q3Table::repaintCell(int row, int col)
4357 {
4358 if (row == -1 || col == -1)
4359 return;
4360 QRect cg = cellGeometry(row, col);
4361 QRect r(QPoint(cg.x() - 2, cg.y() - 2),
4362 QSize(cg.width() + 4, cg.height() + 4));
4363 repaintContents(r, false);
4364 }
4365
contentsToViewport2(int x,int y,int & vx,int & vy)4366 void Q3Table::contentsToViewport2(int x, int y, int& vx, int& vy)
4367 {
4368 const QPoint v = contentsToViewport2(QPoint(x, y));
4369 vx = v.x();
4370 vy = v.y();
4371 }
4372
contentsToViewport2(const QPoint & p)4373 QPoint Q3Table::contentsToViewport2(const QPoint &p)
4374 {
4375 return QPoint(p.x() - contentsX(),
4376 p.y() - contentsY());
4377 }
4378
viewportToContents2(const QPoint & vp)4379 QPoint Q3Table::viewportToContents2(const QPoint& vp)
4380 {
4381 return QPoint(vp.x() + contentsX(),
4382 vp.y() + contentsY());
4383 }
4384
viewportToContents2(int vx,int vy,int & x,int & y)4385 void Q3Table::viewportToContents2(int vx, int vy, int& x, int& y)
4386 {
4387 const QPoint c = viewportToContents2(QPoint(vx, vy));
4388 x = c.x();
4389 y = c.y();
4390 }
4391
4392 /*!
4393 This function should be called whenever the column width of \a col
4394 has been changed. It updates the geometry of any affected columns
4395 and repaints the table to reflect the changes it has made.
4396 */
4397
columnWidthChanged(int col)4398 void Q3Table::columnWidthChanged(int col)
4399 {
4400 int p = columnPos(col);
4401 if (d->hasColSpan)
4402 p = contentsX();
4403 updateContents(p, contentsY(), contentsWidth(), visibleHeight());
4404 QSize s(tableSize());
4405 int w = contentsWidth();
4406 resizeContents(s.width(), s.height());
4407 if (contentsWidth() < w)
4408 repaintContents(s.width(), contentsY(),
4409 w - s.width() + 1, visibleHeight(), true);
4410 else
4411 repaintContents(w, contentsY(),
4412 s.width() - w + 1, visibleHeight(), false);
4413
4414 // update widgets that are affected by this change
4415 if (widgets.size()) {
4416 int last = isHidden() ? numCols() - 1 : d->lastVisCol;
4417 for (int c = col; c <= last; ++c)
4418 updateColWidgets(c);
4419 }
4420 delayedUpdateGeometries();
4421 }
4422
4423 /*!
4424 This function should be called whenever the row height of \a row
4425 has been changed. It updates the geometry of any affected rows and
4426 repaints the table to reflect the changes it has made.
4427 */
4428
rowHeightChanged(int row)4429 void Q3Table::rowHeightChanged(int row)
4430 {
4431 int p = rowPos(row);
4432 if (d->hasRowSpan)
4433 p = contentsY();
4434 updateContents(contentsX(), p, visibleWidth(), contentsHeight());
4435 QSize s(tableSize());
4436 int h = contentsHeight();
4437 resizeContents(s.width(), s.height());
4438 if (contentsHeight() < h) {
4439 repaintContents(contentsX(), contentsHeight(),
4440 visibleWidth(), h - s.height() + 1, true);
4441 } else {
4442 repaintContents(contentsX(), h,
4443 visibleWidth(), s.height() - h + 1, false);
4444 }
4445
4446 // update widgets that are affected by this change
4447 if (widgets.size()) {
4448 d->lastVisRow = rowAt(contentsY() + visibleHeight() + (s.height() - h + 1));
4449 int last = isHidden() ? numRows() - 1 : d->lastVisRow;
4450 for (int r = row; r <= last; ++r)
4451 updateRowWidgets(r);
4452 }
4453 delayedUpdateGeometries();
4454 }
4455
4456 /*! \internal */
4457
updateRowWidgets(int row)4458 void Q3Table::updateRowWidgets(int row)
4459 {
4460 for (int i = 0; i < numCols(); ++i) {
4461 QWidget *w = cellWidget(row, i);
4462 if (!w)
4463 continue;
4464 moveChild(w, columnPos(i), rowPos(row));
4465 w->resize(columnWidth(i) - 1, rowHeight(row) - 1);
4466 }
4467 }
4468
4469 /*! \internal */
4470
updateColWidgets(int col)4471 void Q3Table::updateColWidgets(int col)
4472 {
4473 for (int i = 0; i < numRows(); ++i) {
4474 QWidget *w = cellWidget(i, col);
4475 if (!w)
4476 continue;
4477 moveChild(w, columnPos(col), rowPos(i));
4478 w->resize(columnWidth(col) - 1, rowHeight(i) - 1);
4479 }
4480 }
4481
4482 /*!
4483 This function is called when column order is to be changed, i.e.
4484 when the user moved the column header \a section from \a fromIndex
4485 to \a toIndex.
4486
4487 If you want to change the column order programmatically, call
4488 swapRows() or swapColumns();
4489
4490 \sa Q3Header::indexChange() rowIndexChanged()
4491 */
4492
columnIndexChanged(int,int fromIndex,int toIndex)4493 void Q3Table::columnIndexChanged(int, int fromIndex, int toIndex)
4494 {
4495 if (doSort && lastSortCol == fromIndex && topHeader)
4496 topHeader->setSortIndicator(toIndex, topHeader->sortIndicatorOrder());
4497 repaintContents(contentsX(), contentsY(),
4498 visibleWidth(), visibleHeight(), false);
4499 }
4500
4501 /*!
4502 This function is called when the order of the rows is to be
4503 changed, i.e. the user moved the row header section \a section
4504 from \a fromIndex to \a toIndex.
4505
4506 If you want to change the order programmatically, call swapRows()
4507 or swapColumns();
4508
4509 \sa Q3Header::indexChange() columnIndexChanged()
4510 */
4511
rowIndexChanged(int,int,int)4512 void Q3Table::rowIndexChanged(int, int, int)
4513 {
4514 repaintContents(contentsX(), contentsY(),
4515 visibleWidth(), visibleHeight(), false);
4516 }
4517
4518 /*!
4519 This function is called when the column \a col has been clicked.
4520 The default implementation sorts this column if sorting() is true.
4521 */
4522
columnClicked(int col)4523 void Q3Table::columnClicked(int col)
4524 {
4525 if (!sorting())
4526 return;
4527
4528 if (col == lastSortCol) {
4529 asc = !asc;
4530 } else {
4531 lastSortCol = col;
4532 asc = true;
4533 }
4534 sortColumn(lastSortCol, asc);
4535 }
4536
4537 /*!
4538 \property Q3Table::sorting
4539 \brief whether a click on the header of a column sorts that column
4540
4541 \sa sortColumn()
4542 */
4543
setSorting(bool b)4544 void Q3Table::setSorting(bool b)
4545 {
4546 doSort = b;
4547 if (topHeader)
4548 topHeader->setSortIndicator(b ? lastSortCol : -1);
4549 }
4550
sorting() const4551 bool Q3Table::sorting() const
4552 {
4553 return doSort;
4554 }
4555
4556 static bool inUpdateGeometries = false;
4557
delayedUpdateGeometries()4558 void Q3Table::delayedUpdateGeometries()
4559 {
4560 d->geomTimer->start(0, true);
4561 }
4562
updateGeometriesSlot()4563 void Q3Table::updateGeometriesSlot()
4564 {
4565 updateGeometries();
4566 }
4567
4568 /*!
4569 This function updates the geometries of the left and top header.
4570 You do not normally need to call this function.
4571 */
4572
updateGeometries()4573 void Q3Table::updateGeometries()
4574 {
4575 if (inUpdateGeometries)
4576 return;
4577 inUpdateGeometries = true;
4578 QSize ts = tableSize();
4579 if (topHeader->offset() &&
4580 ts.width() < topHeader->offset() + topHeader->width())
4581 horizontalScrollBar()->setValue(ts.width() - topHeader->width());
4582 if (leftHeader->offset() &&
4583 ts.height() < leftHeader->offset() + leftHeader->height())
4584 verticalScrollBar()->setValue(ts.height() - leftHeader->height());
4585
4586 leftHeader->setGeometry(QStyle::visualRect(layoutDirection(), rect(), QRect(frameWidth(), topMargin() + frameWidth(),
4587 VERTICALMARGIN, visibleHeight())));
4588 topHeader->setGeometry(QStyle::visualRect(layoutDirection(), rect(), QRect(VERTICALMARGIN + frameWidth(), frameWidth(),
4589 visibleWidth(), topMargin())));
4590 horizontalScrollBar()->raise();
4591 verticalScrollBar()->raise();
4592 topHeader->updateStretches();
4593 leftHeader->updateStretches();
4594 inUpdateGeometries = false;
4595 }
4596
4597 /*!
4598 Returns the width of column \a col.
4599
4600 \sa setColumnWidth() rowHeight()
4601 */
4602
columnWidth(int col) const4603 int Q3Table::columnWidth(int col) const
4604 {
4605 return topHeader->sectionSize(col);
4606 }
4607
4608 /*!
4609 Returns the height of row \a row.
4610
4611 \sa setRowHeight() columnWidth()
4612 */
4613
rowHeight(int row) const4614 int Q3Table::rowHeight(int row) const
4615 {
4616 return leftHeader->sectionSize(row);
4617 }
4618
4619 /*!
4620 Returns the x-coordinate of the column \a col in content
4621 coordinates.
4622
4623 \sa columnAt() rowPos()
4624 */
4625
columnPos(int col) const4626 int Q3Table::columnPos(int col) const
4627 {
4628 return topHeader->sectionPos(col);
4629 }
4630
4631 /*!
4632 Returns the y-coordinate of the row \a row in content coordinates.
4633
4634 \sa rowAt() columnPos()
4635 */
4636
rowPos(int row) const4637 int Q3Table::rowPos(int row) const
4638 {
4639 return leftHeader->sectionPos(row);
4640 }
4641
4642 /*!
4643 Returns the number of the column at position \a x. \a x must be
4644 given in content coordinates.
4645
4646 \sa columnPos() rowAt()
4647 */
4648
columnAt(int x) const4649 int Q3Table::columnAt(int x) const
4650 {
4651 return topHeader->sectionAt(x);
4652 }
4653
4654 /*!
4655 Returns the number of the row at position \a y. \a y must be given
4656 in content coordinates.
4657
4658 \sa rowPos() columnAt()
4659 */
4660
rowAt(int y) const4661 int Q3Table::rowAt(int y) const
4662 {
4663 return leftHeader->sectionAt(y);
4664 }
4665
4666 /*!
4667 Returns the bounding rectangle of the cell at \a row, \a col in
4668 content coordinates.
4669 */
4670
cellGeometry(int row,int col) const4671 QRect Q3Table::cellGeometry(int row, int col) const
4672 {
4673 Q3TableItem *itm = item(row, col);
4674
4675 if (!itm || (itm->rowSpan() == 1 && itm->colSpan() == 1))
4676 return QRect(columnPos(col), rowPos(row),
4677 columnWidth(col), rowHeight(row));
4678
4679 while (row != itm->row())
4680 row--;
4681 while (col != itm->col())
4682 col--;
4683
4684 QRect rect(columnPos(col), rowPos(row),
4685 columnWidth(col), rowHeight(row));
4686
4687 for (int r = 1; r < itm->rowSpan(); ++r)
4688 rect.setHeight(rect.height() + rowHeight(r + row));
4689
4690 for (int c = 1; c < itm->colSpan(); ++c)
4691 rect.setWidth(rect.width() + columnWidth(c + col));
4692
4693 return rect;
4694 }
4695
4696 /*!
4697 Returns the size of the table.
4698
4699 This is the same as the coordinates of the bottom-right edge of
4700 the last table cell.
4701 */
4702
tableSize() const4703 QSize Q3Table::tableSize() const
4704 {
4705 return QSize(columnPos(numCols() - 1) + columnWidth(numCols() - 1),
4706 rowPos(numRows() - 1) + rowHeight(numRows() - 1));
4707 }
4708
4709 /*!
4710 \property Q3Table::numRows
4711 \brief The number of rows in the table
4712
4713 \sa numCols
4714 */
4715
numRows() const4716 int Q3Table::numRows() const
4717 {
4718 return leftHeader->count();
4719 }
4720
4721 /*!
4722 \property Q3Table::numCols
4723 \brief The number of columns in the table
4724
4725 \sa numRows
4726 */
4727
numCols() const4728 int Q3Table::numCols() const
4729 {
4730 return topHeader->count();
4731 }
4732
saveContents(Q3PtrVector<Q3TableItem> & tmp,Q3PtrVector<Q3Table::TableWidget> & tmp2)4733 void Q3Table::saveContents(Q3PtrVector<Q3TableItem> &tmp,
4734 Q3PtrVector<Q3Table::TableWidget> &tmp2)
4735 {
4736 int nCols = numCols();
4737 if (editRow != -1 && editCol != -1)
4738 endEdit(editRow, editCol, false, edMode != Editing);
4739 tmp.resize(contents.size());
4740 tmp2.resize(widgets.size());
4741 int i;
4742 for (i = 0; i < (int)tmp.size(); ++i) {
4743 Q3TableItem *item = contents[ i ];
4744 if (item && (item->row() * nCols) + item->col() == i)
4745 tmp.insert(i, item);
4746 else
4747 tmp.insert(i, 0);
4748 }
4749 for (i = 0; i < (int)tmp2.size(); ++i) {
4750 QWidget *w = widgets[ i ];
4751 if (w)
4752 tmp2.insert(i, new TableWidget(w, i / nCols, i % nCols));
4753 else
4754 tmp2.insert(i, 0);
4755 }
4756 }
4757
updateHeaderAndResizeContents(Q3TableHeader * header,int num,int rowCol,int width,bool & updateBefore)4758 void Q3Table::updateHeaderAndResizeContents(Q3TableHeader *header,
4759 int num, int rowCol,
4760 int width, bool &updateBefore)
4761 {
4762 updateBefore = rowCol < num;
4763 if (rowCol > num) {
4764 header->Q3Header::resizeArrays(rowCol);
4765 header->Q3TableHeader::resizeArrays(rowCol);
4766 int old = num;
4767 clearSelection(false);
4768 int i = 0;
4769 for (i = old; i < rowCol; ++i)
4770 header->addLabel(QString(), width);
4771 } else {
4772 clearSelection(false);
4773 if (header == leftHeader) {
4774 while (numRows() > rowCol)
4775 header->removeLabel(numRows() - 1);
4776 } else {
4777 while (numCols() > rowCol)
4778 header->removeLabel(numCols() - 1);
4779 }
4780 }
4781
4782 contents.setAutoDelete(false);
4783 contents.clear();
4784 contents.setAutoDelete(true);
4785 widgets.setAutoDelete(false);
4786 widgets.clear();
4787 widgets.setAutoDelete(true);
4788 resizeData(numRows() * numCols());
4789
4790 // keep numStretches in sync
4791 int n = 0;
4792 for (uint i = 0; i < header->stretchable.size(); i++)
4793 n += (header->stretchable.at(i) & 1); // avoid cmp
4794 header->numStretches = n;
4795 }
4796
restoreContents(Q3PtrVector<Q3TableItem> & tmp,Q3PtrVector<Q3Table::TableWidget> & tmp2)4797 void Q3Table::restoreContents(Q3PtrVector<Q3TableItem> &tmp,
4798 Q3PtrVector<Q3Table::TableWidget> &tmp2)
4799 {
4800 int i;
4801 int nCols = numCols();
4802 for (i = 0; i < (int)tmp.size(); ++i) {
4803 Q3TableItem *it = tmp[ i ];
4804 if (it) {
4805 int idx = (it->row() * nCols) + it->col();
4806 if ((uint)idx < contents.size() &&
4807 it->row() == idx / nCols && it->col() == idx % nCols) {
4808 contents.insert(idx, it);
4809 if (it->rowSpan() > 1 || it->colSpan() > 1) {
4810 int ridx, iidx;
4811 for (int irow = 0; irow < it->rowSpan(); irow++) {
4812 ridx = idx + irow * nCols;
4813 for (int icol = 0; icol < it->colSpan(); icol++) {
4814 iidx = ridx + icol;
4815 if (idx != iidx && (uint)iidx < contents.size())
4816 contents.insert(iidx, it);
4817 }
4818 }
4819
4820 }
4821 } else {
4822 delete it;
4823 }
4824 }
4825 }
4826 for (i = 0; i < (int)tmp2.size(); ++i) {
4827 TableWidget *w = tmp2[ i ];
4828 if (w) {
4829 int idx = (w->row * nCols) + w->col;
4830 if ((uint)idx < widgets.size() &&
4831 w->row == idx / nCols && w->col == idx % nCols)
4832 widgets.insert(idx, w->wid);
4833 else
4834 delete w->wid;
4835 delete w;
4836 }
4837 }
4838 }
4839
finishContentsResze(bool updateBefore)4840 void Q3Table::finishContentsResze(bool updateBefore)
4841 {
4842 QRect r(cellGeometry(numRows() - 1, numCols() - 1));
4843 resizeContents(r.right() + 1, r.bottom() + 1);
4844 updateGeometries();
4845 if (updateBefore)
4846 repaintContents(contentsX(), contentsY(),
4847 visibleWidth(), visibleHeight(), true);
4848 else
4849 repaintContents(contentsX(), contentsY(),
4850 visibleWidth(), visibleHeight(), false);
4851
4852 if (isRowSelection(selectionMode())) {
4853 int r = curRow;
4854 curRow = -1;
4855 setCurrentCell(r, curCol);
4856 }
4857 }
4858
setNumRows(int r)4859 void Q3Table::setNumRows(int r)
4860 {
4861 if (r < 0)
4862 return;
4863
4864 if (r < numRows()) {
4865 // Removed rows are no longer hidden, and should thus be removed from "hiddenRows"
4866 for (int rr = numRows()-1; rr >= r; --rr) {
4867 if (d->hiddenRows.find(rr))
4868 d->hiddenRows.remove(rr);
4869 }
4870 }
4871
4872 fontChange(font()); // invalidate the sizeHintCache
4873
4874 Q3PtrVector<Q3TableItem> tmp;
4875 Q3PtrVector<TableWidget> tmp2;
4876 saveContents(tmp, tmp2);
4877
4878 bool updatesEnabled = leftHeader->updatesEnabled();
4879 if (updatesEnabled)
4880 leftHeader->setUpdatesEnabled(false);
4881
4882 bool updateBefore;
4883 updateHeaderAndResizeContents(leftHeader, numRows(), r, 20, updateBefore);
4884
4885 int w = fontMetrics().width(QString::number(r) + QLatin1Char('W'));
4886 if (VERTICALMARGIN > 0 && w > VERTICALMARGIN)
4887 setLeftMargin(w);
4888
4889 restoreContents(tmp, tmp2);
4890
4891 leftHeader->calculatePositions();
4892 finishContentsResze(updateBefore);
4893 if (updatesEnabled) {
4894 leftHeader->setUpdatesEnabled(true);
4895 leftHeader->update();
4896 }
4897 leftHeader->updateCache();
4898 if (curRow >= numRows()) {
4899 curRow = numRows() - 1;
4900 if (curRow < 0)
4901 curCol = -1;
4902 else
4903 repaintCell(curRow, curCol);
4904 }
4905
4906 if (curRow > numRows())
4907 curRow = numRows();
4908 }
4909
setNumCols(int c)4910 void Q3Table::setNumCols(int c)
4911 {
4912 if (c < 0)
4913 return;
4914
4915 if (c < numCols()) {
4916 // Removed columns are no longer hidden, and should thus be removed from "hiddenCols"
4917 for (int cc = numCols()-1; cc >= c; --cc) {
4918 if (d->hiddenCols.find(cc))
4919 d->hiddenCols.remove(cc);
4920 }
4921 }
4922
4923 fontChange(font()); // invalidate the sizeHintCache
4924
4925 Q3PtrVector<Q3TableItem> tmp;
4926 Q3PtrVector<TableWidget> tmp2;
4927 saveContents(tmp, tmp2);
4928
4929 bool updatesEnabled = topHeader->updatesEnabled();
4930 if (updatesEnabled)
4931 topHeader->setUpdatesEnabled(false);
4932
4933 bool updateBefore;
4934 updateHeaderAndResizeContents(topHeader, numCols(), c, 100, updateBefore);
4935
4936 restoreContents(tmp, tmp2);
4937
4938 topHeader->calculatePositions();
4939 finishContentsResze(updateBefore);
4940 if (updatesEnabled) {
4941 topHeader->setUpdatesEnabled(true);
4942 topHeader->update();
4943 }
4944 topHeader->updateCache();
4945 if (curCol >= numCols()) {
4946 curCol = numCols() - 1;
4947 if (curCol < 0)
4948 curRow = -1;
4949 else
4950 repaintCell(curRow, curCol);
4951 }
4952 }
4953
4954 /*! Sets the section labels of the verticalHeader() to \a labels */
4955
setRowLabels(const QStringList & labels)4956 void Q3Table::setRowLabels(const QStringList &labels)
4957 {
4958 leftHeader->setLabels(labels);
4959 }
4960
4961 /*! Sets the section labels of the horizontalHeader() to \a labels */
4962
setColumnLabels(const QStringList & labels)4963 void Q3Table::setColumnLabels(const QStringList &labels)
4964 {
4965 topHeader->setLabels(labels);
4966 }
4967
4968 /*!
4969 This function returns the widget which should be used as an editor
4970 for the contents of the cell at \a row, \a col.
4971
4972 If \a initFromCell is true, the editor is used to edit the current
4973 contents of the cell (so the editor widget should be initialized
4974 with this content). If \a initFromCell is false, the content of
4975 the cell is replaced with the new content which the user entered
4976 into the widget created by this function.
4977
4978 The default functionality is as follows: if \a initFromCell is
4979 true or the cell has a Q3TableItem and the table item's
4980 Q3TableItem::isReplaceable() is false then the cell is asked to
4981 create an appropriate editor (using Q3TableItem::createEditor()).
4982 Otherwise a QLineEdit is used as the editor.
4983
4984 If you want to create your own editor for certain cells, implement
4985 a custom Q3TableItem subclass and reimplement
4986 Q3TableItem::createEditor().
4987
4988 If you are not using \l{Q3TableItem}s and you don't want to use a
4989 QLineEdit as the default editor, subclass Q3Table and reimplement
4990 this function with code like this:
4991 \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 5
4992 Ownership of the editor widget is transferred to the caller.
4993
4994 If you reimplement this function return 0 for read-only cells. You
4995 will need to reimplement setCellContentFromEditor() to retrieve
4996 the data the user entered.
4997
4998 \sa Q3TableItem::createEditor()
4999 */
5000
createEditor(int row,int col,bool initFromCell) const5001 QWidget *Q3Table::createEditor(int row, int col, bool initFromCell) const
5002 {
5003 if (isReadOnly() || isRowReadOnly(row) || isColumnReadOnly(col))
5004 return 0;
5005
5006 QWidget *e = 0;
5007
5008 // the current item in the cell should be edited if possible
5009 Q3TableItem *i = item(row, col);
5010 if (initFromCell || (i && !i->isReplaceable())) {
5011 if (i) {
5012 if (i->editType() == Q3TableItem::Never)
5013 return 0;
5014
5015 e = i->createEditor();
5016 if (!e)
5017 return 0;
5018 }
5019 }
5020
5021 // no contents in the cell yet, so open the default editor
5022 if (!e) {
5023 e = new QLineEdit(viewport(), "qt_lineeditor");
5024 ((QLineEdit*)e)->setFrame(false);
5025 }
5026
5027 return e;
5028 }
5029
5030 /*!
5031 This function is called to start in-place editing of the cell at
5032 \a row, \a col. Editing is achieved by creating an editor
5033 (createEditor() is called) and setting the cell's editor with
5034 setCellWidget() to the newly created editor. (After editing is
5035 complete endEdit() will be called to replace the cell's content
5036 with the editor's content.) If \a replace is true the editor will
5037 start empty; otherwise it will be initialized with the cell's
5038 content (if any), i.e. the user will be modifying the original
5039 cell content.
5040
5041 \sa endEdit()
5042 */
5043
beginEdit(int row,int col,bool replace)5044 QWidget *Q3Table::beginEdit(int row, int col, bool replace)
5045 {
5046 if (isReadOnly() || isRowReadOnly(row) || isColumnReadOnly(col))
5047 return 0;
5048 if ( row < 0 || row >= numRows() || col < 0 || col >= numCols() )
5049 return 0;
5050 Q3TableItem *itm = item(row, col);
5051 if (itm && !itm->isEnabled())
5052 return 0;
5053 if (cellWidget(row, col))
5054 return 0;
5055 ensureCellVisible(row, col);
5056 QWidget *e = createEditor(row, col, !replace);
5057 if (!e)
5058 return 0;
5059 setCellWidget(row, col, e);
5060 e->setActiveWindow();
5061 e->setFocus();
5062 updateCell(row, col);
5063 return e;
5064 }
5065
5066 /*!
5067 This function is called when in-place editing of the cell at \a
5068 row, \a col is requested to stop.
5069
5070 If the cell is not being edited or \a accept is false the function
5071 returns and the cell's contents are left unchanged.
5072
5073 If \a accept is true the content of the editor must be transferred
5074 to the relevant cell. If \a replace is true the current content of
5075 this cell should be replaced by the content of the editor (this
5076 means removing the current Q3TableItem of the cell and creating a
5077 new one for the cell). Otherwise (if possible) the content of the
5078 editor should just be set to the existing Q3TableItem of this cell.
5079
5080 setCellContentFromEditor() is called to replace the contents of
5081 the cell with the contents of the cell's editor.
5082
5083 Finally clearCellWidget() is called to remove the editor widget.
5084
5085 \sa setCellContentFromEditor(), beginEdit()
5086 */
5087
endEdit(int row,int col,bool accept,bool replace)5088 void Q3Table::endEdit(int row, int col, bool accept, bool replace)
5089 {
5090 QWidget *editor = cellWidget(row, col);
5091 if (!editor)
5092 return;
5093
5094 if (!accept) {
5095 if (row == editRow && col == editCol)
5096 setEditMode(NotEditing, -1, -1);
5097 clearCellWidget(row, col);
5098 updateCell(row, col);
5099 viewport()->setFocus();
5100 updateCell(row, col);
5101 return;
5102 }
5103
5104 Q3TableItem *i = item(row, col);
5105 QString oldContent;
5106 if (i)
5107 oldContent = i->text();
5108
5109 if (!i || replace) {
5110 setCellContentFromEditor(row, col);
5111 i = item(row, col);
5112 } else {
5113 i->setContentFromEditor(editor);
5114 }
5115
5116 if (row == editRow && col == editCol)
5117 setEditMode(NotEditing, -1, -1);
5118
5119 viewport()->setFocus();
5120 updateCell(row, col);
5121
5122 if (!i || (oldContent != i->text()))
5123 emit valueChanged(row, col);
5124
5125 clearCellWidget(row, col);
5126 }
5127
5128 /*!
5129 This function is called to replace the contents of the cell at \a
5130 row, \a col with the contents of the cell's editor.
5131
5132 If there already exists a Q3TableItem for the cell,
5133 it calls Q3TableItem::setContentFromEditor() on this Q3TableItem.
5134
5135 If, for example, you want to create different \l{Q3TableItem}s
5136 depending on the contents of the editor, you might reimplement
5137 this function.
5138
5139 If you want to work without \l{Q3TableItem}s, you will need to
5140 reimplement this function to save the data the user entered into
5141 your data structure. (See the notes on large tables.)
5142
5143 \sa Q3TableItem::setContentFromEditor() createEditor()
5144 */
5145
setCellContentFromEditor(int row,int col)5146 void Q3Table::setCellContentFromEditor(int row, int col)
5147 {
5148 QWidget *editor = cellWidget(row, col);
5149 if (!editor)
5150 return;
5151
5152 Q3TableItem *i = item(row, col);
5153 if (i) {
5154 i->setContentFromEditor(editor);
5155 } else {
5156 QLineEdit *le = qobject_cast<QLineEdit*>(editor);
5157 if (le)
5158 setText(row, col, le->text());
5159 }
5160 }
5161
5162 /*!
5163 Returns true if the \l EditMode is \c Editing or \c Replacing;
5164 otherwise (i.e. the \l EditMode is \c NotEditing) returns false.
5165
5166 \sa Q3Table::EditMode
5167 */
5168
isEditing() const5169 bool Q3Table::isEditing() const
5170 {
5171 return edMode != NotEditing;
5172 }
5173
5174 /*!
5175 Returns the current edit mode
5176
5177 \sa Q3Table::EditMode
5178 */
5179
editMode() const5180 Q3Table::EditMode Q3Table::editMode() const
5181 {
5182 return edMode;
5183 }
5184
5185 /*!
5186 Returns the current edited row
5187 */
5188
currEditRow() const5189 int Q3Table::currEditRow() const
5190 {
5191 return editRow;
5192 }
5193
5194 /*!
5195 Returns the current edited column
5196 */
5197
currEditCol() const5198 int Q3Table::currEditCol() const
5199 {
5200 return editCol;
5201 }
5202
5203 /*!
5204 Returns a single integer which identifies a particular \a row and \a
5205 col by mapping the 2D table to a 1D array.
5206
5207 This is useful, for example, if you have a sparse table and want to
5208 use a Q3IntDict to map integers to the cells that are used.
5209 */
5210
indexOf(int row,int col) const5211 int Q3Table::indexOf(int row, int col) const
5212 {
5213 return (row * numCols()) + col;
5214 }
5215
5216 /*! \internal
5217 */
5218
repaintSelections(Q3TableSelection * oldSelection,Q3TableSelection * newSelection,bool updateVertical,bool updateHorizontal)5219 void Q3Table::repaintSelections(Q3TableSelection *oldSelection,
5220 Q3TableSelection *newSelection,
5221 bool updateVertical, bool updateHorizontal)
5222 {
5223 if (!oldSelection && !newSelection)
5224 return;
5225 if (oldSelection && newSelection && *oldSelection == *newSelection)
5226 return;
5227 if (oldSelection && !oldSelection->isActive())
5228 oldSelection = 0;
5229
5230 bool optimizeOld = false;
5231 bool optimizeNew = false;
5232
5233 QRect old;
5234 if (oldSelection)
5235 old = rangeGeometry(oldSelection->topRow(),
5236 oldSelection->leftCol(),
5237 oldSelection->bottomRow(),
5238 oldSelection->rightCol(),
5239 optimizeOld);
5240 else
5241 old = QRect(0, 0, 0, 0);
5242
5243 QRect cur;
5244 if (newSelection)
5245 cur = rangeGeometry(newSelection->topRow(),
5246 newSelection->leftCol(),
5247 newSelection->bottomRow(),
5248 newSelection->rightCol(),
5249 optimizeNew);
5250 else
5251 cur = QRect(0, 0, 0, 0);
5252 int i;
5253
5254 if (!optimizeOld || !optimizeNew ||
5255 old.width() > SHRT_MAX || old.height() > SHRT_MAX ||
5256 cur.width() > SHRT_MAX || cur.height() > SHRT_MAX) {
5257 QRect rr = cur.united(old);
5258 repaintContents(rr, false);
5259 } else {
5260 old = QRect(contentsToViewport2(old.topLeft()), old.size());
5261 cur = QRect(contentsToViewport2(cur.topLeft()), cur.size());
5262 QRegion r1(old);
5263 QRegion r2(cur);
5264 QRegion r3 = r1.subtracted(r2);
5265 QRegion r4 = r2.subtracted(r1);
5266
5267 for (i = 0; i < (int)r3.rects().count(); ++i) {
5268 QRect r(r3.rects()[ i ]);
5269 r = QRect(viewportToContents2(r.topLeft()), r.size());
5270 repaintContents(r, false);
5271 }
5272 for (i = 0; i < (int)r4.rects().count(); ++i) {
5273 QRect r(r4.rects()[ i ]);
5274 r = QRect(viewportToContents2(r.topLeft()), r.size());
5275 repaintContents(r, false);
5276 }
5277 }
5278
5279 int top, left, bottom, right;
5280 {
5281 int oldTopRow = oldSelection ? oldSelection->topRow() : numRows() - 1;
5282 int newTopRow = newSelection ? newSelection->topRow() : numRows() - 1;
5283 top = QMIN(oldTopRow, newTopRow);
5284 }
5285
5286 {
5287 int oldLeftCol = oldSelection ? oldSelection->leftCol() : numCols() - 1;
5288 int newLeftCol = newSelection ? newSelection->leftCol() : numCols() - 1;
5289 left = QMIN(oldLeftCol, newLeftCol);
5290 }
5291
5292 {
5293 int oldBottomRow = oldSelection ? oldSelection->bottomRow() : 0;
5294 int newBottomRow = newSelection ? newSelection->bottomRow() : 0;
5295 bottom = QMAX(oldBottomRow, newBottomRow);
5296 }
5297
5298 {
5299 int oldRightCol = oldSelection ? oldSelection->rightCol() : 0;
5300 int newRightCol = newSelection ? newSelection->rightCol() : 0;
5301 right = QMAX(oldRightCol, newRightCol);
5302 }
5303
5304 if (updateHorizontal && numCols() > 0 && left >= 0 && !isRowSelection(selectionMode())) {
5305 register int *s = &topHeader->states.data()[left];
5306 for (i = left; i <= right; ++i) {
5307 if (!isColumnSelected(i))
5308 *s = Q3TableHeader::Normal;
5309 else if (isColumnSelected(i, true))
5310 *s = Q3TableHeader::Selected;
5311 else
5312 *s = Q3TableHeader::Bold;
5313 ++s;
5314 }
5315 topHeader->repaint(false);
5316 }
5317
5318 if (updateVertical && numRows() > 0 && top >= 0) {
5319 register int *s = &leftHeader->states.data()[top];
5320 for (i = top; i <= bottom; ++i) {
5321 if (!isRowSelected(i))
5322 *s = Q3TableHeader::Normal;
5323 else if (isRowSelected(i, true))
5324 *s = Q3TableHeader::Selected;
5325 else
5326 *s = Q3TableHeader::Bold;
5327 ++s;
5328 }
5329 leftHeader->repaint(false);
5330 }
5331 }
5332
5333 /*!
5334 Repaints all selections
5335 */
5336
repaintSelections()5337 void Q3Table::repaintSelections()
5338 {
5339 if (selections.isEmpty())
5340 return;
5341
5342 QRect r;
5343 for (Q3TableSelection *s = selections.first(); s; s = selections.next()) {
5344 bool b;
5345 r = r.united(rangeGeometry(s->topRow(),
5346 s->leftCol(),
5347 s->bottomRow(),
5348 s->rightCol(), b));
5349 }
5350
5351 repaintContents(r, false);
5352 }
5353
5354 /*!
5355 Clears all selections and repaints the appropriate regions if \a
5356 repaint is true.
5357
5358 \sa removeSelection()
5359 */
5360
clearSelection(bool repaint)5361 void Q3Table::clearSelection(bool repaint)
5362 {
5363 if (selections.isEmpty())
5364 return;
5365 bool needRepaint = !selections.isEmpty();
5366
5367 QRect r;
5368 for (Q3TableSelection *s = selections.first(); s; s = selections.next()) {
5369 bool b;
5370 r = r.united(rangeGeometry(s->topRow(),
5371 s->leftCol(),
5372 s->bottomRow(),
5373 s->rightCol(), b));
5374 }
5375
5376 currentSel = 0;
5377 selections.clear();
5378
5379 if (needRepaint && repaint)
5380 repaintContents(r, false);
5381
5382 leftHeader->setSectionStateToAll(Q3TableHeader::Normal);
5383 leftHeader->repaint(false);
5384 if (!isRowSelection(selectionMode())) {
5385 topHeader->setSectionStateToAll(Q3TableHeader::Normal);
5386 topHeader->repaint(false);
5387 }
5388 topHeader->setSectionState(curCol, Q3TableHeader::Bold);
5389 leftHeader->setSectionState(curRow, Q3TableHeader::Bold);
5390 emit selectionChanged();
5391 }
5392
5393 /*! \internal
5394 */
5395
rangeGeometry(int topRow,int leftCol,int bottomRow,int rightCol,bool & optimize)5396 QRect Q3Table::rangeGeometry(int topRow, int leftCol,
5397 int bottomRow, int rightCol, bool &optimize)
5398 {
5399 topRow = QMAX(topRow, rowAt(contentsY()));
5400 leftCol = QMAX(leftCol, columnAt(contentsX()));
5401 int ra = rowAt(contentsY() + visibleHeight());
5402 if (ra != -1)
5403 bottomRow = QMIN(bottomRow, ra);
5404 int ca = columnAt(contentsX() + visibleWidth());
5405 if (ca != -1)
5406 rightCol = QMIN(rightCol, ca);
5407 optimize = true;
5408 QRect rect;
5409 for (int r = topRow; r <= bottomRow; ++r) {
5410 for (int c = leftCol; c <= rightCol; ++c) {
5411 rect = rect.united(cellGeometry(r, c));
5412 Q3TableItem *i = item(r, c);
5413 if (i && (i->rowSpan() > 1 || i->colSpan() > 1))
5414 optimize = false;
5415 }
5416 }
5417 return rect;
5418 }
5419
5420 /*!
5421 This function is called to activate the next cell if in-place
5422 editing was finished by pressing the Enter key.
5423
5424 The default behaviour is to move from top to bottom, i.e. move to
5425 the cell beneath the cell being edited. Reimplement this function
5426 if you want different behaviour, e.g. moving from left to right.
5427 */
5428
activateNextCell()5429 void Q3Table::activateNextCell()
5430 {
5431 int firstRow = 0;
5432 while (d->hiddenRows.find(firstRow))
5433 firstRow++;
5434 int firstCol = 0;
5435 while (d->hiddenCols.find(firstCol))
5436 firstCol++;
5437 int nextRow = curRow;
5438 int nextCol = curCol;
5439 while (d->hiddenRows.find(++nextRow)) {}
5440 if (nextRow >= numRows()) {
5441 nextRow = firstRow;
5442 while (d->hiddenCols.find(++nextCol)) {}
5443 if (nextCol >= numCols())
5444 nextCol = firstCol;
5445 }
5446
5447 if (!currentSel || !currentSel->isActive() ||
5448 (currentSel->leftCol() == currentSel->rightCol() &&
5449 currentSel->topRow() == currentSel->bottomRow())) {
5450 clearSelection();
5451 setCurrentCell(nextRow, nextCol);
5452 } else {
5453 if (curRow < currentSel->bottomRow())
5454 setCurrentCell(nextRow, curCol);
5455 else if (curCol < currentSel->rightCol())
5456 setCurrentCell(currentSel->topRow(), nextCol);
5457 else
5458 setCurrentCell(currentSel->topRow(), currentSel->leftCol());
5459 }
5460
5461 }
5462
5463 /*! \internal
5464 */
5465
fixRow(int & row,int y)5466 void Q3Table::fixRow(int &row, int y)
5467 {
5468 if (row == -1) {
5469 if (y < 0)
5470 row = 0;
5471 else
5472 row = numRows() - 1;
5473 }
5474 }
5475
5476 /*! \internal
5477 */
5478
fixCol(int & col,int x)5479 void Q3Table::fixCol(int &col, int x)
5480 {
5481 if (col == -1) {
5482 if (x < 0)
5483 col = 0;
5484 else
5485 col = numCols() - 1;
5486 }
5487 }
5488
5489 struct SortableTableItem
5490 {
5491 Q3TableItem *item;
5492 };
5493
5494 #if defined(Q_C_CALLBACKS)
5495 extern "C" {
5496 #endif
5497
5498 #ifdef Q_OS_WINCE
cmpTableItems(const void * n1,const void * n2)5499 static int _cdecl cmpTableItems(const void *n1, const void *n2)
5500 #else
5501 static int cmpTableItems(const void *n1, const void *n2)
5502 #endif
5503 {
5504 if (!n1 || !n2)
5505 return 0;
5506
5507 SortableTableItem *i1 = (SortableTableItem *)n1;
5508 SortableTableItem *i2 = (SortableTableItem *)n2;
5509
5510 return i1->item->key().localeAwareCompare(i2->item->key());
5511 }
5512
5513 #if defined(Q_C_CALLBACKS)
5514 }
5515 #endif
5516
5517 /*!
5518 Sorts column \a col. If \a ascending is true the sort is in
5519 ascending order, otherwise the sort is in descending order.
5520
5521 If \a wholeRows is true, entire rows are sorted using swapRows();
5522 otherwise only cells in the column are sorted using swapCells().
5523
5524 Note that if you are not using Q3TableItems you will need to
5525 reimplement swapRows() and swapCells(). (See the notes on large
5526 tables.)
5527
5528 \sa swapRows()
5529 */
5530
sortColumn(int col,bool ascending,bool wholeRows)5531 void Q3Table::sortColumn(int col, bool ascending, bool wholeRows)
5532 {
5533 int filledRows = 0, i;
5534 for (i = 0; i < numRows(); ++i) {
5535 Q3TableItem *itm = item(i, col);
5536 if (itm)
5537 filledRows++;
5538 }
5539
5540 if (!filledRows)
5541 return;
5542
5543 SortableTableItem *items = new SortableTableItem[ filledRows ];
5544 int j = 0;
5545 for (i = 0; i < numRows(); ++i) {
5546 Q3TableItem *itm = item(i, col);
5547 if (!itm)
5548 continue;
5549 items[ j++ ].item = itm;
5550 }
5551
5552 qsort(items, filledRows, sizeof(SortableTableItem), cmpTableItems);
5553
5554 bool updatesWereEnabled = updatesEnabled();
5555 if (updatesWereEnabled)
5556 setUpdatesEnabled(false);
5557 for (i = 0; i < numRows(); ++i) {
5558 if (i < filledRows) {
5559 if (ascending) {
5560 if (items[ i ].item->row() == i)
5561 continue;
5562 if (wholeRows)
5563 swapRows(items[ i ].item->row(), i);
5564 else
5565 swapCells(items[ i ].item->row(), col, i, col);
5566 } else {
5567 if (items[ i ].item->row() == filledRows - i - 1)
5568 continue;
5569 if (wholeRows)
5570 swapRows(items[ i ].item->row(), filledRows - i - 1);
5571 else
5572 swapCells(items[ i ].item->row(), col,
5573 filledRows - i - 1, col);
5574 }
5575 }
5576 }
5577 if (updatesWereEnabled)
5578 setUpdatesEnabled(true);
5579 if (topHeader)
5580 topHeader->setSortIndicator(col, ascending ? Qt::Ascending : Qt::Descending);
5581
5582 if (!wholeRows)
5583 repaintContents(columnPos(col), contentsY(),
5584 columnWidth(col), visibleHeight(), false);
5585 else
5586 repaintContents(contentsX(), contentsY(),
5587 visibleWidth(), visibleHeight(), false);
5588
5589 delete [] items;
5590 }
5591
5592 /*!
5593 Hides row \a row.
5594
5595 \sa showRow() hideColumn()
5596 */
5597
hideRow(int row)5598 void Q3Table::hideRow(int row)
5599 {
5600 if (d->hiddenRows.find(row))
5601 return;
5602 d->hiddenRows.replace(row, new int(leftHeader->sectionSize(row)));
5603 leftHeader->resizeSection(row, 0);
5604 leftHeader->setResizeEnabled(false, row);
5605 if (isRowStretchable(row))
5606 leftHeader->numStretches--;
5607 rowHeightChanged(row);
5608 if (curRow == row) {
5609 int r = curRow;
5610 int c = curCol;
5611 int k = (r >= numRows() - 1 ? Key_Up : Key_Down);
5612 fixCell(r, c, k);
5613 if (numRows() > 0)
5614 setCurrentCell(r, c);
5615 }
5616 }
5617
5618 /*!
5619 Hides column \a col.
5620
5621 \sa showColumn() hideRow()
5622 */
5623
hideColumn(int col)5624 void Q3Table::hideColumn(int col)
5625 {
5626 if (!numCols() || d->hiddenCols.find(col))
5627 return;
5628 d->hiddenCols.replace(col, new int(topHeader->sectionSize(col)));
5629 topHeader->resizeSection(col, 0);
5630 topHeader->setResizeEnabled(false, col);
5631 if (isColumnStretchable(col))
5632 topHeader->numStretches--;
5633 columnWidthChanged(col);
5634 if (curCol == col) {
5635 int r = curRow;
5636 int c = curCol;
5637 int k = (c >= numCols() - 1 ? Key_Left : Key_Right);
5638 fixCell(r, c, k);
5639 if (numCols() > 0)
5640 setCurrentCell(r, c);
5641 }
5642 }
5643
5644 /*!
5645 Shows row \a row.
5646
5647 \sa hideRow() showColumn()
5648 */
5649
showRow(int row)5650 void Q3Table::showRow(int row)
5651 {
5652 int *h = d->hiddenRows.find(row);
5653 if (h) {
5654 int rh = *h;
5655 d->hiddenRows.remove(row);
5656 setRowHeight(row, rh);
5657 if (isRowStretchable(row))
5658 leftHeader->numStretches++;
5659 } else if (rowHeight(row) == 0) {
5660 setRowHeight(row, 20);
5661 }
5662 leftHeader->setResizeEnabled(true, row);
5663 }
5664
5665 /*!
5666 Shows column \a col.
5667
5668 \sa hideColumn() showRow()
5669 */
5670
showColumn(int col)5671 void Q3Table::showColumn(int col)
5672 {
5673 int *w = d->hiddenCols.find(col);
5674 if (w) {
5675 int cw = *w;
5676 d->hiddenCols.remove(col);
5677 setColumnWidth(col, cw);
5678 if (isColumnStretchable(col))
5679 topHeader->numStretches++;
5680 } else if (columnWidth(col) == 0) {
5681 setColumnWidth(col, 20);
5682 }
5683 topHeader->setResizeEnabled(true, col);
5684 }
5685
5686 /*!
5687 Returns true if row \a row is hidden; otherwise returns
5688 false.
5689
5690 \sa hideRow(), isColumnHidden()
5691 */
isRowHidden(int row) const5692 bool Q3Table::isRowHidden(int row) const
5693 {
5694 return d->hiddenRows.find(row);
5695 }
5696
5697 /*!
5698 Returns true if column \a col is hidden; otherwise returns
5699 false.
5700
5701 \sa hideColumn(), isRowHidden()
5702 */
isColumnHidden(int col) const5703 bool Q3Table::isColumnHidden(int col) const
5704 {
5705 return d->hiddenCols.find(col);
5706 }
5707
5708 /*!
5709 Resizes column \a col to be \a w pixels wide.
5710
5711 \sa columnWidth() setRowHeight()
5712 */
5713
setColumnWidth(int col,int w)5714 void Q3Table::setColumnWidth(int col, int w)
5715 {
5716 int *ow = d->hiddenCols.find(col);
5717 if (ow) {
5718 d->hiddenCols.replace(col, new int(w));
5719 } else {
5720 topHeader->resizeSection(col, w);
5721 columnWidthChanged(col);
5722 }
5723 }
5724
5725 /*!
5726 Resizes row \a row to be \a h pixels high.
5727
5728 \sa rowHeight() setColumnWidth()
5729 */
5730
setRowHeight(int row,int h)5731 void Q3Table::setRowHeight(int row, int h)
5732 {
5733 int *oh = d->hiddenRows.find(row);
5734 if (oh) {
5735 d->hiddenRows.replace(row, new int(h));
5736 } else {
5737 leftHeader->resizeSection(row, h);
5738 rowHeightChanged(row);
5739 }
5740 }
5741
5742 /*!
5743 Resizes column \a col so that the column width is wide enough to
5744 display the widest item the column contains.
5745
5746 \sa adjustRow()
5747 */
5748
adjustColumn(int col)5749 void Q3Table::adjustColumn(int col)
5750 {
5751 int w;
5752 if ( currentColumn() == col ) {
5753 QFont f = font();
5754 f.setBold(true);
5755 w = topHeader->sectionSizeHint( col, QFontMetrics(f) ).width();
5756 } else {
5757 w = topHeader->sectionSizeHint( col, fontMetrics() ).width();
5758 }
5759 if (topHeader->iconSet(col))
5760 w += topHeader->iconSet(col)->pixmap().width();
5761 w = QMAX(w, 20);
5762 for (int i = 0; i < numRows(); ++i) {
5763 Q3TableItem *itm = item(i, col);
5764 if (!itm) {
5765 QWidget *widget = cellWidget(i, col);
5766 if (widget)
5767 w = QMAX(w, widget->sizeHint().width());
5768 } else {
5769 if (itm->colSpan() > 1)
5770 w = QMAX(w, itm->sizeHint().width() / itm->colSpan());
5771 else
5772 w = QMAX(w, itm->sizeHint().width());
5773 }
5774 }
5775 w = QMAX(w, QApplication::globalStrut().width());
5776 setColumnWidth(col, w);
5777 }
5778
5779 /*!
5780 Resizes row \a row so that the row height is tall enough to
5781 display the tallest item the row contains.
5782
5783 \sa adjustColumn()
5784 */
5785
adjustRow(int row)5786 void Q3Table::adjustRow(int row)
5787 {
5788 int h = 20;
5789 h = QMAX(h, leftHeader->sectionSizeHint(row, leftHeader->fontMetrics()).height());
5790 if (leftHeader->iconSet(row))
5791 h = QMAX(h, leftHeader->iconSet(row)->pixmap().height());
5792 for (int i = 0; i < numCols(); ++i) {
5793 Q3TableItem *itm = item(row, i);
5794 if (!itm) {
5795 QWidget *widget = cellWidget(row, i);
5796 if (widget)
5797 h = QMAX(h, widget->sizeHint().height());
5798 } else {
5799 if (itm->rowSpan() > 1)
5800 h = QMAX(h, itm->sizeHint().height() / itm->rowSpan());
5801 else
5802 h = QMAX(h, itm->sizeHint().height());
5803 }
5804 }
5805 h = QMAX(h, QApplication::globalStrut().height());
5806 setRowHeight(row, h);
5807 }
5808
5809 /*!
5810 If \a stretch is true, column \a col is set to be stretchable;
5811 otherwise column \a col is set to be unstretchable.
5812
5813 If the table widget's width decreases or increases stretchable
5814 columns will grow narrower or wider to fit the space available as
5815 completely as possible. The user cannot manually resize stretchable
5816 columns.
5817
5818 \sa isColumnStretchable() setRowStretchable() adjustColumn()
5819 */
5820
setColumnStretchable(int col,bool stretch)5821 void Q3Table::setColumnStretchable(int col, bool stretch)
5822 {
5823 topHeader->setSectionStretchable(col, stretch);
5824
5825 if (stretch && d->hiddenCols.find(col))
5826 topHeader->numStretches--;
5827 }
5828
5829 /*!
5830 If \a stretch is true, row \a row is set to be stretchable;
5831 otherwise row \a row is set to be unstretchable.
5832
5833 If the table widget's height decreases or increases stretchable
5834 rows will grow shorter or taller to fit the space available as
5835 completely as possible. The user cannot manually resize
5836 stretchable rows.
5837
5838 \sa isRowStretchable() setColumnStretchable()
5839 */
5840
setRowStretchable(int row,bool stretch)5841 void Q3Table::setRowStretchable(int row, bool stretch)
5842 {
5843 leftHeader->setSectionStretchable(row, stretch);
5844
5845 if (stretch && d->hiddenRows.find(row))
5846 leftHeader->numStretches--;
5847 }
5848
5849 /*!
5850 Returns true if column \a col is stretchable; otherwise returns
5851 false.
5852
5853 \sa setColumnStretchable() isRowStretchable()
5854 */
5855
isColumnStretchable(int col) const5856 bool Q3Table::isColumnStretchable(int col) const
5857 {
5858 return topHeader->isSectionStretchable(col);
5859 }
5860
5861 /*!
5862 Returns true if row \a row is stretchable; otherwise returns
5863 false.
5864
5865 \sa setRowStretchable() isColumnStretchable()
5866 */
5867
isRowStretchable(int row) const5868 bool Q3Table::isRowStretchable(int row) const
5869 {
5870 return leftHeader->isSectionStretchable(row);
5871 }
5872
5873 /*!
5874 Takes the table item \a i out of the table. This function does \e
5875 not delete the table item. You must either delete the table item
5876 yourself or put it into a table (using setItem()) which will then
5877 take ownership of it.
5878
5879 Use this function if you want to move an item from one cell in a
5880 table to another, or to move an item from one table to another,
5881 reinserting the item with setItem().
5882
5883 If you want to exchange two cells use swapCells().
5884 */
5885
takeItem(Q3TableItem * i)5886 void Q3Table::takeItem(Q3TableItem *i)
5887 {
5888 if (!i)
5889 return;
5890 if (i->row() != -1 && i->col() != -1) {
5891 QRect rect = cellGeometry(i->row(), i->col());
5892 contents.setAutoDelete(false);
5893 int bottom = i->row() + i->rowSpan();
5894 if (bottom > numRows())
5895 bottom = numRows();
5896 int right = i->col() + i->colSpan();
5897 if (right > numCols())
5898 right = numCols();
5899 for (int r = i->row(); r < bottom; ++r) {
5900 for (int c = i->col(); c < right; ++c)
5901 contents.remove(indexOf(r, c));
5902 }
5903 contents.setAutoDelete(true);
5904 repaintContents(rect, false);
5905 int orow = i->row();
5906 int ocol = i->col();
5907 i->setRow(-1);
5908 i->setCol(-1);
5909 i->updateEditor(orow, ocol);
5910 }
5911 i->t = 0;
5912 }
5913
5914 /*!
5915 Sets the widget \a e to the cell at \a row, \a col and takes care of
5916 placing and resizing the widget when the cell geometry changes.
5917
5918 By default widgets are inserted into a vector with numRows() *
5919 numCols() elements. In very large tables you will probably want to
5920 store the widgets in a data structure that consumes less memory (see
5921 the notes on large tables). To support the use of your own data
5922 structure this function calls insertWidget() to add the widget to
5923 the internal data structure. To use your own data structure
5924 reimplement insertWidget(), cellWidget() and clearCellWidget().
5925
5926 Cell widgets are created dynamically with the \c new operator. The
5927 cell widgets are destroyed automatically once the table is
5928 destroyed; the table takes ownership of the widget when using
5929 setCellWidget.
5930
5931 */
5932
setCellWidget(int row,int col,QWidget * e)5933 void Q3Table::setCellWidget(int row, int col, QWidget *e)
5934 {
5935 if (!e || row >= numRows() || col >= numCols())
5936 return;
5937
5938 QWidget *w = cellWidget(row, col);
5939 if (w && row == editRow && col == editCol)
5940 endEdit(editRow, editCol, false, edMode != Editing);
5941
5942 e->installEventFilter(this);
5943 clearCellWidget(row, col);
5944 if (e->parent() != viewport())
5945 e->reparent(viewport(), QPoint(0,0));
5946 Q3TableItem *itm = item(row, col);
5947 if (itm && itm->row() >= 0 && itm->col() >= 0) { // get the correct row and col if the item is spanning
5948 row = itm->row();
5949 col = itm->col();
5950 }
5951 insertWidget(row, col, e);
5952 QRect cr = cellGeometry(row, col);
5953 e->resize(cr.size());
5954 moveChild(e, cr.x(), cr.y());
5955 e->show();
5956 }
5957
5958 /*!
5959 Inserts widget \a w at \a row, \a col into the internal
5960 data structure. See the documentation of setCellWidget() for
5961 further details.
5962
5963 If you don't use \l{Q3TableItem}s you may need to reimplement this
5964 function: see the notes on large tables.
5965 */
5966
insertWidget(int row,int col,QWidget * w)5967 void Q3Table::insertWidget(int row, int col, QWidget *w)
5968 {
5969 if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1)
5970 return;
5971
5972 if ((int)widgets.size() != numRows() * numCols())
5973 widgets.resize(numRows() * numCols());
5974
5975 widgets.insert(indexOf(row, col), w);
5976 }
5977
5978 /*!
5979 Returns the widget that has been set for the cell at \a row, \a
5980 col, or 0 if no widget has been set.
5981
5982 If you don't use \l{Q3TableItem}s you may need to reimplement this
5983 function: see the notes on large tables.
5984
5985 \sa clearCellWidget() setCellWidget()
5986 */
5987
cellWidget(int row,int col) const5988 QWidget *Q3Table::cellWidget(int row, int col) const
5989 {
5990 if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1)
5991 return 0;
5992
5993 if ((int)widgets.size() != numRows() * numCols())
5994 ((Q3Table*)this)->widgets.resize(numRows() * numCols());
5995
5996 return widgets[ indexOf(row, col) ];
5997 }
5998
5999 /*!
6000 Removes the widget (if there is one) set for the cell at \a row,
6001 \a col.
6002
6003 If you don't use \l{Q3TableItem}s you may need to reimplement this
6004 function: see the notes on large tables.
6005
6006 This function deletes the widget at \a row, \a col. Note that the
6007 widget is not deleted immediately; instead QObject::deleteLater()
6008 is called on the widget to avoid problems with timing issues.
6009
6010 \sa cellWidget() setCellWidget()
6011 */
6012
clearCellWidget(int row,int col)6013 void Q3Table::clearCellWidget(int row, int col)
6014 {
6015 if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1)
6016 return;
6017
6018 if ((int)widgets.size() != numRows() * numCols())
6019 widgets.resize(numRows() * numCols());
6020
6021 QWidget *w = cellWidget(row, col);
6022 if (w) {
6023 w->removeEventFilter(this);
6024 w->hide();
6025 w->deleteLater();
6026 }
6027 widgets.setAutoDelete(false);
6028 widgets.remove(indexOf(row, col));
6029 widgets.setAutoDelete(true);
6030 }
6031
6032 /*!
6033 \fn void Q3Table::dropped (QDropEvent * e)
6034
6035 This signal is emitted when a drop event occurred on the table.
6036
6037 \a e contains information about the drop.
6038 */
6039
6040 /*!
6041 If \a b is true, the table starts a drag (see dragObject()) when
6042 the user presses and moves the mouse on a selected cell.
6043 */
6044
setDragEnabled(bool b)6045 void Q3Table::setDragEnabled(bool b)
6046 {
6047 dEnabled = b;
6048 }
6049
6050 /*!
6051 If this function returns true, the table supports dragging.
6052
6053 \sa setDragEnabled()
6054 */
6055
dragEnabled() const6056 bool Q3Table::dragEnabled() const
6057 {
6058 return dEnabled;
6059 }
6060
6061 /*!
6062 Inserts \a count empty rows at row \a row. Also clears the selection(s).
6063
6064 \sa insertColumns() removeRow()
6065 */
6066
insertRows(int row,int count)6067 void Q3Table::insertRows(int row, int count)
6068 {
6069 // special case, so a call like insertRow(currentRow(), 1) also
6070 // works, when we have 0 rows and currentRow() is -1
6071 if (row == -1 && curRow == -1)
6072 row = 0;
6073 if (row < 0 || count <= 0)
6074 return;
6075
6076 if (curRow >= row && curRow < row + count)
6077 curRow = row + count;
6078
6079 --row;
6080 if (row >= numRows())
6081 return;
6082
6083 bool updatesWereEnabled = updatesEnabled();
6084 if (updatesWereEnabled)
6085 setUpdatesEnabled(false);
6086 bool leftHeaderUpdatesEnabled = leftHeader->updatesEnabled();
6087 if (leftHeaderUpdatesEnabled)
6088 leftHeader->setUpdatesEnabled(false);
6089 int oldLeftMargin = leftMargin();
6090
6091 setNumRows(numRows() + count);
6092
6093 for (int i = numRows() - count - 1; i > row; --i)
6094 leftHeader->swapSections(i, i + count);
6095
6096 if (leftHeaderUpdatesEnabled)
6097 leftHeader->setUpdatesEnabled(leftHeaderUpdatesEnabled);
6098
6099 if (updatesWereEnabled)
6100 setUpdatesEnabled(true);
6101
6102 int cr = QMAX(0, currentRow());
6103 int cc = QMAX(0, currentColumn());
6104 if (curRow > row)
6105 curRow -= count; // this is where curRow was
6106 setCurrentCell(cr, cc, true, false); // without ensureCellVisible
6107
6108 // Repaint the header
6109 if (leftHeaderUpdatesEnabled) {
6110 int y = rowPos(row) - contentsY();
6111 if (leftMargin() != oldLeftMargin || d->hasRowSpan)
6112 y = 0; // full repaint
6113 QRect rect(0, y, leftHeader->width(), contentsHeight());
6114 leftHeader->update(rect);
6115 }
6116
6117 if (updatesWereEnabled) {
6118 int p = rowPos(row);
6119 if (d->hasRowSpan)
6120 p = contentsY();
6121 updateContents(contentsX(), p, visibleWidth(), contentsHeight() + 1);
6122 }
6123 }
6124
6125 /*!
6126 Inserts \a count empty columns at column \a col. Also clears the selection(s).
6127
6128 \sa insertRows() removeColumn()
6129 */
6130
insertColumns(int col,int count)6131 void Q3Table::insertColumns(int col, int count)
6132 {
6133 // see comment in insertRows()
6134 if (col == -1 && curCol == -1)
6135 col = 0;
6136 if (col < 0 || count <= 0)
6137 return;
6138
6139 if (curCol >= col && curCol < col + count)
6140 curCol = col + count;
6141
6142 --col;
6143 if (col >= numCols())
6144 return;
6145
6146 bool updatesWereEnabled = updatesEnabled();
6147 if (updatesWereEnabled)
6148 setUpdatesEnabled(false);
6149 bool topHeaderUpdatesEnabled = topHeader->updatesEnabled();
6150 if (topHeaderUpdatesEnabled)
6151 topHeader->setUpdatesEnabled(false);
6152 int oldTopMargin = topMargin();
6153
6154 setNumCols(numCols() + count);
6155
6156 for (int i = numCols() - count - 1; i > col; --i)
6157 topHeader->swapSections(i, i + count);
6158
6159 if (topHeaderUpdatesEnabled)
6160 topHeader->setUpdatesEnabled(true);
6161 if (updatesWereEnabled)
6162 setUpdatesEnabled(true);
6163
6164 int cr = QMAX(0, currentRow());
6165 int cc = QMAX(0, currentColumn());
6166 if (curCol > col)
6167 curCol -= count; // this is where curCol was
6168 setCurrentCell(cr, cc, true, false); // without ensureCellVisible
6169
6170 // Repaint the header
6171 if (topHeaderUpdatesEnabled) {
6172 int x = columnPos(col) - contentsX();
6173 if (topMargin() != oldTopMargin || d->hasColSpan)
6174 x = 0; // full repaint
6175 QRect rect(x, 0, contentsWidth(), topHeader->height());
6176 topHeader->update(rect);
6177 }
6178
6179 if (updatesWereEnabled) {
6180 int p = columnPos(col);
6181 if (d->hasColSpan)
6182 p = contentsX();
6183 updateContents(p, contentsY(), contentsWidth() + 1, visibleHeight());
6184 }
6185 }
6186
6187 /*!
6188 Removes row \a row, and deletes all its cells including any table
6189 items and widgets the cells may contain. Also clears the selection(s).
6190
6191 \sa hideRow() insertRows() removeColumn() removeRows()
6192 */
6193
removeRow(int row)6194 void Q3Table::removeRow(int row)
6195 {
6196 if (row < 0 || row >= numRows())
6197 return;
6198 if (row < numRows() - 1) {
6199 if (d->hiddenRows.find(row))
6200 d->hiddenRows.remove(row);
6201
6202 for (int i = row; i < numRows() - 1; ++i)
6203 ((Q3TableHeader*)verticalHeader())->swapSections(i, i + 1);
6204 }
6205 setNumRows(numRows() - 1);
6206 }
6207
6208 /*!
6209 Removes the rows listed in the array \a rows, and deletes all their
6210 cells including any table items and widgets the cells may contain.
6211
6212 The array passed in must only contain valid rows (in the range
6213 from 0 to numRows() - 1) with no duplicates, and must be sorted in
6214 ascending order. Also clears the selection(s).
6215
6216 \sa removeRow() insertRows() removeColumns()
6217 */
6218
removeRows(const Q3MemArray<int> & rows)6219 void Q3Table::removeRows(const Q3MemArray<int> &rows)
6220 {
6221 if (rows.count() == 0)
6222 return;
6223 int i;
6224 for (i = 0; i < (int)rows.count() - 1; ++i) {
6225 for (int j = rows[i] - i; j < rows[i + 1] - i - 1; j++) {
6226 ((Q3TableHeader*)verticalHeader())->swapSections(j, j + i + 1);
6227 }
6228 }
6229
6230 for (int j = rows[i] - i; j < numRows() - (int)rows.size(); j++)
6231 ((Q3TableHeader*)verticalHeader())->swapSections(j, j + rows.count());
6232
6233 setNumRows(numRows() - rows.count());
6234 }
6235
6236 /*!
6237 Removes column \a col, and deletes all its cells including any
6238 table items and widgets the cells may contain. Also clears the
6239 selection(s).
6240
6241 \sa removeColumns() hideColumn() insertColumns() removeRow()
6242 */
6243
removeColumn(int col)6244 void Q3Table::removeColumn(int col)
6245 {
6246 if (col < 0 || col >= numCols())
6247 return;
6248 if (col < numCols() - 1) {
6249 if (d->hiddenCols.find(col))
6250 d->hiddenCols.remove(col);
6251
6252 for (int i = col; i < numCols() - 1; ++i)
6253 ((Q3TableHeader*)horizontalHeader())->swapSections(i, i + 1);
6254 }
6255 setNumCols(numCols() - 1);
6256 }
6257
6258 /*!
6259 Removes the columns listed in the array \a cols, and deletes all
6260 their cells including any table items and widgets the cells may
6261 contain.
6262
6263 The array passed in must only contain valid columns (in the range
6264 from 0 to numCols() - 1) with no duplicates, and must be sorted in
6265 ascending order. Also clears the selection(s).
6266
6267 \sa removeColumn() insertColumns() removeRows()
6268 */
6269
removeColumns(const Q3MemArray<int> & cols)6270 void Q3Table::removeColumns(const Q3MemArray<int> &cols)
6271 {
6272 if (cols.count() == 0)
6273 return;
6274 int i;
6275 for (i = 0; i < (int)cols.count() - 1; ++i) {
6276 for (int j = cols[i] - i; j < cols[i + 1] - i - 1; j++) {
6277 ((Q3TableHeader*)horizontalHeader())->swapSections(j, j + i + 1);
6278 }
6279 }
6280
6281 for (int j = cols[i] - i; j < numCols() - (int)cols.size(); j++)
6282 ((Q3TableHeader*)horizontalHeader())->swapSections(j, j + cols.count());
6283
6284 setNumCols(numCols() - cols.count());
6285 }
6286
6287 /*!
6288 Starts editing the cell at \a row, \a col.
6289
6290 If \a replace is true the content of this cell will be replaced by
6291 the content of the editor when editing is finished, i.e. the user
6292 will be entering new data; otherwise the current content of the
6293 cell (if any) will be modified in the editor.
6294
6295 \sa beginEdit()
6296 */
6297
editCell(int row,int col,bool replace)6298 void Q3Table::editCell(int row, int col, bool replace)
6299 {
6300 if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1)
6301 return;
6302
6303 if (beginEdit(row, col, replace)) {
6304 edMode = Editing;
6305 editRow = row;
6306 editCol = col;
6307 }
6308 }
6309
6310 #ifndef QT_NO_DRAGANDDROP
6311
6312 /*!
6313 This event handler is called whenever a Q3Table object receives a
6314 \l QDragEnterEvent \a e, i.e. when the user pressed the mouse
6315 button to drag something.
6316
6317 The focus is moved to the cell where the QDragEnterEvent occurred.
6318 */
6319
contentsDragEnterEvent(QDragEnterEvent * e)6320 void Q3Table::contentsDragEnterEvent(QDragEnterEvent *e)
6321 {
6322 oldCurrentRow = curRow;
6323 oldCurrentCol = curCol;
6324 int tmpRow = rowAt(e->pos().y());
6325 int tmpCol = columnAt(e->pos().x());
6326 fixRow(tmpRow, e->pos().y());
6327 fixCol(tmpCol, e->pos().x());
6328 if (e->source() != (QObject*)cellWidget(currentRow(), currentColumn()))
6329 setCurrentCell(tmpRow, tmpCol, false, true);
6330 e->accept();
6331 }
6332
6333 /*!
6334 This event handler is called whenever a Q3Table object receives a
6335 \l QDragMoveEvent \a e, i.e. when the user actually drags the
6336 mouse.
6337
6338 The focus is moved to the cell where the QDragMoveEvent occurred.
6339 */
6340
contentsDragMoveEvent(QDragMoveEvent * e)6341 void Q3Table::contentsDragMoveEvent(QDragMoveEvent *e)
6342 {
6343 int tmpRow = rowAt(e->pos().y());
6344 int tmpCol = columnAt(e->pos().x());
6345 fixRow(tmpRow, e->pos().y());
6346 fixCol(tmpCol, e->pos().x());
6347 if (e->source() != (QObject*)cellWidget(currentRow(), currentColumn()))
6348 setCurrentCell(tmpRow, tmpCol, false, true);
6349 e->accept();
6350 }
6351
6352 /*!
6353 This event handler is called when a drag activity leaves \e this
6354 Q3Table object with event \a e.
6355 */
6356
contentsDragLeaveEvent(QDragLeaveEvent *)6357 void Q3Table::contentsDragLeaveEvent(QDragLeaveEvent *)
6358 {
6359 setCurrentCell(oldCurrentRow, oldCurrentCol, false, true);
6360 }
6361
6362 /*!
6363 This event handler is called when the user ends a drag and drop by
6364 dropping something onto \e this Q3Table and thus triggers the drop
6365 event, \a e.
6366 */
6367
contentsDropEvent(QDropEvent * e)6368 void Q3Table::contentsDropEvent(QDropEvent *e)
6369 {
6370 setCurrentCell(oldCurrentRow, oldCurrentCol, false, true);
6371 emit dropped(e);
6372 }
6373
6374 /*!
6375 If the user presses the mouse on a selected cell, starts moving
6376 (i.e. dragging), and dragEnabled() is true, this function is
6377 called to obtain a drag object. A drag using this object begins
6378 immediately unless dragObject() returns 0.
6379
6380 By default this function returns 0. You might reimplement it and
6381 create a Q3DragObject depending on the selected items.
6382
6383 \sa dropped()
6384 */
6385
dragObject()6386 Q3DragObject *Q3Table::dragObject()
6387 {
6388 return 0;
6389 }
6390
6391 /*!
6392 Starts a drag.
6393
6394 Usually you don't need to call or reimplement this function yourself.
6395
6396 \sa dragObject()
6397 */
6398
startDrag()6399 void Q3Table::startDrag()
6400 {
6401 if (startDragRow == -1 || startDragCol == -1)
6402 return;
6403
6404 startDragRow = startDragCol = -1;
6405
6406 Q3DragObject *drag = dragObject();
6407 if (!drag)
6408 return;
6409
6410 drag->drag();
6411 }
6412
6413 #endif
6414
6415 /*! \internal */
windowActivationChange(bool oldActive)6416 void Q3Table::windowActivationChange(bool oldActive)
6417 {
6418 if (oldActive && autoScrollTimer)
6419 autoScrollTimer->stop();
6420
6421 if (!isVisible())
6422 return;
6423
6424 if (palette().active() != palette().inactive())
6425 updateContents();
6426 }
6427
6428 /*!
6429 \internal
6430 */
setEnabled(bool b)6431 void Q3Table::setEnabled(bool b)
6432 {
6433 if (!b) {
6434 // editor will lose focus, causing a crash deep in setEnabled(),
6435 // so we'll end the edit early.
6436 endEdit(editRow, editCol, true, edMode != Editing);
6437 }
6438 Q3ScrollView::setEnabled(b);
6439 }
6440
6441
6442 /*
6443 \class Q3TableHeader
6444 \brief The Q3TableHeader class allows for creation and manipulation
6445 of table headers.
6446
6447 \compat
6448
6449 Q3Table uses this subclass of Q3Header for its headers. Q3Table has a
6450 horizontalHeader() for displaying column labels, and a
6451 verticalHeader() for displaying row labels.
6452
6453 */
6454
6455 /*
6456 \enum Q3TableHeader::SectionState
6457
6458 This enum type denotes the state of the header's text
6459
6460 \value Normal the default
6461 \value Bold
6462 \value Selected typically represented by showing the section "sunken"
6463 or "pressed in"
6464 */
6465
6466 /*!
6467 Creates a new table header called \a name with \a i sections. It
6468 is a child of widget \a parent and attached to table \a t.
6469 */
6470
Q3TableHeader(int i,Q3Table * t,QWidget * parent,const char * name)6471 Q3TableHeader::Q3TableHeader(int i, Q3Table *t,
6472 QWidget *parent, const char *name)
6473 : Q3Header(i, parent, name), mousePressed(false), startPos(-1),
6474 table(t), caching(false), resizedSection(-1),
6475 numStretches(0)
6476 {
6477 setIsATableHeader(true);
6478 d = 0;
6479 states.resize(i);
6480 stretchable.resize(i);
6481 states.fill(Normal, -1);
6482 stretchable.fill(false, -1);
6483 autoScrollTimer = new QTimer(this);
6484 connect(autoScrollTimer, SIGNAL(timeout()),
6485 this, SLOT(doAutoScroll()));
6486 #ifndef NO_LINE_WIDGET
6487 line1 = new QWidget(table->viewport(), "qt_line1");
6488 line1->hide();
6489 line1->setBackgroundMode(PaletteText);
6490 table->addChild(line1);
6491 line2 = new QWidget(table->viewport(), "qt_line2");
6492 line2->hide();
6493 line2->setBackgroundMode(PaletteText);
6494 table->addChild(line2);
6495 #else
6496 d = new Q3TableHeaderPrivate;
6497 d->oldLinePos = -1; //outside, in contents coords
6498 #endif
6499 connect(this, SIGNAL(sizeChange(int,int,int)),
6500 this, SLOT(sectionWidthChanged(int,int,int)));
6501 connect(this, SIGNAL(indexChange(int,int,int)),
6502 this, SLOT(indexChanged(int,int,int)));
6503
6504 stretchTimer = new QTimer(this);
6505 widgetStretchTimer = new QTimer(this);
6506 connect(stretchTimer, SIGNAL(timeout()),
6507 this, SLOT(updateStretches()));
6508 connect(widgetStretchTimer, SIGNAL(timeout()),
6509 this, SLOT(updateWidgetStretches()));
6510 startPos = -1;
6511 }
6512
6513 /*!
6514 Adds a new section, \a size pixels wide (or high for vertical
6515 headers) with the label \a s. If \a size is negative the section's
6516 size is calculated based on the width (or height) of the label's
6517 text.
6518 */
6519
addLabel(const QString & s,int size)6520 void Q3TableHeader::addLabel(const QString &s , int size)
6521 {
6522 Q3Header::addLabel(s, size);
6523 if (count() > (int)states.size()) {
6524 int s = states.size();
6525 states.resize(count());
6526 stretchable.resize(count());
6527 for (; s < count(); ++s) {
6528 states[ s ] = Normal;
6529 stretchable[ s ] = false;
6530 }
6531 }
6532 }
6533
removeLabel(int section)6534 void Q3TableHeader::removeLabel(int section)
6535 {
6536 Q3Header::removeLabel(section);
6537 if (section == (int)states.size() - 1) {
6538 states.resize(states.size() - 1);
6539 stretchable.resize(stretchable.size() - 1);
6540 }
6541 }
6542
resizeArrays(int n)6543 void Q3TableHeader::resizeArrays(int n)
6544 {
6545 int old = states.size();
6546 states.resize(n);
6547 stretchable.resize(n);
6548 if (n > old) {
6549 for (int i = old; i < n; ++i) {
6550 stretchable[ i ] = false;
6551 states[ i ] = Normal;
6552 }
6553 }
6554 }
6555
setLabel(int section,const QString & s,int size)6556 void Q3TableHeader::setLabel(int section, const QString & s, int size)
6557 {
6558 Q3Header::setLabel(section, s, size);
6559 sectionLabelChanged(section);
6560 }
6561
setLabel(int section,const QIconSet & iconset,const QString & s,int size)6562 void Q3TableHeader::setLabel(int section, const QIconSet & iconset,
6563 const QString & s, int size)
6564 {
6565 Q3Header::setLabel(section, iconset, s, size);
6566 sectionLabelChanged(section);
6567 }
6568
6569 /*!
6570 Sets the SectionState of section \a s to \a astate.
6571
6572 \sa sectionState()
6573 */
6574
setSectionState(int s,SectionState astate)6575 void Q3TableHeader::setSectionState(int s, SectionState astate)
6576 {
6577 if (s < 0 || s >= (int)states.count())
6578 return;
6579 if (states.data()[ s ] == astate)
6580 return;
6581 if (isRowSelection(table->selectionMode()) && orientation() == Horizontal)
6582 return;
6583
6584 states.data()[ s ] = astate;
6585 if (updatesEnabled()) {
6586 if (orientation() == Horizontal)
6587 repaint(sectionPos(s) - offset(), 0, sectionSize(s), height(), false);
6588 else
6589 repaint(0, sectionPos(s) - offset(), width(), sectionSize(s), false);
6590 }
6591 }
6592
setSectionStateToAll(SectionState state)6593 void Q3TableHeader::setSectionStateToAll(SectionState state)
6594 {
6595 if (isRowSelection(table->selectionMode()) && orientation() == Horizontal)
6596 return;
6597
6598 register int *d = (int *) states.data();
6599 int n = count();
6600
6601 while (n >= 4) {
6602 d[0] = state;
6603 d[1] = state;
6604 d[2] = state;
6605 d[3] = state;
6606 d += 4;
6607 n -= 4;
6608 }
6609
6610 if (n > 0) {
6611 d[0] = state;
6612 if (n > 1) {
6613 d[1] = state;
6614 if (n > 2) {
6615 d[2] = state;
6616 }
6617 }
6618 }
6619 }
6620
6621 /*!
6622 Returns the SectionState of section \a s.
6623
6624 \sa setSectionState()
6625 */
6626
sectionState(int s) const6627 Q3TableHeader::SectionState Q3TableHeader::sectionState(int s) const
6628 {
6629 return (s < 0 || s >= (int)states.count() ? Normal : (Q3TableHeader::SectionState)states[s]);
6630 }
6631
6632 /*! \reimp
6633 */
6634
paintEvent(QPaintEvent * e)6635 void Q3TableHeader::paintEvent(QPaintEvent *e)
6636 {
6637 QPainter p(this);
6638 p.setPen(colorGroup().buttonText());
6639 int pos = orientation() == Horizontal
6640 ? e->rect().left()
6641 : e->rect().top();
6642 int id = mapToIndex(sectionAt(pos + offset()));
6643 if (id < 0) {
6644 if (pos > 0)
6645 return;
6646 else
6647 id = 0;
6648 }
6649
6650 QRegion reg = e->region();
6651 for (int i = id; i < count(); i++) {
6652 QRect r = sRect(i);
6653 reg -= r;
6654 p.save();
6655 if (!(orientation() == Horizontal && isRowSelection(table->selectionMode())) &&
6656 (sectionState(i) == Bold || sectionState(i) == Selected)) {
6657 QFont f(font());
6658 f.setBold(true);
6659 p.setFont(f);
6660 }
6661 paintSection(&p, i, r);
6662 p.restore();
6663 if ((orientation() == Horizontal && r. right() >= e->rect().right())
6664 || (orientation() == Vertical && r. bottom() >= e->rect().bottom()))
6665 return;
6666 }
6667 p.end();
6668 if (!reg.isEmpty())
6669 erase(reg);
6670 }
6671
6672 /*!
6673 \reimp
6674
6675 Paints the header section with index \a index into the rectangular
6676 region \a fr on the painter \a p.
6677 */
6678
paintSection(QPainter * p,int index,const QRect & fr)6679 void Q3TableHeader::paintSection(QPainter *p, int index, const QRect& fr)
6680 {
6681 int section = mapToSection(index);
6682 if (section < 0 || cellSize(section) <= 0)
6683 return;
6684
6685 if (sectionState(index) != Selected ||
6686 (orientation() == Horizontal && isRowSelection(table->selectionMode()))) {
6687 Q3Header::paintSection(p, index, fr);
6688 } else {
6689 QStyleOptionHeader opt;
6690 opt.palette = palette();
6691 opt.rect = fr;
6692 opt.state = QStyle::State_Off | (orient == Qt::Horizontal ? QStyle::State_Horizontal
6693 : QStyle::State_None);
6694 if (isEnabled())
6695 opt.state |= QStyle::State_Enabled;
6696 if (isClickEnabled()) {
6697 if (sectionState(index) == Selected) {
6698 opt.state |= QStyle::State_Sunken;
6699 if (!mousePressed)
6700 opt.state |= QStyle::State_On;
6701 }
6702 }
6703 if (!(opt.state & QStyle::State_Sunken))
6704 opt.state |= QStyle::State_Raised;
6705 style()->drawControl(QStyle::CE_HeaderSection, &opt, p, this);
6706 paintSectionLabel(p, index, fr);
6707 }
6708 }
6709
real_pos(const QPoint & p,Qt::Orientation o)6710 static int real_pos(const QPoint &p, Qt::Orientation o)
6711 {
6712 if (o == Qt::Horizontal)
6713 return p.x();
6714 return p.y();
6715 }
6716
6717 /*! \reimp
6718 */
6719
mousePressEvent(QMouseEvent * e)6720 void Q3TableHeader::mousePressEvent(QMouseEvent *e)
6721 {
6722 if (e->button() != LeftButton)
6723 return;
6724 Q3Header::mousePressEvent(e);
6725 mousePressed = true;
6726 pressPos = real_pos(e->pos(), orientation());
6727 if (!table->currentSel || (e->state() & ShiftButton) != ShiftButton)
6728 startPos = -1;
6729 setCaching(true);
6730 resizedSection = -1;
6731 #ifdef QT_NO_CURSOR
6732 isResizing = false;
6733 #else
6734 isResizing = cursor().shape() != ArrowCursor;
6735 if (!isResizing && sectionAt(pressPos) != -1)
6736 doSelection(e);
6737 #endif
6738 }
6739
6740 /*! \reimp
6741 */
6742
mouseMoveEvent(QMouseEvent * e)6743 void Q3TableHeader::mouseMoveEvent(QMouseEvent *e)
6744 {
6745 if ((e->state() & MouseButtonMask) != LeftButton // Using LeftButton simulates old behavior.
6746 #ifndef QT_NO_CURSOR
6747 || cursor().shape() != ArrowCursor
6748 #endif
6749 || ((e->state() & ControlButton) == ControlButton &&
6750 (orientation() == Horizontal
6751 ? table->columnMovingEnabled() : table->rowMovingEnabled()))) {
6752 Q3Header::mouseMoveEvent(e);
6753 return;
6754 }
6755
6756 if (!doSelection(e))
6757 Q3Header::mouseMoveEvent(e);
6758 }
6759
doSelection(QMouseEvent * e)6760 bool Q3TableHeader::doSelection(QMouseEvent *e)
6761 {
6762 int p = real_pos(e->pos(), orientation()) + offset();
6763
6764 if (isRowSelection(table->selectionMode())) {
6765 if (orientation() == Horizontal)
6766 return true;
6767 if (table->selectionMode() == Q3Table::SingleRow) {
6768 int secAt = sectionAt(p);
6769 if (secAt == -1)
6770 return true;
6771 table->setCurrentCell(secAt, table->currentColumn());
6772 return true;
6773 }
6774 }
6775
6776 if (startPos == -1) {
6777 int secAt = sectionAt(p);
6778 if (((e->state() & ControlButton) != ControlButton && (e->state() & ShiftButton) != ShiftButton)
6779 || table->selectionMode() == Q3Table::Single
6780 || table->selectionMode() == Q3Table::SingleRow) {
6781 startPos = p;
6782 bool b = table->signalsBlocked();
6783 table->blockSignals(true);
6784 table->clearSelection();
6785 table->blockSignals(b);
6786 }
6787 saveStates();
6788
6789 if (table->selectionMode() != Q3Table::NoSelection) {
6790 startPos = p;
6791 Q3TableSelection *oldSelection = table->currentSel;
6792
6793 if (orientation() == Vertical) {
6794 if (!table->isRowSelected(secAt, true)) {
6795 table->currentSel = new Q3TableSelection();
6796 table->selections.append(table->currentSel);
6797 table->currentSel->init(secAt, 0);
6798 table->currentSel->expandTo(secAt, table->numCols() - 1);
6799 emit table->selectionChanged();
6800 }
6801 table->setCurrentCell(secAt, 0);
6802 } else { // orientation == Horizontal
6803 if (!table->isColumnSelected(secAt, true)) {
6804 table->currentSel = new Q3TableSelection();
6805 table->selections.append(table->currentSel);
6806 table->currentSel->init(0, secAt);
6807 table->currentSel->expandTo(table->numRows() - 1, secAt);
6808 emit table->selectionChanged();
6809 }
6810 table->setCurrentCell(0, secAt);
6811 }
6812
6813 if ((orientation() == Horizontal && table->isColumnSelected(secAt))
6814 || (orientation() == Vertical && table->isRowSelected(secAt))) {
6815 setSectionState(secAt, Selected);
6816 }
6817
6818 table->repaintSelections(oldSelection, table->currentSel,
6819 orientation() == Horizontal,
6820 orientation() == Vertical);
6821 if (sectionAt(p) != -1)
6822 endPos = p;
6823
6824 return true;
6825 }
6826 }
6827
6828 if (sectionAt(p) != -1)
6829 endPos = p;
6830 if (startPos != -1) {
6831 updateSelections();
6832 p -= offset();
6833 if (orientation() == Horizontal && (p < 0 || p > width())) {
6834 doAutoScroll();
6835 autoScrollTimer->start(100, true);
6836 } else if (orientation() == Vertical && (p < 0 || p > height())) {
6837 doAutoScroll();
6838 autoScrollTimer->start(100, true);
6839 }
6840 return true;
6841 }
6842 return table->selectionMode() == Q3Table::NoSelection;
6843 }
6844
mayOverwriteMargin(int before,int after)6845 static inline bool mayOverwriteMargin(int before, int after)
6846 {
6847 /*
6848 0 is the only user value that we always respect. We also never
6849 shrink a margin, in case the user wanted it that way.
6850 */
6851 return before != 0 && before < after;
6852 }
6853
sectionLabelChanged(int section)6854 void Q3TableHeader::sectionLabelChanged(int section)
6855 {
6856 emit sectionSizeChanged(section);
6857
6858 // this does not really belong here
6859 if (orientation() == Horizontal) {
6860 int h = sizeHint().height();
6861 if (h != height() && mayOverwriteMargin(table->topMargin(), h))
6862 table->setTopMargin(h);
6863 } else {
6864 int w = sizeHint().width();
6865 if (w != width() && mayOverwriteMargin((QApplication::reverseLayout() ? table->rightMargin() : table->leftMargin()), w))
6866 table->setLeftMargin(w);
6867 }
6868 }
6869
6870 /*! \reimp */
mouseReleaseEvent(QMouseEvent * e)6871 void Q3TableHeader::mouseReleaseEvent(QMouseEvent *e)
6872 {
6873 if (e->button() != LeftButton)
6874 return;
6875 autoScrollTimer->stop();
6876 mousePressed = false;
6877 setCaching(false);
6878 Q3Header::mouseReleaseEvent(e);
6879 #ifndef NO_LINE_WIDGET
6880 line1->hide();
6881 line2->hide();
6882 #else
6883 if (d->oldLinePos >= 0)
6884 if (orientation() == Horizontal)
6885 table->updateContents(d->oldLinePos, table->contentsY(),
6886 1, table->visibleHeight());
6887 else
6888 table->updateContents( table->contentsX(), d->oldLinePos,
6889 table->visibleWidth(), 1);
6890 d->oldLinePos = -1;
6891 #endif
6892 if (resizedSection != -1) {
6893 emit sectionSizeChanged(resizedSection);
6894 updateStretches();
6895 }
6896
6897 //Make sure all newly selected sections are painted one last time
6898 QRect selectedRects;
6899 for (int i = 0; i < count(); i++) {
6900 if(sectionState(i) == Selected)
6901 selectedRects |= sRect(i);
6902 }
6903 if(!selectedRects.isNull())
6904 repaint(selectedRects);
6905 }
6906
6907 /*! \reimp
6908 */
6909
mouseDoubleClickEvent(QMouseEvent * e)6910 void Q3TableHeader::mouseDoubleClickEvent(QMouseEvent *e)
6911 {
6912 if (e->button() != LeftButton)
6913 return;
6914 if (isResizing) {
6915 int p = real_pos(e->pos(), orientation()) + offset();
6916 int section = sectionAt(p);
6917 if (section == -1)
6918 return;
6919 section--;
6920 if (p >= sectionPos(count() - 1) + sectionSize(count() - 1))
6921 ++section;
6922 while (sectionSize(section) == 0)
6923 section--;
6924 if (section < 0)
6925 return;
6926 int oldSize = sectionSize(section);
6927 if (orientation() == Horizontal) {
6928 table->adjustColumn(section);
6929 int newSize = sectionSize(section);
6930 if (oldSize != newSize)
6931 emit sizeChange(section, oldSize, newSize);
6932 for (int i = 0; i < table->numCols(); ++i) {
6933 if (table->isColumnSelected(i) && sectionSize(i) != 0)
6934 table->adjustColumn(i);
6935 }
6936 } else {
6937 table->adjustRow(section);
6938 int newSize = sectionSize(section);
6939 if (oldSize != newSize)
6940 emit sizeChange(section, oldSize, newSize);
6941 for (int i = 0; i < table->numRows(); ++i) {
6942 if (table->isRowSelected(i) && sectionSize(i) != 0)
6943 table->adjustRow(i);
6944 }
6945 }
6946 }
6947 }
6948
6949 /*! \reimp
6950 */
6951
resizeEvent(QResizeEvent * e)6952 void Q3TableHeader::resizeEvent(QResizeEvent *e)
6953 {
6954 stretchTimer->stop();
6955 widgetStretchTimer->stop();
6956 Q3Header::resizeEvent(e);
6957 if (numStretches == 0)
6958 return;
6959 stretchTimer->start(0, true);
6960 }
6961
updateStretches()6962 void Q3TableHeader::updateStretches()
6963 {
6964 if (numStretches == 0)
6965 return;
6966
6967 int dim = orientation() == Horizontal ? width() : height();
6968 if (sectionPos(count() - 1) + sectionSize(count() - 1) == dim)
6969 return;
6970 int i;
6971 int pd = dim - (sectionPos(count() - 1)
6972 + sectionSize(count() - 1));
6973 bool block = signalsBlocked();
6974 blockSignals(true);
6975 for (i = 0; i < (int)stretchable.count(); ++i) {
6976 if (!stretchable[i] ||
6977 (stretchable[i] && table->d->hiddenCols[i]))
6978 continue;
6979 pd += sectionSize(i);
6980 }
6981 pd /= numStretches;
6982 for (i = 0; i < (int)stretchable.count(); ++i) {
6983 if (!stretchable[i] ||
6984 (stretchable[i] && table->d->hiddenCols[i]))
6985 continue;
6986 if (i == (int)stretchable.count() - 1 &&
6987 sectionPos(i) + pd < dim)
6988 pd = dim - sectionPos(i);
6989 resizeSection(i, QMAX(20, pd));
6990 }
6991 blockSignals(block);
6992 table->repaintContents(false);
6993 widgetStretchTimer->start(100, true);
6994 }
6995
updateWidgetStretches()6996 void Q3TableHeader::updateWidgetStretches()
6997 {
6998 QSize s = table->tableSize();
6999 table->resizeContents(s.width(), s.height());
7000 for (int i = 0; i < table->numCols(); ++i)
7001 table->updateColWidgets(i);
7002 }
7003
updateSelections()7004 void Q3TableHeader::updateSelections()
7005 {
7006 if (table->selectionMode() == Q3Table::NoSelection ||
7007 (isRowSelection(table->selectionMode()) && orientation() != Vertical ))
7008 return;
7009 int a = sectionAt(startPos);
7010 int b = sectionAt(endPos);
7011 int start = QMIN(a, b);
7012 int end = QMAX(a, b);
7013 register int *s = states.data();
7014 for (int i = 0; i < count(); ++i) {
7015 if (i < start || i > end)
7016 *s = oldStates.data()[ i ];
7017 else
7018 *s = Selected;
7019 ++s;
7020 }
7021 repaint(false);
7022
7023 if (table->currentSel) {
7024 Q3TableSelection oldSelection = *table->currentSel;
7025 if (orientation() == Vertical)
7026 table->currentSel->expandTo(b, table->horizontalHeader()->count() - 1);
7027 else
7028 table->currentSel->expandTo(table->verticalHeader()->count() - 1, b);
7029 table->repaintSelections(&oldSelection, table->currentSel,
7030 orientation() == Horizontal,
7031 orientation() == Vertical);
7032 }
7033 emit table->selectionChanged();
7034 }
7035
saveStates()7036 void Q3TableHeader::saveStates()
7037 {
7038 oldStates.resize(count());
7039 register int *s = states.data();
7040 register int *s2 = oldStates.data();
7041 for (int i = 0; i < count(); ++i) {
7042 *s2 = *s;
7043 ++s2;
7044 ++s;
7045 }
7046 }
7047
doAutoScroll()7048 void Q3TableHeader::doAutoScroll()
7049 {
7050 QPoint pos = mapFromGlobal(QCursor::pos());
7051 int p = real_pos(pos, orientation()) + offset();
7052 if (sectionAt(p) != -1)
7053 endPos = p;
7054 if (orientation() == Horizontal)
7055 table->ensureVisible(endPos, table->contentsY());
7056 else
7057 table->ensureVisible(table->contentsX(), endPos);
7058 updateSelections();
7059 autoScrollTimer->start(100, true);
7060 }
7061
sectionWidthChanged(int col,int,int)7062 void Q3TableHeader::sectionWidthChanged(int col, int, int)
7063 {
7064 resizedSection = col;
7065 if (orientation() == Horizontal) {
7066 #ifndef NO_LINE_WIDGET
7067 table->moveChild(line1, Q3Header::sectionPos(col) - 1,
7068 table->contentsY());
7069 line1->resize(1, table->visibleHeight());
7070 line1->show();
7071 line1->raise();
7072 table->moveChild(line2,
7073 Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1,
7074 table->contentsY());
7075 line2->resize(1, table->visibleHeight());
7076 line2->show();
7077 line2->raise();
7078 #else
7079 QPainter p(table->viewport());
7080 int lx = Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1;
7081 int ly = table->contentsY();
7082
7083 if (lx != d->oldLinePos) {
7084 QPoint pt = table->contentsToViewport(QPoint(lx, ly));
7085 p.drawLine(pt.x(), pt.y()+1,
7086 pt.x(), pt.y()+ table->visibleHeight());
7087 if (d->oldLinePos >= 0)
7088 table->repaintContents(d->oldLinePos, table->contentsY(),
7089 1, table->visibleHeight());
7090
7091 d->oldLinePos = lx;
7092 }
7093 #endif
7094 } else {
7095 #ifndef NO_LINE_WIDGET
7096 table->moveChild(line1, table->contentsX(),
7097 Q3Header::sectionPos(col) - 1);
7098 line1->resize(table->visibleWidth(), 1);
7099 line1->show();
7100 line1->raise();
7101 table->moveChild(line2, table->contentsX(),
7102 Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1);
7103 line2->resize(table->visibleWidth(), 1);
7104 line2->show();
7105 line2->raise();
7106
7107 #else
7108 QPainter p(table->viewport());
7109 int lx = table->contentsX();
7110 int ly = Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1;
7111
7112 if (ly != d->oldLinePos) {
7113 QPoint pt = table->contentsToViewport(QPoint(lx, ly));
7114 p.drawLine(pt.x()+1, pt.y(),
7115 pt.x() + table->visibleWidth(), pt.y());
7116 if (d->oldLinePos >= 0)
7117 table->repaintContents( table->contentsX(), d->oldLinePos,
7118 table->visibleWidth(), 1);
7119 d->oldLinePos = ly;
7120 }
7121
7122 #endif
7123 }
7124 }
7125
7126 /*!
7127 \reimp
7128
7129 Returns the size of section \a section in pixels or -1 if \a
7130 section is out of range.
7131 */
7132
sectionSize(int section) const7133 int Q3TableHeader::sectionSize(int section) const
7134 {
7135 if (count() <= 0 || section < 0 || section >= count())
7136 return -1;
7137 if (caching && section < (int)sectionSizes.count())
7138 return sectionSizes[ section ];
7139 return Q3Header::sectionSize(section);
7140 }
7141
7142 /*!
7143 \reimp
7144
7145 Returns the start position of section \a section in pixels or -1
7146 if \a section is out of range.
7147
7148 \sa sectionAt()
7149 */
7150
sectionPos(int section) const7151 int Q3TableHeader::sectionPos(int section) const
7152 {
7153 if (count() <= 0 || section < 0 || section >= count())
7154 return -1;
7155 if (caching && section < (int)sectionPoses.count())
7156 return sectionPoses[ section ];
7157 return Q3Header::sectionPos(section);
7158 }
7159
7160 /*!
7161 \reimp
7162
7163 Returns the number of the section at index position \a pos or -1
7164 if there is no section at the position given.
7165
7166 \sa sectionPos()
7167 */
7168
sectionAt(int pos) const7169 int Q3TableHeader::sectionAt(int pos) const
7170 {
7171 if (!caching || sectionSizes.count() <= 0 || sectionPoses.count() <= 0)
7172 return Q3Header::sectionAt(pos);
7173 if (count() <= 0 || pos > sectionPoses[ count() - 1 ] + sectionSizes[ count() - 1 ])
7174 return -1;
7175 int l = 0;
7176 int r = count() - 1;
7177 int i = ((l+r+1) / 2);
7178 while (r - l) {
7179 if (sectionPoses[i] > pos)
7180 r = i -1;
7181 else
7182 l = i;
7183 i = ((l+r+1) / 2);
7184 }
7185 if (sectionPoses[i] <= pos &&
7186 pos <= sectionPoses[i] + sectionSizes[ mapToSection(i) ])
7187 return mapToSection(i);
7188 return -1;
7189 }
7190
updateCache()7191 void Q3TableHeader::updateCache()
7192 {
7193 sectionPoses.resize(count());
7194 sectionSizes.resize(count());
7195 if (!caching)
7196 return;
7197 for (int i = 0; i < count(); ++i) {
7198 sectionSizes[ i ] = Q3Header::sectionSize(i);
7199 sectionPoses[ i ] = Q3Header::sectionPos(i);
7200 }
7201 }
7202
setCaching(bool b)7203 void Q3TableHeader::setCaching(bool b)
7204 {
7205 if (caching == b)
7206 return;
7207 caching = b;
7208 sectionPoses.resize(count());
7209 sectionSizes.resize(count());
7210 if (b) {
7211 for (int i = 0; i < count(); ++i) {
7212 sectionSizes[ i ] = Q3Header::sectionSize(i);
7213 sectionPoses[ i ] = Q3Header::sectionPos(i);
7214 }
7215 }
7216 }
7217
7218 /*!
7219 If \a b is true, section \a s is stretchable; otherwise the
7220 section is not stretchable.
7221
7222 \sa isSectionStretchable()
7223 */
7224
setSectionStretchable(int s,bool b)7225 void Q3TableHeader::setSectionStretchable(int s, bool b)
7226 {
7227 if (stretchable[ s ] == b)
7228 return;
7229 stretchable[ s ] = b;
7230 if (b)
7231 numStretches++;
7232 else
7233 numStretches--;
7234 }
7235
7236 /*!
7237 Returns true if section \a s is stretcheable; otherwise returns
7238 false.
7239
7240 \sa setSectionStretchable()
7241 */
7242
isSectionStretchable(int s) const7243 bool Q3TableHeader::isSectionStretchable(int s) const
7244 {
7245 return stretchable[ s ];
7246 }
7247
swapSections(int oldIdx,int newIdx,bool swapTable)7248 void Q3TableHeader::swapSections(int oldIdx, int newIdx, bool swapTable)
7249 {
7250 extern bool qt_qheader_label_return_null_strings; // qheader.cpp
7251 qt_qheader_label_return_null_strings = true;
7252
7253 QIconSet oldIconSet, newIconSet;
7254 if (iconSet(oldIdx))
7255 oldIconSet = *iconSet(oldIdx);
7256 if (iconSet(newIdx))
7257 newIconSet = *iconSet(newIdx);
7258 QString oldLabel = label(oldIdx);
7259 QString newLabel = label(newIdx);
7260 bool sectionsHasContent = !(oldIconSet.isNull() && newIconSet.isNull()
7261 && oldLabel.isNull() && newLabel.isNull());
7262 if (sectionsHasContent) {
7263 Q3HeaderData *data = static_cast<Q3Header*>(this)->d;
7264 bool oldNullLabel = qt_get_null_label_bit(data, oldIdx);
7265 bool newNullLabel = qt_get_null_label_bit(data, newIdx);
7266 setLabel(oldIdx, newIconSet, newLabel);
7267 setLabel(newIdx, oldIconSet, oldLabel);
7268 qt_set_null_label_bit(data, oldIdx, newNullLabel);
7269 qt_set_null_label_bit(data, newIdx, oldNullLabel);
7270 }
7271
7272 qt_qheader_label_return_null_strings = false;
7273
7274 int w1 = sectionSize(oldIdx);
7275 int w2 = sectionSize(newIdx);
7276 if (w1 != w2) {
7277 resizeSection(oldIdx, w2);
7278 resizeSection(newIdx, w1);
7279 }
7280
7281 if (!swapTable)
7282 return;
7283 if (orientation() == Horizontal)
7284 table->swapColumns(oldIdx, newIdx);
7285 else
7286 table->swapRows(oldIdx, newIdx);
7287 }
7288
indexChanged(int sec,int oldIdx,int newIdx)7289 void Q3TableHeader::indexChanged(int sec, int oldIdx, int newIdx)
7290 {
7291 newIdx = mapToIndex(sec);
7292 if (oldIdx > newIdx)
7293 moveSection(sec, oldIdx + 1);
7294 else
7295 moveSection(sec, oldIdx);
7296
7297 if (oldIdx < newIdx) {
7298 while (oldIdx < newIdx) {
7299 swapSections(oldIdx, oldIdx + 1);
7300 oldIdx++;
7301 }
7302 } else {
7303 while (oldIdx > newIdx) {
7304 swapSections(oldIdx - 1, oldIdx);
7305 oldIdx--;
7306 }
7307 }
7308
7309 table->repaintContents(table->contentsX(), table->contentsY(),
7310 table->visibleWidth(), table->visibleHeight());
7311 }
7312
setLabels(const QStringList & labels)7313 void Q3TableHeader::setLabels(const QStringList & labels)
7314 {
7315 int i = 0;
7316 const int c = QMIN(count(), (int)labels.count());
7317 bool updates = updatesEnabled();
7318 if (updates)
7319 setUpdatesEnabled(false);
7320 for (QStringList::ConstIterator it = labels.begin(); i < c; ++i, ++it) {
7321 if (i == c - 1) {
7322 if (updates)
7323 setUpdatesEnabled(true);
7324 setLabel(i, *it);
7325 } else {
7326 Q3Header::setLabel(i, *it);
7327 emit sectionSizeChanged(i);
7328 }
7329 }
7330 }
7331
7332 QT_END_NAMESPACE
7333
7334 #include "q3table.moc"
7335