1 /***************************************************************************
2  *   Copyright (C) 2004-2005 by David Saxton                               *
3  *   david@bluehaze.org                                                    *
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 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  ***************************************************************************/
10 
11 #include "circuitview.h"
12 #include "colorcombo.h"
13 #include "contexthelp.h"
14 #include "cnitem.h"
15 #include "cnitemgroup.h"
16 #include "doublespinbox.h"
17 #include "itemdocument.h"
18 #include "itemeditor.h"
19 #include "iteminterface.h"
20 #include "itemview.h"
21 #include "ktechlab.h"
22 #include "lineedit.h"
23 
24 #include <KComboBox>
25 #include <KUrlRequester>
26 #include <KToolBar>
27 #include <KXMLGUIFactory>
28 
29 #include <QDebug>
30 #include <QApplication>
31 #include <QLabel>
32 #include <QCheckBox>
33 #include <QSpinBox>
34 
35 #include <cassert>
36 
37 ItemInterface * ItemInterface::m_pSelf = nullptr;
38 
self()39 ItemInterface * ItemInterface::self()
40 {
41 	if ( !m_pSelf )
42 		m_pSelf = new ItemInterface();
43 
44 	return m_pSelf;
45 }
46 
47 
ItemInterface()48 ItemInterface::ItemInterface()
49 	: QObject( KTechlab::self() )
50     , m_isInTbDataChanged(false)
51 {
52 	m_pActiveItemEditorToolBar = nullptr;
53 	p_cvb = nullptr;
54 	p_itemGroup = nullptr;
55 	p_lastItem = nullptr;
56 	m_currentActionTicket = -1;
57 	m_toolBarWidgetID = -1;
58 }
59 
60 
~ItemInterface()61 ItemInterface::~ItemInterface()
62 {
63 }
64 
65 
slotGetActionTicket()66 void ItemInterface::slotGetActionTicket()
67 {
68 	m_currentActionTicket = p_cvb ? p_cvb->getActionTicket() : -1;
69 }
70 
71 
slotItemDocumentChanged(ItemDocument * doc)72 void ItemInterface::slotItemDocumentChanged( ItemDocument * doc )
73 {
74 	slotClearAll();
75 	if ( ItemDocument * itemDocument = dynamic_cast<ItemDocument*>((Document*)p_cvb) )
76 	{
77 		disconnect( itemDocument, SIGNAL(selectionChanged()), this, SLOT(slotUpdateItemInterface()) );
78 	}
79 
80 	p_itemGroup = nullptr;
81 	p_cvb = doc;
82 
83 	slotGetActionTicket();
84 
85 	if (!p_cvb)
86 		return;
87 
88 	connect( p_cvb, SIGNAL(selectionChanged()), this, SLOT(slotUpdateItemInterface()) );
89 
90 	p_itemGroup = p_cvb->selectList();
91 
92 	slotUpdateItemInterface();
93 }
94 
95 
clearItemEditorToolBar()96 void ItemInterface::clearItemEditorToolBar()
97 {
98 	if ( m_pActiveItemEditorToolBar && m_toolBarWidgetID != -1 ) {
99 		//m_pActiveItemEditorToolBar->removeItem(m_toolBarWidgetID); // TODO add proper replacmenet
100         m_pActiveItemEditorToolBar->clear();
101     }
102 	m_toolBarWidgetID = -1;
103 	itemEditTBCleared();
104 }
105 
106 
slotClearAll()107 void ItemInterface::slotClearAll()
108 {
109 	ContextHelp::self()->slotClear();
110 	ItemEditor::self()->slotClear();
111 	clearItemEditorToolBar();
112 	p_lastItem = nullptr;
113 }
114 
115 
slotMultipleSelected()116 void ItemInterface::slotMultipleSelected()
117 {
118 	ContextHelp::self()->slotMultipleSelected();
119 	ItemEditor::self()->slotMultipleSelected();
120 	clearItemEditorToolBar();
121 	p_lastItem = nullptr;
122 }
123 
124 
slotUpdateItemInterface()125 void ItemInterface::slotUpdateItemInterface()
126 {
127 	if (!p_itemGroup)
128 		return;
129 
130 	slotGetActionTicket();
131 	updateItemActions();
132 
133 	if (!p_itemGroup->itemsAreSameType() )
134 	{
135 		slotMultipleSelected();
136 		return;
137 	}
138 	if ( p_lastItem && p_itemGroup->activeItem() )
139 	{
140 		ItemEditor::self()->itemGroupUpdated( p_itemGroup );
141 		return;
142 	}
143 
144 	p_lastItem = p_itemGroup->activeItem();
145 	if (!p_lastItem)
146 	{
147 		slotClearAll();
148 		return;
149 	}
150 
151 	ContextHelp::self()->slotUpdate(p_lastItem);
152 	ItemEditor::self()->slotUpdate(p_itemGroup);
153 	if ( CNItem * cnItem = dynamic_cast<CNItem*>((Item*)p_lastItem) )
154 	{
155 		ItemEditor::self()->slotUpdate(cnItem);
156 	}
157 
158 	// Update item editor toolbar
159 	if ( ItemView * itemView = dynamic_cast<ItemView*>(p_cvb->activeView()) )
160 	{
161 		if ( KTechlab * ktl = KTechlab::self() )
162 		{
163 			if ( (m_pActiveItemEditorToolBar = dynamic_cast<KToolBar*>(ktl->factory()->container("itemEditorTB",itemView)) ) )
164 			{
165 				//m_pActiveItemEditorToolBar->setFullSize( true ); // TODO proper replacement
166                 m_pActiveItemEditorToolBar->adjustSize();
167 				QWidget * widget = configWidget();
168 				m_toolBarWidgetID = 1;
169 				// m_pActiveItemEditorToolBar->insertWidget( m_toolBarWidgetID, 0, widget ); // TODO properly fix
170                 m_pActiveItemEditorToolBar->addWidget( widget );
171 			}
172 		}
173 	}
174 }
175 
176 
updateItemActions()177 void ItemInterface::updateItemActions()
178 {
179 	ItemView * itemView = ((ItemDocument*)p_cvb) ? dynamic_cast<ItemView*>(p_cvb->activeView()) : nullptr;
180 	if ( !itemView )
181 		return;
182 
183 	bool itemsSelected = p_itemGroup && p_itemGroup->itemCount();
184 
185 	itemView->actionByName("edit_raise")->setEnabled(itemsSelected);
186 	itemView->actionByName("edit_lower")->setEnabled(itemsSelected);
187 
188 	if ( KTechlab::self() )
189 	{
190 		KTechlab::self()->actionByName("edit_cut")->setEnabled(itemsSelected);
191 		KTechlab::self()->actionByName("edit_copy")->setEnabled(itemsSelected);
192 	}
193 
194 	CNItemGroup * cnItemGroup = dynamic_cast<CNItemGroup*>((ItemGroup*)p_itemGroup);
195 	CircuitView * circuitView = dynamic_cast<CircuitView*>(itemView);
196 
197 	if ( cnItemGroup && circuitView  )
198 	{
199 		bool canFlip = cnItemGroup->canFlip();
200 		circuitView->actionByName("edit_flip_horizontally")->setEnabled( canFlip );
201 		circuitView->actionByName("edit_flip_vertically")->setEnabled( canFlip );
202 
203 		bool canRotate = cnItemGroup->canRotate();
204 		circuitView->actionByName("edit_rotate_ccw")->setEnabled( canRotate );
205 		circuitView->actionByName("edit_rotate_cw")->setEnabled( canRotate );
206 	}
207 }
208 
209 
setFlowPartOrientation(unsigned orientation)210 void ItemInterface::setFlowPartOrientation( unsigned orientation )
211 {
212 	CNItemGroup *cnItemGroup = dynamic_cast<CNItemGroup*>((ItemGroup*)p_itemGroup);
213 	if (!cnItemGroup)
214 		return;
215 
216 	cnItemGroup->setFlowPartOrientation( orientation );
217 }
218 
219 
setComponentOrientation(int angleDegrees,bool flipped)220 void ItemInterface::setComponentOrientation( int angleDegrees, bool flipped )
221 {
222 	CNItemGroup *cnItemGroup = dynamic_cast<CNItemGroup*>((ItemGroup*)p_itemGroup);
223 	if (!cnItemGroup)
224 		return;
225 
226 	cnItemGroup->setComponentOrientation( angleDegrees, flipped );
227 }
228 
229 
itemEditTBCleared()230 void ItemInterface::itemEditTBCleared()
231 {
232 	m_stringLineEditMap.clear();
233 	m_stringComboBoxMap.clear();
234 	m_stringURLReqMap.clear();
235 	m_intSpinBoxMap.clear();
236 	m_doubleSpinBoxMap.clear();
237 	m_colorComboMap.clear();
238 	m_boolCheckMap.clear();
239 }
240 
241 
242 // The bool specifies whether advanced data should be shown
configWidget()243 QWidget * ItemInterface::configWidget()
244 {
245 	if ( !p_itemGroup || !p_itemGroup->activeItem() || !m_pActiveItemEditorToolBar )
246 		return nullptr;
247 
248 	VariantDataMap *variantMap = p_itemGroup->activeItem()->variantMap();
249 
250 	QWidget * parent = m_pActiveItemEditorToolBar;
251 
252 	// Create new widget with the toolbar or dialog as the parent
253 	QWidget * configWidget = new QWidget( parent /*, "tbConfigWidget" */ );
254     configWidget->setObjectName("tbConfigWidget");
255     {
256         // 2018.12.02
257         //configWidget->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding, 1, 1 ) );
258         QSizePolicy p(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
259         p.setHorizontalStretch(1);
260         p.setVerticalStretch(1);
261         configWidget->setSizePolicy(p);
262     }
263 
264 	QHBoxLayout * configLayout = new QHBoxLayout( configWidget );
265 // 	configLayout->setAutoAdd( true );
266 	configLayout->setSpacing( 6 );
267 
268 // 	configLayout->addItem( new QSpacerItem( 0, 0,  QSizePolicy::Expanding, QSizePolicy::Fixed ) );
269 
270 	const VariantDataMap::iterator vaEnd = variantMap->end();
271 	for ( VariantDataMap::iterator vait = variantMap->begin(); vait != vaEnd; ++vait )
272 	{
273 		if ( vait.value()->isHidden() || vait.value()->isAdvanced() )
274 			continue;
275 
276 		const Variant::Type::Value type = vait.value()->type();
277 
278 		// common to all types apart from bool
279 		QString toolbarCaption = vait.value()->toolbarCaption();
280 		if ( type != Variant::Type::Bool && !toolbarCaption.isEmpty() )
281 			configLayout->addWidget( new QLabel( toolbarCaption, configWidget ) );
282 
283 		QWidget * editWidget = nullptr; // Should be set to the created widget
284 
285 		switch( type )
286 		{
287 			case Variant::Type::Port:
288 			case Variant::Type::Pin:
289 			case Variant::Type::VarName:
290 			case Variant::Type::Combo:
291 			case Variant::Type::Select:
292 			case Variant::Type::KeyPad:
293 			case Variant::Type::SevenSegment:
294 			{
295 				QString value = vait.value()->displayString();
296 				if ( !value.isEmpty() && !vait.value()->allowed().contains(value) )
297 					vait.value()->appendAllowed(value);
298 
299 				const QStringList allowed = vait.value()->allowed();
300 
301 				KComboBox * box = new KComboBox(configWidget);
302 
303 				box->insertItems(box->count(), allowed);
304 				box->setCurrentItem(value);
305 
306 				if ( type == Variant::Type::VarName || type == Variant::Type::Combo )
307 					box->setEditable( true );
308 
309 				m_stringComboBoxMap[vait.key()] = box;
310 				connectMapWidget( box, SIGNAL(editTextChanged(const QString &)));
311 				connectMapWidget( box, SIGNAL(activated(const QString &)));
312 
313 				connect( *vait, SIGNAL(valueChangedStrAndTrue(const QString &, bool)),
314                          box, SLOT(setCurrentItem(const QString &, bool)) );
315 
316 				editWidget = box;
317 				break;
318 			}
319 			case Variant::Type::FileName:
320 			{
321                 qDebug() << Q_FUNC_INFO << "create FileName";
322 				QString value = vait.value()->value().toString();
323 				if ( !vait.value()->allowed().contains(value) )
324 					vait.value()->appendAllowed(value);
325 
326 				const QStringList allowed = vait.value()->allowed();
327 
328 				KUrlComboRequester * urlreq = new KUrlComboRequester( configWidget );
329 				urlreq->setFilter( vait.value()->filter() );
330 				connectMapWidget( urlreq, SIGNAL(urlSelected(QUrl)) );
331 				m_stringURLReqMap[vait.key()] = urlreq;
332 
333 				KComboBox * box = urlreq->comboBox();
334 				box->insertItems(box->count(), allowed);
335 				box->setEditable( true );
336 
337 				// Note this has to be called after inserting the allowed list
338 				urlreq->setUrl(QUrl::fromLocalFile(vait.value()->value().toString()));
339 
340 				// Generally we only want a file name once the user has finished typing out the full file name.
341 				connectMapWidget( box, SIGNAL(returnPressed(const QString &)));
342 				connectMapWidget( box, SIGNAL(activated(const QString &)));
343 
344 				connect( *vait, SIGNAL(valueChanged(const QString &)), box, SLOT(setEditText(const QString &)) );
345 
346 				editWidget = urlreq;
347 				break;
348 			}
349 			case Variant::Type::String:
350 			{
351 				LineEdit * edit = new LineEdit( configWidget );
352 
353 				edit->setText( vait.value()->value().toString() );
354 				connectMapWidget(edit,SIGNAL(textChanged(const QString &)));
355 				m_stringLineEditMap[vait.key()] = edit;
356 				editWidget = edit;
357 
358 				connect( *vait, SIGNAL(valueChanged(const QString &)), edit, SLOT(setText(const QString &)) );
359 
360 				break;
361 			}
362 			case Variant::Type::Int:
363 			{
364 				QSpinBox *spin = new QSpinBox(configWidget);
365 				spin->setMinimum((int)vait.value()->minValue());
366 				spin->setMaximum((int)vait.value()->maxValue());
367 				spin->setValue(vait.value()->value().toInt());
368 
369 				connectMapWidget( spin, SIGNAL(valueChanged(int)) );
370 				m_intSpinBoxMap[vait.key()] = spin;
371 				editWidget = spin;
372 
373 				connect( *vait, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int)) );
374 
375 				break;
376 			}
377 			case Variant::Type::Double:
378 			{
379 				DoubleSpinBox *spin = new DoubleSpinBox( vait.value()->minValue(), vait.value()->maxValue(), vait.value()->minAbsValue(), vait.value()->value().toDouble(), vait.value()->unit(), configWidget );
380 
381 				connectMapWidget( spin, SIGNAL(valueChanged(double)));
382 				m_doubleSpinBoxMap[vait.key()] = spin;
383 				editWidget = spin;
384 
385 				connect( *vait, SIGNAL(valueChanged(double)), spin, SLOT(setValue(double)) );
386 
387 				break;
388 			}
389 			case Variant::Type::Color:
390 			{
391 				QColor value = vait.value()->value().value<QColor>();
392 
393 				ColorCombo * colorBox = new ColorCombo( (ColorCombo::ColorScheme)vait.value()->colorScheme(), configWidget );
394 
395 				colorBox->setColor( value );
396 				connectMapWidget( colorBox, SIGNAL(activated(const QColor &)));
397 				m_colorComboMap[vait.key()] = colorBox;
398 
399 				connect( *vait, SIGNAL(valueChanged(const QColor &)), colorBox, SLOT(setColor(const QColor &)) );
400 
401 				editWidget = colorBox;
402 				break;
403 			}
404 			case Variant::Type::Bool:
405 			{
406 				const bool value = vait.value()->value().toBool();
407 				QCheckBox * box = new QCheckBox( vait.value()->toolbarCaption(), configWidget );
408 
409 				box->setChecked(value);
410 				connectMapWidget( box, SIGNAL(toggled(bool)));
411 				m_boolCheckMap[vait.key()] = box;
412 
413 				connect( *vait, SIGNAL(valueChanged(bool)), box, SLOT(setChecked(bool)) );
414 
415 				editWidget = box;
416 				break;
417 			}
418 			case Variant::Type::Raw:
419 			case Variant::Type::PenStyle:
420 			case Variant::Type::PenCapStyle:
421 			case Variant::Type::Multiline:
422 			case Variant::Type::RichText:
423 			case Variant::Type::None:
424 			{
425 				// Do nothing, as these data types are not handled in the toolbar
426 				break;
427 			}
428 		}
429 
430 		if ( !editWidget )
431 			continue;
432 
433         const int widgetH = QFontMetrics( configWidget->font() ).height() + 2;
434         editWidget->setMinimumHeight( widgetH );  // note: this is hack-ish; something is not ok with the layout
435         editWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
436 
437 		// In the case of the toolbar, we don't want it too high
438 		if ( editWidget->height() > parent->height()-2 )
439 			editWidget->setMaximumHeight( parent->height()-2 );
440 
441 		switch ( type )
442 		{
443 			case Variant::Type::VarName:
444 			case Variant::Type::Combo:
445 			case Variant::Type::String:
446 			{
447 				QSizePolicy p( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed /*, 1, 1 */ );
448                 p.setHorizontalStretch(1);
449                 p.setVerticalStretch(1);
450 
451 				editWidget->setSizePolicy( p );
452 				editWidget->setMaximumWidth( 250 );
453 				break;
454 			}
455 
456 			case Variant::Type::FileName:
457 			case Variant::Type::Port:
458 			case Variant::Type::Pin:
459 			case Variant::Type::Select:
460 			case Variant::Type::KeyPad:
461 			case Variant::Type::SevenSegment:
462 			case Variant::Type::Int:
463 			case Variant::Type::Double:
464 			case Variant::Type::Color:
465 			case Variant::Type::Bool:
466 			case Variant::Type::Raw:
467 			case Variant::Type::PenStyle:
468 			case Variant::Type::PenCapStyle:
469 			case Variant::Type::Multiline:
470 			case Variant::Type::RichText:
471 			case Variant::Type::None:
472 				break;
473 		}
474 
475 		configLayout->addWidget( editWidget );
476 	}
477 
478 	configLayout->addItem( new QSpacerItem( 0, 0,  QSizePolicy::Expanding, QSizePolicy::Fixed ) );
479 
480 	return configWidget;
481 }
482 
483 
connectMapWidget(QWidget * widget,const char * _signal)484 void ItemInterface::connectMapWidget( QWidget *widget, const char *_signal )
485 {
486 	connect( widget, _signal, this, SLOT(tbDataChanged()) );
487 }
488 
489 // TODO move to separate file
490 struct BoolLock {
491     bool *m_flagPtr;
BoolLockBoolLock492     BoolLock(bool *flagPtr) : m_flagPtr(flagPtr) {
493         if (m_flagPtr == nullptr) {
494             qCritical() << Q_FUNC_INFO << "nullptr flagPtr";
495             return;
496         }
497         if (*m_flagPtr == true) {
498             qWarning() << Q_FUNC_INFO << "flag expected to be false, addr=" << m_flagPtr << " Doing nothing";
499             m_flagPtr = nullptr;
500         } else {
501             *m_flagPtr = true;
502         }
503     }
~BoolLockBoolLock504     ~BoolLock() {
505         if (m_flagPtr != nullptr) {
506             *m_flagPtr = false;
507         }
508     }
509 };
510 
tbDataChanged()511 void ItemInterface::tbDataChanged()
512 {
513     qDebug() << Q_FUNC_INFO << "begin";
514     if (m_isInTbDataChanged) {
515         qDebug() << Q_FUNC_INFO << "avoiding recursion, returning";
516         return;
517     }
518     BoolLock inTbChangedLock(&m_isInTbDataChanged);
519 	// Manual string values
520 	const LineEditMap::iterator m_stringLineEditMapEnd = m_stringLineEditMap.end();
521 	for ( LineEditMap::iterator leit = m_stringLineEditMap.begin(); leit != m_stringLineEditMapEnd; ++leit )
522 	{
523 		slotSetData( leit.key(), leit.value()->text() );
524 	}
525 
526 	// String values from comboboxes
527 	const KComboBoxMap::iterator m_stringComboBoxMapEnd = m_stringComboBoxMap.end();
528 	for ( KComboBoxMap::iterator cmit = m_stringComboBoxMap.begin(); cmit != m_stringComboBoxMapEnd; ++cmit )
529 	{
530         qDebug() << Q_FUNC_INFO << "set KCombo data for " << cmit.key() << " to " << cmit.value()->currentText();
531 		slotSetData( cmit.key(), cmit.value()->currentText() );
532 	}
533 
534 	// Colors values from colorcombos
535 	const ColorComboMap::iterator m_colorComboMapEnd = m_colorComboMap.end();
536 	for ( ColorComboMap::iterator ccit = m_colorComboMap.begin(); ccit != m_colorComboMapEnd; ++ccit )
537 	{
538 		slotSetData( ccit.key(), ccit.value()->color() );
539 	}
540 
541 	// Bool values from checkboxes
542 	const QCheckBoxMap::iterator m_boolCheckMapEnd = m_boolCheckMap.end();
543 	for ( QCheckBoxMap::iterator chit = m_boolCheckMap.begin(); chit != m_boolCheckMapEnd; ++chit )
544 	{
545 		slotSetData( chit.key(), chit.value()->isChecked() );
546 	}
547 
548 	const IntSpinBoxMap::iterator m_intSpinBoxMapEnd = m_intSpinBoxMap.end();
549 	for ( IntSpinBoxMap::iterator it = m_intSpinBoxMap.begin(); it != m_intSpinBoxMapEnd; ++it )
550 	{
551 		slotSetData( it.key(), it.value()->value() );
552 	}
553 
554 	// (?) Combined values from spin boxes and combo boxes
555 	// (?) Get values from all spin boxes
556 
557 	const DoubleSpinBoxMap::iterator m_doubleSpinBoxMapEnd = m_doubleSpinBoxMap.end();
558 	for ( DoubleSpinBoxMap::iterator sbit = m_doubleSpinBoxMap.begin(); sbit != m_doubleSpinBoxMapEnd; ++sbit )
559 	{
560 // 		VariantDataMap::iterator vait = variantData.find(sbit.key());
561 		slotSetData( sbit.key(), sbit.value()->value() );
562 	}
563 
564 	// Filenames from KUrlRequesters
565 	const KUrlReqMap::iterator m_stringURLReqMapEnd = m_stringURLReqMap.end();
566 	for ( KUrlReqMap::iterator urlit = m_stringURLReqMap.begin(); urlit != m_stringURLReqMapEnd; ++urlit )
567 	{
568         qDebug() << Q_FUNC_INFO << "set kurlrequester data for " << urlit.key() << " to " << urlit.value()->url();
569         QVariant urlVar( urlit.value()->url().path() );
570         qDebug() << Q_FUNC_INFO << "urlVar=" << urlVar << " urlVar.toUrl=" << urlVar.toUrl();
571 		slotSetData( urlit.key(), urlVar );
572 	}
573 
574 	if (p_cvb)
575 		p_cvb->setModified(true);
576 }
577 
578 
setProperty(Variant * v)579 void ItemInterface::setProperty( Variant * v )
580 {
581 	slotSetData( v->id(), v->value() );
582 }
583 
584 
slotSetData(const QString & id,QVariant value)585 void ItemInterface::slotSetData( const QString &id, QVariant value )
586 {
587 	if ( !p_itemGroup || (p_itemGroup->itemCount() == 0) ) {
588         qDebug() << Q_FUNC_INFO << "p_itemGroup not valid:" << p_itemGroup;
589 		return;
590     }
591 
592 	if ( !p_itemGroup->itemsAreSameType() )
593 	{
594 		qDebug() << Q_FUNC_INFO << "Items are not the same type!"<<endl;
595 		return;
596 	}
597 	qDebug() << Q_FUNC_INFO << "id=" << id << " value=" << value;
598 
599 	const ItemList itemList = p_itemGroup->items(true);
600 	const ItemList::const_iterator end = itemList.end();
601 	for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it )
602 	{
603 		if (*it)
604 			(*it)->property(id)->setValue(value);
605 	}
606 
607 	if (p_cvb)
608 		p_cvb->setModified(true);
609 
610 	ItemEditor::self()->itemGroupUpdated( p_itemGroup );
611 
612 	if (p_cvb)
613 		p_cvb->requestStateSave(m_currentActionTicket);
614 }
615