1 /***************************************************************************
2                           rkaccordiontable  -  description
3                              -------------------
4     begin                : Fri Oct 24 2015
5     copyright            : (C) 2015-2018 by Thomas Friedrichsmeier
6     email                : thomas.friedrichsmeier@kdemail.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "rkaccordiontable.h"
19 
20 #include <QPointer>
21 #include <QTimer>
22 #include <QVBoxLayout>
23 #include <QAbstractProxyModel>
24 #include <QToolButton>
25 #include <QHBoxLayout>
26 #include <QLabel>
27 
28 #include <KLocalizedString>
29 
30 #include "rkcommonfunctions.h"
31 #include "rkstandardicons.h"
32 
33 #include "../debug.h"
34 
35 /** Maps from the Optionset data model to the model used internally in the RKAccordionTable
36  *  (a dummy child item is inserted for each actual row). This item can _not_ actually be accessed
37  *  in a meaningful way. The only purpose is to provide a placeholder to expand / collapse in the view. */
38 class RKAccordionDummyModel : public QAbstractProxyModel {
39 	Q_OBJECT
40 public:
RKAccordionDummyModel(QObject * parent)41 	RKAccordionDummyModel (QObject *parent) : QAbstractProxyModel (parent) {
42 		add_trailing_columns = 1;
43 		add_trailing_rows = 1;
44 	};
45 
mapFromSource(const QModelIndex & sindex) const46 	QModelIndex mapFromSource (const QModelIndex& sindex) const override {
47 		if (!sindex.isValid ()) return QModelIndex ();
48 		// we're using Source row as "Internal ID", here. This _would_ fall on our feet when removing rows, _if_ we'd actually
49 		// have to be able to map the dummy rows back to their real parents.
50 		return (createIndex (sindex.row (), mapColumnFromSource (sindex.column ()), real_item_id));
51 	}
52 
mapColumnFromSource(int column) const53 	inline int mapColumnFromSource (int column) const {
54 		return qMax (0, column);
55 	}
56 
mapColumnToSource(int column) const57 	inline int mapColumnToSource (int column) const {
58 		return qMin (sourceModel ()->columnCount () - 1, column);
59 	}
60 
isFakeColumn(int column) const61 	inline bool isFakeColumn (int column) const {
62 		return (column >= mapColumnFromSource (sourceModel ()->columnCount ()));
63 	}
64 
mapToSource(const QModelIndex & pindex) const65 	QModelIndex mapToSource (const QModelIndex& pindex) const override {
66 		if (!pindex.isValid ()) return QModelIndex ();
67 		if (pindex.internalId () == real_item_id) {
68 			return sourceModel ()->index (pindex.row (), mapColumnToSource (pindex.column ()));
69 		} else if (pindex.internalId () == trailing_item_id) {
70 			return QModelIndex ();
71 		} else {
72 			return sourceModel ()->index (pindex.internalId (), 0);
73 		}
74 	}
75 
flags(const QModelIndex & index) const76 	Qt::ItemFlags flags (const QModelIndex& index) const override {
77 		if (isFake (index)) {
78 			if (index.internalId () == trailing_item_id) return (Qt::ItemIsEnabled);
79 			return (Qt::NoItemFlags);
80 		}
81 		return (QAbstractProxyModel::flags (index));
82 	}
83 
rowCount(const QModelIndex & parent=QModelIndex ()) const84 	int rowCount (const QModelIndex& parent = QModelIndex ()) const override {
85 		if (isFake (parent)) return 0;
86 		if (parent.isValid ()) return 1;
87 		return sourceModel ()->rowCount (mapToSource (parent)) + add_trailing_rows;
88 	}
89 
data(const QModelIndex & proxyIndex,int role=Qt::DisplayRole) const90     QVariant data (const QModelIndex& proxyIndex, int role = Qt::DisplayRole) const override {
91 		if (isFake (proxyIndex)) {
92 			if (proxyIndex.internalId () == trailing_item_id) {
93 				if (role == Qt::DisplayRole) {
94 					return i18n ("Click to add new row");
95 				} else if (role == Qt::FontRole) {
96 					QFont font;
97 					font.setItalic (true);
98 					return font;
99 				} else if (role == Qt::DecorationRole) {
100 					return RKStandardIcons::getIcon (RKStandardIcons::ActionInsertRow);
101 				}
102 			}
103 			return QVariant ();
104 		}
105 		if (isFakeColumn (proxyIndex.column ()) && (role == Qt::DisplayRole)) return QVariant ();
106 		return QAbstractProxyModel::data (proxyIndex, role);
107 	}
108 
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)109 	bool dropMimeData (const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override {
110 		// Ok, I don't understand why exactly, but something goes wrong while mapping this back to the source model. So we help it a bit:
111 		Q_UNUSED (column);
112 
113 		if (isFake (parent)) {
114 			RK_ASSERT (false);
115 			return false;
116 		}
117 		if (parent.isValid ()) row = parent.row ();
118 		return sourceModel ()->dropMimeData (data, action, row, 0, QModelIndex ());
119 	}
120 
headerData(int section,Qt::Orientation orientation,int role=Qt::DisplayRole) const121 	QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override {
122 		if ((orientation == Qt::Horizontal) && isFakeColumn (section) && (role == Qt::DisplayRole)) return QVariant ();
123 		return QAbstractProxyModel::headerData (section, orientation, role);
124 	}
125 
hasChildren(const QModelIndex & parent) const126 	bool hasChildren (const QModelIndex& parent) const override {
127 		return (!isFake (parent));
128 	}
129 
columnCount(const QModelIndex & parent=QModelIndex ()) const130 	int columnCount (const QModelIndex& parent = QModelIndex ()) const override {
131 		if (isFake (parent)) return 1;
132 		return mapColumnFromSource (sourceModel ()->columnCount (mapToSource (parent))) + add_trailing_columns;
133 	}
134 
index(int row,int column,const QModelIndex & parent=QModelIndex ()) const135 	QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex ()) const override {
136 		if (!parent.isValid ()) {
137 			if (row == sourceModel ()->rowCount ()) return createIndex (row, column, trailing_item_id);
138 			return createIndex (row, column, real_item_id);
139 		}
140 		RK_ASSERT (parent.internalId () >= trailing_item_id);
141 		return createIndex (row, column, parent.row ());
142 	}
143 
parent(const QModelIndex & child) const144 	QModelIndex parent (const QModelIndex& child) const override {
145 		if (child.internalId () == real_item_id) return QModelIndex ();
146 		else if (child.internalId () == trailing_item_id) return QModelIndex ();
147 		return createIndex (child.internalId (), 0, real_item_id);
148 	}
149 
setSourceModel(QAbstractItemModel * source_model)150 	void setSourceModel (QAbstractItemModel* source_model) override {
151 		/* More than these would be needed for a proper proxy of any model, but in our case, we only have to support the RKOptionsetDisplayModel */
152 		connect (source_model, &QAbstractItemModel::rowsInserted, this, &RKAccordionDummyModel::r_rowsInserted);
153 		connect (source_model, &QAbstractItemModel::rowsRemoved, this, &RKAccordionDummyModel::r_rowsRemoved);
154 		connect (source_model, &QAbstractItemModel::dataChanged, this, &RKAccordionDummyModel::r_dataChanged);
155 		connect (source_model, &QAbstractItemModel::headerDataChanged, this, &RKAccordionDummyModel::r_headerDataChanged);
156 		connect (source_model, &QAbstractItemModel::layoutChanged, this, &RKAccordionDummyModel::r_layoutChanged);
157 		QAbstractProxyModel::setSourceModel (source_model);
158 	}
159 
isFake(const QModelIndex & index) const160 	bool isFake (const QModelIndex& index) const {
161 		return (index.isValid () && (index.internalId () != real_item_id));
162 	}
163 
164 	static const quint32 real_item_id = 0xFFFFFFFF;
165 	static const quint32 trailing_item_id = 0xFFFFFFFE;
166 	int add_trailing_columns;
167 	int add_trailing_rows;
168 public slots:
r_rowsInserted(const QModelIndex & parent,int start,int end)169 	void r_rowsInserted (const QModelIndex& parent, int start, int end) {
170 		RK_TRACE (MISC);
171 		RK_ASSERT (!parent.isValid ());
172 
173 		beginInsertRows (mapFromSource (parent), start, end);
174 		endInsertRows ();
175 	}
r_rowsRemoved(const QModelIndex & parent,int start,int end)176 	void r_rowsRemoved (const QModelIndex& parent, int start, int end) {
177 		RK_TRACE (MISC);
178 		RK_ASSERT (!parent.isValid ());
179 
180 		beginRemoveRows (mapFromSource (parent), start, end);
181 		endRemoveRows ();
182 	}
r_dataChanged(const QModelIndex & from,const QModelIndex & to)183 	void r_dataChanged (const QModelIndex& from, const QModelIndex& to) {
184 		emit (dataChanged (mapFromSource (from), mapFromSource (to)));
185 	}
r_headerDataChanged(Qt::Orientation o,int from,int to)186 	void r_headerDataChanged(Qt::Orientation o,int from,int to) {
187 		emit (headerDataChanged (o, from, to));
188 	}
r_layoutChanged()189 	void r_layoutChanged () {
190 		emit (layoutChanged());
191 	}
192 };
193 
194 /** Protects the given child widget from deletion */
195 class RKWidgetGuard : public QWidget {
196 public:
RKWidgetGuard(QWidget * parent,QWidget * widget_to_guard,QWidget * fallback_parent)197 	RKWidgetGuard (QWidget *parent, QWidget *widget_to_guard, QWidget *fallback_parent) : QWidget (parent) {
198 		RK_TRACE (MISC);
199 		RK_ASSERT (widget_to_guard);
200 
201 		guarded = widget_to_guard;
202 		RKWidgetGuard::fallback_parent = fallback_parent;
203 
204 		QVBoxLayout *layout = new QVBoxLayout (this);
205 		layout->setContentsMargins (0, 0, 0, 0);
206 		guarded->setParent (this);
207 		layout->addWidget (guarded);
208 	}
209 
~RKWidgetGuard()210 	~RKWidgetGuard () {
211 		RK_TRACE (MISC);
212 		if ((!guarded.isNull ()) && guarded->parent () == this) {
213 			guarded->setParent (fallback_parent);
214 		}
215 	}
216 private:
217 	QPointer<QWidget> guarded;
218 	QWidget *fallback_parent;
219 };
220 
221 #include <QStyledItemDelegate>
222 #include <QPainter>
223 /** Responsible for drawing expand / collapse indicators in first column */
224 class RKAccordionDelegate : public QStyledItemDelegate {
225 public:
RKAccordionDelegate(RKAccordionTable * parent)226 	RKAccordionDelegate (RKAccordionTable* parent) : QStyledItemDelegate (parent) {
227 		table = parent;
228 		expanded = RKStandardIcons::getIcon (RKStandardIcons::ActionCollapseUp);
229 		collapsed = RKStandardIcons::getIcon (RKStandardIcons::ActionExpandDown);
230 	}
initStyleOption(QStyleOptionViewItem * option,const QModelIndex & index) const231 	void initStyleOption (QStyleOptionViewItem* option, const QModelIndex& index) const override {
232 		QStyledItemDelegate::initStyleOption (option, index);
233 		if (!pmodel->isFake (index)) {
234 			option->icon = table->isExpanded (index) ? expanded : collapsed;
235 			option->features |= QStyleOptionViewItem::HasDecoration;
236 		}
237 	}
238 	RKAccordionDummyModel *pmodel;
239 	RKAccordionTable* table;
240 	QIcon expanded;
241 	QIcon collapsed;
242 };
243 
244 #include <QScrollBar>
245 #include <QHeaderView>
RKAccordionTable(QWidget * parent)246 RKAccordionTable::RKAccordionTable (QWidget* parent) : QTreeView (parent) {
247 	RK_TRACE (MISC);
248 
249 	show_add_remove_buttons = false;
250 	handling_a_click = false;
251 
252 	// This may seem like excessive wrapping. The point is to be able to manipulate the editor_widget_container's sizeHint(), while
253 	// keeping the editor_widget's sizeHint() intact.
254 	editor_widget_container = new QWidget ();
255 	QHBoxLayout *layout = new QHBoxLayout (editor_widget_container);
256 	layout->setContentsMargins (0, 0, 0, 0);
257 	editor_widget = new QWidget (editor_widget_container);
258 	new QVBoxLayout (editor_widget);
259 	layout->addWidget (editor_widget);
260 
261 	setSelectionMode (SingleSelection);
262 	setDragEnabled (true);
263 	setAcceptDrops (true);
264 	setDragDropMode (InternalMove);
265 	setDropIndicatorShown (false);
266 	setDragDropOverwriteMode (false);
267 	setIndentation (0);
268 	setRootIsDecorated (false);
269 	setAlternatingRowColors (true);
270 	setExpandsOnDoubleClick (false);   // we expand on single click, instead
271 	setItemsExpandable (false);        // custom handling
272 	setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
273 
274 	pmodel = new RKAccordionDummyModel (0);
275 	RKAccordionDelegate* delegate = new RKAccordionDelegate (this);
276 	delegate->pmodel = pmodel;
277 	setItemDelegateForColumn (0, delegate);
278 
279 	connect (this, &QTreeView::expanded, this, &RKAccordionTable::rowExpanded);
280 	connect (this, &QTreeView::clicked, this, &RKAccordionTable::rowClicked);
281 }
282 
~RKAccordionTable()283 RKAccordionTable::~RKAccordionTable () {
284 	RK_TRACE (MISC);
285 
286 	// Qt 4.8.6: The model must _not_ be a child of this view, and must _not_ be deleted along with it, or else there will be a crash
287 	// on destruction _if_ (and only if) there are any trailing dummy rows. (Inside QAbstractItemModelPrivate::removePersistentIndexData())
288 	// No, I do not understand this, yes, this is worrysome, but no idea, what could be the actual cause.
289 	pmodel->deleteLater ();
290 	delete editor_widget_container;
291 }
292 
drawRow(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const293 void RKAccordionTable::drawRow (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
294 	if (index.parent ().isValid ()) {    // must be the editor widget
295 		painter->fillRect (option.rect, palette ().background ());  // fill to paper over any padding around the widget (whereever it comes from)
296 		QTreeView::drawRow (painter, option, index);
297 		painter->drawLine (option.rect.bottomLeft (), option.rect.bottomRight ());
298 	} else {
299 		QTreeView::drawRow (painter, option, index);
300 		if (isExpanded (index)) {
301 			painter->drawLine (option.rect.topLeft (), option.rect.topRight ());
302 		}
303 	}
304 }
305 
setShowAddRemoveButtons(bool show)306 void RKAccordionTable::setShowAddRemoveButtons (bool show) {
307 	RK_TRACE (MISC);
308 	show_add_remove_buttons = show;
309 	pmodel->add_trailing_columns = show;
310 	pmodel->add_trailing_rows = show;
311 }
312 
sizeHintWithoutEditor() const313 QSize RKAccordionTable::sizeHintWithoutEditor () const {
314 	RK_TRACE (MISC);
315 
316 	// NOTE: This is not totally correct, but seems to be, roughly. We can't use sizeHintForRow(0) for height calculation, as the model may be empty
317 	// (for "driven" optionsets.
318 	return (QSize (minimumSizeHint ().width (), header ()->sizeHint().height () + horizontalScrollBar ()->sizeHint ().height () + QFontMetrics (QFont ()).lineSpacing () * 4));
319 }
320 
sizeHint() const321 QSize RKAccordionTable::sizeHint () const {
322 	RK_TRACE (MISC);
323 
324 	QSize swoe = sizeHintWithoutEditor ();
325 	QSize min = editor_widget->sizeHint ();
326 	min.setHeight (min.height () + swoe.height ());
327 	min.setWidth (qMax (min.width (), swoe.width ()));
328 	return min;
329 }
330 
resizeEvent(QResizeEvent * event)331 void RKAccordionTable::resizeEvent (QResizeEvent* event) {
332 	RK_TRACE (MISC);
333 
334 	QSize esh = editor_widget->sizeHint ();
335 	int available_height = height () - sizeHintWithoutEditor ().height ();
336 	int extra_height = available_height - esh.height ();
337 	editor_widget_container->setMinimumHeight (esh.height () + qMax (0, 2 * (extra_height / 3)));
338 	editor_widget_container->setMaximumWidth (width ());
339 
340 	QTreeView::resizeEvent (event);
341 
342 	// NOTE: For Qt 4.8.6, an expanded editor row will _not_ be updated, automatically.
343 	// We have to force this by hiding / unhiding it.
344 	QModelIndex expanded;
345 	for (int i = 0; i < model ()->rowCount (); ++i) {
346 		if (isExpanded (model ()->index (i, 0))) {
347 			expanded = model ()->index (i, 0);
348 			break;
349 		}
350 	}
351 	if (expanded.isValid ()) {
352 		setUpdatesEnabled (false);
353 		setRowHidden (0, expanded, true);
354 		setRowHidden (0, expanded, false);
355 		setUpdatesEnabled (true);
356 	}
357 }
358 
activateRow(int row)359 void RKAccordionTable::activateRow (int row) {
360 	RK_TRACE (MISC);
361 
362 	setExpanded (model ()->index (row, 0), true);
363 }
364 
365 // Gaaah! currentIndexChanged() (on click) seems to be called _before_ rowClick ().
366 // But we _have to_ expand items activated via currentChanged() (could have been keyboard, too), and we _have to_
367 // expand items on click, if they are not expanded (as it could be the already-current-item), but we _must _not_
368 // collapse the item we have just expanded from currentChanged().
369 // To make things worse, rowClicked () is called with a delay (probably when Qt is sure it wasn't a double click),
370 // so a simple "swallow any click in this event cycle" is not enough.
371 // Solution, if mouse was clicked, prevent currentChanged() from handling anything.
mousePressEvent(QMouseEvent * event)372 void RKAccordionTable::mousePressEvent (QMouseEvent* event) {
373 	handling_a_click = true;
374 	QTreeView::mousePressEvent (event);
375 	handling_a_click = false;
376 }
377 
rowClicked(QModelIndex row)378 void RKAccordionTable::rowClicked (QModelIndex row) {
379 	RK_TRACE (MISC);
380 
381 	row = model ()->index (row.row (), 0, row.parent ());   // Fix up index to point to column 0, or isExpanded() will always return false
382 	if (isExpanded (row) && currentIndex ().row () == row.row ()) {
383 		setExpanded (row, false);
384 	} else {
385 		if (!pmodel->isFake (row)) {
386 			if (currentIndex ().row () == row.row ()) {
387 				setExpanded (row, true);
388 			}
389 			// Expanding of rows, when current has changed is handled in currenChanged(), only.
390 		}
391 	}
392 	if (!row.parent ().isValid ()) {
393 		if (row.row () >= pmodel->rowCount () - pmodel->add_trailing_rows) {
394 			emit (addRow (row.row ()));
395 		}
396 	}
397 }
398 
currentChanged(const QModelIndex & current,const QModelIndex & previous)399 void RKAccordionTable::currentChanged (const QModelIndex& current, const QModelIndex& previous) {
400 	RK_TRACE (MISC);
401 	Q_UNUSED (previous);
402 
403 	if (handling_a_click) return;
404 	if (!pmodel->isFake (current)) {
405 		setExpanded (current, true);
406 		emit (activated (current.row ()));
407 	}
408 }
409 
rowExpanded(QModelIndex row)410 void RKAccordionTable::rowExpanded (QModelIndex row) {
411 	RK_TRACE (MISC);
412 
413 	for (int i = 0; i < pmodel->rowCount () - pmodel->add_trailing_rows; ++i) {
414 		QModelIndex _row = model ()->index (i, 0);
415 		if (i != row.row ()) {
416 			setIndexWidget (model ()->index (0, 0, _row), 0);
417 			setExpanded (_row, false);
418 		}
419 	}
420 	setFirstColumnSpanned (0, row, true);
421 	setIndexWidget (model ()->index (0, 0, row), new RKWidgetGuard (0, editor_widget_container, this));
422 	setCurrentIndex (row);
423 	scrollTo (row, EnsureVisible);                          // yes, we want both scrolls: We want the header row above the widget, if possible at all,
424 	scrollTo (model ()->index (0, 0, row), EnsureVisible);  // but of course, having the header row visible without the widget is not enough...
425 }
426 
updateWidget()427 void RKAccordionTable::updateWidget () {
428 	RK_TRACE (MISC);
429 
430 	bool seen_expanded = false;
431 	for (int i = 0; i < pmodel->rowCount () - pmodel->add_trailing_rows; ++i) {
432 		QModelIndex row = model ()->index (i, 0);
433 		if (isExpanded (row) && !seen_expanded) {
434 			rowExpanded (row);
435 			seen_expanded = true;
436 		}
437 
438 		if (show_add_remove_buttons) {
439 			QModelIndex button_index = model ()->index (i, model ()->columnCount () - 1);
440 			if (!indexWidget (button_index)) {
441 				QWidget *display_buttons = new QWidget;
442 				QHBoxLayout *layout = new QHBoxLayout (display_buttons);
443 				layout->setContentsMargins (0, 0, 0, 0);
444 				layout->setSpacing (0);
445 
446 				QToolButton *remove_button = new QToolButton (display_buttons);
447 				remove_button->setAutoRaise (true);
448 				connect (remove_button, &QToolButton::clicked, this, &RKAccordionTable::removeClicked);
449 				remove_button->setIcon (RKStandardIcons::getIcon (RKStandardIcons::ActionDeleteRow));
450 				RKCommonFunctions::setTips (i18n ("Remove this row / element"), remove_button);
451 				layout->addWidget (remove_button);
452 
453 				setIndexWidget (button_index, display_buttons);
454 
455 				if (i == 0) {
456 					header ()->setStretchLastSection (false);  // we stretch the second to last, instead
457 					header ()->resizeSection (button_index.column (), rowHeight (row));
458 					header ()->setSectionResizeMode (button_index.column (), QHeaderView::Fixed);
459 					header ()->setSectionResizeMode (button_index.column () - 1, QHeaderView::Stretch);
460 				}
461 			}
462 		}
463 	}
464 
465 	if (pmodel->add_trailing_rows) {
466 		setFirstColumnSpanned (pmodel->rowCount () - pmodel->add_trailing_rows, QModelIndex (), true);
467 	}
468 }
469 
rowOfButton(QObject * button) const470 int RKAccordionTable::rowOfButton (QObject* button) const {
471 	RK_TRACE (MISC);
472 
473 	if (!button) return -1;
474 
475 	// we rely on the fact that the buttons in use, here, are encapsulaped in a parent widget, which is set as indexWidget()
476 	QObject* button_parent = button->parent ();
477 	for (int i = model ()->rowCount () - 1; i >= 0; --i) {
478 		QModelIndex row = model ()->index (i, model ()->columnCount () - 1);
479 		if (button_parent == indexWidget (row)) {
480 			return i;
481 		}
482 	}
483 	RK_ASSERT (false);
484 	return -1;
485 }
486 
removeClicked()487 void RKAccordionTable::removeClicked () {
488 	RK_TRACE (MISC);
489 
490 	int row = rowOfButton (sender ());
491 	if (row < 0) {
492 		RK_ASSERT (row >= 0);
493 		return;
494 	}
495 	emit (removeRow (row));
496 }
497 
setModel(QAbstractItemModel * model)498 void RKAccordionTable::setModel (QAbstractItemModel* model) {
499 	RK_TRACE (MISC);
500 
501 	pmodel->setSourceModel (model);
502 	QTreeView::setModel (pmodel);
503 	connect (pmodel, &QAbstractItemModel::layoutChanged, this, &RKAccordionTable::updateWidget);
504 	connect (pmodel, &QAbstractItemModel::rowsInserted, this, &RKAccordionTable::updateWidget);
505 	connect (pmodel, &QAbstractItemModel::rowsRemoved, this, &RKAccordionTable::updateWidget);
506 
507 	if (pmodel->rowCount () > 0) expand (pmodel->index (0, 0));
508 
509 	updateWidget ();
510 	updateGeometry ();   // TODO: Not so clean to call this, here. But at this point we know the display_widget has been constructed, too
511 }
512 
513 #include "rkaccordiontable.moc"
514