1 /***************************************************************************
2                           rkvareditmodel  -  description
3                              -------------------
4     begin                : Mon Nov 05 2007
5     copyright            : (C) 2007, 2010, 2011, 2012, 2014 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 "rkvareditmodel.h"
19 
20 #include <KLocalizedString>
21 
22 #include <QColor>
23 #include <QTimer>
24 #include <QFont>
25 
26 #include "../core/rcontainerobject.h"
27 #include "../core/rkmodificationtracker.h"
28 #include "../core/rkrownames.h"
29 #include "../rbackend/rkrinterface.h"
30 #include "../rkglobals.h"
31 
32 #include "../debug.h"
33 
RKVarEditModel(QObject * parent)34 RKVarEditModel::RKVarEditModel (QObject *parent) : RKVarEditModelBase (parent), RObjectListener (RObjectListener::DataModel) {
35 	RK_TRACE (EDITOR);
36 
37 	meta_model = 0;
38 	trailing_rows = trailing_cols = 0;
39 	var_col_offset = 0;
40 	edit_blocks = 0;
41 	rownames = 0;
42 	header_locked = false;
43 	duplicate_check_triggered = false;
44 	reset_scheduled = false;
45 
46 	addNotificationType (RObjectListener::ObjectRemoved);
47 	addNotificationType (RObjectListener::MetaChanged);
48 	addNotificationType (RObjectListener::DataChanged);
49 }
50 
~RKVarEditModel()51 RKVarEditModel::~RKVarEditModel () {
52 	RK_TRACE (EDITOR);
53 
54 	for (int i = objects.size () - 1; i >= 0; --i) stopListenForObject (objects[i]);
55 }
56 
getObject(int index) const57 RKVariable* RKVarEditModel::getObject (int index) const {
58 	RK_TRACE (EDITOR);
59 
60 	if (index >= trueCols ()) {
61 		RK_ASSERT (false);
62 		return 0;
63 	}
64 	return objects[index];
65 }
66 
addObject(int index,RKVariable * object)67 void RKVarEditModel::addObject (int index, RKVariable* object) {
68 	RK_TRACE (EDITOR);
69 	RK_ASSERT (object);
70 
71 	if ((index < 0) || (index >= objects.size ())) index = objects.size ();
72 
73 	beginInsertColumns (QModelIndex (), index, index);
74 	if (meta_model) meta_model->beginAddDataObject (index);
75 	if (object->isPending () && (!object->getLength ())) object->setLength (trueRows ());	// probably we just created it ourselves
76 	listenForObject (object);
77 	objects.insert (index, object);
78 	if (meta_model) meta_model->endAddDataObject ();
79 	endInsertColumns ();
80 
81 	checkDuplicates ();
82 }
83 
objectRemoved(RObject * object)84 void RKVarEditModel::objectRemoved (RObject* object) {
85 	RK_TRACE (EDITOR);
86 
87 	int index = objects.indexOf (static_cast<RKVariable*> (object));	// no check for isVariable needed. we only need to look up, if we have this object, and where.
88 	if (index < var_col_offset) {
89 		if (index < 0) return;	// e.g. the data.frame object
90 		// the rownames object should only be deleted, when the whole data.frame is gone)
91 		RK_ASSERT (objects.size () <= var_col_offset);
92 	}
93 
94 	beginRemoveColumns (QModelIndex (), index, index);
95 	if (meta_model) meta_model->beginRemoveDataObject (index);
96 	stopListenForObject (objects.takeAt (index));
97 	if (meta_model) meta_model->endRemoveDataObject ();
98 	endRemoveColumns ();
99 
100 	if (objects.size () <= var_col_offset) emit (modelDepleted ());	// editor may or may want to auto-destruct
101 }
102 
checkDuplicates()103 void RKVarEditModel::checkDuplicates () {
104 	RK_TRACE (EDITOR);
105 
106 	if (duplicate_check_triggered) return;
107 	duplicate_check_triggered = true;
108 	QTimer::singleShot (0, this, SLOT (checkDuplicatesNow()));
109 }
110 
checkDuplicatesNow()111 void RKVarEditModel::checkDuplicatesNow () {
112 	RK_TRACE (EDITOR);
113 
114 	duplicate_check_triggered = false;
115 
116 	QStringList dupes;
117 	for (int i = var_col_offset; i < objects.size (); ++i) {
118 		QString name = objects[i]->getShortName ();
119 		for (int j = i+1; j < objects.size (); ++j) {
120 			if (objects[j]->getShortName () == name) {
121 				if (objects[i]->getFullName () == objects[j]->getFullName ()) {
122 					dupes.append (objects[i]->getFullName ());
123 					j = objects.size ();		// break
124 				}
125 			}
126 		}
127 	}
128 
129 	if (!dupes.isEmpty ()) emit (hasDuplicates (dupes));
130 }
131 
objectMetaChanged(RObject * changed)132 void RKVarEditModel::objectMetaChanged (RObject* changed) {
133 	RK_TRACE (EDITOR);
134 
135 	int cindex = objects.indexOf (static_cast<RKVariable*> (changed));	// no check for isVariable needed. we only need to look up, if we have this object, and where.
136 	if (cindex < 0) return;	// none of our business
137 
138 	if (meta_model) meta_model->objectMetaChanged (cindex);
139 }
140 
scheduleReset()141 void RKVarEditModel::scheduleReset () {
142 	RK_TRACE (EDITOR);
143 
144 	if (!reset_scheduled) {
145 		reset_scheduled = true;
146 		QTimer::singleShot (0, this, SLOT (doResetNow()));
147 		beginResetModel ();
148 	}
149 }
150 
doResetNow()151 void RKVarEditModel::doResetNow () {
152 	RK_TRACE (EDITOR);
153 	RK_ASSERT (reset_scheduled);
154 
155 	reset_scheduled = false;
156 	endResetModel ();
157 }
158 
objectDataChanged(RObject * object,const RObject::ChangeSet * changes)159 void RKVarEditModel::objectDataChanged (RObject* object, const RObject::ChangeSet *changes) {
160 	RK_TRACE (EDITOR);
161 
162 	int cindex = objects.indexOf (static_cast<RKVariable*> (object));	// no check for isVariable needed. we only need to look up, if we have this object, and where.
163 	if (cindex < 0) return;	// none of our buisiness
164 
165 	RK_ASSERT (changes);
166 
167 	if (changes->full_reset) {
168 		scheduleReset ();
169 		return;
170 	}
171 	emit (dataChanged (index (changes->from_index, cindex), index (changes->to_index, cindex)));
172 }
173 
doInsertColumns(int,int)174 void RKVarEditModel::doInsertColumns (int, int) {
175 	RK_TRACE (EDITOR);
176 	RK_ASSERT (false);	// should be implemented in a subclass, or never called
177 }
178 
getMetaModel()179 RKVarEditMetaModel* RKVarEditModel::getMetaModel () {
180 	RK_TRACE (EDITOR);
181 
182 	if (!meta_model) meta_model = new RKVarEditMetaModel (this);
183 
184 	return meta_model;
185 }
186 
insertRows(int row,int count,const QModelIndex & parent)187 bool RKVarEditModel::insertRows (int row, int count, const QModelIndex& parent) {
188 	RK_TRACE (EDITOR);
189 
190 	if (edit_blocks || parent.isValid () || objects.isEmpty () || (row > apparentRows ())) {
191 		RK_ASSERT (false);
192 		return false;
193 	}
194 	if (row > trueRows ()) row = trueRows ();
195 	int lastrow = row+count - 1;
196 	RK_ASSERT (row >= 0);
197 	RK_ASSERT (lastrow >= row);
198 
199 	beginInsertRows (QModelIndex (), row, row+count-1);
200 	doInsertRowsInBackend (row, count);
201 	for (int i=0; i < objects.size (); ++i) {
202 // TODO: this does not emit any data change notifications to other editors
203 		objects[i]->insertRows (row, count);
204 	}
205 	endInsertRows ();
206 
207 	return true;
208 }
209 
removeRows(int row,int count,const QModelIndex & parent)210 bool RKVarEditModel::removeRows (int row, int count, const QModelIndex& parent) {
211 	RK_TRACE (EDITOR);
212 
213 	int lastrow = row+count - 1;
214 	if (edit_blocks || parent.isValid () || objects.isEmpty () || (lastrow >= (apparentRows ()))) {
215 		RK_ASSERT (false);
216 		return false;
217 	}
218 	if (lastrow >= objects[0]->getLength ()) lastrow = objects[0]->getLength () - 1;
219 	RK_ASSERT (row >= 0);
220 	RK_ASSERT (lastrow >= row);
221 
222 	beginRemoveRows (QModelIndex (), row, lastrow);
223 	doRemoveRowsInBackend (row, lastrow - row + 1);
224 	for (int i=0; i < objects.size (); ++i) {
225 // TODO: this does not emit any data change notifications to other editors
226 		objects[i]->removeRows (row, lastrow);
227 	}
228 	endRemoveRows ();
229 
230 	return true;
231 }
232 
doInsertRowsInBackend(int,int)233 void RKVarEditModel::doInsertRowsInBackend (int, int) {
234 	RK_TRACE (EDITOR);
235 
236 	// TODO: implement
237 	RK_ASSERT (false);
238 }
239 
doRemoveRowsInBackend(int,int)240 void RKVarEditModel::doRemoveRowsInBackend (int, int) {
241 	RK_TRACE (EDITOR);
242 
243 	// TODO: implement
244 	RK_ASSERT (false);
245 }
246 
rowCount(const QModelIndex & parent) const247 int RKVarEditModel::rowCount (const QModelIndex& parent) const {
248 	RK_TRACE (EDITOR);
249 
250 	if (parent.isValid ()) return 0;
251 	if (objects.isEmpty ()) {
252 		RK_ASSERT (false);
253 		return 0;
254 	}
255 	return (apparentRows ());
256 }
257 
columnCount(const QModelIndex & parent) const258 int RKVarEditModel::columnCount (const QModelIndex& parent) const {
259 	RK_TRACE (EDITOR);
260 
261 	if (parent.isValid ()) return 0;
262 	return (apparentCols ());
263 }
264 
data(const QModelIndex & index,int role) const265 QVariant RKVarEditModel::data (const QModelIndex& index, int role) const {
266 	RK_TRACE (EDITOR);
267 
268 	if (!index.isValid ()) return QVariant ();
269 	int row = index.row ();
270 	int col = index.column ();
271 	if ((col >= apparentCols ()) || (row >= apparentRows ())) {
272 		RK_ASSERT (false);
273 		return QVariant ();
274 	}
275 
276 	if (col < var_col_offset) {
277 		// TODO: make this look more like a normal header
278 		if (role == Qt::ForegroundRole) return (QColor (Qt::blue));
279 	}
280 
281 	// on a trailing row / col
282 	if ((col >= objects.size ()) || (row >= objects[0]->getLength ())) {
283 		if (role == Qt::BackgroundRole) return (QColor (Qt::gray));
284 		if (role == Qt::ToolTipRole) {
285 			if (col >= objects.size ()) return (i18n ("Type on these fields to add new columns"));
286 			else return (i18n ("Type on these fields to add new rows"));
287 		}
288 		return QVariant ();
289 	}
290 
291 	// a normal cell
292 	RKVariable *var = objects[col];
293 	RK_ASSERT (var);
294 
295 	if (role == Qt::EditRole) return var->getText (row, false);
296 
297 	RKVariable::Status status = var->cellStatus (row);
298 	if (role == Qt::DisplayRole) {
299 		if (status == RKVariable::ValueUnused) return QString ("<NA>");
300 		return var->getText (row, true);
301 	}
302 	if (role == Qt::BackgroundRole) {
303 		if (status == RKVariable::ValueInvalid) return (QColor (Qt::red));
304 	} else if (role == Qt::ToolTipRole) {
305 		if (status == RKVariable::ValueInvalid) return (i18n ("This value is not allowed, here"));
306 	}
307 	if ((role == Qt::ForegroundRole) && ((status == RKVariable::ValueUnknown) || (status == RKVariable::ValueUnused))) return (QColor (Qt::lightGray));
308 	if (role == Qt::TextAlignmentRole) {
309 		if (var->getAlignment () == RKVariable::AlignCellLeft) return ((int) Qt::AlignLeft | Qt::AlignVCenter);
310 		else return ((int) Qt::AlignRight | Qt::AlignVCenter);
311 	}
312 
313 	return QVariant ();
314 }
315 
flags(const QModelIndex & index) const316 Qt::ItemFlags RKVarEditModel::flags (const QModelIndex& index) const {
317 	RK_TRACE (EDITOR);
318 
319 	Qt::ItemFlags flags = 0;
320 
321 	if (!index.isValid ()) return flags;
322 	int row = index.row ();
323 	int col = index.column ();
324 	if ((col >= apparentCols ()) || (row >= apparentRows ())) {
325 		RK_ASSERT (false);
326 		return flags;
327 	}
328 
329 	if ((col < var_col_offset) && header_locked) return flags;
330 
331 	if (!edit_blocks) flags |= Qt::ItemIsEditable | Qt::ItemIsEnabled;
332 	flags |= Qt::ItemIsSelectable;   // NOTE: Setting this even for trailing rows / cols, as highlighting of current cell is based on this   // if ((col < objects.size ()) && (row < objects[0]->getLength ())) flags |= Qt::ItemIsSelectable;
333 
334 	return flags;
335 }
336 
setData(const QModelIndex & index,const QVariant & value,int role)337 bool RKVarEditModel::setData (const QModelIndex& index, const QVariant& value, int role) {
338 	RK_TRACE (EDITOR);
339 
340 	if (!index.isValid ()) return false;
341 	int row = index.row ();
342 	int col = index.column ();
343 	if (edit_blocks || (role != Qt::EditRole) || (col >= apparentCols ()) || (row >= apparentRows ())) {
344 		RK_ASSERT (false);
345 		return false;
346 	}
347 
348 	if (col >= objects.size ()) {		// trailing col
349 		// somebody should add a column for us
350 		doInsertColumns (objects.size (), 1);
351 
352 		if (col >= objects.size ()) {
353 			// apparently, no column has been added in the above signal
354 			return false;
355 		}
356 	}
357 	if (row >= objects[0]->getLength ()) {		// trailing row
358 		insertRows (objects[0]->getLength (), 1);
359 	}
360 
361 	// edit of normal cells
362 	RKVariable* var = objects[col];
363 	RK_ASSERT (var);
364 	var->setText (row, value.toString ());
365 	return true;
366 }
367 
headerData(int section,Qt::Orientation orientation,int role) const368 QVariant RKVarEditModel::headerData (int section, Qt::Orientation orientation, int role) const {
369 	RK_TRACE (EDITOR);
370 
371 	if (orientation == Qt::Horizontal) {
372 		if (role == Qt::DisplayRole) {
373 			if (section >= objects.size ()) return i18n ("#New Variable#");
374 			if (section < var_col_offset) return i18n ("Row names");
375 			return (QString::number (section - var_col_offset + 1));
376 		} else if (role == Qt::BackgroundRole) {
377 			if ((section < objects.size  ()) && objects[section]->hasInvalidFields ()) return QVariant (QColor (Qt::red));
378 		} else if ((role == Qt::ToolTipRole) || (role == Qt::StatusTipRole)) {
379 			if ((section < objects.size  ()) && objects[section]->hasInvalidFields ()) return i18n ("This column contains one or more invalid fields");
380 		}
381 	} else {
382 		if ((role == Qt::DisplayRole) && (section < rownames->getLength ())) {
383 			return rownames->getText (section);
384 		}
385 	}
386 
387 	return QVariant ();
388 }
389 
getTextMatrix(const QItemSelectionRange & range) const390 RKTextMatrix RKVarEditModel::getTextMatrix (const QItemSelectionRange& range) const {
391 	RK_TRACE (EDITOR);
392 
393 	if ((!range.isValid ()) || objects.isEmpty ()) return RKTextMatrix ();
394 
395 // NOTE: of course, when the range is small, this is terribly inefficient. On the other hand, it doesn't really matter, then, either.
396 	QItemSelectionRange erange = range.intersected (QItemSelectionRange (index (0, 0), index (trueRows () - 1, trueCols () - 1)));
397 	int top = erange.top ();
398 	int bottom = erange.bottom ();
399 	int left = erange.left ();
400 	int right = erange.right ();
401 
402 	RKTextMatrix ret;
403 	int tcol = 0;
404 	for (int col = left; col <= right; ++col) {
405 		QString* data = objects[col]->getCharacter (top, bottom);
406 		RK_ASSERT (data);
407 		ret.setColumn (tcol, data, bottom - top + 1);
408 		delete [] data;
409 		++tcol;
410 	}
411 
412 	return ret;
413 }
414 
blankRange(const QItemSelectionRange & range)415 void RKVarEditModel::blankRange (const QItemSelectionRange& range) {
416 	RK_TRACE (EDITOR);
417 
418 	if ((!range.isValid ()) || objects.isEmpty ()) return;
419 
420 // NOTE: of course, when the range is small, this is terribly inefficient. On the other hand, it doesn't really matter, then, either.
421 	QItemSelectionRange erange = range.intersected (QItemSelectionRange (index (0, 0), index (trueRows () - 1, trueCols () - 1)));
422 	int top = erange.top ();
423 	int bottom = erange.bottom ();
424 	int left = erange.left ();
425 	int right = erange.right ();
426 
427 	for (int col = left; col <= right; ++col) {
428 		RKVariable* var = objects[col];
429 		for (int row = top; row <= bottom; ++row) {
430 			var->setText (row, QString ());
431 		}
432 	}
433 }
434 
setTextMatrix(const QModelIndex & offset,const RKTextMatrix & text,const QItemSelectionRange & confine_to)435 void RKVarEditModel::setTextMatrix (const QModelIndex& offset, const RKTextMatrix& text, const QItemSelectionRange& confine_to) {
436 	RK_TRACE (EDITOR);
437 
438 // NOTE: of course, when the range is small, this is terribly inefficient. On the other hand, it doesn't really matter, then, either. Single cells will be set using setData()
439 	if ((!offset.isValid ()) || text.isEmpty ()) return;
440 
441 	int top = offset.row ();
442 	int left = offset.column ();
443 	int bottom = top + text.numRows () - 1;
444 	int right = left + text.numColumns () - 1;
445 	if (confine_to.isValid ()) {
446 		if (confine_to.top () > top) return;	// can't confine top-left. Should have been set by caller
447 		if (confine_to.left () > left) return;
448 		bottom = qMin (confine_to.bottom (), bottom);
449 		right = qMin (confine_to.right (), right);
450 	}
451 
452 // TODO: some models might not support column addition.
453 	if (right >= trueCols ()) doInsertColumns (objects.size (), right - trueCols () + 1);
454 	RK_ASSERT (right < trueCols ());
455 	int current_rows = objects[0]->getLength ();
456 	if (bottom >= current_rows) insertRows (current_rows, bottom - current_rows + 1);
457 
458 	int tcol = 0;
459 	for (int col = left; col <= right; ++col) {
460 		RKVariable* var = objects[col];
461 		int trow = 0;
462 		for (int row = top; row <= bottom; ++row) {
463 			var->setText (row, text.getText (trow, tcol));
464 			++trow;
465 		}
466 		++tcol;
467 	}
468 }
469 
restoreObject(RObject * object,RCommandChain * chain)470 void RKVarEditModel::restoreObject (RObject* object, RCommandChain* chain) {
471 	RK_TRACE (EDITOR);
472 
473 	RK_ASSERT (objects.contains (static_cast<RKVariable*> (object)));
474 	static_cast<RKVariable*> (object)->restore (chain);
475 }
476 
477 /////////////////// RKVarEditMetaModel ////////////////////////
478 
RKVarEditMetaModel(RKVarEditModel * data_model)479 RKVarEditMetaModel::RKVarEditMetaModel (RKVarEditModel* data_model) : RKVarEditModelBase (data_model) {
480 	RK_TRACE (EDITOR);
481 	RK_ASSERT (data_model);
482 
483 	RKVarEditMetaModel::data_model = data_model;
484 }
485 
~RKVarEditMetaModel()486 RKVarEditMetaModel::~RKVarEditMetaModel () {
487 	RK_TRACE (EDITOR);
488 }
489 
beginAddDataObject(int index)490 void RKVarEditMetaModel::beginAddDataObject (int index) {
491 	RK_TRACE (EDITOR);
492 
493 	beginInsertColumns (QModelIndex (), index, index);
494 }
495 
endAddDataObject()496 void RKVarEditMetaModel::endAddDataObject () {
497 	RK_TRACE (EDITOR);
498 
499 	endInsertColumns ();
500 }
501 
beginRemoveDataObject(int index)502 void RKVarEditMetaModel::beginRemoveDataObject (int index) {
503 	RK_TRACE (EDITOR);
504 
505 	beginRemoveColumns (QModelIndex (), index, index);
506 }
507 
endRemoveDataObject()508 void RKVarEditMetaModel::endRemoveDataObject () {
509 	RK_TRACE (EDITOR);
510 
511 	endRemoveColumns ();
512 }
513 
objectMetaChanged(int atcolumn)514 void RKVarEditMetaModel::objectMetaChanged (int atcolumn) {
515 	RK_TRACE (EDITOR);
516 
517 	emit (dataChanged (index (0, atcolumn), index (RowCount - 1, atcolumn)));
518 	emit (headerDataChanged (Qt::Horizontal, atcolumn, atcolumn));
519 }
520 
rowCount(const QModelIndex & parent) const521 int RKVarEditMetaModel::rowCount (const QModelIndex& parent) const {
522 	RK_TRACE (EDITOR);
523 
524 	if (parent.isValid ()) return 0;
525 	return RowCount;
526 }
527 
columnCount(const QModelIndex & parent) const528 int RKVarEditMetaModel::columnCount (const QModelIndex& parent) const {
529 	RK_TRACE (EDITOR);
530 
531 	return (data_model->columnCount (parent));
532 }
533 
data(const QModelIndex & index,int role) const534 QVariant RKVarEditMetaModel::data (const QModelIndex& index, int role) const {
535 	RK_TRACE (EDITOR);
536 
537 	if (!index.isValid ()) return QVariant ();
538 	int row = index.row ();
539 	int col = index.column ();
540 	if ((col >= data_model->apparentCols ()) || (row >= RowCount)) {
541 		RK_ASSERT (false);
542 		return QVariant ();
543 	}
544 
545 	if (col < var_col_offset) {
546 		if (role == Qt::BackgroundRole) return (QColor (Qt::lightGray));
547 		return QVariant ();
548 	}
549 
550 	// on a trailing col
551 	if (col >= data_model->objects.size ()) {
552 		if (role == Qt::BackgroundRole) return (QColor (Qt::gray));
553 		if (role == Qt::ToolTipRole) return (i18n ("Type on these fields to add new columns"));
554 		return QVariant ();
555 	}
556 
557 	if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) {
558 		RKVariable *var = data_model->objects[col];
559 		RK_ASSERT (var);
560 
561 		if (row == NameRow) return var->getShortName ();
562 		if (row == LabelRow) return var->getLabel ();
563 		if (row == TypeRow) {
564 			if (role == Qt::EditRole) return QString::number (var->getDataType ());
565 			return RObject::typeToText (var->getDataType ());
566 		}
567 		if (row == FormatRow) return var->getFormattingOptionsString ();
568 		if (row == LevelsRow) return var->getValueLabelString ();
569 	}
570 
571 	if ((role == Qt::FontRole) && (row == NameRow)) {
572 		QFont font;
573 		font.setBold (true);
574 		return (font);
575 	}
576 
577 	return QVariant ();
578 }
579 
flags(const QModelIndex & index) const580 Qt::ItemFlags RKVarEditMetaModel::flags (const QModelIndex& index) const {
581 	RK_TRACE (EDITOR);
582 
583 	Qt::ItemFlags flags = 0;
584 
585 	if (!index.isValid ()) return flags;
586 	int row = index.row ();
587 	int col = index.column ();
588 	if ((col >= data_model->apparentCols ()) || (row >= RowCount)) {
589 		RK_ASSERT (false);
590 		return flags;
591 	}
592 
593 	if (col < var_col_offset) return flags;
594 	if (!data_model->edit_blocks) flags |= Qt::ItemIsEditable | Qt::ItemIsEnabled;
595 	flags |= Qt::ItemIsSelectable;   // NOTE: Setting this even for trailing rows / cols, as highlighting of current cell is based on this   // if ((col < data_model->objects.size ()) && (row < RowCount)) flags |= Qt::ItemIsSelectable;
596 
597 	return flags;
598 }
599 
setData(const QModelIndex & index,const QVariant & value,int role)600 bool RKVarEditMetaModel::setData (const QModelIndex& index, const QVariant& value, int role) {
601 	RK_TRACE (EDITOR);
602 
603 	if (!index.isValid ()) return false;
604 	int row = index.row ();
605 	int col = index.column ();
606 	if (data_model->edit_blocks || (role != Qt::EditRole) || (col >= data_model->apparentCols ()) || (row >= RowCount)) {
607 		RK_ASSERT (false);
608 		return false;
609 	}
610 
611 	if (col < var_col_offset) {
612 		RK_ASSERT (false);
613 		return false;
614 	}
615 
616 	if (col >= data_model->objects.size ()) {		// trailing col
617 		// somebody should add a column for us
618 		data_model->doInsertColumns (data_model->objects.size (), 1);
619 
620 		if (col >= data_model->objects.size ()) {
621 			// apparently, no column has been added in the above signal
622 			return false;
623 		}
624 	}
625 
626 	// edit of normal cells
627 	RKVariable* var = data_model->objects[col];
628 	RK_ASSERT (var);
629 
630 	if (row == NameRow) {
631 		if (var->getShortName () != value.toString ()) {
632 			if (!var->canRename ()) return false;
633 			if (var->parentObject ()->isContainer ()) {
634 				RKGlobals::tracker ()->renameObject (var, static_cast<RContainerObject*> (var->parentObject ())->validizeName (value.toString ()));
635 			} else return false;
636 		}
637 	} else if (row == LabelRow) {
638 		var->setLabel (value.toString (), true);
639 	} else if (row == TypeRow) {
640 		var->setVarType ((RObject::RDataType) value.toInt ());
641 	} else if (row == FormatRow) {
642 		var->setFormattingOptionsString (value.toString ());
643 	} else if (row == LevelsRow) {
644 		if (value.toString () != var->getValueLabelString ()) {
645 			var->setValueLabelString (value.toString ());
646 		}
647 	}
648 
649 	return true;
650 }
651 
getValueLabels(int column) const652 RObject::ValueLabels RKVarEditMetaModel::getValueLabels (int column) const {
653 	RK_TRACE (EDITOR);
654 
655 	if (column >= trueCols ()) return RObject::ValueLabels ();
656 	return (getObject (column)->getValueLabels ());
657 }
658 
setValueLabels(int column,const RObject::ValueLabels & labels)659 void RKVarEditMetaModel::setValueLabels (int column, const RObject::ValueLabels& labels) {
660 	RK_TRACE (EDITOR);
661 
662 	if (column >= data_model->apparentCols ()) {
663 		RK_ASSERT (false);
664 		return;
665 	}
666 	if (column >= trueCols ()) {
667 		data_model->doInsertColumns (trueCols (), 1);
668 	}
669 	RKVariable* var = getObject (column);
670 	RK_ASSERT (var);
671 	var->setValueLabels (labels);
672 }
673 
headerData(int section,Qt::Orientation orientation,int role) const674 QVariant RKVarEditMetaModel::headerData (int section, Qt::Orientation orientation, int role) const {
675 	RK_TRACE (EDITOR);
676 
677 	if (orientation == Qt::Horizontal) {
678 		return data_model->headerData (section, orientation, role);
679 	}
680 
681 	if (role == Qt::DisplayRole) {
682 		if (section == NameRow) return (i18n ("Name"));
683 		if (section == LabelRow) return (i18n ("Label"));
684 		if (section == TypeRow) return (i18n ("Type"));
685 		if (section == FormatRow) return (i18n ("Format"));
686 		if (section == LevelsRow) return (i18n ("Levels"));
687 	}
688 	if (role == Qt::ToolTipRole) {
689 		if (section == NameRow) return (i18n ("Edit these fields to rename variables."));
690 		if (section == LabelRow) return (i18n ("A descriptive label for each column (optional)."));
691 		if (section == TypeRow) return (i18n ("Type of data."));
692 		if (section == FormatRow) return (i18n ("Double click on these fields to customize data display."));
693 		if (section == LevelsRow) return (i18n ("Double click on these fields to edit factor levels."));
694 	}
695 
696 	return QVariant ();
697 }
698 
getTextMatrix(const QItemSelectionRange & range) const699 RKTextMatrix RKVarEditMetaModel::getTextMatrix (const QItemSelectionRange& range) const {
700 	RK_TRACE (EDITOR);
701 
702 	if ((!range.isValid ()) || data_model->objects.isEmpty ()) return RKTextMatrix ();
703 
704 // NOTE: of course, when the range is small, this is terribly inefficient. On the other hand, it doesn't really matter, then, either.
705 	QItemSelectionRange erange = range.intersected (QItemSelectionRange (index (0, 0), index (trueRows () - 1, trueCols () - 1)));
706 	int top = erange.top ();
707 	int bottom = erange.bottom ();
708 	int left = erange.left ();
709 	int right = erange.right ();
710 
711 	RKTextMatrix ret;
712 	int tcol = 0;
713 	for (int col = left; col <= right; ++col) {
714 		int trow = 0;
715 		for (int row = top; row <= bottom; ++row) {
716 			QVariant celldata = data (index (row, col), Qt::EditRole);
717 			if (!celldata.isValid ()) {
718 				RK_ASSERT (false);
719 				break;
720 			}
721 			ret.setText (trow, tcol, celldata.toString ());
722 			++trow;
723 		}
724 		++tcol;
725 	}
726 
727 	return ret;
728 }
729 
blankRange(const QItemSelectionRange & range)730 void RKVarEditMetaModel::blankRange (const QItemSelectionRange& range) {
731 	RK_TRACE (EDITOR);
732 
733 	if ((!range.isValid ()) || data_model->objects.isEmpty ()) return;
734 
735 // NOTE: of course, when the range is small, this is terribly inefficient. On the other hand, it doesn't really matter, then, either.
736 	QItemSelectionRange erange = range.intersected (QItemSelectionRange (index (0, 0), index (trueRows () - 1, trueCols () - 1)));
737 	int top = erange.top ();
738 	int bottom = erange.bottom ();
739 	int left = erange.left ();
740 	int right = erange.right ();
741 
742 	for (int col = left; col <= right; ++col) {
743 		RKVariable* var = data_model->objects[col];
744 		var->lockSyncing (true);
745 		for (int row = top; row <= bottom; ++row) {
746 			setData (createIndex (row, col), QString (), Qt::EditRole);
747 		}
748 		var->lockSyncing (false);
749 	}
750 }
751 
setTextMatrix(const QModelIndex & offset,const RKTextMatrix & text,const QItemSelectionRange & confine_to)752 void RKVarEditMetaModel::setTextMatrix (const QModelIndex& offset, const RKTextMatrix& text, const QItemSelectionRange& confine_to) {
753 	RK_TRACE (EDITOR);
754 
755 	if ((!offset.isValid ()) || text.isEmpty ()) return;
756 
757 	int top = offset.row ();
758 	int left = offset.column ();
759 	int bottom = top + text.numRows () - 1;
760 	int right = left + text.numColumns () - 1;
761 	if (confine_to.isValid ()) {
762 		if (confine_to.top () > top) return;	// can't confine top-left. Should have been set by caller
763 		if (confine_to.left () > left) return;
764 		bottom = qMin (confine_to.bottom (), bottom);
765 		right = qMin (confine_to.right (), right);
766 	}
767 
768 // TODO: some models might not support column addition.
769 	if (right >= trueCols ()) data_model->doInsertColumns (trueCols (), right - trueCols () + 1);
770 	RK_ASSERT (right < data_model->objects.size ());
771 	bottom = qMin (bottom, trueRows () - 1);
772 
773 	int tcol = 0;
774 	for (int col = left; col <= right; ++col) {
775 		int trow = 0;
776 		RKVariable* var = data_model->objects[col];
777 		var->lockSyncing (true);
778 		for (int row = top; row <= bottom; ++row) {
779 			setData (index (row, col), text.getText (trow, tcol), Qt::EditRole);
780 			++trow;
781 		}
782 		var->lockSyncing (false);
783 		++tcol;
784 	}
785 }
786 
787 
788 /////////////////// RKVarEditDataFrameModel ////////////////////////
789 
790 
RKVarEditDataFrameModel(RContainerObject * dataframe,QObject * parent)791 RKVarEditDataFrameModel::RKVarEditDataFrameModel (RContainerObject* dataframe, QObject* parent) : RKVarEditModel (parent) {
792 	RK_TRACE (EDITOR);
793 
794 	init (dataframe);
795 }
796 
RKVarEditDataFrameModel(const QString & validized_name,RContainerObject * parent_object,RCommandChain * chain,int initial_cols,QObject * parent)797 RKVarEditDataFrameModel::RKVarEditDataFrameModel (const QString& validized_name, RContainerObject* parent_object, RCommandChain* chain, int initial_cols, QObject* parent) : RKVarEditModel (parent) {
798 	RK_TRACE (EDITOR);
799 
800 	RObject *object = parent_object->createPendingChild (validized_name, -1, true, true);
801 	RK_ASSERT (object->isDataFrame ());
802 	RContainerObject* df = static_cast<RContainerObject*> (object);
803 
804 // initialize the new object
805 	for (int i = 0; i < initial_cols; ++i) {
806 		RObject* child = df->createPendingChild (df->validizeName (QString ()), -1, false, false);
807 		RK_ASSERT (child->isVariable ());
808 		// let's start with one (empty) row, to avoid confusion
809 		static_cast<RKVariable*> (child)->setLength (1);
810 	}
811 
812 	init (df);
813 
814 	// creates the pending object in the backend
815 	pushTable (chain);
816 }
817 
init(RContainerObject * dataframe)818 void RKVarEditDataFrameModel::init (RContainerObject* dataframe) {
819 	RK_TRACE (EDITOR);
820 
821 	RKVarEditDataFrameModel::dataframe = dataframe;
822 
823 	trailing_rows = 1;
824 	trailing_cols = 1;
825 
826 	addNotificationType (RObjectListener::ChildAdded);
827 	addNotificationType (RObjectListener::ChildMoved);
828 	listenForObject (dataframe);
829 
830 	for (int i = 0; i < dataframe->numChildren (); ++i) {
831 		RObject* obj = dataframe->findChildByIndex (i);
832 		RK_ASSERT (obj);
833 		RK_ASSERT (obj->isVariable ());
834 		addObject (i, static_cast<RKVariable*> (obj));
835 	}
836 	rownames = dataframe->rowNames ();
837 	addObject (0, rownames);
838 	getMetaModel ()->var_col_offset = var_col_offset = 1;
839 }
840 
~RKVarEditDataFrameModel()841 RKVarEditDataFrameModel::~RKVarEditDataFrameModel () {
842 	RK_TRACE (EDITOR);
843 
844 	if (dataframe) stopListenForObject (dataframe);
845 }
846 
insertColumns(int column,int count,const QModelIndex & parent)847 bool RKVarEditDataFrameModel::insertColumns (int column, int count, const QModelIndex& parent) {
848 	RK_TRACE (EDITOR);
849 
850 	if (parent.isValid ()) {
851 		RK_ASSERT (false);
852 		return false;
853 	}
854 
855 	if (column > trueCols ()) column = trueCols ();
856 	if (column < var_col_offset) column = var_col_offset;
857 	for (int col = column; col < (column + count); ++col) {
858 		RObject *obj = dataframe->createPendingChild (dataframe->validizeName (QString ()), col-var_col_offset);
859 		RK_ASSERT (obj->isVariable ());
860 //		addObject (col, obj);	// the object will be added via RKModificationTracker::addObject -> this::childAdded. That will also take care of calling beginInsertColumns()/endInsertColumns()
861 
862 		RKGlobals::rInterface ()->issueCommand (new RCommand (".rk.data.frame.insert.column (" + dataframe->getFullName () + ", \"" + obj->getShortName () + "\", " + QString::number (col+1-var_col_offset) + ")", RCommand::App | RCommand::Sync));
863 	}
864 
865 	return true;
866 }
867 
removeColumns(int column,int count,const QModelIndex & parent)868 bool RKVarEditDataFrameModel::removeColumns (int column, int count, const QModelIndex& parent) {
869 	RK_TRACE (EDITOR);
870 
871 	if (parent.isValid ()) {
872 		RK_ASSERT (false);
873 		return false;
874 	}
875 
876 	if (column < var_col_offset) {
877 		RK_ASSERT (false);
878 		return false;
879 	}
880 
881 	while ((column + count) > objects.size ()) --count;
882 	for (int i = column + count - 1; i >= column; --i) {	// we start at the end so that the index remains valid
883 		RKGlobals::tracker ()->removeObject (objects[i]);
884 		// the comment in insertColumns, above: The object will be removed from our list in objectRemoved().
885 	}
886 	return true;
887 }
888 
doInsertRowsInBackend(int row,int count)889 void RKVarEditDataFrameModel::doInsertRowsInBackend (int row, int count) {
890 	RK_TRACE (EDITOR);
891 
892 	// TODO: most of the time we're only adding one row at a time, still we should have a function to add multiple rows at once.
893 	for (int i = row; i < row + count; ++i) {
894 		RKGlobals::rInterface ()->issueCommand (new RCommand (".rk.data.frame.insert.row (" + dataframe->getFullName () + ", " + QString::number (i+1) + ')', RCommand::App | RCommand::Sync));
895 	}
896 }
897 
doRemoveRowsInBackend(int row,int count)898 void RKVarEditDataFrameModel::doRemoveRowsInBackend (int row, int count) {
899 	RK_TRACE (EDITOR);
900 
901 	for (int i = row + count - 1; i >= row; --i) {
902 		RKGlobals::rInterface ()->issueCommand (new RCommand (".rk.data.frame.delete.row (" + dataframe->getFullName () + ", " + QString::number (i+1) + ')', RCommand::App | RCommand::Sync));
903 	}
904 }
905 
objectRemoved(RObject * object)906 void RKVarEditDataFrameModel::objectRemoved (RObject* object) {
907 	RK_TRACE (EDITOR);
908 
909 	if (object == dataframe) {
910 		while (!objects.isEmpty ()) RKVarEditModel::objectRemoved (objects.last());		// NOTE: The rownames object (index position 0) must always go away last!
911 		stopListenForObject (dataframe);
912 		dataframe = 0;
913 	}
914 
915 	RKVarEditModel::objectRemoved (object);
916 
917 	// if the dataframe is gone, the editor will most certainly want to auto-destruct.
918 	// since the model will be taken down as well, this has to come last in the function.
919 	if (!dataframe) emit (modelObjectDestroyed ());
920 }
921 
childAdded(int index,RObject * parent)922 void RKVarEditDataFrameModel::childAdded (int index, RObject* parent) {
923 	RK_TRACE (EDITOR);
924 
925 	if (parent == dataframe) {
926 		RObject* child = dataframe->findChildByIndex (index);
927 		RK_ASSERT (child);
928 
929 		if (child->isVariable ()) addObject (index + var_col_offset, static_cast<RKVariable*> (child));
930 		else RK_ASSERT (false);
931 	}
932 }
933 
childMoved(int old_index,int new_index,RObject * parent)934 void RKVarEditDataFrameModel::childMoved (int old_index, int new_index, RObject* parent) {
935 	RK_TRACE (EDITOR);
936 
937 	if (parent == dataframe) {
938 		RObject *child = dataframe->findChildByIndex (new_index);	// it's at the new position, already
939 		RK_ASSERT (objects.size () > (old_index + var_col_offset));
940 		RK_ASSERT (child == objects[old_index + var_col_offset]);
941 		// if an object has changed position, there should be at least two objects left. Hence, the objectRemoved-call will never lead to editor destruction
942 		RK_ASSERT (objects.size () >= (var_col_offset + 2));
943 		objectRemoved (child);
944 
945 		RK_ASSERT (child->isVariable ());
946 		addObject (new_index + var_col_offset, static_cast<RKVariable*> (child));
947 	} else {
948 		// even though we are listening for the child objects as well, we should see move notifications
949 		// only for children of the parent.
950 		RK_ASSERT (false);
951 	}
952 }
953 
doInsertColumns(int index,int count)954 void RKVarEditDataFrameModel::doInsertColumns (int index, int count) {
955 	RK_TRACE (EDITOR);
956 
957 	insertColumns (index, count);
958 }
959 
pushTable(RCommandChain * sync_chain)960 void RKVarEditDataFrameModel::pushTable (RCommandChain *sync_chain) {
961 	RK_TRACE (EDITOR);
962 
963 	// first push real data
964 	QString command = dataframe->getFullName ();
965 	command.append (" <- data.frame (");
966 
967 	RK_ASSERT (objects.size ());
968 	RKVariable* child = objects[0];
969 	QString na_vector = "=as.numeric (rep (NA, " + QString::number (child->getLength ()) + "))";
970 	for (int col=var_col_offset; col < objects.size (); ++col) {
971 		if (col > var_col_offset) command.append (", ");
972 		command.append (objects[col]->getShortName () + na_vector);
973 	}
974 	command.append (")");
975 
976 	// push all children
977 	RKGlobals::rInterface ()->issueCommand (new RCommand (command, RCommand::Sync), sync_chain);
978 	for (int col=0; col < objects.size (); ++col) {
979 		objects[col]->restore (sync_chain);
980 	}
981 
982 	// now store the meta-data
983 	dataframe->writeMetaData (sync_chain);
984 
985 	RKGlobals::rInterface ()->issueCommand (new RCommand (QString (), RCommand::Sync | RCommand::EmptyCommand | RCommand::ObjectListUpdate), sync_chain);
986 }
987 
restoreObject(RObject * object,RCommandChain * chain)988 void RKVarEditDataFrameModel::restoreObject (RObject* object, RCommandChain* chain) {
989 	RK_TRACE (EDITOR);
990 
991 	if (object == dataframe) {
992 		pushTable (chain);
993 	} else {
994 		RKVarEditModel::restoreObject (object, chain);
995 	}
996 }
997 
998 
999