1 | /* This file is part of the KDE project |
2 | Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net> |
3 | Copyright (C) 2008 Thomas Zander <zander@kde.org> |
4 | |
5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Library General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2 of the License, or (at your option) any later version. |
9 | |
10 | This library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Library General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Library General Public License |
16 | along with this library; see the file COPYING.LIB. If not, write to |
17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #include "Binding.h" |
22 | #include "BindingModel.h" |
23 | |
24 | #include <QRect> |
25 | |
26 | #include <kdebug.h> |
27 | |
28 | #include "CellStorage.h" |
29 | #include "Map.h" |
30 | #include "Sheet.h" |
31 | #include "Value.h" |
32 | |
33 | using namespace Calligra::Sheets; |
34 | |
35 | class Binding::Private : public QSharedData |
36 | { |
37 | public: |
38 | BindingModel* model; |
39 | Private(Binding *q) : model(new BindingModel(q)) {} |
40 | ~Private() { delete model; } |
41 | }; |
42 | |
43 | |
44 | Binding::Binding() |
45 | : d(new Private(this)) |
46 | { |
47 | } |
48 | |
49 | Binding::Binding(const Region& region) |
50 | : d(new Private(this)) |
51 | { |
52 | Q_ASSERT(region.isValid()); |
53 | d->model->setRegion(region); |
54 | } |
55 | |
56 | Binding::Binding(const Binding& other) |
57 | : d(other.d) |
58 | { |
59 | } |
60 | |
61 | Binding::~Binding() |
62 | { |
63 | } |
64 | |
65 | bool Binding::isEmpty() const |
66 | { |
67 | return d->model->region().isEmpty(); |
68 | } |
69 | |
70 | QAbstractItemModel* Binding::model() const |
71 | { |
72 | return d->model; |
73 | } |
74 | |
75 | const Calligra::Sheets::Region& Binding::region() const |
76 | { |
77 | return d->model->region(); |
78 | } |
79 | |
80 | void Binding::setRegion(const Region& region) |
81 | { |
82 | d->model->setRegion(region); |
83 | } |
84 | |
85 | void Binding::update(const Region& region) |
86 | { |
87 | QRect rect; |
88 | Region changedRegion; |
89 | const QPoint offset = d->model->region().firstRange().topLeft(); |
90 | const QRect range = d->model->region().firstRange(); |
91 | const Sheet* sheet = d->model->region().firstSheet(); |
92 | Region::ConstIterator end(region.constEnd()); |
93 | for (Region::ConstIterator it = region.constBegin(); it != end; ++it) { |
94 | if (sheet != (*it)->sheet()) |
95 | continue; |
96 | rect = range & (*it)->rect(); |
97 | rect.translate(-offset.x(), -offset.y()); |
98 | if (rect.isValid()) { |
99 | d->model->emitDataChanged(rect); |
100 | changedRegion.add(rect, (*it)->sheet()); |
101 | } |
102 | } |
103 | d->model->emitChanged(changedRegion); |
104 | } |
105 | |
106 | void Binding::operator=(const Binding & other) |
107 | { |
108 | d = other.d; |
109 | } |
110 | |
111 | bool Binding::operator==(const Binding& other) const |
112 | { |
113 | return d == other.d; |
114 | } |
115 | |
116 | bool Binding::operator<(const Binding& other) const |
117 | { |
118 | return d < other.d; |
119 | } |
120 | |
121 | QHash<QString, QVector<QRect> > BindingModel::cellRegion() const |
122 | { |
123 | QHash<QString, QVector<QRect> > answer; |
124 | Region::ConstIterator end = m_region.constEnd(); |
125 | for (Region::ConstIterator it = m_region.constBegin(); it != end; ++it) { |
126 | if (!(*it)->isValid()) { |
127 | continue; |
128 | } |
129 | answer[(*it)->name()].append((*it)->rect()); |
130 | } |
131 | return answer; |
132 | } |
133 | |
134 | bool BindingModel::setCellRegion(const QString& regionName) |
135 | { |
136 | Q_ASSERT(m_region.isValid()); |
137 | Q_ASSERT(m_region.firstSheet()); |
138 | const Map* const map = m_region.firstSheet()->map(); |
139 | const Region region = Region(regionName, map); |
140 | if (!region.isValid()) { |
141 | kDebug() << qPrintable(regionName) << "is not a valid region." ; |
142 | return false; |
143 | } |
144 | // Clear the old binding. |
145 | Region::ConstIterator end = m_region.constEnd(); |
146 | for (Region::ConstIterator it = m_region.constBegin(); it != end; ++it) { |
147 | if (!(*it)->isValid()) { |
148 | continue; |
149 | } |
150 | // FIXME Stefan: This may also clear other bindings! |
151 | (*it)->sheet()->cellStorage()->setBinding(Region((*it)->rect(), (*it)->sheet()), Binding()); |
152 | } |
153 | // Set the new region |
154 | m_region = region; |
155 | end = m_region.constEnd(); |
156 | for (Region::ConstIterator it = m_region.constBegin(); it != end; ++it) { |
157 | if (!(*it)->isValid()) { |
158 | continue; |
159 | } |
160 | (*it)->sheet()->cellStorage()->setBinding(Region((*it)->rect(), (*it)->sheet()), *m_binding); |
161 | } |
162 | return true; |
163 | } |
164 | |
165 | |
166 | /////// BindingModel |
167 | |
168 | BindingModel::BindingModel(Binding* binding, QObject *parent) |
169 | : QAbstractTableModel(parent) |
170 | , m_binding(binding) |
171 | { |
172 | } |
173 | |
174 | bool BindingModel::isCellRegionValid(const QString& regionName) const |
175 | { |
176 | Q_CHECK_PTR(m_region.firstSheet()); |
177 | Q_CHECK_PTR(m_region.firstSheet()->map()); |
178 | return Region(regionName, m_region.firstSheet()->map()).isValid(); |
179 | } |
180 | |
181 | void BindingModel::emitChanged(const Region& region) |
182 | { |
183 | emit changed(region); |
184 | } |
185 | |
186 | void BindingModel::emitDataChanged(const QRect& rect) |
187 | { |
188 | const QPoint tl = rect.topLeft(); |
189 | const QPoint br = rect.bottomRight(); |
190 | //kDebug(36005) << "emit QAbstractItemModel::dataChanged" << QString("%1:%2").arg(tl).arg(br); |
191 | emit dataChanged(index(tl.y(), tl.x()), index(br.y(), br.x())); |
192 | } |
193 | |
194 | QVariant BindingModel::data(const QModelIndex& index, int role) const |
195 | { |
196 | if ((m_region.isEmpty()) || (role != Qt::EditRole && role != Qt::DisplayRole)) |
197 | return QVariant(); |
198 | const QPoint offset = m_region.firstRange().topLeft(); |
199 | const Sheet* sheet = m_region.firstSheet(); |
200 | int row = offset.y() + index.row(); |
201 | int column = offset.x() + index.column(); |
202 | Value value = sheet->cellStorage()->value(column, row); |
203 | |
204 | switch (role) { |
205 | case Qt::DisplayRole: { |
206 | // return the in the cell displayed test |
207 | Cell c(sheet, column, row); |
208 | bool showFormula = false; |
209 | return c.displayText(Style(), &value, &showFormula); |
210 | } |
211 | case Qt::EditRole: { |
212 | // return the actual cell value |
213 | // KoChart::Value is either: |
214 | // - a double (interpreted as a value) |
215 | // - a QString (interpreted as a label) |
216 | // - a QDateTime (interpreted as a date/time value) |
217 | // - Invalid (interpreted as empty) |
218 | QVariant variant; |
219 | switch (value.type()) { |
220 | case Value::Float: |
221 | case Value::Integer: |
222 | if (value.format() == Value::fmt_DateTime || |
223 | value.format() == Value::fmt_Date || |
224 | value.format() == Value::fmt_Time) { |
225 | variant.setValue<QDateTime>(value.asDateTime(sheet->map()->calculationSettings())); |
226 | break; |
227 | } // fall through |
228 | case Value::Boolean: |
229 | case Value::Complex: |
230 | case Value::Array: |
231 | variant.setValue<double>(numToDouble(value.asFloat())); |
232 | break; |
233 | case Value::String: |
234 | case Value::Error: |
235 | variant.setValue<QString>(value.asString()); |
236 | break; |
237 | case Value::Empty: |
238 | case Value::CellRange: |
239 | default: |
240 | break; |
241 | } |
242 | return variant; |
243 | } |
244 | } |
245 | //kDebug() << index.column() <<"," << index.row() <<"," << variant; |
246 | return QVariant(); |
247 | } |
248 | |
249 | const Calligra::Sheets::Region& BindingModel::region() const |
250 | { |
251 | return m_region; |
252 | } |
253 | |
254 | QVariant BindingModel::(int section, Qt::Orientation orientation, int role) const |
255 | { |
256 | if ((m_region.isEmpty()) || (role != Qt::EditRole && role != Qt::DisplayRole)) |
257 | return QVariant(); |
258 | const QPoint offset = m_region.firstRange().topLeft(); |
259 | const int col = (orientation == Qt::Vertical) ? offset.x() : offset.x() + section; |
260 | const int row = (orientation == Qt::Vertical) ? offset.y() + section : offset.y(); |
261 | const Sheet* sheet = m_region.firstSheet(); |
262 | const Value value = sheet->cellStorage()->value(col, row); |
263 | return value.asVariant(); |
264 | } |
265 | |
266 | int BindingModel::rowCount(const QModelIndex& parent) const |
267 | { |
268 | Q_UNUSED(parent); |
269 | return m_region.isEmpty() ? 0 : m_region.firstRange().height(); |
270 | } |
271 | |
272 | int BindingModel::columnCount(const QModelIndex& parent) const |
273 | { |
274 | Q_UNUSED(parent); |
275 | return m_region.isEmpty() ? 0 : m_region.firstRange().width(); |
276 | } |
277 | |
278 | void BindingModel::setRegion(const Region& region) |
279 | { |
280 | m_region = region; |
281 | } |
282 | |
283 | #include "BindingModel.moc" |
284 | |