package SettingsTree; use strict; use warnings; use QtCore4; use QtGui4; use QtCore4::isa qw( Qt::TreeWidget ); use QtCore4::slots setAutoRefresh => ['bool'], setFallbacksEnabled => ['bool'], maybeRefresh => [], refresh => [], updateSetting => ['QTreeWidgetItem *']; use VariantDelegate; sub settings() { return this->{settings}; } sub refreshTimer() { return this->{refreshTimer}; } sub autoRefresh() { return this->{autoRefresh}; } sub groupIcon() { return this->{groupIcon}; } sub keyIcon() { return this->{keyIcon}; } sub NEW { my ($class, $parent) = @_; $class->SUPER::NEW($parent); setItemDelegate(VariantDelegate(this)); my @labels = (this->tr('Setting'), this->tr('Type'), this->tr('Value')); setHeaderLabels(\@labels); header()->setResizeMode(0, Qt::HeaderView::Stretch()); header()->setResizeMode(2, Qt::HeaderView::Stretch()); this->{settings} = undef; this->{refreshTimer} = Qt::Timer(this); refreshTimer->setInterval(2000); this->{autoRefresh} = 0; this->{groupIcon} = Qt::Icon(); this->{keyIcon} = Qt::Icon(); groupIcon->addPixmap(style()->standardPixmap(Qt::Style::SP_DirClosedIcon()), Qt::Icon::Normal(), Qt::Icon::Off()); groupIcon->addPixmap(style()->standardPixmap(Qt::Style::SP_DirOpenIcon()), Qt::Icon::Normal(), Qt::Icon::On()); keyIcon->addPixmap(style()->standardPixmap(Qt::Style::SP_FileIcon())); this->connect(refreshTimer(), SIGNAL 'timeout()', this, SLOT 'maybeRefresh()'); } sub setSettingsObject { my ($settings) = @_; if ( defined this->settings() ) { this->settings->setParent(undef); } this->{settings} = $settings; clear(); if ($settings) { $settings->setParent(this); refresh(); if (autoRefresh) { refreshTimer->start(); } } else { refreshTimer->stop(); } } sub sizeHint { return Qt::Size(800, 600); } sub setAutoRefresh { my ($autoRefresh) = @_; this->{autoRefresh} = $autoRefresh; if (settings()) { if (autoRefresh()) { maybeRefresh(); refreshTimer->start(); } else { refreshTimer->stop(); } } } sub setFallbacksEnabled { my ($enabled) = @_; if (settings()) { settings()->setFallbacksEnabled($enabled); refresh(); } } sub maybeRefresh { if (state() != Qt::TreeWidget::EditingState()) { refresh(); } } sub refresh { if (!settings()) { return; } disconnect(this, SIGNAL 'itemChanged(QTreeWidgetItem*,int)', this, SLOT 'updateSetting(QTreeWidgetItem*)'); settings->sync(); updateChildItems(undef); this->connect(this, SIGNAL 'itemChanged(QTreeWidgetItem*,int)', this, SLOT 'updateSetting(QTreeWidgetItem*)'); } sub event { my ($event) = @_; if ($event->type() == Qt::Event::WindowActivate()) { if (isActiveWindow() && autoRefresh) { maybeRefresh(); } } return this->SUPER::event($event); } sub updateSetting { my ($item) = @_; my $key = $item->text(0); my $ancestor = $item->parent(); while ($ancestor) { $key = $ancestor->text(0) . '/' . $key; $ancestor = $ancestor->parent(); } settings->setValue($key, $item->data(2, Qt::UserRole())); if (autoRefresh) { refresh(); } } sub updateChildItems { my ($parent) = @_; my $dividerIndex = 0; foreach my $group ( @{settings->childGroups()} ) { my $child = Qt::TreeWidgetItem(); my $childIndex = findChild($parent, $group, $dividerIndex); if ($childIndex != -1) { $child = childAt($parent, $childIndex); $child->setText(1, ''); $child->setText(2, ''); $child->setData(2, Qt::UserRole(), Qt::Variant()); moveItemForward($parent, $childIndex, $dividerIndex); } else { $child = createItem($group, $parent, $dividerIndex); } $child->setIcon(0, groupIcon); ++$dividerIndex; settings->beginGroup($group); updateChildItems($child); settings->endGroup(); } foreach my $key ( @{settings->childKeys()} ) { my $child = Qt::TreeWidgetItem(); my $childIndex = findChild($parent, $key, 0); if ($childIndex == -1 || $childIndex >= $dividerIndex) { if ($childIndex != -1) { $child = childAt($parent, $childIndex); for (my $i = 0; $i < $child->childCount(); ++$i) { childAt($child, $i)->DESTROY(); } moveItemForward($parent, $childIndex, $dividerIndex); } else { $child = createItem($key, $parent, $dividerIndex); } $child->setIcon(0, keyIcon); ++$dividerIndex; } else { $child = childAt($parent, $childIndex); } my $value = settings->value($key); if ($value->type() == Qt::Variant::Invalid()) { $child->setText(1, 'Invalid'); } else { $child->setText(1, $value->typeName()); } $child->setText(2, VariantDelegate::displayText($value)); $child->setData(2, Qt::UserRole(), $value); } while ($dividerIndex < childCount($parent)) { childAt($parent, $dividerIndex)->DESTROY(); } } sub createItem { my ($text, $parent, $index) = @_; my $after = 0; if ($index != 0) { $after = childAt($parent, $index - 1); } my $item = Qt::TreeWidgetItem(); if ($parent) { $item = Qt::TreeWidgetItem($parent, $after); } else { $item = Qt::TreeWidgetItem(this, $after); } $item->setText(0, $text); $item->setFlags($item->flags() | Qt::ItemIsEditable()); return $item; } sub childAt { my ($parent, $index) = @_; if ($parent) { return $parent->child($index); } else { return topLevelItem($index); } } sub childCount { my ($parent) = @_; if ($parent) { return $parent->childCount(); } else { return topLevelItemCount(); } } sub findChild { my ($parent, $text, $startIndex) = @_; for (my $i = $startIndex; $i < childCount($parent); ++$i) { if (childAt($parent, $i)->text(0) eq $text) { return $i; } } return -1; } sub moveItemForward { my ($parent, $oldIndex, $newIndex) = @_; for (my $i = 0; $i < $oldIndex - $newIndex; ++$i) { # XXX delete childAt($parent, $newIndex); } } 1;