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
33using namespace Calligra::Sheets;
34
35class Binding::Private : public QSharedData
36{
37public:
38 BindingModel* model;
39 Private(Binding *q) : model(new BindingModel(q)) {}
40 ~Private() { delete model; }
41};
42
43
44Binding::Binding()
45 : d(new Private(this))
46{
47}
48
49Binding::Binding(const Region& region)
50 : d(new Private(this))
51{
52 Q_ASSERT(region.isValid());
53 d->model->setRegion(region);
54}
55
56Binding::Binding(const Binding& other)
57 : d(other.d)
58{
59}
60
61Binding::~Binding()
62{
63}
64
65bool Binding::isEmpty() const
66{
67 return d->model->region().isEmpty();
68}
69
70QAbstractItemModel* Binding::model() const
71{
72 return d->model;
73}
74
75const Calligra::Sheets::Region& Binding::region() const
76{
77 return d->model->region();
78}
79
80void Binding::setRegion(const Region& region)
81{
82 d->model->setRegion(region);
83}
84
85void 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
106void Binding::operator=(const Binding & other)
107{
108 d = other.d;
109}
110
111bool Binding::operator==(const Binding& other) const
112{
113 return d == other.d;
114}
115
116bool Binding::operator<(const Binding& other) const
117{
118 return d < other.d;
119}
120
121QHash<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
134bool 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
168BindingModel::BindingModel(Binding* binding, QObject *parent)
169 : QAbstractTableModel(parent)
170 , m_binding(binding)
171{
172}
173
174bool 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
181void BindingModel::emitChanged(const Region& region)
182{
183 emit changed(region);
184}
185
186void 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
194QVariant 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
249const Calligra::Sheets::Region& BindingModel::region() const
250{
251 return m_region;
252}
253
254QVariant BindingModel::headerData(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
266int BindingModel::rowCount(const QModelIndex& parent) const
267{
268 Q_UNUSED(parent);
269 return m_region.isEmpty() ? 0 : m_region.firstRange().height();
270}
271
272int BindingModel::columnCount(const QModelIndex& parent) const
273{
274 Q_UNUSED(parent);
275 return m_region.isEmpty() ? 0 : m_region.firstRange().width();
276}
277
278void BindingModel::setRegion(const Region& region)
279{
280 m_region = region;
281}
282
283#include "BindingModel.moc"
284