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