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