1 /***************************************************************************
2 rktableview - description
3 -------------------
4 begin : Tue Nov 06
5 copyright : (C) 2012 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 "rktableview.h"
19
20 #include <QKeyEvent>
21
22 #include "../debug.h"
23
RKTableView(QWidget * parent)24 RKTableView::RKTableView (QWidget *parent) : QTableView (parent) {
25 RK_TRACE (MISC);
26
27 trailing_columns = 0;
28 trailing_rows = 0;
29 }
30
~RKTableView()31 RKTableView::~RKTableView () {
32 RK_TRACE (MISC);
33 }
34
apparentColumns() const35 int RKTableView::apparentColumns () const {
36 return model ()->columnCount ();
37 }
38
apparentRows() const39 int RKTableView::apparentRows () const {
40 return model ()->rowCount ();
41 }
42
getSelectionBoundaries() const43 QItemSelectionRange RKTableView::getSelectionBoundaries () const {
44 RK_TRACE (MISC);
45
46 RK_ASSERT (selectionModel ());
47 QItemSelection sel = selectionModel ()->selection ();
48 if (sel.isEmpty ()){
49 QModelIndex current = currentIndex ();
50 if ((!current.isValid ()) || isIndexHidden (current)) return (QItemSelectionRange ());
51
52 return (QItemSelectionRange (currentIndex (), currentIndex ()));
53 } else {
54 RK_ASSERT (sel.size () == 1);
55
56 QItemSelectionRange range = sel[0];
57 while (isColumnHidden (range.left ())) {
58 // purge hidden leading columns from the range
59 range = QItemSelectionRange (model ()->index (range.top (), range.left () + 1, rootIndex ()), range.bottomRight ());
60 }
61 return (range);
62 }
63 }
64
editorDone(QWidget * editor,RKItemDelegate::EditorDoneReason reason)65 void RKTableView::editorDone (QWidget* editor, RKItemDelegate::EditorDoneReason reason) {
66 RK_TRACE (EDITOR);
67
68 int row = currentIndex ().row ();
69 int col = currentIndex ().column ();
70
71 closeEditor (editor, QAbstractItemDelegate::NoHint);
72
73 if (reason == RKItemDelegate::EditorExitRight) ++col;
74 else if (reason == RKItemDelegate::EditorExitLeft) --col;
75 else if (reason == RKItemDelegate::EditorExitUp) --row;
76 else if (reason == RKItemDelegate::EditorExitDown) ++row;
77
78 if (row >= trueRows ()) {
79 // if we have edited the trailing row, a new row may have been inserted, apparently *above* the
80 // current index. We need to fix this up. Basically, we can only ever be in the last row after
81 // a reject, or an exit to the next row
82 if ((reason != RKItemDelegate::EditorExitDown) && (reason != RKItemDelegate::EditorReject)) --row;
83 }
84 if (col >= trueColumns ()) {
85 // see above
86 if ((reason != RKItemDelegate::EditorExitRight) && (reason != RKItemDelegate::EditorReject)) --col;
87 }
88
89 row = qMin (row, apparentRows () - 1);
90 col = qMin (col, apparentColumns () - 1);
91 setCurrentIndex (model ()->index (row, col));
92 }
93
setRKItemDelegate(RKItemDelegate * delegate)94 void RKTableView::setRKItemDelegate (RKItemDelegate* delegate) {
95 RK_TRACE (EDITOR);
96
97 setItemDelegate (delegate);
98 connect (delegate, &RKItemDelegate::doCloseEditor, this, &RKTableView::editorDone);
99 }
100
keyPressEvent(QKeyEvent * e)101 void RKTableView::keyPressEvent (QKeyEvent *e) {
102 RK_TRACE (EDITOR);
103
104 if ((e->key () == Qt::Key_Delete) || (e->key () == Qt::Key_Backspace)) {
105 emit (blankSelectionRequest ());
106 e->accept ();
107 } else {
108 QTableView::keyPressEvent (e);
109 scrollTo (currentIndex ()); // why oh why isn't this the default behavior?
110 }
111 }
112
113
114 /////////////////// RKItemDelegate /////////////////////
115
116 #include "../dataeditor/rkvareditmodel.h"
117 #include "celleditor.h"
118 #include "editformatdialog.h"
119 #include "editlabelsdialog.h"
120
RKItemDelegate(QObject * parent,RKVarEditModel * datamodel)121 RKItemDelegate::RKItemDelegate (QObject *parent, RKVarEditModel* datamodel) : QItemDelegate (parent) {
122 RK_TRACE (EDITOR);
123
124 RKItemDelegate::datamodel = datamodel;
125 metamodel = 0;
126 genericmodel = 0;
127 locked_for_modal_editor = false;
128 }
129
RKItemDelegate(QObject * parent,RKVarEditMetaModel * metamodel)130 RKItemDelegate::RKItemDelegate (QObject *parent, RKVarEditMetaModel* metamodel) : QItemDelegate (parent) {
131 RK_TRACE (EDITOR);
132
133 RKItemDelegate::metamodel = metamodel;
134 datamodel = 0;
135 genericmodel = 0;
136 locked_for_modal_editor = false;
137 }
138
RKItemDelegate(QObject * parent,QAbstractItemModel * model,bool dummy)139 RKItemDelegate::RKItemDelegate (QObject *parent, QAbstractItemModel* model, bool dummy) : QItemDelegate (parent) {
140 RK_TRACE (EDITOR);
141 Q_UNUSED (dummy);
142
143 genericmodel = model;
144 metamodel = 0;
145 datamodel = 0;
146 locked_for_modal_editor = false;
147 }
148
~RKItemDelegate()149 RKItemDelegate::~RKItemDelegate () {
150 RK_TRACE (EDITOR);
151 }
152
createEditor(QWidget * parent,const QStyleOptionViewItem & option,const QModelIndex & index) const153 QWidget* RKItemDelegate::createEditor (QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const {
154 RK_TRACE (EDITOR);
155
156 QWidget* ed;
157 if (metamodel) {
158 int row = index.row ();
159 if (row == RKVarEditMetaModel::FormatRow) {
160 ed = new EditFormatDialogProxy (parent);
161 const_cast<RKItemDelegate*> (this)->locked_for_modal_editor = true;
162 } else if (row == RKVarEditMetaModel::LevelsRow) {
163 ed = new EditLabelsDialogProxy (parent);
164 const_cast<RKItemDelegate*> (this)->locked_for_modal_editor = true;
165 } else {
166 ed = new CellEditor (parent);
167 }
168 } else {
169 RK_ASSERT (datamodel || genericmodel);
170 ed = new CellEditor (parent);
171 }
172
173 ed->setFont (option.font);
174 // NOTE: Can't use new SIGNAL/SLOT syntax, here, as the editors are of different types (TODO: define a common base class)
175 connect (ed, SIGNAL (done(QWidget*,RKItemDelegate::EditorDoneReason)), this, SLOT (editorDone(QWidget*,RKItemDelegate::EditorDoneReason)));
176 return ed;
177 }
178
setEditorData(QWidget * editor,const QModelIndex & index) const179 void RKItemDelegate::setEditorData (QWidget* editor, const QModelIndex& index) const {
180 RK_TRACE (EDITOR);
181
182 if (!index.isValid ()) return;
183
184 if (metamodel) {
185 int row = index.row ();
186 if (row == RKVarEditMetaModel::FormatRow) {
187 EditFormatDialogProxy* fed = static_cast<EditFormatDialogProxy*> (editor);
188 fed->initialize (RKVariable::parseFormattingOptionsString (metamodel->data (index, Qt::EditRole).toString ()), metamodel->headerData (index.column (), Qt::Horizontal).toString ());
189 } else if (row == RKVarEditMetaModel::LevelsRow) {
190 EditLabelsDialogProxy* led = static_cast<EditLabelsDialogProxy*> (editor);
191 led->initialize (metamodel->getValueLabels (index.column ()), metamodel->headerData (index.column (), Qt::Horizontal).toString ());
192 } else {
193 CellEditor* ced = static_cast<CellEditor*> (editor);
194 ced->setText (metamodel->data (index, Qt::EditRole).toString ());
195
196 if (row == RKVarEditMetaModel::TypeRow) {
197 RObject::ValueLabels labels;
198 for (int i = RObject::MinKnownDataType; i <= RObject::MaxKnownDataType; ++i) {
199 labels.insert (QString::number (i), RObject::typeToText ((RObject::RDataType) i));
200 }
201 ced->setValueLabels (labels);
202 }
203 }
204 } else {
205 CellEditor* ced = static_cast<CellEditor*> (editor);
206 ced->setText (index.data (Qt::EditRole).toString ());
207 if (datamodel) {
208 if (index.column () < datamodel->trueCols ()) {
209 ced->setValueLabels (datamodel->getObject (index.column ())->getValueLabels ());
210 }
211 } else {
212 RK_ASSERT (genericmodel);
213 }
214 }
215 }
216
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const217 void RKItemDelegate::setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const {
218 RK_TRACE (EDITOR);
219
220 if (!index.isValid ()) return;
221
222 if (metamodel) {
223 RK_ASSERT (model == metamodel);
224
225 int row = index.row ();
226 if (row == RKVarEditMetaModel::FormatRow) {
227 EditFormatDialogProxy* fed = static_cast<EditFormatDialogProxy*> (editor);
228 model->setData (index, RKVariable::formattingOptionsToString (fed->getOptions ()), Qt::EditRole);
229 return;
230 } else if (row == RKVarEditMetaModel::LevelsRow) {
231 EditLabelsDialogProxy* led = static_cast<EditLabelsDialogProxy*> (editor);
232 metamodel->setValueLabels (index.column (), led->getLabels ());
233 return;
234 } // else all others use the regular CellEditor
235 } else if (datamodel) {
236 RK_ASSERT (model == datamodel);
237 } else {
238 RK_ASSERT (genericmodel && (model == genericmodel));
239 }
240
241 CellEditor* ced = static_cast<CellEditor*> (editor);
242 model->setData (index, ced->text (), Qt::EditRole);
243 }
244
eventFilter(QObject * object,QEvent * event)245 bool RKItemDelegate::eventFilter (QObject* object, QEvent* event) {
246 RK_TRACE (EDITOR);
247
248 if (locked_for_modal_editor) return false; // Needed on MacOSX: Pressing Ok in one of the modal editors seems to
249 // generate a Return-like event.
250 // This would be handled *before* the editor had a chance to update its data,
251 // thus committing the old, not new state.
252
253 QWidget *editor = qobject_cast<QWidget*> (object);
254 if (!editor) return false;
255
256 if (event->type() == QEvent::KeyPress) {
257 QKeyEvent* ke = static_cast<QKeyEvent *> (event);
258 if (ke->key () == Qt::Key_Tab) editorDone (editor, EditorExitRight);
259 else if (ke->key () == Qt::Key_Tab) editorDone (editor, EditorExitRight);
260 else if (ke->key () == Qt::Key_Enter) editorDone (editor, EditorExitDown);
261 else if (ke->key () == Qt::Key_Return) editorDone (editor, EditorExitDown);
262 else return QItemDelegate::eventFilter (editor, event);
263 return true;
264 }
265 return QItemDelegate::eventFilter (editor, event);
266 }
267
editorDone(QWidget * editor,RKItemDelegate::EditorDoneReason reason)268 void RKItemDelegate::editorDone (QWidget* editor, RKItemDelegate::EditorDoneReason reason) {
269 RK_TRACE (EDITOR);
270
271 if (reason != EditorReject) commitData (editor);
272 emit (doCloseEditor (editor, reason));
273 locked_for_modal_editor = false;
274 }
275
276