source: trunk/src/scripttools/debugging/qscriptdebuggerlocalsmodel.cpp@ 561

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 31.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtSCriptTools module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qscriptdebuggerlocalsmodel_p.h"
43#include "qscriptdebuggercommandschedulerjob_p.h"
44#include "qscriptdebuggervalue_p.h"
45#include "qscriptdebuggerresponse_p.h"
46#include "qscriptdebuggerevent_p.h"
47#include "qscriptdebuggervalueproperty_p.h"
48#include "qscriptdebuggercommandschedulerinterface_p.h"
49#include "qscriptdebuggercommandschedulerfrontend_p.h"
50#include "qscriptdebuggerjobschedulerinterface_p.h"
51#include "qscriptdebuggerobjectsnapshotdelta_p.h"
52
53#include "private/qabstractitemmodel_p.h"
54
55#include <QtCore/qdebug.h>
56#include <QtCore/qcoreapplication.h>
57#include <QtGui/qbrush.h>
58#include <QtGui/qfont.h>
59
60Q_DECLARE_METATYPE(QScriptDebuggerObjectSnapshotDelta)
61
62QT_BEGIN_NAMESPACE
63
64struct QScriptDebuggerLocalsModelNode
65{
66 enum PopulationState {
67 NotPopulated,
68 Populating,
69 Populated
70 };
71
72 QScriptDebuggerLocalsModelNode()
73 : parent(0), populationState(NotPopulated), snapshotId(-1), changed(false) {}
74
75 QScriptDebuggerLocalsModelNode(
76 const QScriptDebuggerValueProperty &prop,
77 QScriptDebuggerLocalsModelNode *par)
78 : property(prop), parent(par),
79 populationState(NotPopulated), snapshotId(-1), changed(false)
80 {
81 parent->children.append(this);
82 }
83
84 ~QScriptDebuggerLocalsModelNode() { qDeleteAll(children); }
85
86 QScriptDebuggerLocalsModelNode *findChild(const QString &name)
87 {
88 for (int i = 0; i < children.size(); ++i) {
89 QScriptDebuggerLocalsModelNode *child = children.at(i);
90 if (child->property.name() == name)
91 return child;
92 }
93 return 0;
94 }
95
96 QScriptDebuggerValueProperty property;
97 QScriptDebuggerLocalsModelNode *parent;
98 QList<QScriptDebuggerLocalsModelNode*> children;
99 PopulationState populationState;
100 int snapshotId;
101 bool changed;
102};
103
104class QScriptDebuggerLocalsModelPrivate
105 : public QAbstractItemModelPrivate
106{
107 Q_DECLARE_PUBLIC(QScriptDebuggerLocalsModel)
108public:
109 QScriptDebuggerLocalsModelPrivate();
110 ~QScriptDebuggerLocalsModelPrivate();
111
112 static QScriptDebuggerLocalsModelPrivate *get(QScriptDebuggerLocalsModel *q);
113
114 QModelIndex addTopLevelObject(const QString &name, const QScriptDebuggerValue &object);
115
116 QScriptDebuggerLocalsModelNode *nodeFromIndex(const QModelIndex &index) const;
117 QModelIndex indexFromNode(QScriptDebuggerLocalsModelNode *node) const;
118 bool isTopLevelNode(QScriptDebuggerLocalsModelNode *node) const;
119
120 void populateIndex(const QModelIndex &index);
121 void reallyPopulateIndex(const QModelIndex &index,
122 const QScriptDebuggerValuePropertyList &props);
123 void syncIndex(const QModelIndex &index);
124 void reallySyncIndex(const QModelIndex &index,
125 const QScriptDebuggerObjectSnapshotDelta &delta);
126 void syncTopLevelNodes();
127 void removeTopLevelNodes();
128 void emitScopeObjectAvailable(const QModelIndex &index);
129
130 void emitDataChanged(const QModelIndex &tl, const QModelIndex &br);
131 void removeChild(const QModelIndex &parentIndex,
132 QScriptDebuggerLocalsModelNode *parentNode, int row);
133 void depopulate(QScriptDebuggerLocalsModelNode *node);
134 void repopulate(QScriptDebuggerLocalsModelNode *node);
135 void addChildren(const QModelIndex &parentIndex,
136 QScriptDebuggerLocalsModelNode *parentNode,
137 const QScriptDebuggerValuePropertyList &props);
138
139 void deleteObjectSnapshots(const QList<qint64> &snapshotIds);
140 void deleteAllObjectSnapshots();
141
142 QScriptDebuggerJobSchedulerInterface *jobScheduler;
143 QScriptDebuggerCommandSchedulerInterface *commandScheduler;
144 QScriptDebuggerLocalsModelNode *invisibleRootNode;
145 int frameIndex;
146};
147
148QScriptDebuggerLocalsModelPrivate::QScriptDebuggerLocalsModelPrivate()
149{
150 invisibleRootNode = new QScriptDebuggerLocalsModelNode();
151 frameIndex = -1;
152}
153
154QScriptDebuggerLocalsModelPrivate::~QScriptDebuggerLocalsModelPrivate()
155{
156 delete invisibleRootNode;
157}
158
159void QScriptDebuggerLocalsModelPrivate::emitDataChanged(const QModelIndex &tl, const QModelIndex &br)
160{
161 q_func()->dataChanged(tl, br);
162}
163
164static QList<qint64> findSnapshotIdsRecursively(QScriptDebuggerLocalsModelNode *root)
165{
166 QList<qint64> result;
167 if (root->snapshotId == -1) {
168 Q_ASSERT(root->children.isEmpty());
169 return result;
170 }
171 QList<QScriptDebuggerLocalsModelNode*> nodeStack;
172 nodeStack.append(root);
173 while (!nodeStack.isEmpty()) {
174 QScriptDebuggerLocalsModelNode *node = nodeStack.takeFirst();
175 result.append(node->snapshotId);
176 for (int i = 0; i < node->children.count(); ++i) {
177 QScriptDebuggerLocalsModelNode *child = node->children.at(i);
178 if (child->snapshotId != -1)
179 nodeStack.prepend(child);
180 }
181 }
182 return result;
183}
184
185void QScriptDebuggerLocalsModelPrivate::removeChild(const QModelIndex &parentIndex,
186 QScriptDebuggerLocalsModelNode *parentNode,
187 int row)
188{
189 Q_Q(QScriptDebuggerLocalsModel);
190 q->beginRemoveRows(parentIndex, row, row);
191 QScriptDebuggerLocalsModelNode *child = parentNode->children.takeAt(row);
192 QList<qint64> snapshotIds = findSnapshotIdsRecursively(child);
193 delete child;
194 q->endRemoveRows();
195 deleteObjectSnapshots(snapshotIds);
196}
197
198void QScriptDebuggerLocalsModelPrivate::depopulate(QScriptDebuggerLocalsModelNode *node)
199{
200 Q_Q(QScriptDebuggerLocalsModel);
201 bool hasChildren = !node->children.isEmpty();
202 if (hasChildren)
203 q->beginRemoveRows(indexFromNode(node), 0, node->children.count() - 1);
204 QList<qint64> snapshotIds = findSnapshotIdsRecursively(node);
205 qDeleteAll(node->children);
206 node->children.clear();
207 node->snapshotId = -1;
208 node->populationState = QScriptDebuggerLocalsModelNode::NotPopulated;
209 if (hasChildren)
210 q->endRemoveRows();
211 deleteObjectSnapshots(snapshotIds);
212}
213
214void QScriptDebuggerLocalsModelPrivate::repopulate(QScriptDebuggerLocalsModelNode *node)
215{
216 if (node->populationState != QScriptDebuggerLocalsModelNode::Populated)
217 return;
218 depopulate(node);
219 if (node->property.value().type() == QScriptDebuggerValue::ObjectValue)
220 populateIndex(indexFromNode(node));
221}
222
223void QScriptDebuggerLocalsModelPrivate::addChildren(const QModelIndex &parentIndex,
224 QScriptDebuggerLocalsModelNode *parentNode,
225 const QScriptDebuggerValuePropertyList &props)
226{
227 Q_Q(QScriptDebuggerLocalsModel);
228 if (props.isEmpty())
229 return;
230 int first = parentNode->children.size();
231 int last = first + props.size() - 1;
232 q->beginInsertRows(parentIndex, first, last);
233 for (int i = 0; i < props.size(); ++i)
234 new QScriptDebuggerLocalsModelNode(props.at(i), parentNode);
235 q->endInsertRows();
236}
237
238void QScriptDebuggerLocalsModelPrivate::deleteObjectSnapshots(const QList<qint64> &snapshotIds)
239{
240 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler, 0);
241 for (int i = 0; i < snapshotIds.size(); ++i)
242 frontend.scheduleDeleteScriptObjectSnapshot(snapshotIds.at(i));
243}
244
245void QScriptDebuggerLocalsModelPrivate::deleteAllObjectSnapshots()
246{
247 QList<qint64> snapshotIds;
248 for (int i = 0; i < invisibleRootNode->children.count(); ++i)
249 snapshotIds += findSnapshotIdsRecursively(invisibleRootNode->children.at(i));
250 deleteObjectSnapshots(snapshotIds);
251}
252
253QScriptDebuggerLocalsModelPrivate *QScriptDebuggerLocalsModelPrivate::get(QScriptDebuggerLocalsModel *q)
254{
255 return q->d_func();
256}
257
258namespace {
259
260class SetPropertyJob : public QScriptDebuggerCommandSchedulerJob
261{
262public:
263 SetPropertyJob(const QPersistentModelIndex &index,
264 const QString &expression,
265 QScriptDebuggerCommandSchedulerInterface *scheduler)
266 : QScriptDebuggerCommandSchedulerJob(scheduler),
267 m_index(index), m_expression(expression), m_state(0) {}
268
269 QScriptDebuggerLocalsModelPrivate *model() const
270 {
271 if (!m_index.isValid())
272 return 0;
273 QAbstractItemModel *m = const_cast<QAbstractItemModel*>(m_index.model());
274 QScriptDebuggerLocalsModel *lm = qobject_cast<QScriptDebuggerLocalsModel*>(m);
275 return QScriptDebuggerLocalsModelPrivate::get(lm);
276 }
277
278 void start()
279 {
280 if (!m_index.isValid()) {
281 // nothing to do, the node has been removed
282 return;
283 }
284 QScriptDebuggerLocalsModelNode *node = model()->nodeFromIndex(m_index);
285 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
286 frontend.scheduleEvaluate(model()->frameIndex, m_expression,
287 QString::fromLatin1("set property '%0' (%1)")
288 .arg(node->property.name())
289 .arg(QDateTime::currentDateTime().toString()));
290 }
291
292 void handleResponse(const QScriptDebuggerResponse &, int)
293 {
294 switch (m_state) {
295 case 0:
296 hibernateUntilEvaluateFinished();
297 ++m_state;
298 break;
299 case 1:
300 finish();
301 break;
302 }
303 }
304
305 void evaluateFinished(const QScriptDebuggerValue &result)
306 {
307 if (!m_index.isValid()) {
308 // nothing to do, the node has been removed
309 return;
310 }
311 QScriptDebuggerLocalsModelNode *node = model()->nodeFromIndex(m_index);
312 Q_ASSERT(node->parent != 0);
313 QScriptDebuggerValue object = node->parent->property.value();
314 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
315 frontend.scheduleSetScriptValueProperty(object, node->property.name(), result);
316 }
317
318private:
319 QPersistentModelIndex m_index;
320 QString m_expression;
321 int m_state;
322};
323
324} // namespace
325
326QScriptDebuggerLocalsModelNode *QScriptDebuggerLocalsModelPrivate::nodeFromIndex(
327 const QModelIndex &index) const
328{
329 if (!index.isValid())
330 return invisibleRootNode;
331 return static_cast<QScriptDebuggerLocalsModelNode*>(index.internalPointer());
332}
333
334QModelIndex QScriptDebuggerLocalsModelPrivate::indexFromNode(
335 QScriptDebuggerLocalsModelNode *node) const
336{
337 if (!node || (node == invisibleRootNode))
338 return QModelIndex();
339 QScriptDebuggerLocalsModelNode *par = node->parent;
340 int row = par ? par->children.indexOf(node) : 0;
341 return createIndex(row, 0, node);
342}
343
344bool QScriptDebuggerLocalsModelPrivate::isTopLevelNode(QScriptDebuggerLocalsModelNode *node) const
345{
346 return node && (node->parent == invisibleRootNode);
347}
348
349namespace {
350
351class PopulateModelIndexJob : public QScriptDebuggerCommandSchedulerJob
352{
353public:
354 PopulateModelIndexJob(const QPersistentModelIndex &index,
355 QScriptDebuggerCommandSchedulerInterface *scheduler)
356 : QScriptDebuggerCommandSchedulerJob(scheduler),
357 m_index(index), m_state(0)
358 { }
359
360 QScriptDebuggerLocalsModelPrivate *model() const
361 {
362 if (!m_index.isValid())
363 return 0;
364 QAbstractItemModel *m = const_cast<QAbstractItemModel*>(m_index.model());
365 QScriptDebuggerLocalsModel *lm = qobject_cast<QScriptDebuggerLocalsModel*>(m);
366 return QScriptDebuggerLocalsModelPrivate::get(lm);
367 }
368
369 void start()
370 {
371 if (!m_index.isValid()) {
372 // nothing to do, the node has been removed
373 return;
374 }
375 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
376 frontend.scheduleNewScriptObjectSnapshot();
377 }
378
379 void handleResponse(const QScriptDebuggerResponse &response,
380 int)
381 {
382 if (!m_index.isValid()) {
383 // the node has been removed
384 finish();
385 return;
386 }
387 switch (m_state) {
388 case 0: {
389 QScriptDebuggerLocalsModelNode *node = model()->nodeFromIndex(m_index);
390 Q_ASSERT(node->populationState == QScriptDebuggerLocalsModelNode::Populating);
391 node->snapshotId = response.resultAsInt();
392 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
393 frontend.scheduleScriptObjectSnapshotCapture(node->snapshotId, node->property.value());
394 ++m_state;
395 } break;
396 case 1: {
397 QScriptDebuggerObjectSnapshotDelta delta;
398 delta = qvariant_cast<QScriptDebuggerObjectSnapshotDelta>(response.result());
399 Q_ASSERT(delta.removedProperties.isEmpty());
400 Q_ASSERT(delta.changedProperties.isEmpty());
401 QScriptDebuggerValuePropertyList props = delta.addedProperties;
402 model()->reallyPopulateIndex(m_index, props);
403 finish();
404 } break;
405 }
406 }
407
408private:
409 QPersistentModelIndex m_index;
410 int m_state;
411};
412
413} // namespace
414
415void QScriptDebuggerLocalsModelPrivate::populateIndex(
416 const QModelIndex &index)
417{
418 if (!index.isValid())
419 return;
420 QScriptDebuggerLocalsModelNode *node = nodeFromIndex(index);
421 if (node->populationState != QScriptDebuggerLocalsModelNode::NotPopulated)
422 return;
423 if (node->property.value().type() != QScriptDebuggerValue::ObjectValue)
424 return;
425 node->populationState = QScriptDebuggerLocalsModelNode::Populating;
426 QScriptDebuggerJob *job = new PopulateModelIndexJob(index, commandScheduler);
427 jobScheduler->scheduleJob(job);
428}
429
430void QScriptDebuggerLocalsModelPrivate::reallyPopulateIndex(
431 const QModelIndex &index,
432 const QScriptDebuggerValuePropertyList &props)
433{
434 if (!index.isValid())
435 return;
436 QScriptDebuggerLocalsModelNode *node = nodeFromIndex(index);
437 Q_ASSERT(node->populationState == QScriptDebuggerLocalsModelNode::Populating);
438 node->populationState = QScriptDebuggerLocalsModelNode::Populated;
439 addChildren(index, node, props);
440}
441
442QScriptDebuggerLocalsModel::QScriptDebuggerLocalsModel(
443 QScriptDebuggerJobSchedulerInterface *jobScheduler,
444 QScriptDebuggerCommandSchedulerInterface *commandScheduler,
445 QObject *parent)
446 : QAbstractItemModel(*new QScriptDebuggerLocalsModelPrivate, parent)
447{
448 Q_D(QScriptDebuggerLocalsModel);
449 d->jobScheduler = jobScheduler;
450 d->commandScheduler = commandScheduler;
451}
452
453QScriptDebuggerLocalsModel::~QScriptDebuggerLocalsModel()
454{
455}
456
457QModelIndex QScriptDebuggerLocalsModelPrivate::addTopLevelObject(const QString &name, const QScriptDebuggerValue &object)
458{
459 Q_Q(QScriptDebuggerLocalsModel);
460 QScriptDebuggerLocalsModelNode *node = invisibleRootNode->findChild(name);
461 if (node)
462 return indexFromNode(node);
463 QScriptDebuggerValueProperty prop(name, object,
464 QString::fromLatin1(""), // ### string representation of object
465 /*flags=*/0);
466 int rowIndex = invisibleRootNode->children.size();
467 q->beginInsertRows(QModelIndex(), rowIndex, rowIndex);
468 node = new QScriptDebuggerLocalsModelNode(prop, invisibleRootNode);
469 q->endInsertRows();
470 return indexFromNode(node);
471}
472
473namespace {
474
475class InitModelJob : public QScriptDebuggerCommandSchedulerJob
476{
477public:
478 InitModelJob(QScriptDebuggerLocalsModelPrivate *model,
479 int frameIndex,
480 QScriptDebuggerCommandSchedulerInterface *scheduler)
481 : QScriptDebuggerCommandSchedulerJob(scheduler),
482 m_model(model), m_frameIndex(frameIndex), m_state(0)
483 { }
484
485 void start()
486 {
487 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
488 frontend.scheduleGetScopeChain(m_frameIndex);
489 }
490
491 void handleResponse(const QScriptDebuggerResponse &response,
492 int)
493 {
494 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
495 switch (m_state) {
496 case 0: {
497 QScriptDebuggerValueList scopeChain = response.resultAsScriptValueList();
498 for (int i = 0; i < scopeChain.size(); ++i) {
499 const QScriptDebuggerValue &scopeObject = scopeChain.at(i);
500 QString name = QString::fromLatin1("Scope");
501 if (i > 0)
502 name.append(QString::fromLatin1(" (%0)").arg(i));
503 QModelIndex index = m_model->addTopLevelObject(name, scopeObject);
504 if (i == 0)
505 m_model->emitScopeObjectAvailable(index);
506 }
507 frontend.scheduleGetThisObject(m_frameIndex);
508 ++m_state;
509 } break;
510 case 1: {
511 QScriptDebuggerValue thisObject = response.resultAsScriptValue();
512 m_model->addTopLevelObject(QLatin1String("this"), thisObject);
513 finish();
514 } break;
515 }
516 }
517
518private:
519 QScriptDebuggerLocalsModelPrivate *m_model;
520 int m_frameIndex;
521 int m_state;
522};
523
524} // namespace
525
526void QScriptDebuggerLocalsModel::init(int frameIndex)
527{
528 Q_D(QScriptDebuggerLocalsModel);
529 d->frameIndex = frameIndex;
530 QScriptDebuggerJob *job = new InitModelJob(d, frameIndex, d->commandScheduler);
531 d->jobScheduler->scheduleJob(job);
532}
533
534namespace {
535
536class SyncModelJob : public QScriptDebuggerCommandSchedulerJob
537{
538public:
539 SyncModelJob(QScriptDebuggerLocalsModelPrivate *model,
540 int frameIndex,
541 QScriptDebuggerCommandSchedulerInterface *scheduler)
542 : QScriptDebuggerCommandSchedulerJob(scheduler),
543 m_model(model), m_frameIndex(frameIndex), m_state(0)
544 { }
545
546 void start()
547 {
548 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
549 frontend.scheduleGetScopeChain(m_frameIndex);
550 }
551
552 void handleResponse(const QScriptDebuggerResponse &response,
553 int)
554 {
555 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
556 switch (m_state) {
557 case 0: {
558 QScriptDebuggerValueList scopeChain = response.resultAsScriptValueList();
559 m_topLevelObjects << scopeChain;
560 frontend.scheduleGetThisObject(m_frameIndex);
561 ++m_state;
562 } break;
563 case 1: {
564 QScriptDebuggerValue thisObject = response.resultAsScriptValue();
565 m_topLevelObjects.append(thisObject);
566 bool equal = (m_topLevelObjects.size() == m_model->invisibleRootNode->children.size());
567 for (int i = 0; equal && (i < m_topLevelObjects.size()); ++i) {
568 const QScriptDebuggerValue &object = m_topLevelObjects.at(i);
569 equal = (object == m_model->invisibleRootNode->children.at(i)->property.value());
570 }
571 if (!equal) {
572 // the scope chain and/or this-object changed, so invalidate the model.
573 // we could try to be more clever, i.e. figure out
574 // exactly which objects were popped/pushed
575 m_model->removeTopLevelNodes();
576 for (int j = 0; j < m_topLevelObjects.size(); ++j) {
577 const QScriptDebuggerValue &object = m_topLevelObjects.at(j);
578 QString name;
579 if (j == m_topLevelObjects.size()-1) {
580 name = QString::fromLatin1("this");
581 } else {
582 name = QString::fromLatin1("Scope");
583 if (j > 0)
584 name.append(QString::fromLatin1(" (%0)").arg(j));
585 }
586 QModelIndex index = m_model->addTopLevelObject(name, object);
587 if (j == 0)
588 m_model->emitScopeObjectAvailable(index);
589 }
590 } else {
591 m_model->syncTopLevelNodes();
592 }
593 finish();
594 } break;
595 }
596 }
597
598private:
599 QScriptDebuggerLocalsModelPrivate *m_model;
600 int m_frameIndex;
601 int m_state;
602 QScriptDebuggerValueList m_topLevelObjects;
603};
604
605} // namespace
606
607void QScriptDebuggerLocalsModel::sync(int frameIndex)
608{
609 Q_D(QScriptDebuggerLocalsModel);
610 d->frameIndex = frameIndex;
611 QScriptDebuggerJob *job = new SyncModelJob(d, frameIndex, d->commandScheduler);
612 d->jobScheduler->scheduleJob(job);
613}
614
615namespace {
616
617class SyncModelIndexJob : public QScriptDebuggerCommandSchedulerJob
618{
619public:
620 SyncModelIndexJob(const QPersistentModelIndex &index,
621 QScriptDebuggerCommandSchedulerInterface *scheduler)
622 : QScriptDebuggerCommandSchedulerJob(scheduler),
623 m_index(index)
624 { }
625
626 QScriptDebuggerLocalsModelPrivate *model() const
627 {
628 if (!m_index.isValid())
629 return 0;
630 QAbstractItemModel *m = const_cast<QAbstractItemModel*>(m_index.model());
631 QScriptDebuggerLocalsModel *lm = qobject_cast<QScriptDebuggerLocalsModel*>(m);
632 return QScriptDebuggerLocalsModelPrivate::get(lm);
633 }
634
635 void start()
636 {
637 if (!m_index.isValid()) {
638 // nothing to do, the node has been removed
639 return;
640 }
641 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
642 QScriptDebuggerLocalsModelNode *node = model()->nodeFromIndex(m_index);
643 frontend.scheduleScriptObjectSnapshotCapture(node->snapshotId, node->property.value());
644 }
645
646 void handleResponse(const QScriptDebuggerResponse &response,
647 int)
648 {
649 QScriptDebuggerObjectSnapshotDelta delta;
650 delta = qvariant_cast<QScriptDebuggerObjectSnapshotDelta>(response.result());
651 model()->reallySyncIndex(m_index, delta);
652 finish();
653 }
654
655private:
656 QPersistentModelIndex m_index;
657};
658
659} // namespace
660
661void QScriptDebuggerLocalsModelPrivate::syncIndex(const QModelIndex &index)
662{
663 if (!index.isValid())
664 return;
665 QScriptDebuggerLocalsModelNode *node = nodeFromIndex(index);
666 if (node->populationState != QScriptDebuggerLocalsModelNode::Populated)
667 return;
668 QScriptDebuggerJob *job = new SyncModelIndexJob(index, commandScheduler);
669 jobScheduler->scheduleJob(job);
670}
671
672void QScriptDebuggerLocalsModelPrivate::reallySyncIndex(const QModelIndex &index,
673 const QScriptDebuggerObjectSnapshotDelta &delta)
674{
675 if (!index.isValid())
676 return;
677 QScriptDebuggerLocalsModelNode *node = nodeFromIndex(index);
678 // update or remove existing children
679 for (int i = 0; i < node->children.count(); ++i) {
680 QScriptDebuggerLocalsModelNode *child = node->children.at(i);
681 int j;
682 for (j = 0; j < delta.changedProperties.count(); ++j) {
683 if (child->property.name() == delta.changedProperties.at(j).name()) {
684 child->property = delta.changedProperties.at(j);
685 child->changed = true;
686 emitDataChanged(index, index.sibling(0, 1));
687 repopulate(child);
688 break;
689 }
690 }
691 if (j != delta.changedProperties.count())
692 continue; // was changed
693 for (j = 0; j < delta.removedProperties.count(); ++j) {
694 if (child->property.name() == delta.removedProperties.at(j)) {
695 removeChild(index, node, i);
696 --i;
697 break;
698 }
699 }
700 if (j != delta.removedProperties.count())
701 continue; // was removed
702 // neither changed nor removed, but its children might be
703 if (child->populationState == QScriptDebuggerLocalsModelNode::Populated) {
704 QScriptDebuggerJob *job = new SyncModelIndexJob(indexFromNode(child), commandScheduler);
705 jobScheduler->scheduleJob(job);
706 }
707 }
708 addChildren(index, node, delta.addedProperties);
709}
710
711void QScriptDebuggerLocalsModelPrivate::syncTopLevelNodes()
712{
713 Q_Q(QScriptDebuggerLocalsModel);
714 for (int i = 0; i < invisibleRootNode->children.count(); ++i) {
715 QModelIndex index = q->index(i, 0, QModelIndex());
716 syncIndex(index);
717 if (i == 0)
718 emit q->scopeObjectAvailable(index);
719 }
720}
721
722void QScriptDebuggerLocalsModelPrivate::removeTopLevelNodes()
723{
724 while (!invisibleRootNode->children.isEmpty())
725 removeChild(QModelIndex(), invisibleRootNode, 0);
726}
727
728void QScriptDebuggerLocalsModelPrivate::emitScopeObjectAvailable(const QModelIndex &index)
729{
730 emit q_func()->scopeObjectAvailable(index);
731}
732
733int QScriptDebuggerLocalsModel::frameIndex() const
734{
735 Q_D(const QScriptDebuggerLocalsModel);
736 return d->frameIndex;
737}
738
739/*!
740 \reimp
741*/
742QModelIndex QScriptDebuggerLocalsModel::index(int row, int column, const QModelIndex &parent) const
743{
744 Q_D(const QScriptDebuggerLocalsModel);
745 QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(parent);
746 if ((row < 0) || (row >= node->children.count()))
747 return QModelIndex();
748 return createIndex(row, column, node->children.at(row));
749}
750
751/*!
752 \reimp
753*/
754QModelIndex QScriptDebuggerLocalsModel::parent(const QModelIndex &index) const
755{
756 Q_D(const QScriptDebuggerLocalsModel);
757 if (!index.isValid())
758 return QModelIndex();
759 QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(index);
760 return d->indexFromNode(node->parent);
761}
762
763/*!
764 \reimp
765*/
766int QScriptDebuggerLocalsModel::columnCount(const QModelIndex &) const
767{
768 return 2;
769}
770
771/*!
772 \reimp
773*/
774int QScriptDebuggerLocalsModel::rowCount(const QModelIndex &parent) const
775{
776 Q_D(const QScriptDebuggerLocalsModel);
777 // ### need this to make it work with a sortfilterproxymodel (QSFPM is too eager)
778 const_cast<QScriptDebuggerLocalsModel*>(this)->fetchMore(parent);
779 QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(parent);
780 return node ? node->children.count() : 0;
781}
782
783/*!
784 \reimp
785*/
786QVariant QScriptDebuggerLocalsModel::data(const QModelIndex &index, int role) const
787{
788 Q_D(const QScriptDebuggerLocalsModel);
789 if (!index.isValid())
790 return QVariant();
791 QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(index);
792 if (role == Qt::DisplayRole) {
793 if (index.column() == 0)
794 return node->property.name();
795 else if (index.column() == 1) {
796 QString str = node->property.valueAsString();
797 if (str.indexOf(QLatin1Char('\n')) != -1) {
798 QStringList lines = str.split(QLatin1Char('\n'));
799 int lineCount = lines.size();
800 if (lineCount > 1) {
801 lines = lines.mid(0, 1);
802 lines.append(QString::fromLatin1("(... %0 more lines ...)").arg(lineCount - 1));
803 }
804 str = lines.join(QLatin1String("\n"));
805 }
806 return str;
807 }
808 } else if (role == Qt::EditRole) {
809 if ((index.column() == 1) && !d->isTopLevelNode(node)) {
810 QString str = node->property.valueAsString();
811 if (node->property.value().type() == QScriptDebuggerValue::StringValue) {
812 // escape
813 str.replace(QLatin1Char('\"'), QLatin1String("\\\""));
814 str.prepend(QLatin1Char('\"'));
815 str.append(QLatin1Char('\"'));
816 }
817 return str;
818 }
819 } else if (role == Qt::ToolTipRole) {
820 if (index.column() == 1) {
821 QString str = node->property.valueAsString();
822 if (str.indexOf(QLatin1Char('\n')) != -1)
823 return str;
824 }
825 }
826 // ### do this in the delegate
827 else if (role == Qt::BackgroundRole) {
828 if (d->isTopLevelNode(node))
829 return QBrush(Qt::darkGray);
830 } else if (role == Qt::TextColorRole) {
831 if (d->isTopLevelNode(node))
832 return QColor(Qt::white);
833 } else if (role == Qt::FontRole) {
834 if (d->isTopLevelNode(node) || node->changed) {
835 QFont fnt;
836 fnt.setBold(true);
837 return fnt;
838 }
839 }
840 return QVariant();
841}
842
843/*!
844 \reimp
845*/
846bool QScriptDebuggerLocalsModel::setData(const QModelIndex &index, const QVariant &value, int role)
847{
848 Q_D(QScriptDebuggerLocalsModel);
849 if (!index.isValid())
850 return false;
851 if (role != Qt::EditRole)
852 return false;
853 QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(index);
854 if (!node)
855 return false;
856 QString expr = value.toString().trimmed();
857 if (expr.isEmpty())
858 return false;
859 QScriptDebuggerJob *job = new SetPropertyJob(index, expr, d->commandScheduler);
860 d->jobScheduler->scheduleJob(job);
861 return true;
862}
863
864/*!
865 \reimp
866*/
867QVariant QScriptDebuggerLocalsModel::headerData(int section, Qt::Orientation orient, int role) const
868{
869 if (orient == Qt::Horizontal) {
870 if (role == Qt::DisplayRole) {
871 if (section == 0)
872 return QCoreApplication::translate("QScriptDebuggerLocalsModel", "Name");
873 else if (section == 1)
874 return QCoreApplication::translate("QScriptDebuggerLocalsModel", "Value");
875 }
876 }
877 return QVariant();
878}
879
880/*!
881 \reimp
882*/
883Qt::ItemFlags QScriptDebuggerLocalsModel::flags(const QModelIndex &index) const
884{
885 Q_D(const QScriptDebuggerLocalsModel);
886 if (!index.isValid())
887 return 0;
888 Qt::ItemFlags ret = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
889 if ((index.column() == 1) && index.parent().isValid()) {
890 QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(index);
891 if (!(node->property.flags() & QScriptValue::ReadOnly))
892 ret |= Qt::ItemIsEditable;
893 }
894 return ret;
895}
896
897/*!
898 \reimp
899*/
900bool QScriptDebuggerLocalsModel::hasChildren(const QModelIndex &parent) const
901{
902 Q_D(const QScriptDebuggerLocalsModel);
903 QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(parent);
904 if (!node)
905 return false;
906 return !node->children.isEmpty()
907 || ((node->property.value().type() == QScriptDebuggerValue::ObjectValue)
908 && (node->populationState == QScriptDebuggerLocalsModelNode::NotPopulated));
909}
910
911/*!
912 \reimp
913*/
914bool QScriptDebuggerLocalsModel::canFetchMore(const QModelIndex &parent) const
915{
916 Q_D(const QScriptDebuggerLocalsModel);
917 if (!parent.isValid())
918 return false;
919 QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(parent);
920 return node
921 && (node->property.value().type() == QScriptDebuggerValue::ObjectValue)
922 && (node->populationState == QScriptDebuggerLocalsModelNode::NotPopulated);
923}
924
925/*!
926 \reimp
927*/
928void QScriptDebuggerLocalsModel::fetchMore(const QModelIndex &parent)
929{
930 Q_D(QScriptDebuggerLocalsModel);
931 d->populateIndex(parent);
932}
933
934QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.