1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2013 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 **************************************************************************/
19 
20 #include "ConfigurationContentsWidget.h"
21 #include "../../../core/ActionsManager.h"
22 #include "../../../core/NetworkManagerFactory.h"
23 #include "../../../core/SettingsManager.h"
24 #include "../../../core/ThemesManager.h"
25 #include "../../../ui/ColorWidget.h"
26 #include "../../../ui/OptionWidget.h"
27 
28 #include "ui_ConfigurationContentsWidget.h"
29 
30 #include <QtCore/QMetaEnum>
31 #include <QtGui/QClipboard>
32 #include <QtGui/QKeyEvent>
33 #include <QtGui/QPainter>
34 #include <QtWidgets/QMenu>
35 #include <QtWidgets/QMessageBox>
36 
37 namespace Otter
38 {
39 
ConfigurationOptionDelegate(QObject * parent)40 ConfigurationOptionDelegate::ConfigurationOptionDelegate(QObject *parent) : ItemDelegate(parent)
41 {
42 }
43 
initStyleOption(QStyleOptionViewItem * option,const QModelIndex & index) const44 void ConfigurationOptionDelegate::initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const
45 {
46 	ItemDelegate::initStyleOption(option, index);
47 
48 	const SettingsManager::OptionDefinition definition(SettingsManager::getOptionDefinition(index.data(ConfigurationContentsWidget::IdentifierRole).toInt()));
49 
50 	option->text = SettingsManager::createDisplayValue(definition.identifier, index.data(Qt::DisplayRole));
51 
52 	switch (definition.type)
53 	{
54 		case SettingsManager::ColorType:
55 			{
56 				QPixmap pixmap(option->fontMetrics.height(), option->fontMetrics.height());
57 				pixmap.fill(Qt::transparent);
58 
59 				QPainter painter(&pixmap);
60 				painter.setRenderHints(QPainter::Antialiasing);
61 
62 				ColorWidget::drawThumbnail(&painter, QColor(index.data(Qt::DisplayRole).toString()), option->palette, pixmap.rect());
63 
64 				painter.end();
65 
66 				option->features |= QStyleOptionViewItem::HasDecoration;
67 				option->decorationSize = pixmap.size();
68 				option->icon = QIcon(pixmap);
69 			}
70 
71 			break;
72 		case SettingsManager::EnumerationType:
73 			{
74 				const QString value(index.data(Qt::DisplayRole).toString());
75 
76 				if (definition.hasIcons())
77 				{
78 					option->features |= QStyleOptionViewItem::HasDecoration;
79 				}
80 
81 				for (int i = 0; i < definition.choices.count(); ++i)
82 				{
83 					if (definition.choices.at(i).value == value)
84 					{
85 						option->icon = definition.choices.at(i).icon;
86 
87 						break;
88 					}
89 				}
90 			}
91 
92 			break;
93 		case SettingsManager::FontType:
94 			option->font = QFont(index.data(Qt::DisplayRole).toString());
95 
96 			break;
97 		default:
98 			break;
99 	}
100 }
101 
setEditorData(QWidget * editor,const QModelIndex & index) const102 void ConfigurationOptionDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
103 {
104 	OptionWidget *widget(qobject_cast<OptionWidget*>(editor));
105 
106 	if (widget && !index.sibling(index.row(), 0).data(ConfigurationContentsWidget::IsModifiedRole).toBool())
107 	{
108 		widget->setValue(index.data(Qt::EditRole));
109 	}
110 }
111 
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const112 void ConfigurationOptionDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
113 {
114 	const OptionWidget *widget(qobject_cast<OptionWidget*>(editor));
115 
116 	if (widget)
117 	{
118 		const QModelIndex optionIndex(index.sibling(index.row(), 0));
119 		QFont font(optionIndex.data(Qt::FontRole).value<QFont>());
120 		font.setBold(widget->getValue() != widget->getDefaultValue());
121 
122 		model->setData(index, widget->getValue(), Qt::EditRole);
123 		model->setData(optionIndex, font, Qt::FontRole);
124 		model->setData(optionIndex, true, ConfigurationContentsWidget::IsModifiedRole);
125 	}
126 }
127 
createEditor(QWidget * parent,const QStyleOptionViewItem & option,const QModelIndex & index) const128 QWidget* ConfigurationOptionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
129 {
130 	Q_UNUSED(option)
131 
132 	const SettingsManager::OptionDefinition definition(SettingsManager::getOptionDefinition(index.data(ConfigurationContentsWidget::IdentifierRole).toInt()));
133 	OptionWidget *widget(new OptionWidget(index.data(Qt::EditRole), definition.type, parent));
134 	widget->setDefaultValue(definition.defaultValue);
135 	widget->setFocus();
136 
137 	if (definition.type == SettingsManager::EnumerationType)
138 	{
139 		widget->setChoices(definition.choices);
140 	}
141 
142 	connect(widget, &OptionWidget::commitData, this, &ConfigurationOptionDelegate::commitData);
143 
144 	return widget;
145 }
146 
ConfigurationContentsWidget(const QVariantMap & parameters,Window * window,QWidget * parent)147 ConfigurationContentsWidget::ConfigurationContentsWidget(const QVariantMap &parameters, Window *window, QWidget *parent) : ContentsWidget(parameters, window, parent),
148 	m_model(new QStandardItemModel(this)),
149 	m_ui(new Ui::ConfigurationContentsWidget)
150 {
151 	m_ui->setupUi(this);
152 	m_ui->filterLineEditWidget->setClearOnEscape(true);
153 
154 	NetworkManagerFactory::initialize();
155 
156 	const QMetaEnum metaEnum(SettingsManager::getInstance()->metaObject()->enumerator(SettingsManager::getInstance()->metaObject()->indexOfEnumerator(QLatin1String("OptionType").data())));
157 	const QStringList options(SettingsManager::getOptions());
158 	QStandardItem *groupItem(nullptr);
159 	bool canResetAll(false);
160 
161 	for (int i = 0; i < options.count(); ++i)
162 	{
163 		const int identifier(SettingsManager::getOptionIdentifier(options.at(i)));
164 		const QStringList option(options.at(i).split(QLatin1Char('/')));
165 		const QVariant value(SettingsManager::getOption(identifier));
166 		const SettingsManager::OptionDefinition definition(SettingsManager::getOptionDefinition(identifier));
167 
168 		if (!definition.flags.testFlag(SettingsManager::OptionDefinition::IsEnabledFlag) || !definition.flags.testFlag(SettingsManager::OptionDefinition::IsVisibleFlag))
169 		{
170 			continue;
171 		}
172 
173 		if (!groupItem || groupItem->text() != option.first())
174 		{
175 			groupItem = new QStandardItem(ThemesManager::createIcon(QLatin1String("inode-directory")), option.first());
176 
177 			m_model->appendRow(groupItem);
178 		}
179 
180 		QString type(metaEnum.valueToKey(definition.type));
181 		type.chop(4);
182 
183 		QList<QStandardItem*> optionItems({new QStandardItem(option.last()), new QStandardItem(type.toLower()), new QStandardItem((value.type() == QVariant::StringList) ? value.toStringList().join(QLatin1String(", ")) : value.toString())});
184 		optionItems[0]->setFlags(optionItems[0]->flags() | Qt::ItemNeverHasChildren);
185 		optionItems[1]->setFlags(optionItems[1]->flags() | Qt::ItemNeverHasChildren);
186 		optionItems[2]->setData(QSize(-1, 30), Qt::SizeHintRole);
187 		optionItems[2]->setData(identifier, IdentifierRole);
188 		optionItems[2]->setData(options.at(i), NameRole);
189 		optionItems[2]->setFlags(optionItems[2]->flags() | Qt::ItemNeverHasChildren);
190 
191 		if (definition.flags.testFlag(SettingsManager::OptionDefinition::RequiresRestartFlag))
192 		{
193 			optionItems[2]->setData(true, RequiresRestartRole);
194 		}
195 
196 		if (value != definition.defaultValue)
197 		{
198 			QFont font(optionItems[0]->font());
199 			font.setBold(true);
200 
201 			optionItems[0]->setFont(font);
202 
203 			if (identifier != SettingsManager::Browser_MigrationsOption)
204 			{
205 				canResetAll = true;
206 			}
207 		}
208 
209 		groupItem->appendRow(optionItems);
210 	}
211 
212 	m_model->setHorizontalHeaderLabels({tr("Name"), tr("Type"), tr("Value")});
213 	m_model->sort(0);
214 
215 	m_ui->configurationViewWidget->setViewMode(ItemViewWidget::TreeView);
216 	m_ui->configurationViewWidget->setModel(m_model);
217 	m_ui->configurationViewWidget->setLayoutDirection(Qt::LeftToRight);
218 	m_ui->configurationViewWidget->setItemDelegateForColumn(2, new ConfigurationOptionDelegate(this));
219 	m_ui->configurationViewWidget->setFilterRoles({Qt::DisplayRole, NameRole});
220 	m_ui->configurationViewWidget->installEventFilter(this);
221 	m_ui->resetAllButton->setEnabled(canResetAll);
222 
223 	if (isSidebarPanel())
224 	{
225 		m_ui->detailsWidget->hide();
226 	}
227 
228 	connect(SettingsManager::getInstance(), &SettingsManager::optionChanged, this, &ConfigurationContentsWidget::handleOptionChanged);
229 	connect(m_ui->configurationViewWidget, &ItemViewWidget::customContextMenuRequested, this, &ConfigurationContentsWidget::showContextMenu);
230 	connect(m_ui->configurationViewWidget, &ItemViewWidget::needsActionsUpdate, this, &ConfigurationContentsWidget::updateActions);
231 	connect(m_ui->configurationViewWidget, &ItemViewWidget::clicked, this, &ConfigurationContentsWidget::handleIndexClicked);
232 	connect(m_ui->configurationViewWidget, &ItemViewWidget::modified, [&]()
233 	{
234 		m_ui->resetAllButton->setEnabled(true);
235 		m_ui->saveAllButton->setEnabled(true);
236 	});
237 	connect(m_ui->configurationViewWidget->selectionModel(), &QItemSelectionModel::currentChanged, this, &ConfigurationContentsWidget::handleCurrentIndexChanged);
238 	connect(m_ui->filterLineEditWidget, &LineEditWidget::textChanged, m_ui->configurationViewWidget, &ItemViewWidget::setFilterString);
239 	connect(m_ui->resetAllButton, &QPushButton::clicked, [&]()
240 	{
241 		saveAll(true);
242 	});
243 	connect(m_ui->saveAllButton, &QPushButton::clicked, [&]()
244 	{
245 		saveAll(false);
246 	});
247 }
248 
~ConfigurationContentsWidget()249 ConfigurationContentsWidget::~ConfigurationContentsWidget()
250 {
251 	delete m_ui;
252 }
253 
changeEvent(QEvent * event)254 void ConfigurationContentsWidget::changeEvent(QEvent *event)
255 {
256 	ContentsWidget::changeEvent(event);
257 
258 	if (event->type() == QEvent::LanguageChange)
259 	{
260 		m_ui->retranslateUi(this);
261 
262 		m_model->setHorizontalHeaderLabels({tr("Name"), tr("Type"), tr("Value")});
263 	}
264 }
265 
triggerAction(int identifier,const QVariantMap & parameters,ActionsManager::TriggerType trigger)266 void ConfigurationContentsWidget::triggerAction(int identifier, const QVariantMap &parameters, ActionsManager::TriggerType trigger)
267 {
268 	switch (identifier)
269 	{
270 		case ActionsManager::FindAction:
271 		case ActionsManager::QuickFindAction:
272 			m_ui->filterLineEditWidget->setFocus();
273 
274 			break;
275 		case ActionsManager::ActivateContentAction:
276 			m_ui->configurationViewWidget->setFocus();
277 
278 			break;
279 		default:
280 			ContentsWidget::triggerAction(identifier, parameters, trigger);
281 
282 			break;
283 	}
284 }
285 
closeEvent(QCloseEvent * event)286 void ConfigurationContentsWidget::closeEvent(QCloseEvent *event)
287 {
288 	if (m_ui->configurationViewWidget->isModified())
289 	{
290 		const int result(QMessageBox::question(this, tr("Question"), tr("The settings have been changed.\nDo you want to save them?"), QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel));
291 
292 		if (result == QMessageBox::Cancel)
293 		{
294 			event->ignore();
295 
296 			return;
297 		}
298 
299 		if (result == QMessageBox::Yes)
300 		{
301 			saveAll(false);
302 		}
303 	}
304 
305 	event->accept();
306 }
307 
print(QPrinter * printer)308 void ConfigurationContentsWidget::print(QPrinter *printer)
309 {
310 	m_ui->configurationViewWidget->render(printer);
311 }
312 
copyOptionName()313 void ConfigurationContentsWidget::copyOptionName()
314 {
315 	const QModelIndex index(m_ui->configurationViewWidget->currentIndex().sibling(m_ui->configurationViewWidget->currentIndex().row(), 2));
316 
317 	if (index.isValid())
318 	{
319 		QApplication::clipboard()->setText(index.data(NameRole).toString());
320 	}
321 }
322 
copyOptionValue()323 void ConfigurationContentsWidget::copyOptionValue()
324 {
325 	const QModelIndex index(m_ui->configurationViewWidget->currentIndex().sibling(m_ui->configurationViewWidget->currentIndex().row(), 2));
326 
327 	if (index.isValid())
328 	{
329 		QApplication::clipboard()->setText(index.data(Qt::EditRole).toString());
330 	}
331 }
332 
resetOption()333 void ConfigurationContentsWidget::resetOption()
334 {
335 	const QModelIndex index(m_ui->configurationViewWidget->currentIndex().sibling(m_ui->configurationViewWidget->currentIndex().row(), 2));
336 
337 	if (index.isValid())
338 	{
339 		SettingsManager::setOption(index.data(IdentifierRole).toInt(), SettingsManager::getOptionDefinition(index.data(IdentifierRole).toInt()).defaultValue);
340 
341 		m_model->setData(index.sibling(index.row(), 0), false, IsModifiedRole);
342 
343 		updateActions();
344 	}
345 }
346 
saveOption()347 void ConfigurationContentsWidget::saveOption()
348 {
349 	const QModelIndex index(m_ui->configurationViewWidget->currentIndex().sibling(m_ui->configurationViewWidget->currentIndex().row(), 2));
350 
351 	if (index.isValid())
352 	{
353 		SettingsManager::setOption(index.data(IdentifierRole).toInt(), index.data(Qt::EditRole));
354 
355 		m_model->setData(index.sibling(index.row(), 0), false, IsModifiedRole);
356 
357 		updateActions();
358 	}
359 }
360 
saveAll(bool reset)361 void ConfigurationContentsWidget::saveAll(bool reset)
362 {
363 	if (reset && QMessageBox::question(this, tr("Question"), tr("Do you really want to restore default values of all options?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
364 	{
365 		return;
366 	}
367 
368 	for (int i = 0; i < m_model->rowCount(); ++i)
369 	{
370 		const QModelIndex groupIndex(m_model->index(i, 0));
371 		const int optionAmount(m_model->rowCount(groupIndex));
372 
373 		if (optionAmount == 0)
374 		{
375 			continue;
376 		}
377 
378 		for (int j = 0; j < optionAmount; ++j)
379 		{
380 			const QModelIndex optionIndex(groupIndex.child(j, 0));
381 			const bool isModified(optionIndex.data(IsModifiedRole).toBool());
382 
383 			if (reset || isModified)
384 			{
385 				const QModelIndex valueIndex(groupIndex.child(j, 2));
386 				const int identifier(valueIndex.data(IdentifierRole).toInt());
387 				const QVariant defaultValue(SettingsManager::getOptionDefinition(identifier).defaultValue);
388 
389 				if (reset && identifier != SettingsManager::Browser_MigrationsOption && valueIndex.data(Qt::EditRole) != defaultValue)
390 				{
391 					SettingsManager::setOption(identifier, defaultValue);
392 
393 					QFont font(optionIndex.data(Qt::FontRole).isNull() ? m_ui->configurationViewWidget->font() : optionIndex.data(Qt::FontRole).value<QFont>());
394 					font.setBold(false);
395 
396 					m_model->setData(optionIndex, font, Qt::FontRole);
397 					m_model->setData(valueIndex, defaultValue, Qt::EditRole);
398 				}
399 				else if (!reset && isModified)
400 				{
401 					SettingsManager::setOption(identifier, valueIndex.data(Qt::EditRole));
402 				}
403 
404 				if (isModified)
405 				{
406 					m_model->setData(optionIndex, false, IsModifiedRole);
407 				}
408 			}
409 		}
410 	}
411 
412 	m_ui->configurationViewWidget->setModified(false);
413 	m_ui->saveAllButton->setEnabled(false);
414 
415 	if (reset)
416 	{
417 		m_ui->resetAllButton->setEnabled(false);
418 	}
419 
420 	connect(m_ui->configurationViewWidget, &ItemViewWidget::needsActionsUpdate, this, &ConfigurationContentsWidget::updateActions);
421 
422 	updateActions();
423 }
424 
handleOptionChanged(int identifier,const QVariant & value)425 void ConfigurationContentsWidget::handleOptionChanged(int identifier, const QVariant &value)
426 {
427 	const QString name(SettingsManager::getOptionName(identifier));
428 	const bool wasModified(m_ui->configurationViewWidget->isModified());
429 
430 	for (int i = 0; i < m_model->rowCount(); ++i)
431 	{
432 		const QModelIndex groupIndex(m_model->index(i, 0));
433 		const QString groupTitle(groupIndex.data(Qt::DisplayRole).toString());
434 
435 		if (groupTitle.isEmpty() || !name.startsWith(groupTitle))
436 		{
437 			continue;
438 		}
439 
440 		const int optionAmount(m_model->rowCount(groupIndex));
441 
442 		for (int j = 0; j < optionAmount; ++j)
443 		{
444 			const QModelIndex valueIndex(groupIndex.child(j, 2));
445 
446 			if (valueIndex.data(IdentifierRole).toInt() == identifier)
447 			{
448 				const QModelIndex optionIndex(groupIndex.child(j, 0));
449 
450 				if (!optionIndex.data(IsModifiedRole).toBool())
451 				{
452 					QFont font(optionIndex.data(Qt::FontRole).isNull() ? m_ui->configurationViewWidget->font() : optionIndex.data(Qt::FontRole).value<QFont>());
453 					font.setBold(value != SettingsManager::getOptionDefinition(identifier).defaultValue);
454 
455 					m_model->setData(optionIndex, font, Qt::FontRole);
456 					m_model->setData(valueIndex, value, Qt::EditRole);
457 				}
458 
459 				break;
460 			}
461 		}
462 	}
463 
464 	if (!wasModified)
465 	{
466 		m_ui->configurationViewWidget->setModified(false);
467 	}
468 
469 	updateActions();
470 }
471 
handleCurrentIndexChanged(const QModelIndex & currentIndex,const QModelIndex & previousIndex)472 void ConfigurationContentsWidget::handleCurrentIndexChanged(const QModelIndex &currentIndex, const QModelIndex &previousIndex)
473 {
474 	if (previousIndex.parent().isValid() && previousIndex.column() == 2)
475 	{
476 		m_ui->configurationViewWidget->closePersistentEditor(previousIndex);
477 	}
478 
479 	if (currentIndex.parent().isValid() && currentIndex.column() == 2)
480 	{
481 		m_ui->configurationViewWidget->openPersistentEditor(currentIndex);
482 	}
483 }
484 
handleIndexClicked(const QModelIndex & index)485 void ConfigurationContentsWidget::handleIndexClicked(const QModelIndex &index)
486 {
487 	if (index.parent().isValid() && index.column() != 2)
488 	{
489 		m_ui->configurationViewWidget->setCurrentIndex(index.sibling(index.row(), 2));
490 	}
491 }
492 
showContextMenu(const QPoint & position)493 void ConfigurationContentsWidget::showContextMenu(const QPoint &position)
494 {
495 	const QModelIndex index(m_ui->configurationViewWidget->indexAt(position));
496 	QMenu menu(this);
497 
498 	if (index.isValid() && index.parent() != m_ui->configurationViewWidget->rootIndex())
499 	{
500 		menu.addAction(tr("Copy Option Name"), this, &ConfigurationContentsWidget::copyOptionName);
501 		menu.addAction(tr("Copy Option Value"), this, &ConfigurationContentsWidget::copyOptionValue);
502 		menu.addSeparator();
503 		menu.addAction(tr("Save Value"), this, &ConfigurationContentsWidget::saveOption)->setEnabled(index.sibling(index.row(), 0).data(IsModifiedRole).toBool());
504 		menu.addAction(tr("Restore Default Value"), this, &ConfigurationContentsWidget::resetOption)->setEnabled(index.sibling(index.row(), 2).data(Qt::EditRole) != SettingsManager::getOptionDefinition(index.sibling(index.row(), 2).data(IdentifierRole).toInt()).defaultValue);
505 		menu.addSeparator();
506 	}
507 
508 	menu.addAction(tr("Expand All"), m_ui->configurationViewWidget, &ItemViewWidget::expandAll);
509 	menu.addAction(tr("Collapse All"), m_ui->configurationViewWidget, &ItemViewWidget::collapseAll);
510 	menu.exec(m_ui->configurationViewWidget->mapToGlobal(position));
511 }
512 
updateActions()513 void ConfigurationContentsWidget::updateActions()
514 {
515 	const QModelIndex index(m_ui->configurationViewWidget->selectionModel()->hasSelection() ? m_ui->configurationViewWidget->currentIndex().sibling(m_ui->configurationViewWidget->currentIndex().row(), 2) : QModelIndex());
516 	const int identifier(index.data(IdentifierRole).toInt());
517 
518 	if (identifier >= 0 && index.parent().isValid())
519 	{
520 		m_ui->nameLabelWidget->setText(SettingsManager::getOptionName(identifier));
521 		m_ui->currentValueLabelWidget->setText(SettingsManager::createDisplayValue(identifier, SettingsManager::getOption(identifier)));
522 		m_ui->defaultValueLabelWidget->setText(SettingsManager::createDisplayValue(identifier, SettingsManager::getOptionDefinition(identifier).defaultValue));
523 	}
524 	else
525 	{
526 		m_ui->nameLabelWidget->clear();
527 		m_ui->currentValueLabelWidget->clear();
528 		m_ui->defaultValueLabelWidget->clear();
529 	}
530 }
531 
getTitle() const532 QString ConfigurationContentsWidget::getTitle() const
533 {
534 	return tr("Advanced Configuration");
535 }
536 
getType() const537 QLatin1String ConfigurationContentsWidget::getType() const
538 {
539 	return QLatin1String("config");
540 }
541 
getUrl() const542 QUrl ConfigurationContentsWidget::getUrl() const
543 {
544 	return QUrl(QLatin1String("about:config"));
545 }
546 
getIcon() const547 QIcon ConfigurationContentsWidget::getIcon() const
548 {
549 	return ThemesManager::createIcon(QLatin1String("configuration"), false);
550 }
551 
eventFilter(QObject * object,QEvent * event)552 bool ConfigurationContentsWidget::eventFilter(QObject *object, QEvent *event)
553 {
554 	if (object == m_ui->configurationViewWidget && event->type() == QEvent::KeyPress)
555 	{
556 		const QModelIndex index(m_ui->configurationViewWidget->currentIndex());
557 
558 		if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Right && index.parent().isValid())
559 		{
560 			m_ui->configurationViewWidget->setCurrentIndex(index.sibling(index.row(), 2));
561 		}
562 	}
563 
564 	return ContentsWidget::eventFilter(object, event);
565 }
566 
567 }
568