$search
00001 /* 00002 Aseba - an event-based framework for distributed robot control 00003 Copyright (C) 2007--2012: 00004 Stephane Magnenat <stephane at magnenat dot net> 00005 (http://stephane.magnenat.net) 00006 and other contributors, see authors.txt for details 00007 00008 This program is free software: you can redistribute it and/or modify 00009 it under the terms of the GNU Lesser General Public License as published 00010 by the Free Software Foundation, version 3 of the License. 00011 00012 This program is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 GNU Lesser General Public License for more details. 00016 00017 You should have received a copy of the GNU Lesser General Public License 00018 along with this program. If not, see <http://www.gnu.org/licenses/>. 00019 */ 00020 00021 #include "TargetModels.h" 00022 #include <QtDebug> 00023 #include <QtGui> 00024 00025 #include <TargetModels.moc> 00026 00027 namespace Aseba 00028 { 00031 00032 VariableListener::VariableListener(TargetVariablesModel* variablesModel) : 00033 variablesModel(variablesModel) 00034 { 00035 00036 } 00037 00038 VariableListener::~VariableListener() 00039 { 00040 if (variablesModel) 00041 variablesModel->unsubscribeViewPlugin(this); 00042 } 00043 00044 bool VariableListener::subscribeToVariableOfInterest(const QString& name) 00045 { 00046 return variablesModel->subscribeToVariableOfInterest(this, name); 00047 } 00048 00049 void VariableListener::unsubscribeToVariableOfInterest(const QString& name) 00050 { 00051 variablesModel->unsubscribeToVariableOfInterest(this, name); 00052 } 00053 00054 void VariableListener::unsubscribeToVariablesOfInterest() 00055 { 00056 variablesModel->unsubscribeToVariablesOfInterest(this); 00057 } 00058 00059 void VariableListener::invalidateVariableModel() 00060 { 00061 variablesModel = 0; 00062 } 00063 00064 00065 TargetVariablesModel::~TargetVariablesModel() 00066 { 00067 for (VariableListenersNameMap::iterator it = variableListenersMap.begin(); it != variableListenersMap.end(); ++it) 00068 { 00069 it.key()->invalidateVariableModel(); 00070 } 00071 } 00072 00073 int TargetVariablesModel::rowCount(const QModelIndex &parent) const 00074 { 00075 if (parent.isValid()) 00076 { 00077 if (parent.parent().isValid() || (variables.at(parent.row()).value.size() == 1)) 00078 return 0; 00079 else 00080 return variables.at(parent.row()).value.size(); 00081 } 00082 else 00083 return variables.size(); 00084 } 00085 00086 int TargetVariablesModel::columnCount(const QModelIndex & parent) const 00087 { 00088 return 2; 00089 } 00090 00091 QModelIndex TargetVariablesModel::index(int row, int column, const QModelIndex &parent) const 00092 { 00093 if (parent.isValid()) 00094 return createIndex(row, column, parent.row()); 00095 else 00096 { 00097 // top-level indices shall not point outside the variable array 00098 if (row < 0 || row >= variables.length()) 00099 return QModelIndex(); 00100 else 00101 return createIndex(row, column, -1); 00102 } 00103 } 00104 00105 QModelIndex TargetVariablesModel::parent(const QModelIndex &index) const 00106 { 00107 if (index.isValid() && (index.internalId() != -1)) 00108 return createIndex(index.internalId(), 0, -1); 00109 else 00110 return QModelIndex(); 00111 } 00112 00113 QVariant TargetVariablesModel::data(const QModelIndex &index, int role) const 00114 { 00115 if (index.parent().isValid()) 00116 { 00117 if (role != Qt::DisplayRole) 00118 return QVariant(); 00119 00120 if (index.column() == 0) 00121 return index.row(); 00122 else 00123 return variables.at(index.parent().row()).value[index.row()]; 00124 } 00125 else 00126 { 00127 QString name = variables.at(index.row()).name; 00128 // hidden variable 00129 if (name.left(1) == "_") 00130 { 00131 if (role == Qt::ForegroundRole) 00132 return QApplication::palette().color(QPalette::Disabled, QPalette::Text); 00133 else if (role == Qt::FontRole) 00134 { 00135 QFont font; 00136 font.setItalic(true); 00137 return font; 00138 } 00139 } 00140 if (index.column() == 0) 00141 { 00142 if (role == Qt::DisplayRole) 00143 return name; 00144 return QVariant(); 00145 } 00146 else 00147 { 00148 if (role == Qt::DisplayRole) 00149 { 00150 if (variables.at(index.row()).value.size() == 1) 00151 return variables.at(index.row()).value[0]; 00152 else 00153 return QString("(%0)").arg(variables.at(index.row()).value.size()); 00154 } 00155 else if (role == Qt::ForegroundRole) 00156 { 00157 if (variables.at(index.row()).value.size() == 1) 00158 return QVariant(); 00159 else 00160 return QApplication::palette().color(QPalette::Disabled, QPalette::Text); 00161 } 00162 else 00163 return QVariant(); 00164 } 00165 } 00166 } 00167 00168 QVariant TargetVariablesModel::headerData(int section, Qt::Orientation orientation, int role) const 00169 { 00170 //Q_UNUSED(section) 00171 Q_UNUSED(orientation) 00172 Q_UNUSED(role) 00173 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) 00174 { 00175 if (section == 0) 00176 return tr("names"); 00177 else 00178 return tr("values"); 00179 } 00180 return QVariant(); 00181 } 00182 00183 Qt::ItemFlags TargetVariablesModel::flags(const QModelIndex &index) const 00184 { 00185 if (!index.isValid()) 00186 return 0; 00187 00188 if (index.column() == 1) 00189 { 00190 if (index.parent().isValid()) 00191 return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable; 00192 else if (variables.at(index.row()).value.size() == 1) 00193 return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable; 00194 else 00195 return 0; 00196 } 00197 else 00198 { 00199 if (index.parent().isValid()) 00200 return Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable; 00201 else 00202 return Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable; 00203 } 00204 } 00205 00206 bool TargetVariablesModel::setData(const QModelIndex &index, const QVariant &value, int role) 00207 { 00208 if (index.isValid() && role == Qt::EditRole) 00209 { 00210 if (index.parent().isValid()) 00211 { 00212 int variableValue; 00213 bool ok; 00214 variableValue = value.toInt(&ok); 00215 Q_ASSERT(ok); 00216 00217 variables[index.parent().row()].value[index.row()] = variableValue; 00218 emit variableValuesChanged(variables[index.parent().row()].pos + index.row(), VariablesDataVector(1, variableValue)); 00219 00220 return true; 00221 } 00222 else if (variables.at(index.row()).value.size() == 1) 00223 { 00224 int variableValue; 00225 bool ok; 00226 variableValue = value.toInt(&ok); 00227 Q_ASSERT(ok); 00228 00229 variables[index.row()].value[0] = variableValue; 00230 emit variableValuesChanged(variables[index.row()].pos, VariablesDataVector(1, variableValue)); 00231 00232 return true; 00233 } 00234 } 00235 return false; 00236 } 00237 00238 QStringList TargetVariablesModel::mimeTypes () const 00239 { 00240 QStringList types; 00241 types << "text/plain"; 00242 return types; 00243 } 00244 00245 QMimeData * TargetVariablesModel::mimeData ( const QModelIndexList & indexes ) const 00246 { 00247 QString texts; 00248 foreach (QModelIndex index, indexes) 00249 { 00250 if (index.isValid()) 00251 { 00252 const QString text = data(index, Qt::DisplayRole).toString(); 00253 if (index.parent().isValid()) 00254 { 00255 const QString varName = data(index.parent(), Qt::DisplayRole).toString(); 00256 texts += varName + "[" + text + "]"; 00257 } 00258 else 00259 texts += text; 00260 } 00261 } 00262 00263 QMimeData *mimeData = new QMimeData(); 00264 mimeData->setText(texts); 00265 return mimeData; 00266 } 00267 00268 unsigned TargetVariablesModel::getVariablePos(const QString& name) const 00269 { 00270 for (int i = 0; i < variables.size(); ++i) 00271 { 00272 const Variable& variable(variables[i]); 00273 if (variable.name == name) 00274 return variable.pos; 00275 } 00276 return 0; 00277 } 00278 00279 unsigned TargetVariablesModel::getVariableSize(const QString& name) const 00280 { 00281 for (int i = 0; i < variables.size(); ++i) 00282 { 00283 const Variable& variable(variables[i]); 00284 if (variable.name == name) 00285 return variable.value.size(); 00286 } 00287 return 0; 00288 } 00289 00290 VariablesDataVector TargetVariablesModel::getVariableValue(const QString& name) const 00291 { 00292 for (int i = 0; i < variables.size(); ++i) 00293 { 00294 const Variable& variable(variables[i]); 00295 if (variable.name == name) 00296 return variable.value; 00297 } 00298 return VariablesDataVector(); 00299 } 00300 00301 void TargetVariablesModel::updateVariablesStructure(const Compiler::VariablesMap *variablesMap) 00302 { 00303 // Build a new list of variables 00304 QList<Variable> newVariables; 00305 for (Compiler::VariablesMap::const_iterator it = variablesMap->begin(); it != variablesMap->end(); ++it) 00306 { 00307 // create new variable 00308 Variable var; 00309 var.name = QString::fromStdWString(it->first); 00310 var.pos = it->second.first; 00311 var.value.resize(it->second.second); 00312 00313 // find its right place in the array 00314 int i; 00315 for (i = 0; i < newVariables.size(); ++i) 00316 { 00317 if (var.pos < newVariables[i].pos) 00318 break; 00319 } 00320 newVariables.insert(i, var); 00321 } 00322 00323 // compute the difference 00324 int i(0); 00325 int count(std::min(variables.length(), newVariables.length())); 00326 while ( 00327 i < count && 00328 variables[i].name == newVariables[i].name && 00329 variables[i].pos == newVariables[i].pos && 00330 variables[i].value.size() == newVariables[i].value.size() 00331 ) 00332 ++i; 00333 00334 // update starting from the first change point 00335 //qDebug() << "change from " << i << " to " << variables.length(); 00336 if (i != variables.length()) 00337 { 00338 beginRemoveRows(QModelIndex(), i, variables.length()-1); 00339 int removeCount(variables.length() - i); 00340 for (int j = 0; j < removeCount; ++j) 00341 variables.removeLast(); 00342 endRemoveRows(); 00343 } 00344 00345 //qDebug() << "size: " << variables.length(); 00346 00347 if (i != newVariables.length()) 00348 { 00349 beginInsertRows(QModelIndex(), i, newVariables.length()-1); 00350 for (int j = i; j < newVariables.length(); ++j) 00351 variables.append(newVariables[j]); 00352 endInsertRows(); 00353 } 00354 00355 /*variables.clear(); 00356 for (Compiler::VariablesMap::const_iterator it = variablesMap->begin(); it != variablesMap->end(); ++it) 00357 { 00358 // create new variable 00359 Variable var; 00360 var.name = QString::fromStdWString(it->first); 00361 var.pos = it->second.first; 00362 var.value.resize(it->second.second); 00363 00364 // find its right place in the array 00365 int i; 00366 for (i = 0; i < variables.size(); ++i) 00367 { 00368 if (var.pos < variables[i].pos) 00369 break; 00370 } 00371 variables.insert(i, var); 00372 } 00373 00374 reset();*/ 00375 } 00376 00377 void TargetVariablesModel::setVariablesData(unsigned start, const VariablesDataVector &data) 00378 { 00379 size_t dataLength = data.size(); 00380 for (int i = 0; i < variables.size(); ++i) 00381 { 00382 Variable &var = variables[i]; 00383 int varLen = (int)var.value.size(); 00384 int varStart = (int)start - (int)var.pos; 00385 int copyLen = (int)dataLength; 00386 int copyStart = 0; 00387 // crop data before us 00388 if (varStart < 0) 00389 { 00390 copyLen += varStart; 00391 copyStart -= varStart; 00392 varStart = 0; 00393 } 00394 // if nothing to copy, continue 00395 if (copyLen <= 0) 00396 continue; 00397 // crop data after us 00398 if (varStart + copyLen > varLen) 00399 { 00400 copyLen = varLen - varStart; 00401 } 00402 // if nothing to copy, continue 00403 if (copyLen <= 0) 00404 continue; 00405 00406 // copy 00407 copy(data.begin() + copyStart, data.begin() + copyStart + copyLen, var.value.begin() + varStart); 00408 00409 // notify gui 00410 QModelIndex parentIndex = index(i, 0); 00411 emit dataChanged(index(varStart, 0, parentIndex), index(varStart + copyLen, 0, parentIndex)); 00412 00413 // and notify view plugins 00414 for (VariableListenersNameMap::iterator it = variableListenersMap.begin(); it != variableListenersMap.end(); ++it) 00415 { 00416 QStringList &list = it.value(); 00417 for (int v = 0; v < list.size(); v++) 00418 { 00419 if (list[v] == var.name) 00420 it.key()->variableValueUpdated(var.name, var.value); 00421 } 00422 } 00423 } 00424 } 00425 00426 bool TargetVariablesModel::setVariableValues(const QString& name, const VariablesDataVector& values) 00427 { 00428 for (int i = 0; i < variables.size(); ++i) 00429 { 00430 Variable& variable(variables[i]); 00431 if (variable.name == name) 00432 { 00433 // setVariablesData(variable.pos, values); 00434 emit variableValuesChanged(variable.pos, values); 00435 return true; 00436 } 00437 } 00438 return false; 00439 } 00440 00441 void TargetVariablesModel::unsubscribeViewPlugin(VariableListener* listener) 00442 { 00443 variableListenersMap.remove(listener); 00444 } 00445 00446 bool TargetVariablesModel::subscribeToVariableOfInterest(VariableListener* listener, const QString& name) 00447 { 00448 QStringList &list = variableListenersMap[listener]; 00449 list.push_back(name); 00450 for (int i = 0; i < variables.size(); i++) 00451 if (variables[i].name == name) 00452 return true; 00453 return false; 00454 } 00455 00456 void TargetVariablesModel::unsubscribeToVariableOfInterest(VariableListener* listener, const QString& name) 00457 { 00458 QStringList &list = variableListenersMap[listener]; 00459 list.removeAll(name); 00460 } 00461 00462 void TargetVariablesModel::unsubscribeToVariablesOfInterest(VariableListener* plugin) 00463 { 00464 if (variableListenersMap.contains(plugin)) 00465 variableListenersMap.remove(plugin); 00466 } 00467 00468 struct TargetFunctionsModel::TreeItem 00469 { 00470 TreeItem* parent; 00471 QList<TreeItem*> children; 00472 QString name; 00473 QString toolTip; 00474 bool enabled; 00475 bool draggable; 00476 00477 TreeItem() : 00478 parent(0), 00479 name("root"), 00480 enabled(true), 00481 draggable(false) 00482 { } 00483 00484 TreeItem(TreeItem* parent, const QString& name, bool enabled, bool draggable) : 00485 parent(parent), 00486 name(name), 00487 enabled(enabled), 00488 draggable(draggable) 00489 { } 00490 00491 TreeItem(TreeItem* parent, const QString& name, const QString& toolTip, bool enabled, bool draggable) : 00492 parent(parent), 00493 name(name), 00494 toolTip(toolTip), 00495 enabled(enabled), 00496 draggable(draggable) 00497 { } 00498 00499 ~TreeItem() 00500 { 00501 for (int i = 0; i < children.size(); i++) 00502 delete children[i]; 00503 } 00504 00505 TreeItem *getEntry(const QString& name, bool enabled = true) 00506 { 00507 for (int i = 0; i < children.size(); i++) 00508 if (children[i]->name == name) 00509 return children[i]; 00510 00511 children.push_back(new TreeItem(this, name, enabled, draggable)); 00512 return children.last(); 00513 } 00514 }; 00515 00516 00517 00518 TargetFunctionsModel::TargetFunctionsModel(const TargetDescription *descriptionRead, QObject *parent) : 00519 QAbstractItemModel(parent), 00520 root(0), 00521 descriptionRead(descriptionRead), 00522 regExp("\\b") 00523 { 00524 Q_ASSERT(descriptionRead); 00525 setSupportedDragActions(Qt::CopyAction); 00526 recreateTreeFromDescription(false); 00527 } 00528 00529 TargetFunctionsModel::~TargetFunctionsModel() 00530 { 00531 delete root; 00532 } 00533 00534 TargetFunctionsModel::TreeItem *TargetFunctionsModel::getItem(const QModelIndex &index) const 00535 { 00536 if (index.isValid()) 00537 { 00538 TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); 00539 if (item) 00540 return item; 00541 } 00542 return root; 00543 } 00544 00545 QString TargetFunctionsModel::getToolTip(const TargetDescription::NativeFunction& function) const 00546 { 00547 // tooltip, display detailed information with pretty print of template parameters 00548 QString text; 00549 QSet<QString> variablesNames; 00550 00551 text += QString("<b>%1</b>(").arg(QString::fromStdWString(function.name)); 00552 for (size_t i = 0; i < function.parameters.size(); i++) 00553 { 00554 QString variableName(QString::fromStdWString(function.parameters[i].name)); 00555 variablesNames.insert(variableName); 00556 text += variableName; 00557 if (function.parameters[i].size > 1) 00558 text += QString("[%1]").arg(function.parameters[i].size); 00559 else if (function.parameters[i].size < 0) 00560 { 00561 text += QString("[<T%1>]").arg(-function.parameters[i].size); 00562 } 00563 00564 if (i + 1 < function.parameters.size()) 00565 text += QString(", "); 00566 } 00567 00568 QString description = QString::fromStdWString(function.description); 00569 QStringList descriptionWords = description.split(regExp); 00570 for (int i = 0; i < descriptionWords.size(); ++i) 00571 if (variablesNames.contains(descriptionWords.at(i))) 00572 descriptionWords[i] = QString("<tt>%1</tt>").arg(descriptionWords[i]); 00573 00574 text += QString(")<br/>") + descriptionWords.join(" "); 00575 00576 return text; 00577 } 00578 00579 int TargetFunctionsModel::rowCount(const QModelIndex & parent) const 00580 { 00581 return getItem(parent)->children.count(); 00582 } 00583 00584 int TargetFunctionsModel::columnCount(const QModelIndex & /* parent */) const 00585 { 00586 return 1; 00587 } 00588 00589 void TargetFunctionsModel::recreateTreeFromDescription(bool showHidden) 00590 { 00591 if (root) 00592 delete root; 00593 root = new TreeItem; 00594 00595 if (showHidden) 00596 root->getEntry(tr("hidden"), false); 00597 00598 for (size_t i = 0; i < descriptionRead->nativeFunctions.size(); i++) 00599 { 00600 // get the name, split it, and managed hidden 00601 QString name = QString::fromStdWString(descriptionRead->nativeFunctions[i].name); 00602 QStringList splittedName = name.split(".", QString::SkipEmptyParts); 00603 00604 // ignore functions with no name at all 00605 if (splittedName.isEmpty()) 00606 continue; 00607 00608 // get first, check whether hidden, and then iterate 00609 TreeItem* entry = root; 00610 Q_ASSERT(!splittedName[0].isEmpty()); 00611 if (name.at(0) == '_' || name.contains(QString("._"))) 00612 { 00613 if (!showHidden) 00614 continue; 00615 entry = entry->getEntry(tr("hidden"), false); 00616 } 00617 00618 for (int j = 0; j < splittedName.size() - 1; ++j) 00619 entry = entry->getEntry(splittedName[j], entry->enabled); 00620 00621 // for last entry 00622 entry->children.push_back(new TreeItem(entry, name, getToolTip(descriptionRead->nativeFunctions[i]), entry->enabled, true)); 00623 } 00624 00625 reset(); 00626 } 00627 00628 QModelIndex TargetFunctionsModel::parent(const QModelIndex &index) const 00629 { 00630 if (!index.isValid()) 00631 return QModelIndex(); 00632 00633 TreeItem *childItem = getItem(index); 00634 TreeItem *parentItem = childItem->parent; 00635 00636 if (parentItem == root) 00637 return QModelIndex(); 00638 00639 if (parentItem->parent) 00640 return createIndex(parentItem->parent->children.indexOf(const_cast<TreeItem*>(parentItem)), 0, parentItem); 00641 else 00642 return createIndex(0, 0, parentItem); 00643 } 00644 00645 QModelIndex TargetFunctionsModel::index(int row, int column, const QModelIndex &parent) const 00646 { 00647 TreeItem *parentItem = getItem(parent); 00648 TreeItem *childItem = parentItem->children.value(row); 00649 Q_ASSERT(childItem); 00650 00651 if (childItem) 00652 return createIndex(row, column, childItem); 00653 else 00654 return QModelIndex(); 00655 } 00656 00657 QVariant TargetFunctionsModel::data(const QModelIndex &index, int role) const 00658 { 00659 if (!index.isValid() || 00660 (role != Qt::DisplayRole && role != Qt::ToolTipRole && role != Qt::WhatsThisRole)) 00661 return QVariant(); 00662 00663 if (role == Qt::DisplayRole) 00664 { 00665 return getItem(index)->name; 00666 } 00667 else 00668 { 00669 return getItem(index)->toolTip; 00670 } 00671 } 00672 00673 QVariant TargetFunctionsModel::headerData(int section, Qt::Orientation orientation, int role) const 00674 { 00675 Q_UNUSED(section) 00676 Q_UNUSED(orientation) 00677 Q_UNUSED(role) 00678 return QVariant(); 00679 } 00680 00681 Qt::ItemFlags TargetFunctionsModel::flags(const QModelIndex & index) const 00682 { 00683 TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); 00684 if (item) 00685 { 00686 QFlags<Qt::ItemFlag> flags; 00687 flags |= item->enabled ? Qt::ItemIsEnabled : QFlags<Qt::ItemFlag>(); 00688 flags |= item->draggable ? Qt::ItemIsDragEnabled | Qt::ItemIsSelectable : QFlags<Qt::ItemFlag>(); 00689 return flags; 00690 } 00691 else 00692 return Qt::ItemIsEnabled; 00693 } 00694 00695 QStringList TargetFunctionsModel::mimeTypes () const 00696 { 00697 QStringList types; 00698 types << "text/plain"; 00699 return types; 00700 } 00701 00702 QMimeData * TargetFunctionsModel::mimeData ( const QModelIndexList & indexes ) const 00703 { 00704 QString texts; 00705 foreach (QModelIndex index, indexes) 00706 { 00707 if (index.isValid()) 00708 { 00709 QString text = data(index, Qt::DisplayRole).toString(); 00710 texts += text; 00711 } 00712 } 00713 00714 QMimeData *mimeData = new QMimeData(); 00715 mimeData->setText(texts); 00716 return mimeData; 00717 } 00718 00719 00721 }; // Aseba