18 #include "NCLTextualViewPlugin.h"
20 #include <QMetaObject>
21 #include <QMetaMethod>
22 #include <QMessageBox>
24 #include <QApplication>
25 #include <QProgressDialog>
26 #include <QDomDocument>
27 #include <QTextStream>
31 const QString PROLOG (
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!-- Generated by NCL Composer -->\n");
36 nclTextEditor = window->getTextEditor();
38 tmpNclTextEditor = NULL;
42 SIGNAL(elementAdded(QString,QString,QMap<QString,QString>&,
bool)),
44 SIGNAL(addEntity(QString,QString,QMap<QString,QString>&,
bool)));
48 updateModelShortcut =
new QShortcut(window);
49 updateModelShortcut->setKey(QKeySequence(
"F5"));
51 connect(updateModelShortcut, SIGNAL(activated()),
52 this, SLOT(updateCoreModel()) );
54 connect(nclTextEditor, SIGNAL(focusLosted(QFocusEvent*)),
55 this, SLOT(manageFocusLost(QFocusEvent*)));
57 connect(nclTextEditor, SIGNAL(textChanged()),
58 this, SIGNAL(setCurrentProjectAsDirty()));
69 QString data = project->getPluginData(
"br.puc-rio.telemidia.NCLTextualView");
71 QString startEntitiesSep =
"$START_ENTITIES_LINES$";
72 QString endEntitiesSep =
"$END_ENTITIES_LINES$";
73 int indexOfStartEntities = data.indexOf(startEntitiesSep);
74 int indexOfEndEntities = data.indexOf(endEntitiesSep);
78 foreach(key, startEntityOffset.keys())
79 startEntityOffset.remove(key);
80 foreach(key, endEntityOffset.keys())
81 endEntityOffset.remove(key);
83 QString text = data.left(indexOfStartEntities);
84 if(text.isEmpty() || text.isNull())
85 nclTextEditor->setText(PROLOG);
87 nclTextEditor->setText(text);
89 int indexOfStartEntitiesContent = indexOfStartEntities +
90 startEntitiesSep.length();
91 QString startLines = data.mid(indexOfStartEntitiesContent,
92 indexOfEndEntities - indexOfStartEntitiesContent);
94 QString endLines = data.right(data.length() -
95 (indexOfEndEntities+endEntitiesSep.length()));
97 QStringList listStart = startLines.split(
",");
98 QStringList listEnd = endLines.split(
",");
102 for(i = 0; i < listStart.size()-1 && i < listEnd.size()-1; i +=2)
104 startEntityOffset[listStart[i]] = listStart[i+1].toInt();
105 endEntityOffset[listEnd[i]] = listEnd[i+1].toInt();
109 if( i != listStart.size() || i != listEnd.size())
111 qDebug() <<
"The data saved in the file is corrupted. Forcing"
112 <<
" updateFromModel";
117 nclTextEditor->setDocumentUrl(project->getLocation());
119 updateErrorMessages();
130 incrementalUpdateFromModel();
132 updateErrorMessages();
137 nclTextEditor->clear();
138 nclTextEditor->setText(PROLOG);
139 startEntityOffset.clear();
140 endEntityOffset.clear();
142 if(project->getChildren().size())
144 Entity *entity = project;
145 QList <Entity *> entities;
146 entities.push_back(entity);
148 while(entities.size())
150 entity = entities.front();
151 entities.pop_front();
154 onEntityAdded(
"xxx", entity);
157 QVector<Entity *> children = entity->getChildren();
158 for(
int i = 0; i < children.size(); i++)
160 entities.push_back(children.at(i));
168 nclTextEditor->clear();
169 nclTextEditor->setText(PROLOG);
170 QDomDocument doc (
"document");
171 if(project->getChildren().size())
173 Entity *entity = project;
174 QList <Entity *> entities;
175 QList <QDomNode> elements;
177 entities.push_back(entity);
178 elements.push_back(doc);
181 while(entities.size())
183 entity = entities.front();
184 entities.pop_front();
185 current = elements.front();
186 elements.pop_front();
196 elements.push_back(doc);
199 QVector<Entity *> children = entity->getChildren();
200 for(
int i = 0; i < children.size(); i++)
202 entities.push_back(children.at(i));
203 QDomElement el = doc.createElement(children[i]->getType());
204 el.setAttribute(
"oi",
"oi2");
205 el.setAttribute(
"oi2",
"oi3");
206 el.setAttribute(
"oi3",
"oi4");
207 elements.push_back(el);
208 current.appendChild(el);
212 QString *text =
new QString();
213 QTextStream textStream(text);
214 doc.save(textStream, QDomNode::EncodingFromTextStream);
215 nclTextEditor->setText(PROLOG);
216 nclTextEditor->insertAtPos(textStream.readAll(), PROLOG.size());
224 if(pluginID == getPluginInstanceID())
226 currentEntity = entity;
230 QString line =
"<" + entity->getType() +
"";
231 int insertAtOffset = PROLOG.size();
232 bool hasOpennedTag =
false;
235 if(entity->getParentUniqueId() != NULL &&
236 entity->getParent()->getType() !=
"project")
240 if(endEntityOffset.count(entity->getParentUniqueId()))
242 if(isStartEndTag(entity->getParent()))
244 openStartEndTag(entity->getParent());
245 hasOpennedTag =
true;
249 insertAtOffset = endEntityOffset[entity->getParentUniqueId()];
254 deque <QString> *attributes_ordered =
255 NCLStructure::getInstance()->getAttributesOrdered(entity->getType());
257 if(attributes_ordered != NULL)
259 for(
int i = 0; i < attributes_ordered->size(); i++)
261 if(entity->hasAttribute((*attributes_ordered)[i]))
263 line +=
" " + (*attributes_ordered)[i] +
264 "=\"" + entity->getAttribute((*attributes_ordered)[i]) +
"\"";
269 map <QString, bool> *attributes =
270 NCLStructure::getInstance()->getAttributes(entity->getType());
273 QMap <QString, QString>::iterator begin, end, it;
274 entity->getAttributeIterator(begin, end);
275 for (it = begin; it != end; ++it)
277 if(attributes == NULL || !attributes->count(it.key()))
278 line +=
" " + it.key() +
"=\"" + it.value() +
"\"";
282 int startEntitySize = line.size();
284 if(insertAtOffset >= 0 && insertAtOffset <= nclTextEditor->text().length())
286 nclTextEditor->insertAtPos(line, insertAtOffset);
289 updateEntitiesOffset(insertAtOffset, line.size());
291 startEntityOffset[entity->getUniqueId()] = insertAtOffset;
292 endEntityOffset[entity->getUniqueId()] = insertAtOffset + startEntitySize-2;
294 window->getTextEditor()->SendScintilla(QsciScintilla::SCI_SETFOCUS,
true);
297 fixIdentation(insertAtOffset,
true);
299 fixIdentation(insertAtOffset,
false);
301 currentEntity = entity;
304 qWarning() <<
"NCLTextEditor::onEntityAdded Trying to insert a media in a "
305 "position greater than the text size. It will be ignored!"
318 qDebug() <<
"PLUGIN (" + pluginID +
") changed the Entity (" +
319 entity->getType() +
" - " + entity->getUniqueId() +
")";
322 if(pluginID == getPluginInstanceID() && !isSyncing)
325 QString line =
"<" + entity->getType() +
"";
327 QMap <QString, QString>::iterator begin, end, it;
328 entity->getAttributeIterator(begin, end);
329 for (it = begin; it != end; ++it)
332 line +=
" " + it.key() +
"=\"" + it.value() +
"\"";
335 int insertAtOffset = 0;
336 if(startEntityOffset.contains(entity->getUniqueId()))
337 insertAtOffset = startEntityOffset.value(entity->getUniqueId());
339 if(insertAtOffset >= 0 && insertAtOffset <= nclTextEditor->text().size())
341 int previous_length = 0;
342 char curChar = nclTextEditor->SendScintilla(QsciScintilla::SCI_GETCHARAT,
343 insertAtOffset+previous_length);
344 while(curChar !=
'>' &&
345 (insertAtOffset+previous_length) < nclTextEditor->text().size())
348 curChar = nclTextEditor->SendScintilla(QsciScintilla::SCI_GETCHARAT,
349 insertAtOffset+previous_length);
352 curChar = nclTextEditor->SendScintilla(QsciScintilla::SCI_GETCHARAT,
353 insertAtOffset+previous_length-1);
361 if((insertAtOffset+previous_length) == nclTextEditor->text().size())
363 qWarning() <<
"TextEditor could not perform the requested action.";
376 nclTextEditor->SendScintilla(QsciScintilla::SCI_SETSELECTIONSTART,
378 nclTextEditor->SendScintilla(QsciScintilla::SCI_SETSELECTIONEND,
379 insertAtOffset+previous_length);
380 nclTextEditor->removeSelectedText();
382 nclTextEditor->insertAtPos(line, insertAtOffset);
385 int diff_size = line.size() - previous_length;
386 updateEntitiesOffset(insertAtOffset, diff_size);
388 nclTextEditor->SendScintilla( QsciScintilla::SCI_GOTOPOS, insertAtOffset);
392 qWarning() <<
"NCLTextEditor::onEntityAdded Trying to insert a media in a "
393 "position greater than the text size. It will be ignored!"
400 if(pluginID == getPluginInstanceID() && !isSyncing)
403 int startOffset = startEntityOffset[entityID];
404 int endOffset = endEntityOffset[entityID];
406 char curChar = nclTextEditor->SendScintilla(
407 QsciScintilla::SCI_GETCHARAT,
410 while(curChar !=
'>' && startOffset >= 0)
413 curChar = nclTextEditor->SendScintilla( QsciScintilla::SCI_GETCHARAT,
419 curChar = nclTextEditor->SendScintilla( QsciScintilla::SCI_GETCHARAT,
422 while(curChar !=
'>' && endOffset < nclTextEditor->text().size())
425 curChar = nclTextEditor->SendScintilla( QsciScintilla::SCI_GETCHARAT,
428 if(endOffset == nclTextEditor->text().size())
430 qWarning() <<
"TextEditor could not perform the requested action.";
436 while(isspace(curChar) && endOffset < nclTextEditor->text().size())
439 curChar = nclTextEditor->SendScintilla( QsciScintilla::SCI_GETCHARAT,
443 nclTextEditor->SendScintilla(QsciScintilla::SCI_SETSELECTIONSTART,
445 nclTextEditor->SendScintilla(QsciScintilla::SCI_SETSELECTIONEND,
447 nclTextEditor->removeSelectedText();
450 QList<QString> mustRemoveEntity;
455 foreach(key, startEntityOffset.keys())
458 if(startEntityOffset[key] >= startOffset &&
459 endEntityOffset[key]+2 <= endOffset)
461 mustRemoveEntity.append(key);
467 if(startEntityOffset[key] >= startOffset)
469 startEntityOffset[key] -= (endOffset - startOffset);
472 if(endEntityOffset[key] >= endOffset)
474 endEntityOffset[key] -= (endOffset - startOffset);
481 QListIterator<QString> iterator( mustRemoveEntity );
482 while( iterator.hasNext() ){
483 key = iterator.next();
484 startEntityOffset.remove(key);
485 endEntityOffset.remove(key);
498 data.append(nclTextEditor->text());
499 data.append(
"$START_ENTITIES_LINES$");
502 foreach (key, startEntityOffset.keys())
508 data.append(key +
", " + QString::number(startEntityOffset[key]));
510 data.append(
"$END_ENTITIES_LINES$");
512 foreach (key, endEntityOffset.keys())
518 data.append(key +
"," + QString::number(endEntityOffset[key]));
521 emit setPluginData(data);
531 QString *
id = (QString*)param;
532 if(startEntityOffset.contains(*
id))
534 int entityOffset = startEntityOffset.value(*
id);
539 if(entityOffset < nclTextEditor->text().size())
541 nclTextEditor->SendScintilla(QsciScintilla::SCI_GOTOPOS,
543 nclTextEditor->SendScintilla(QsciScintilla::SCI_SETFOCUS,
549 qDebug() <<
"NCLTextualViewPlugin::changeSelectedEntity() "
550 <<
"Entity doesn't exists!";
554 void NCLTextualViewPlugin::updateCoreModel()
557 bool rebuildComposerModelFromScratch =
true;
561 QString text = nclTextEditor->text();
562 QString errorMessage;
563 int errorLine, errorColumn;
565 nclTextEditor->clearErrorIndicators();
566 if(!xmlDoc.setContent(text, &errorMessage, &errorLine, &errorColumn))
569 QMessageBox::warning(nclTextEditor, tr(
"Error"),
570 tr(
"Your document is not a Well-formed XML"));
571 nclTextEditor->keepFocused();
572 nclTextEditor->markError(errorMessage,
"", errorLine-1, errorColumn);
581 sendBroadcastMessage(
"textualStartSync", NULL);
583 tmpNclTextEditor = nclTextEditor;
585 nclTextEditor->setDocumentUrl(project->getLocation());
586 nclTextEditor->setText(tmpNclTextEditor->textWithoutUserInteraction());
589 if(rebuildComposerModelFromScratch)
590 nonIncrementalUpdateCoreModel();
593 incrementalUpdateCoreModel();
595 sendBroadcastMessage(
"textualFinishSync", NULL);
602 void NCLTextualViewPlugin::nonIncrementalUpdateCoreModel()
605 if(project->getChildren().size())
606 emit removeEntity(project->getChildren().at(0),
true);
609 nclTextEditor->clear();
610 nclTextEditor->setText(
"<?xml version=1.0 encoding=ISO-8859-1?>");
611 startEntityOffset.clear();
612 endEntityOffset.clear();
614 QList <QString> parentUids;
615 QString parentUId = project->getUniqueId();
616 parentUids.push_back(parentUId);
618 QList <QDomElement> nodes;
619 QDomElement current = xmlDoc.firstChildElement();
620 nodes.push_back(current);
622 while(!nodes.empty())
624 current = nodes.front();
626 parentUId = parentUids.front();
627 parentUids.pop_front();
629 if(current.tagName() ==
"ncl" && !current.hasAttribute(
"id"))
631 current.setAttribute(
"id",
"myNCLDocID");
635 QMap<QString,QString> atts;
637 QDomNamedNodeMap attributes = current.attributes();
638 for (
int i = 0; i < attributes.length(); i++)
640 QDomAttr item = attributes.item(i).toAttr();
641 atts[item.name()] = item.value();
645 emit addEntity(current.tagName(), parentUId, atts,
false);
646 parentUId = currentEntity->getUniqueId();
648 QDomElement child = current.firstChildElement();
649 while(!child.isNull())
651 nodes.push_back(child);
652 parentUids.push_back(parentUId);
653 child = child.nextSiblingElement();
658 void NCLTextualViewPlugin::incrementalUpdateCoreModel()
660 QProgressDialog dialog(tr(
"Synchronizing with other plugins..."),
661 tr(
"Cancel"), 0, 100,
663 dialog.setAutoClose(
true);
667 QList <QDomNode> nodes;
668 QDomNode current = xmlDoc;
669 nodes.push_back(current);
670 Entity *curEntity = project;
671 QList <Entity *> entities;
672 entities.push_back(curEntity);
675 int total_nodes = 0, progress = 0;
676 while(!nodes.empty())
678 current = nodes.front();
683 QDomElement child = current.firstChildElement();
684 while(!child.isNull())
686 nodes.push_back(child);
687 child = child.nextSiblingElement();
691 dialog.setRange(0, total_nodes);
693 nodes.push_back(current);
694 while(!nodes.empty())
696 dialog.setValue(progress++);
700 current = nodes.front();
702 curEntity = entities.front();
703 entities.pop_front();
705 QVector <QDomElement> children;
706 QDomElement child = current.firstChildElement();
707 while(!child.isNull())
709 children.push_back(child);
710 child = child.nextSiblingElement();
713 QVector <Entity *> entityChildren = curEntity->getChildren();
717 i < children.size() && j < entityChildren.size();
720 bool sameNCLID =
false;
722 if(children[i].hasAttribute(
"id") && entityChildren[j]->hasAttribute(
"id"))
724 if(children[i].attribute(
"id") == entityChildren[j]->getAttribute(
"id"))
727 else if( children[i].hasAttribute(
"name")
728 && entityChildren[j]->hasAttribute(
"name"))
730 if(children[i].attribute(
"name")
731 == entityChildren[j]->getAttribute(
"name"))
735 else if(children[i].hasAttribute(
"alias")
736 && entityChildren[j]->hasAttribute(
"alias"))
738 if(children[i].attribute(
"alias")
739 == entityChildren[j]->getAttribute(
"alias"))
745 if( children[i].tagName() == entityChildren[j]->getType()
750 QMap<QString, QString> atts;
751 QDomNamedNodeMap attributes = children[i].attributes();
752 for (
int k = 0; k < attributes.length(); k++)
754 QDomNode item = attributes.item(k);
755 qDebug() << item.nodeName() << item.nodeValue();
756 atts.insert(item.nodeName(), item.nodeValue());
759 QMap <QString, QString>::iterator begin, end, it;
760 entityChildren[j]->getAttributeIterator(begin, end);
762 bool changed =
false;
763 int entityChildrenAttrSize = 0;
764 for (it = begin; it != end; ++it)
766 if(atts.contains(it.key()) && atts[it.key()]== it.value())
770 entityChildrenAttrSize++;
773 if(entityChildrenAttrSize != atts.size())
777 emit setAttributes(entityChildren[j], atts,
false);
781 qDebug() << entityChildren[j]->getType() << children[i].tagName();
784 emit removeEntity(entityChildren[j],
true);
786 QMap<QString,QString> atts;
787 QDomNamedNodeMap attributes = children[i].attributes();
788 for (
int k = 0; k < attributes.length(); k++)
790 QDomNode item = attributes.item(k);
791 atts[item.nodeName()] = item.nodeValue();
793 emit addEntity(children[i].tagName(), curEntity->getUniqueId(), atts,
798 if(i == children.size())
801 for(; j < entityChildren.size(); j++)
802 emit removeEntity(entityChildren[j],
true);
804 else if(j == entityChildren.size())
807 for(; i < children.size(); i++)
810 QMap<QString,QString> atts;
811 QDomNamedNodeMap attributes = children[i].attributes();
812 for (
int k = 0; k < attributes.length(); k++)
814 QDomNode item = attributes.item(k);
815 atts[item.nodeName()] = item.nodeValue();
817 emit addEntity(children[i].tagName(), curEntity->getUniqueId(), atts,
822 child = current.firstChildElement();
823 while(!child.isNull())
825 nodes.push_back(child);
826 child = child.nextSiblingElement();
828 entityChildren = curEntity->getChildren();
829 for(
int i = 0; i < entityChildren.size(); i++)
830 entities.push_back(entityChildren[i]);
833 dialog.setValue(100);
836 void NCLTextualViewPlugin::syncFinished()
839 delete nclTextEditor;
840 nclTextEditor = tmpNclTextEditor;
841 tmpNclTextEditor = NULL;
843 nclTextEditor->setTextWithoutUserInteraction(nclTextEditor->text());
846 updateErrorMessages();
849 bool NCLTextualViewPlugin::isStartEndTag(Entity *entity)
851 int endOffset = endEntityOffset[entity->getUniqueId()];
854 char curChar = nclTextEditor->SendScintilla(QsciScintilla::SCI_GETCHARAT,
859 qDebug() <<
"isStartEndTag returns true";
865 void NCLTextualViewPlugin::openStartEndTag(Entity *entity)
867 if(isStartEndTag(entity))
869 int endOffset = endEntityOffset[entity->getUniqueId()];
874 nclTextEditor->SendScintilla(QsciScintilla::SCI_SETSELECTIONSTART,
877 nclTextEditor->SendScintilla(QsciScintilla::SCI_SETSELECTIONEND,
880 nclTextEditor->removeSelectedText();
882 QString endTag =
">\n</" + entity->getType() +
">";
884 nclTextEditor->insertAtPos(endTag, endOffset-1);
889 updateEntitiesOffset(endOffset, endTag.size() - 2);
892 fixIdentation(endOffset+2,
false);
893 endEntityOffset[entity->getUniqueId()] = endOffset + 1;
897 void NCLTextualViewPlugin::fixIdentation(
int offset,
bool mustAddTab)
900 int insertAtLine = nclTextEditor->SendScintilla(
901 QsciScintilla::SCI_LINEFROMPOSITION, offset);
903 int totalLines = nclTextEditor->
904 SendScintilla( QsciScintilla::SCI_GETLINECOUNT);
906 qDebug () << totalLines << insertAtLine;
908 if(insertAtLine + 1 >= totalLines)
return;
910 int lineIndent = nclTextEditor
911 ->SendScintilla( QsciScintilla::SCI_GETLINEINDENTATION,
914 if(insertAtLine > 1 && mustAddTab)
915 lineIndent += nclTextEditor->tabWidth();
917 nclTextEditor->SendScintilla( QsciScintilla::SCI_SETLINEINDENTATION,
921 updateEntitiesOffset(offset-1, lineIndent/nclTextEditor->tabWidth());
924 void NCLTextualViewPlugin::updateEntitiesOffset(
int startFrom,
934 foreach(key, startEntityOffset.keys())
936 if(startEntityOffset[key] > startFrom)
937 startEntityOffset[key] += insertedChars;
939 if(endEntityOffset[key] >= startFrom)
940 endEntityOffset[key] += insertedChars;
944 void NCLTextualViewPlugin::printEntitiesOffset()
947 foreach(key, startEntityOffset.keys())
949 int startOffSet = startEntityOffset[key];
950 char startChar = nclTextEditor->SendScintilla(QsciScintilla::SCI_GETCHARAT,
952 int endOffSet = endEntityOffset[key];
953 char endChar = nclTextEditor->SendScintilla(QsciScintilla::SCI_GETCHARAT,
956 qDebug() <<
"key="<< key <<
"(" << project->getEntityById(key)->getType()
957 <<
"; start=" << startOffSet
958 <<
"; start_char=" << startChar <<
"; end=" << endOffSet
959 <<
"; end_char=" << endChar << endl;
963 void NCLTextualViewPlugin::manageFocusLost(QFocusEvent *event)
965 #ifndef NCLEDITOR_STANDALONE
973 if(nclTextEditor->textWithoutUserInteraction() != nclTextEditor->text()
975 && (QApplication::focusWidget() != NULL))
977 int ret = QMessageBox::question(window,
978 tr(
"Textual View synchronization"),
979 tr(
"You have changed the textual content of the NCL \
980 Document. Do you want to synchronize this text with \
986 QMessageBox::Cancel);
990 case QMessageBox::Yes:
993 case QMessageBox::No:
994 nclTextEditor->setText(nclTextEditor->textWithoutUserInteraction());
996 case QMessageBox::Cancel:
997 nclTextEditor->keepFocused();
1001 else if(QApplication::focusWidget() == NULL)
1005 nclTextEditor->keepFocused();
1015 clearValidationMessages(this->pluginInstanceID, NULL);
1017 emit sendBroadcastMessage(
"askAllValidationMessages", NULL);
1022 nclTextEditor->clearErrorIndicators();
1031 pair <QString , QString> *p = (pair <QString, QString> *) param;
1033 int offset = startEntityOffset[p->first];
1035 int line = nclTextEditor->SendScintilla(
1036 QsciScintilla::SCI_LINEFROMPOSITION,
1039 nclTextEditor->markError(p->second,
"", line);