NCL Composer  0.1.5
 All Classes Functions Variables Pages
NCLTreeWidget.cpp
1 /*
2  * Copyright 2011-2012 TeleMidia/PUC-Rio.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see
16  * <http://www.gnu.org/licenses/>.
17  */
18 #include "NCLTreeWidget.h"
19 #include "NCLStructure.h"
20 
21 using namespace composer::language;
22 
23 #include <QInputDialog>
24 #include <QDialog>
25 
26 #include <core/util/ComposerSettings.h>
27 using namespace composer::core::util;
28 
29 NCLTreeWidget::NCLTreeWidget(QWidget *parent) : QTreeWidget(parent)
30 {
31  setContextMenuPolicy(Qt::ActionsContextMenu);
32 
33  createActions();
34  createMenus();
35 
36  QStringList labels;
37  labels << QObject::tr("Element") << QObject::tr("Attributes")
38  << QObject::tr("Element UId") << QObject::tr("Element Id")
39  << QObject::tr("Tagname");
40  setHeaderLabels(labels);
41 
42  setHeaderHidden(true);
43  setColumnHidden(1, true);
44  setColumnHidden(2, true);
45  setColumnHidden(3, true);
46  setColumnHidden(4, true);
47 
48  isExpandedAll = true;
49 
50  /* \todo This defaultFontSize must be configurable. */
51  defaultFont = QFont();
52 
53  setDragEnabled(true);
54 }
55 
57 {
58 
59 }
60 
61 void NCLTreeWidget::createActions ()
62 {
63  insertNodeAct = new QAction( QIcon(":/images/save.png"),
64  tr("&Add child"),
65  this);
66 
67  connect( insertNodeAct, SIGNAL(triggered()),
68  this, SLOT(userAddNewElement()));
69  addAction(insertNodeAct);
70 
71  removeNodeAct = new QAction( QIcon(":/images/delete.png"),
72  tr("&Remove Selected element"),
73  this);
74 
75  connect( removeNodeAct, SIGNAL(triggered()),
76  this, SLOT(userRemoveElement()));
77  removeNodeAct->setShortcutContext(Qt::WidgetShortcut);
78  removeNodeAct->setShortcut(QKeySequence::Delete);
79  addAction(removeNodeAct);
80 
81  expandAllAct = new QAction(tr("&Expand All"), this);
82  expandAllAct->setCheckable(true);
83  expandAllAct->setChecked(true);
84  connect(expandAllAct, SIGNAL(triggered()), this, SLOT(expandAll()));
85  addAction(expandAllAct);
86 
87 }
88 
89 void NCLTreeWidget::createMenus()
90 {
91  elementMenu = new QMenu(this);
92  elementMenu->addAction(insertNodeAct);
93  elementMenu->addAction(removeNodeAct);
94  elementMenu->addSeparator();
95  elementMenu->addAction(expandAllAct);
96 }
97 
99 {
100  // qDebug() << "MainWindow::updateTreeWidget()";
101  QStringList labels;
102  labels << QObject::tr("Element") << QObject::tr("Attributes")
103  << QObject::tr("id");
104 
105  QXmlInputSource inputSource;
106  inputSource.setData( text );
107  QXmlSimpleReader reader;
108 
109  this->setStyleSheet("/*background-color: #ffffff;*/ font-size: 11px;");
110  clear();
111  setHeaderLabels(labels);
112  // header()->setStretchLastSection(false);
113 
114  // FIXME: Uncommenting this option brings some performance problems.
115  // header()->setResizeMode(QHeaderView::ResizeToContents);
116 
117  //TODO: Transform this parser in a puglin
118  NCLParser *handler = new NCLParser(this);
119  connect ( handler,
120  SIGNAL(fatalErrorFound(QString, QString, int, int, int)),
121  this,
122  SLOT(errorNotification(QString, QString, int, int, int)));
123 
124  reader.setContentHandler(handler);
125  reader.setErrorHandler(handler);
126  bool ret = reader.parse(inputSource);
127  delete handler;
128  this->expandAll();
129 
130  return ret;
131 }
132 
133 QTreeWidgetItem* NCLTreeWidget::addElement ( QTreeWidgetItem *father,
134  int pos,
135  QString tagname,
136  QString id,
137  QMap <QString, QString> &attrs,
138  int line_in_text,
139  int column_in_text)
140 {
141 
142  QTreeWidgetItem *child;
143 
144  if(father != 0)
145  {
146  child = new QTreeWidgetItem(0);
147  int p = pos;
148  if(pos == -1)
149  p = father->childCount();
150 
151  father->insertChild(p, child);
152  }
153  else
154  {
155  child = new QTreeWidgetItem(this);
156 
157  if(pos != -1)
158  this->insertTopLevelItem(pos, child);
159  }
160 
161  updateItem(child, tagname, attrs);
162  child->setText(2, id);
163  // child->setText(3, name) // this is done inside update item
164  child->setText(4, tagname);
165  // child->setText(2, QString::number(line_in_text));
166  // child->setText(3, QString::number(column_in_text));
167 
168  repaint();
169 
170  return child;
171 }
172 
173 void NCLTreeWidget::userAddNewElement()
174 {
175  bool ok;
176  QList<QTreeWidgetItem*> selecteds = this->selectedItems();
177 
178  QTreeWidgetItem *item ;
179  QString parentId = "", tagname = "";
180  QStringList strlist;
181 
182  if(selecteds.size())
183  {
184  item = selecteds.at(0);
185  parentId = item->text(2);
186  tagname = item->text(4);
187 
188  map <QString, char> *
189  children = NCLStructure::getInstance()->getChildren(tagname);
190 
191  if(children != NULL)
192  {
193  map <QString, char>::iterator it;
194  for(it = children->begin(); it != children->end(); ++it)
195  {
196  strlist << it->first;
197  }
198  }
199  }
200  else
201  strlist << "ncl"; //\fixme this should be loaded from element that has not parents.
202 
203  QString element = QInputDialog::getItem( this,
204  tr("&Add child"),
205  tr("Element name:"),
206  strlist,
207  0,
208  true,
209  &ok );
210  if(ok)
211  {
212  //Add new Element to OutlineWidget
213  QMap<QString,QString> attr;
214  if(element == "ncl")
215  attr.insert("id", "myNCLID");
216  emit elementAddedByUser (element, parentId, attr, false);
217  }
218 }
219 
220 QTreeWidgetItem *NCLTreeWidget::getItemById(QString itemId)
221 {
222  QList <QTreeWidgetItem*> items = findItems( itemId,
223  Qt::MatchExactly |
224  Qt::MatchRecursive, 2);
225 
226  if(items.size() > 1)
227  {
228  qDebug() << "NCLTreeWidget::getItemById Warning - You have more than "
229  << "one item with id='" << itemId
230  << "' - All them will be deleted!";
231  }
232 
233  for (int i = 0; i < items.size(); i++)
234  {
235  if(items.at(i)->text(2) == itemId)
236  {
237  return items.at(i);
238  }
239  }
240  return NULL;
241 }
242 
243 void NCLTreeWidget::removeItem(QString itemId)
244 {
245  QRegExp exp("*");
246  QList <QTreeWidgetItem*> items = this->findItems(itemId, Qt::MatchExactly
247  | Qt::MatchRecursive, 2);
248  QTreeWidgetItem *item;
249 
250  if(items.size() > 1)
251  {
252  qDebug() << "NCLTreeWidget::removeItem Warning - You have more than "
253  << "one item with id='"<< itemId
254  << "' - All them will be deleted!";
255  }
256  else if(items.size() == 0)
257  {
258  qDebug() << "NCLTreeWidget::removeItem Warning! Item with id ='"
259  << itemId << "' was not found!";
260  }
261 
262  for (int i = 0; i < items.size(); i++)
263  {
264  item = items.at(i);
265  if (item->parent() != NULL)
266  {
267  item->parent()->removeChild(item);
268  qDeleteAll(item->takeChildren());
269  }
270  else
271  {
272  // TODO:
273  // this->clear();
274  int index = indexOfTopLevelItem(item);
275  QTreeWidgetItem *item = takeTopLevelItem(index);
276  qDebug() << "index=" << index << "item=" << item;
277  // delete item;
278  }
279  }
280  repaint();
281 }
282 
283 void NCLTreeWidget::userRemoveElement()
284 {
285  QList<QTreeWidgetItem*> selecteds = this->selectedItems();
286  QTreeWidgetItem *item = selecteds.at (0);
287 
288  if(item == NULL)
289  return;
290 
291  QString id = item->text(2);
292  QString name = item->text(0);
293 
294  if (item != NULL) {
295  int resp = QMessageBox::question(
296  this,
297  tr("Deleting Element"),
298  tr("Do you really want delete the \"%1\" element ?").arg(name),
299  QMessageBox::Yes,
300  QMessageBox::No );
301 
302  if(resp == QMessageBox::Yes) {
303  emit elementRemovedByUser(id);
304  }
305  }
306  repaint();
307 }
308 
309 void NCLTreeWidget::updateItem(QTreeWidgetItem *item, QString tagname,
310  QMap <QString, QString> &attrs)
311 {
312  QIcon icon;
316  if(tagname == "media")
317  {
318  QString type = "";
319 
320  if(attrs.contains("type") && !attrs.values("type").empty())
321  {
322  type = attrs.value("type");
323  }
324  else if(attrs.contains("src") && !attrs.values("src").empty())
325  {
326  QString src = attrs.value("src");
327  QString ext = src.mid(src.lastIndexOf(".") + 1);
328 
329  //TODO: The mapping between extension and media type should be in the
330  // settings file.
331 
332  /* GlobalSettings settings;
333  settings.beginGroup("mimetypes");
334  type = settings.value(ext).toString();
335  settings.endGroup(); */
336 
337  if(ext == "png" || ext == "jpg" || ext == "jpeg" || ext == "gif")
338  {
339  type = "image";
340  }
341  else if(ext == "mp4" || ext == "avi" || ext == "mpeg4" || ext == "mpeg"
342  || ext == "mpg" || ext == "mov" || ext == "ts")
343  {
344  type = "video";
345  }
346  else if(ext == "mp3" || ext == "wav" || ext == "ac3" || ext == "mpa"
347  || ext == "mp2")
348  {
349  type = "audio";
350  }
351  else if(ext == "htm" || ext == "html")
352  {
353  type = "text/html";
354  }
355  else if(ext == "ncl")
356  {
357  type = "application/x-ginga-ncl";
358  }
359  else if(ext == "txt")
360  {
361  type = "text/plain";
362  }
363  else if(ext == "lua")
364  {
365  type = "application/x-ginga-NCLua";
366  }
367  }
368 
369  if(!type.isEmpty())
370  {
371  if(type.startsWith("audio"))
372  icon = QIcon(":/icon/audio");
373  else if(type.startsWith("image"))
374  icon = QIcon(":/icon/image");
375  else if(type.startsWith("video"))
376  icon = QIcon(":/icon/video");
377  else if(type.startsWith("text/html"))
378  icon = QIcon(":/icon/html");
379  else if(type.startsWith("text"))
380  icon = QIcon(":/icon/text");
381  else if(type.startsWith("application/x-ginga-NCLua"))
382  icon = QIcon(":/icon/script");
383  else if(type.startsWith("application/x-ginga-ncl"))
384  icon = QIcon(":/icon/ncl");
385  else if(type.startsWith("application/x-ncl-settings") ||
386  type.startsWith("application/x-ginga-settings"))
387  icon = QIcon(":/icon/settings");
388  else icon = QIcon (":/icon/media");
389  }
390  else
391  icon = QIcon (":/icon/media");
392  }
393  else if(tagname == "context" || tagname == "body")
394  icon = QIcon (":/icon/context");
395  else if(tagname == "meta" || tagname == "metadata")
396  icon = QIcon (":/icon/metadata");
397  else if(tagname == "switch")
398  icon = QIcon (":/icon/switch");
399  else if(tagname == "descriptor")
400  icon = QIcon (":/icon/descriptor");
401  else if(tagname == "link")
402  icon = QIcon (":/icon/link");
403  else if(tagname == "port")
404  icon = QIcon (":/icon/port");
405  else if(tagname == "property")
406  icon = QIcon (":/icon/property");
407  else
408  icon = QIcon (":/icon/element");
409 
410  QString strAttrList = "";
411  QString key;
412  QString name;
413  foreach (key, attrs.keys())
414  {
415  strAttrList += " ";
416  strAttrList += key + "=\"" + attrs[key] + "\"";
417  if(key == "id" || key == "name") {
418  name = attrs[key];
419  }
420  }
421 
422  item->setIcon(0, icon);
423  item->setText(4, tagname);
424 
425  if(name != "")
426  {
427  item->setText(0, tagname + " (" + name + ")");
428  item->setText(3, name);
429  }
430  else
431  item->setText(0, tagname);
432 
433  //item->setText(2, id);
434  //item->setText(1, name);
435 
436  if(isExpandedAll)
437  QTreeWidget::expandAll();
438 
439  repaint();
440 }
441 
442 void NCLTreeWidget::errorNotification( QString message,
443  QString filename,
444  int line,
445  int column,
446  int severity)
447 {
448  //this->setStyleSheet("background-color: #aa0000; font-size: 11px;");
449  emit parserErrorNotify(message, filename, line, column, severity);
450 }
451 
452 void NCLTreeWidget::decreaseFont()
453 {
454  unsigned int newPointSize = font().pointSize()-1;
455  QFont newFont(font());
456  newFont.setPointSize(newPointSize);
457  setFont(newFont);
458 }
459 
460 void NCLTreeWidget::increaseFont()
461 {
462  unsigned int newPointSize = font().pointSize()+1;
463  QFont newFont(font());
464  newFont.setPointSize(newPointSize);
465  setFont(newFont);
466 }
467 
469 {
470  setFont(defaultFont);
471 }
472 
473 void NCLTreeWidget::setDefaultFont(const QFont &defaultFont)
474 {
475  this->defaultFont = defaultFont;
476 }
477 
478 void NCLTreeWidget::wheelEvent(QWheelEvent *event)
479 {
480  if(event->modifiers() == Qt::ControlModifier){
481  if(event->delta() > 0)
482  zoomIn();
483  else
484  zoomOut();
485  event->accept();
486  }
487  else {
488  QTreeWidget::wheelEvent(event);
489  }
490 }
491 
492 void NCLTreeWidget::keyPressEvent(QKeyEvent *event)
493 {
494  if(event->modifiers() & Qt::ControlModifier)
495  {
496  if(event->key() == Qt::Key_Plus) //checks for zoomin event.
497  {
498  zoomIn();
499  event->accept();
500  }
501  else if(event->key() == Qt::Key_Minus) //checks for zoom out event.
502  {
503  zoomOut();
504  event->accept();
505  }
506  else if(event->key() == Qt::Key_0) //checks reset zoom event.
507  {
508  resetZoom();
509  event->accept();
510  }
511  }
512 
513  QTreeWidget::keyPressEvent(event);
514 }
515 
516 void NCLTreeWidget::expandAll()
517 {
518  if(!isExpandedAll)
519  {
520  isExpandedAll = true;
521  QTreeWidget::expandAll();
522  }
523  else isExpandedAll = false;
524 }
525 
527 {
528  increaseFont();
529  //IncreaseIconSize();
530 }
531 
533 {
534  decreaseFont();
535  //DecreaseIconSize();
536 }
537 
539 {
540  resetFont();
541 }
542 
543 void NCLTreeWidget::mouseMoveEvent(QMouseEvent *event)
544 {
545  QTreeWidgetItem *selectedItem = currentItem();
546 
547  // if not left button - return
548  if (!(event->buttons() & Qt::LeftButton)) return;
549 
550  // If the selected Item exists
551  if (selectedItem)
552  {
553  QDrag *drag = new QDrag(this);
554  QMimeData *mimeData = new QMimeData;
555  mimeData->setColorData(Qt::green);
556  mimeData->setData("nclcomposer/mediaid", currentItem()->text(3).toAscii());
557  mimeData->setData("nclcomposer/qnstuid", currentItem()->text(2).toAscii());
558 
559  drag->setMimeData(mimeData);
560  // start drag
561  drag->start(Qt::CopyAction | Qt::MoveAction);
562  }
563 }