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