Spreadsheet: support cell binding
Cell binding allows one to bind a range of cells of one sheet to another
range of cells of an arbitary sheet, including any empty cells in the
range.
The binding is implemented with PropertyExpressionEngine and
PropertySheet::setPathValue(), which binds a special path of
PropertySheet, such as
.cells.Bind.A1.D1
to an expression, such as
tuple(.cells, <<A2>>, <<A5>>)
The A1 and D1 in the example above specifies the binding start and end
cell address. And <<A2>> and <<A5>> are the range of cells to bind to.
Note that you can use any expression that evalutes to string for the
binding destination, e.g. <<A%d>> % B1, which uses the value inside B1
to construct the binding destination. The '.cells' in the tuple shown
above is an example to bind cells of the same PropertySheet. It can be
change to to reference to any other spreadsheet, even those outside the
current document, e.g. Document#Spreadsheet001.cells
This commit is contained in:
@@ -54,22 +54,21 @@ using namespace Gui;
|
||||
|
||||
class ExpressionCompleterModel: public QAbstractItemModel {
|
||||
public:
|
||||
ExpressionCompleterModel(QObject *parent, const App::DocumentObject *obj, bool noProperty)
|
||||
ExpressionCompleterModel(QObject *parent, bool noProperty)
|
||||
:QAbstractItemModel(parent), noProperty(noProperty)
|
||||
{
|
||||
setDocumentObject(obj);
|
||||
}
|
||||
|
||||
void setNoProperty(bool enabled) {
|
||||
noProperty = enabled;
|
||||
}
|
||||
|
||||
void setDocumentObject(const App::DocumentObject *obj) {
|
||||
void setDocumentObject(const App::DocumentObject *obj, bool checkInList) {
|
||||
beginResetModel();
|
||||
if(obj) {
|
||||
currentDoc = obj->getDocument()->getName();
|
||||
currentObj = obj->getNameInDocument();
|
||||
if(!noProperty)
|
||||
if(!noProperty && checkInList)
|
||||
inList = obj->getInListEx(true);
|
||||
} else {
|
||||
currentDoc.clear();
|
||||
@@ -340,9 +339,10 @@ private:
|
||||
* @param parent Parent object owning the completer.
|
||||
*/
|
||||
|
||||
ExpressionCompleter::ExpressionCompleter(const App::DocumentObject * currentDocObj,
|
||||
QObject *parent, bool noProperty)
|
||||
: QCompleter(parent), currentObj(currentDocObj), noProperty(noProperty)
|
||||
ExpressionCompleter::ExpressionCompleter(const App::DocumentObject * currentDocObj,
|
||||
QObject *parent, bool noProperty, bool checkInList)
|
||||
: QCompleter(parent), currentObj(currentDocObj)
|
||||
, noProperty(noProperty), checkInList(checkInList)
|
||||
{
|
||||
setCaseSensitivity(Qt::CaseInsensitive);
|
||||
}
|
||||
@@ -351,18 +351,21 @@ void ExpressionCompleter::init() {
|
||||
if(model())
|
||||
return;
|
||||
|
||||
setModel(new ExpressionCompleterModel(this,currentObj.getObject(),noProperty));
|
||||
auto m = new ExpressionCompleterModel(this,noProperty);
|
||||
m->setDocumentObject(currentObj.getObject(),checkInList);
|
||||
setModel(m);
|
||||
}
|
||||
|
||||
void ExpressionCompleter::setDocumentObject(const App::DocumentObject *obj) {
|
||||
void ExpressionCompleter::setDocumentObject(const App::DocumentObject *obj, bool _checkInList) {
|
||||
if(!obj || !obj->getNameInDocument())
|
||||
currentObj = App::DocumentObjectT();
|
||||
else
|
||||
currentObj = obj;
|
||||
setCompletionPrefix(QString());
|
||||
checkInList = _checkInList;
|
||||
auto m = model();
|
||||
if(m)
|
||||
static_cast<ExpressionCompleterModel*>(m)->setDocumentObject(obj);
|
||||
static_cast<ExpressionCompleterModel*>(m)->setDocumentObject(obj, checkInList);
|
||||
}
|
||||
|
||||
void ExpressionCompleter::setNoProperty(bool enabled) {
|
||||
@@ -372,10 +375,6 @@ void ExpressionCompleter::setNoProperty(bool enabled) {
|
||||
static_cast<ExpressionCompleterModel*>(m)->setNoProperty(enabled);
|
||||
}
|
||||
|
||||
void ExpressionCompleter::setRequireLeadingEqualSign(bool enabled) {
|
||||
requireLeadingEqualSign = enabled;
|
||||
}
|
||||
|
||||
QString ExpressionCompleter::pathFromIndex ( const QModelIndex & index ) const
|
||||
{
|
||||
auto m = model();
|
||||
@@ -459,12 +458,6 @@ void ExpressionCompleter::slotUpdate(const QString & prefix, int pos)
|
||||
// Compute start; if prefix starts with =, start parsing from offset 1.
|
||||
int start = (prefix.size() > 0 && prefix.at(0) == QChar::fromLatin1('=')) ? 1 : 0;
|
||||
|
||||
if (requireLeadingEqualSign && start != 1) {
|
||||
if (auto p = popup())
|
||||
p->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string expression = Base::Tools::toStdString(prefix.mid(start));
|
||||
|
||||
// Tokenize prefix
|
||||
@@ -564,28 +557,33 @@ void ExpressionCompleter::slotUpdate(const QString & prefix, int pos)
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionLineEdit::ExpressionLineEdit(QWidget *parent, bool noProperty, bool requireLeadingEqualSign)
|
||||
ExpressionLineEdit::ExpressionLineEdit(QWidget *parent, bool noProperty, char checkPrefix, bool checkInList)
|
||||
: QLineEdit(parent)
|
||||
, completer(nullptr)
|
||||
, block(true)
|
||||
, noProperty(noProperty)
|
||||
, exactMatch(false)
|
||||
, requireLeadingEqualSign(requireLeadingEqualSign)
|
||||
, checkInList(checkInList)
|
||||
, checkPrefix(checkPrefix)
|
||||
{
|
||||
connect(this, SIGNAL(textEdited(const QString&)), this, SLOT(slotTextChanged(const QString&)));
|
||||
}
|
||||
|
||||
void ExpressionLineEdit::setDocumentObject(const App::DocumentObject * currentDocObj)
|
||||
void ExpressionLineEdit::setPrefix(char prefix) {
|
||||
checkPrefix = prefix;
|
||||
}
|
||||
|
||||
void ExpressionLineEdit::setDocumentObject(const App::DocumentObject * currentDocObj, bool _checkInList)
|
||||
{
|
||||
checkInList = _checkInList;
|
||||
if (completer) {
|
||||
completer->setDocumentObject(currentDocObj);
|
||||
completer->setDocumentObject(currentDocObj, checkInList);
|
||||
return;
|
||||
}
|
||||
if (currentDocObj != 0) {
|
||||
completer = new ExpressionCompleter(currentDocObj, this, noProperty);
|
||||
completer = new ExpressionCompleter(currentDocObj, this, noProperty, checkInList);
|
||||
completer->setWidget(this);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
completer->setRequireLeadingEqualSign(requireLeadingEqualSign);
|
||||
if (!exactMatch)
|
||||
completer->setFilterMode(Qt::MatchContains);
|
||||
connect(completer, SIGNAL(activated(QString)), this, SLOT(slotCompleteText(QString)));
|
||||
@@ -621,6 +619,8 @@ void ExpressionLineEdit::hideCompleter()
|
||||
void ExpressionLineEdit::slotTextChanged(const QString & text)
|
||||
{
|
||||
if (!block) {
|
||||
if(!text.size() || (checkPrefix && text[0]!=QLatin1Char(checkPrefix)))
|
||||
return;
|
||||
Q_EMIT textChanged2(text,cursorPosition());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user