/*************************************************************************** * Copyright (C) 2002-2004 by Alexander Dymo * * cloudtemple@mskat.net * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "propertyeditor.h" #ifndef PURE_QT #include #include #include #else #include "compat_tools.h" #endif #include #include #include #include #include #include #include "property.h" #include "multiproperty.h" #include "propertymachinefactory.h" namespace PropertyLib{ class PropertyItem: public KListViewItem{ public: PropertyItem(PropertyEditor *parent, MultiProperty *property) :KListViewItem(parent, property->description()), m_editor(parent), m_property(property), m_changed(false) { } PropertyItem(PropertyEditor *editor, KListViewItem *parent, MultiProperty *property) :KListViewItem(parent, property->description()), m_editor(editor), m_property(property), m_changed(false) { } /* int type() const { return m_property->type(); } QString name() const { return m_property->name(); } */ MultiProperty *property() const { return m_property; } virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align) { if ((column == 0) && m_changed) { QFont font; font.setBold(true); p->setFont(font); p->setBrush(cg.highlight()); p->setPen(cg.highlightedText()); } if (column == 1) { QRect r(0, 0, m_editor->header()->sectionSize(1), height()); //FIXME: this is ugly, but how else can we deal with ValueFromList properties? QVariant valueToDraw; if (m_property->type() == Property::ValueFromList) valueToDraw = m_property->findValueDescription(); else valueToDraw = m_property->value(); QColorGroup icg(cg); #ifndef PURE_QT icg.setColor(QColorGroup::Background, backgroundColor()); #else icg.setColor(QColorGroup::Background, white); #endif m_editor->machine(m_property)->propertyEditor->drawViewer(p, icg, r, valueToDraw); return; } KListViewItem::paintCell(p, cg, column, width, align); } virtual void setup() { KListViewItem::setup(); setHeight(static_cast(height()*1.5)); } void setChanged(bool changed) { m_changed = changed; } private: PropertyEditor *m_editor; MultiProperty *m_property; bool m_changed; }; class PropertyGroupItem: public KListViewItem{ public: PropertyGroupItem(KListView *parent, const QString &name) :KListViewItem(parent, name) { init(); } PropertyGroupItem(KListViewItem *parent, const QString &name) :KListViewItem(parent, name) { init(); } virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align) { if (column == 0) { QFont font; font.setBold(true); p->setFont(font); p->setBrush(cg.highlight()); p->setPen(cg.highlightedText()); } KListViewItem::paintCell(p, cg, column, width, align); } virtual void setup() { KListViewItem::setup(); setHeight(static_cast(height()*1.4)); } private: void init() { setOpen(true); } }; class SeparatorItem: public KListViewItem{ public: SeparatorItem(KListView *parent) :KListViewItem(parent) { setSelectable(false); } }; PropertyEditor::PropertyEditor(QWidget *parent, const char *name) :KListView(parent, name) { setSorting(-1); addColumn(i18n("Name")); addColumn(i18n("Value")); setAllColumnsShowFocus(true); setColumnWidthMode(0, QListView::Maximum); setResizeMode(QListView::LastColumn); header()->setClickEnabled(false); connect(header(), SIGNAL(sizeChange(int, int, int)), this, SLOT(updateEditorSize())); connect(this, SIGNAL(currentChanged(QListViewItem*)), this, SLOT(slotClicked(QListViewItem*))); m_currentEditItem = 0; m_doubleClickForEdit = true; m_lastClickedItem = 0; m_currentEditWidget = 0; m_list = 0; m_currentEditArea = new QWidget(viewport()); m_currentEditArea->hide(); m_undoButton = new QPushButton(m_currentEditArea); #ifndef PURE_QT m_undoButton->setPixmap(SmallIcon("undo")); #else m_undoButton->setPixmap( QPixmap("undo.xpm") ); #endif m_undoButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding); m_undoButton->resize(m_undoButton->height(), m_undoButton->height()); m_undoButton->hide(); connect(m_undoButton, SIGNAL(clicked()), this, SLOT(undo())); m_currentEditLayout = new QGridLayout(m_currentEditArea, 1, 2, 0, 0); // m_currentEditLayout->addWidget(m_undoButton, 0, 1); } PropertyEditor::~PropertyEditor() { clearMachineCache(); } void PropertyEditor::populateProperties(PropertyList *list) { if (list == 0) return; m_list = list; connect(m_list, SIGNAL(propertyValueChanged(Property*)), this, SLOT(propertyValueChanged(Property*))); const QValueList > >& groups = m_list->propertiesOfGroup(); for (QValueList > >::const_iterator it = groups.begin(); it != groups.end(); ++it) { // qWarning("PropertyEditor::populateProperties: adding group %s", (*it).first.ascii()); PropertyGroupItem *group = 0; if ( (!(*it).first.isEmpty()) && ((*it).second.count() > 0) ) group = new PropertyGroupItem(this, (*it).first); const QValueList &properties = (*it).second; for (QValueList::const_iterator it2 = properties.begin(); it2 != properties.end(); ++it2) { // qWarning("PropertyEditor::populateProperties: adding property %s", (*it2).ascii()); if (group) addProperty(group, *it2); else addProperty(*it2); } } if (firstChild()) { setCurrentItem(firstChild()); setSelected(firstChild(), true); slotClicked(firstChild()); } } void PropertyEditor::addProperty(PropertyGroupItem *group, const QString &name) { if ((*m_list)[name] == 0) return; // qWarning("%s = name : object null ", name.ascii()); PropertyItem *pitem = new PropertyItem(this, group, (*m_list)[name]); addChildProperties(pitem); } void PropertyEditor::addProperty(const QString &name) { if ((*m_list)[name] == 0) return; // qWarning("%s = name : object null ", name.ascii()); PropertyItem *pitem = new PropertyItem(this, (*m_list)[name]); addChildProperties(pitem); } void PropertyEditor::addChildProperties(PropertyItem *parent) { MultiProperty *prop = parent->property(); //force machine creation to get detailed properties appended to current multiproperty if ( !m_registeredForType.contains(prop->name()) && (PropertyMachineFactory::getInstance()->hasDetailedEditors(prop->type())) ) { //FIXME: find better solution machine(prop); } // qWarning("seeking children: count: %d", prop->details.count()); parent->setOpen(true); for (QValueList::iterator it = prop->details.begin(); it != prop->details.end(); ++it) { // qWarning("found child %s", (*it).name().ascii()); new PropertyItem(this, parent, new MultiProperty(&m_detailedList, &(*it))); } } void PropertyEditor::clearProperties() { m_detailedList.clear(); if (!m_list) return; hideEditor(); disconnect(m_list, SIGNAL(propertyValueChanged(Property*)), this, SLOT(propertyValueChanged(Property*))); clear(); delete m_list; m_list = 0; } void PropertyEditor::propertyValueChanged(Property *property) { // qWarning("PropertyEditor::propertyValueChanged"); if (m_currentEditWidget->propertyName() == property->name()) m_currentEditWidget->setValue(property->value(), false); else { // repaint all items QListViewItemIterator it(this); while (it.current()) { repaintItem(it.current()); ++it; } } } void PropertyEditor::propertyChanged(MultiProperty *property, const QVariant &value) { if (!property) return; qWarning("editor: assign %s to %s", property->name().latin1(), value.toString().latin1()); property->setValue(value, false); //highlight changed properties if (m_currentEditItem && (m_currentEditItem->property() == property)) { m_currentEditItem->setChanged(true); repaintItem(m_currentEditItem); } emit changed(); /* if (m_list->contains(name)) { (*m_list)[name]->setValue(value, false); // else if (m_detailedList->contains(*/ } void PropertyEditor::hideEditor() { m_lastClickedItem = 0; m_currentEditItem = 0; if (m_currentEditWidget) { m_currentEditLayout->remove(m_currentEditWidget); m_currentEditWidget->hide(); } m_currentEditLayout->remove(m_undoButton); m_undoButton->hide(); m_currentEditArea->hide(); m_currentEditWidget = 0; } void PropertyEditor::showEditor(PropertyItem *item) { m_currentEditItem = item; placeEditor(item); m_currentEditWidget->show(); m_undoButton->show(); m_currentEditArea->show(); } void PropertyEditor::placeEditor(PropertyItem *item) { QRect r = itemRect(item); if (!r.size().isValid()) { ensureItemVisible(item); r = itemRect(item); } r.setX(header()->sectionPos(1)); r.setWidth(header()->sectionSize(1)); // check if the column is fully visible if (visibleWidth() < r.right()) r.setRight(visibleWidth()); r = QRect(viewportToContents(r.topLeft()), r.size()); if (item->pixmap(1)) { r.setX(r.x() + item->pixmap(1)->width()); } if (PropertyWidget* editor = prepareEditor(item)) { m_currentEditLayout->addWidget(editor, 0, 0); m_currentEditLayout->addWidget(m_undoButton, 0, 1); m_currentEditArea->resize(r.size()); // m_currentEditLayout->invalidate(); moveChild(m_currentEditArea, r.x(), r.y()); m_currentEditWidget = editor; } } PropertyWidget* PropertyEditor::prepareEditor(PropertyItem *item) { PropertyWidget *editorWidget = 0; /* if (item->depth() >= 2) { editorWidget = machine(item->name())->propertyEditor; editorWidget->setValue(m_accessor->value(item->name()), false); } else {*/ editorWidget = machine(item->property())->propertyEditor; editorWidget->setProperty(item->property()); if (item->property()->type() == Property::ValueFromList) editorWidget->setValueList(item->property()->valueList()); editorWidget->setValue(item->property()->value(), false); //} return editorWidget; } void PropertyEditor::updateEditorSize() { if (m_currentEditItem) placeEditor(m_currentEditItem); } void PropertyEditor::slotClicked(QListViewItem *item) { if (item == 0) { hideEditor(); return; } if (item != m_lastClickedItem) { hideEditor(); PropertyItem *it = dynamic_cast(item); if (it) { showEditor(it); } } m_lastClickedItem = item; } Machine *PropertyEditor::machine(MultiProperty *property) { // int type = property->type(); QString name = property->name(); QMap values = property->valueList(); if (m_registeredForType[name] == 0) { m_registeredForType[name] = PropertyMachineFactory::getInstance()->machineForProperty(property); connect(m_registeredForType[name]->propertyEditor, SIGNAL(propertyChanged(MultiProperty*, const QVariant&)), this, SLOT(propertyChanged(MultiProperty*, const QVariant&))); m_registeredForType[name]->propertyEditor->reparent(m_currentEditArea, 0, m_currentEditArea->childrenRect().topLeft()); m_registeredForType[name]->propertyEditor->hide(); } return m_registeredForType[name]; } void PropertyEditor::clearMachineCache() { for (QMap::iterator it = m_registeredForType.begin(); it != m_registeredForType.end(); ++it) { delete it.data(); } m_registeredForType.clear(); } void PropertyEditor::undo() { if ((m_currentEditItem == 0) || (m_currentEditWidget == 0) || (!m_currentEditWidget->isVisible())) return; m_currentEditWidget->undo(); m_currentEditItem->setChanged(false); repaintItem(m_currentEditItem); } } #ifndef PURE_QT #include "propertyeditor.moc" #endif