1 /*
2
3 Firewall Builder
4
5 Copyright (C) 2003 NetCitadel, LLC
6
7 Author: Vadim Kurland vadim@fwbuilder.org
8
9 $Id$
10
11 This program is free software which we release under the GNU General Public
12 License. You may redistribute and/or modify this program under the terms
13 of that license as published by the Free Software Foundation; either
14 version 2 of the License, or (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 To get a copy of the GNU General Public License, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
24 */
25
26
27 #include "config.h"
28 #include "global.h"
29 #include "utils.h"
30
31 #include "FWBSettings.h"
32 #include "FWBTree.h"
33 #include "FWObjectDrag.h"
34 #include "FWObjectPropertiesFactory.h"
35 #include "FWWindow.h"
36 #include "IconSetter.h"
37 #include "ObjectManipulator.h"
38 #include "ObjectTreeView.h"
39 #include "ObjectTreeViewItem.h"
40 #include "ProjectPanel.h"
41
42 #include "fwbuilder/FWObject.h"
43 #include "fwbuilder/Firewall.h"
44 #include "fwbuilder/Group.h"
45 #include "fwbuilder/Interface.h"
46 #include "fwbuilder/NAT.h"
47 #include "fwbuilder/Routing.h"
48 #include "fwbuilder/Policy.h"
49 #include "fwbuilder/Resources.h"
50 #include "fwbuilder/TCPUDPService.h"
51 #include "fwbuilder/ServiceGroup.h"
52 #include "fwbuilder/FWServiceReference.h"
53 #include "fwbuilder/AddressRange.h"
54 #include "fwbuilder/Network.h"
55
56 #include <QAbstractItemView>
57 #include <QBitmap>
58 #include <QDragEnterEvent>
59 #include <QDragLeaveEvent>
60 #include <QDragMoveEvent>
61 #include <QDropEvent>
62 #include <QFocusEvent>
63 #include <QHeaderView>
64 #include <QKeyEvent>
65 #include <QMimeData>
66 #include <QMouseEvent>
67 #include <QPixmap>
68 #include <QStyle>
69 #include <QStyleOption>
70 #include <QtDebug>
71 #include <qpainter.h>
72 #include <qpixmapcache.h>
73
74 #include <iostream>
75 #include <algorithm>
76
77 using namespace std;
78 using namespace libfwbuilder;
79
80 /****************************************************************************
81 *
82 * class ObjectTreeView
83 *
84 ****************************************************************************/
85
ObjectTreeView(ProjectPanel * project,QWidget * parent,const char * name,Qt::WFlags f)86 ObjectTreeView::ObjectTreeView(ProjectPanel* project,
87 QWidget* parent,
88 const char * name,
89 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
90 Qt::WFlags f) :
91 #else
92 Qt::WindowFlags f) :
93 #endif
94 QTreeWidget(parent),
95 m_project(project)
96 {
97 setObjectName(name);
98 this->setParent(parent, f);
99 setFont(st->getTreeFont());
100
101 QPalette updated_palette = palette();
102 updated_palette.setColor(
103 QPalette::Inactive,
104 QPalette::Highlight,
105 QColor("silver"));
106
107 // palette().color(QPalette::Highlight).lighter(300));
108
109 setPalette(updated_palette);
110
111 setExpandsOnDoubleClick(false);
112
113 setDragEnabled(true);
114 item_before_drag_started=NULL;
115 lastSelected = NULL;
116 second_click = false;
117 selectionFrozen = false;
118 expandOrCollapse = false;
119 Lockable = false;
120 Unlockable = false;
121 visible = false;
122 /*
123 * note about process_mouse_release_event
124 *
125 * we use mouseReleaseEvent event to switch object opened in the
126 * editor panel (i.e. we open new object when mouse button is
127 * released rather than when it is pressed). This allows us to
128 * start drag without switching object in the editor. The problem
129 * is that mouseReleaseEvent is received in this widget after the
130 * d&d ends with a drop somewhere else, which triggers call to
131 * mouseReleaseEvent which switches object in the
132 * editor. This is undesired when the editor shows a group and we
133 * try to drag and drop an object into that group. Flag
134 * process_mouse_release_event is used to suppress object
135 * switching when mouseReleaseEvent is called after
136 * successfull drop.
137 */
138 process_mouse_release_event = true;
139
140 connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
141 this, SLOT(currentItemChanged(QTreeWidgetItem*)));
142
143 connect(this, SIGNAL(itemSelectionChanged()),
144 this, SLOT(itemSelectionChanged()));
145
146 connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)),
147 this, SLOT(itemCollapsed(QTreeWidgetItem*)));
148
149 connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
150 this, SLOT(itemExpanded(QTreeWidgetItem*)));
151
152 QStringList qsl;
153 qsl.push_back(tr("Object"));
154 qsl.push_back(tr("Attributes"));
155 setHeaderLabels(qsl);
156
157 //header()->hide();
158
159 header()->setDefaultAlignment(Qt::AlignLeft);
160 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
161 header()->setResizeMode(QHeaderView::Interactive);
162 #else
163 header()->setSectionResizeMode(QHeaderView::Interactive);
164 #endif
165
166 showOrHideAttributesColumn();
167
168 setMinimumSize( QSize( 100, 0 ) );
169
170 setAutoScroll(true);
171 setAutoScrollMargin(50);
172 setAllColumnsShowFocus( true );
173 setSelectionMode( ExtendedSelection );
174 setAcceptDrops( true );
175 setDragDropMode( QAbstractItemView::DragDrop );
176 setRootIsDecorated( true );
177
178 setFocusPolicy(Qt::StrongFocus);
179
180 connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this,
181 SLOT(updateFilter()));
182
183 // disable sorting, otherwise gui crashes when built with
184 // QT 4.3.4 (discovered on Ubuntu Hardy). Crash happened when
185 // second object was added to any branch of the tree.
186 //
187 // This causes crash with Qt 4.6 as well
188 //
189 // setSortingEnabled(true);
190
191 }
192
paintEvent(QPaintEvent * ev)193 void ObjectTreeView::paintEvent(QPaintEvent *ev)
194 {
195 QTreeWidget::paintEvent(ev);
196 }
197
drawRow(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const198 void ObjectTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option,
199 const QModelIndex &index ) const
200 {
201 // qDebug() << "ObjectTreeView::drawRow"
202 // << "QStyleOptionViewItem.state=" << int(option.state)
203 // << "hasFocus()=" << hasFocus()
204 // << "isActiveWindow()=" << isActiveWindow();
205
206 // QWidget *fw = QApplication::focusWidget();
207 // qDebug() << "Currently has focus:" << fw;
208
209 // this is a patch for #2475
210 // Looks like the state remains State_Active even when the tree view widget
211 // loses focus (as long as parent window is active).
212 QStyleOptionViewItem new_opt = option;
213 if ( ! hasFocus()) new_opt.state &= ~QStyle::State_Active;
214 QTreeWidget::drawRow(painter, new_opt, index);
215 }
216
event(QEvent * event)217 bool ObjectTreeView::event( QEvent *event )
218 {
219 if (event->type() == QEvent::ToolTip)
220 {
221 QHelpEvent *he = (QHelpEvent*) event;
222 QPoint pos = he->pos();
223
224 if (st->getObjTooltips())
225 {
226 int cx = pos.x(), cy = pos.y();
227
228 FWObject *obj=NULL;
229 QRect cr;
230
231 QTreeWidgetItem *itm = itemAt(QPoint(cx, cy - header()->height()));
232 if (itm==NULL) return false;
233 ObjectTreeViewItem *oivi = dynamic_cast<ObjectTreeViewItem*>(itm);
234 assert(oivi!=NULL);
235 obj = oivi->getFWObject();
236
237 if (obj==NULL) return false;
238
239 if (obj->getId() == FWObjectDatabase::ANY_ADDRESS_ID ||
240 obj->getId() == FWObjectDatabase::ANY_SERVICE_ID ||
241 obj->getId() == FWObjectDatabase::ANY_INTERVAL_ID ||
242 obj->getId() == FWObjectDatabase::DUMMY_ADDRESS_ID ||
243 obj->getId() == FWObjectDatabase::DUMMY_SERVICE_ID ||
244 obj->getId() == FWObjectDatabase::DUMMY_INTERFACE_ID)
245 return false;
246
247 cr = visualItemRect(itm);
248
249 QRect global = QRect(
250 viewport()->mapToGlobal(cr.topLeft()),
251 viewport()->mapToGlobal(cr.bottomRight()));
252
253 //finally stretch rect up to component's width and even more
254 //(it fixes bug with horizontal scroll)
255 global.setWidth(width() + horizontalOffset());
256
257 QToolTip::showText(mapToGlobal( he->pos() ),
258 FWObjectPropertiesFactory::getObjectPropertiesDetailed(obj,
259 true,
260 true),
261 this, global);
262 }
263
264 return true;
265 }
266
267 return QTreeWidget::event(event);
268 }
269
currentItemChanged(QTreeWidgetItem *)270 void ObjectTreeView::currentItemChanged(QTreeWidgetItem*)
271 {
272 expandOrCollapse = false;
273 }
274
itemCollapsed(QTreeWidgetItem * itm)275 void ObjectTreeView::itemCollapsed(QTreeWidgetItem* itm)
276 {
277 expandOrCollapse = true;
278
279 ObjectTreeViewItem *otvi = dynamic_cast<ObjectTreeViewItem*>(itm);
280 assert(otvi!=NULL);
281 FWObject *o = otvi->getFWObject();
282 if (o)
283 {
284 int id = o->getId();
285 expanded_objects.erase(id);
286 }
287 }
288
itemExpanded(QTreeWidgetItem * itm)289 void ObjectTreeView::itemExpanded(QTreeWidgetItem* itm)
290 {
291 expandOrCollapse = true;
292
293 ObjectTreeViewItem *otvi=dynamic_cast<ObjectTreeViewItem*>(itm);
294 assert(otvi!=NULL);
295 FWObject *o = otvi->getFWObject();
296 if (o)
297 {
298 int id = o->getId();
299 expanded_objects.insert(id);
300 }
301 }
302
303 /*
304 * This method makes list selectedObjects flat. If user selects
305 * several objects in the tree, and some of them have children, QT
306 * puts all the children in the selected objects list even if
307 * corresponding subtrees are collapsed. This method eliminates these
308 * selected children objects.
309 *
310 */
getSimplifiedSelection()311 std::vector<libfwbuilder::FWObject*> ObjectTreeView::getSimplifiedSelection()
312 {
313 vector<FWObject*> so = selectedObjects;
314 vector<FWObject*> so2 = selectedObjects;
315 for (vector<FWObject*>::iterator i=so2.begin(); i!=so2.end(); ++i)
316 {
317 for (vector<FWObject*>::iterator j=i; j!=so2.end(); ++j)
318 {
319 vector<FWObject*>::iterator k=std::find(so.begin(),so.end(),*j);
320 if ( (*j)->isChildOf( *i ) && k!=so.end())
321 so.erase( k );
322 }
323 }
324 return so;
325 }
326
getCurrentObject()327 FWObject* ObjectTreeView::getCurrentObject()
328 {
329 QTreeWidgetItem *ovi = currentItem();
330 ObjectTreeViewItem *otvi=dynamic_cast<ObjectTreeViewItem*>(ovi);
331 if (otvi==NULL) return NULL;
332 return otvi->getFWObject();
333 }
334
focusInEvent(QFocusEvent * ev)335 void ObjectTreeView::focusInEvent(QFocusEvent* ev)
336 {
337 QTreeWidget::focusInEvent(ev);
338 QTreeWidgetItem *ci = currentItem();
339 if (ci) repaint();
340 }
341
focusOutEvent(QFocusEvent * ev)342 void ObjectTreeView::focusOutEvent(QFocusEvent* ev)
343 {
344 QTreeWidget::focusOutEvent(ev);
345 QTreeWidgetItem *ci = currentItem();
346 if (ci) repaint();
347 }
348
updateTreeIcons()349 void ObjectTreeView::updateTreeIcons()
350 {
351 QTreeWidgetItemIterator it(this);
352 for ( ; *it; ++it)
353 {
354 QTreeWidgetItem *itm = *it;
355 ObjectTreeViewItem *otvi = dynamic_cast<ObjectTreeViewItem*>(itm);
356 FWObject *obj = otvi->getFWObject();
357
358 /* We can have obj==0 if it's a user-create subfolder */
359 if (obj == 0) continue;
360
361 QPixmap pm_obj;
362 IconSetter::setObjectIcon(obj, &pm_obj, 0);
363 itm->setIcon(0, pm_obj );
364 }
365 update();
366 }
367
startDrag(Qt::DropActions supportedActions)368 void ObjectTreeView::startDrag(Qt::DropActions supportedActions)
369 {
370 QTreeWidgetItem *ovi = currentItem();
371 if (ovi==NULL) return;
372
373 FWObject *current_obj = getCurrentObject();
374
375 /* User-defined folders can't be dragged */
376 if (current_obj == 0) return;
377
378 if (fwbdebug) qDebug("ObjectTreeView::startDrag: this: %p current_obj: %s",
379 this, current_obj->getName().c_str());
380
381 /* can't drag system folders
382
383 in fact, I have to allow to drag system folders because otherwise
384 QListView triggers highlighting of objects in the tree when user
385 drags mouse cursor across them. This is weird behavior and there
386 does not seem to be any way to turn it off. It happens close to
387 the end of void QListView::mouseMoveEvent( QMouseEvent * e)
388 (See code after they decided that they do not need to call startDrag())
389
390 if (FWBTree().isSystem(obj)) return NULL;
391 */
392 if ((current_obj->getId() == FWObjectDatabase::DUMMY_ADDRESS_ID) ||
393 (current_obj->getId() == FWObjectDatabase::DUMMY_INTERFACE_ID) ||
394 (current_obj->getId() == FWObjectDatabase::DUMMY_SERVICE_ID))
395 return;
396
397 QString icn = (":/Icons/"+current_obj->getTypeName()+"/icon-ref").c_str();
398
399 vector<FWObject*> so = getSimplifiedSelection();
400
401 list<FWObject*> dragobj;
402 for (vector<FWObject*>::iterator v=so.begin(); v!=so.end(); v++)
403 {
404 //m_project->check4Depends(*v, dragobj);
405
406 if (fwbdebug)
407 qDebug("ObjectTreeView::startDrag: adding object to drag list: %s",
408 (*v)->getName().c_str());
409
410 // while obj is still part of the tree, do some clean up
411 // to avoid problems in the future. Create
412 // InterfaceOptions objects for interfaces because we'll
413 // need them for various validations during paste/drop
414 // operation.
415 Interface *intf = Interface::cast(*v);
416 if (intf) intf->getOptionsObject();
417
418 dragobj.push_back( *v );
419 }
420 FWObjectDrag *drag = new FWObjectDrag(dragobj, this);
421
422 QPixmap pm;
423 if ( ! QPixmapCache::find( icn, pm) )
424 {
425 pm.load( icn );
426 QPixmapCache::insert( icn, pm);
427 }
428
429 if (dragobj.size()>1)
430 {
431 QPixmap npm(32,32);
432 QPainter p( &npm );
433 p.fillRect( 0,0,32,32, QBrush( QColor("white"),Qt::SolidPattern ) );
434 p.setBackgroundMode( Qt::TransparentMode );
435 p.drawPixmap( 0, 32-pm.rect().height(), pm);
436 p.setPen( QColor("red") );
437 p.setBrush( QBrush( QColor("red"),Qt::SolidPattern ) );
438 p.drawPie( 16, 0, 16,16, 0, 5760 );
439 QString txt;
440 txt.setNum(dragobj.size());
441 QRect br=p.boundingRect(0, 0, 1000, 1000,
442 Qt::AlignLeft|Qt::AlignVCenter,
443 txt );
444 p.setPen( QColor("white") );
445 p.drawText( 24-br.width()/2 , 4+br.height()/2, txt );
446 p.end();
447 npm.setMask( npm.createHeuristicMask() );
448 drag->setPixmap( npm );
449 } else
450 drag->setPixmap( pm );
451
452 /*
453 * This fragment returns selection in the tree back to the object that
454 * was selected before drag operation has started. This help in the
455 * following case:
456 *
457 * - open a group for editing (group is selected in the tree)
458 * - left-click on another object in the tree, start dragging it
459 *
460 * at this point selection in the tree returns to the group, so when
461 * user finishes d&d operation, the selection in the tree is consisten
462 * with object currently opened in the editor panel.
463 *
464 * There is a problem with this however. If user wants to put an
465 * object from a different library into the group, they have to switch
466 * to that library before doing d&d. When they switch, ObjectTree
467 * shown in the left panel becomes different from the tree in which
468 * the group is located. When d&d finishes, the ObjectTree object
469 * receives mouseReleaseEvent event. Since it is not the right
470 * tree object, it can not properly restore selection and choses an
471 * object that was previously opened in that tree, which in turn
472 * changes the object opened in the editor panel. To make things
473 * worse, this event is only delivered to the tree object on Mac OS X.
474 *
475 *
476 */
477 if (fwbdebug) qDebug("ObjectTreeView::dragObject() this=%p visible=%d",
478 this,visible);
479
480 drag->start(supportedActions);
481 }
482
dragEnterEvent(QDragEnterEvent * ev)483 void ObjectTreeView::dragEnterEvent( QDragEnterEvent *ev)
484 {
485 ev->setAccepted(ev->mimeData()->hasFormat(FWObjectDrag::FWB_MIME_TYPE) );
486 ev->setDropAction(Qt::MoveAction);
487 }
488
489
isValidDropTarget(QTreeWidgetItem * item,list<FWObject * > & objs)490 static bool isValidDropTarget(QTreeWidgetItem *item, list<FWObject *> &objs)
491 {
492 ObjectTreeViewItem *dest = dynamic_cast<ObjectTreeViewItem *>(item);
493 if (dest == 0) return false;
494
495 bool dragIsNoop = true;
496 list<FWObject *>::const_iterator iter;
497 for (iter = objs.begin(); iter != objs.end(); ++iter) {
498 FWObject *dragobj = *iter;
499 assert(dragobj != 0);
500
501 if (Interface::cast(dragobj) != 0 ||
502 Interface::cast(dragobj->getParent()) != 0 ||
503 Policy::cast(dragobj) != 0 ||
504 NAT::cast(dragobj) != 0 ||
505 Routing::cast(dragobj) != 0) return false;
506
507 /* See if destination is a user folder */
508 if (dest->getUserFolderParent() != 0) {
509 /* Dragged object has to match parent of user folder */
510 if (dest->getUserFolderParent() != dragobj->getParent()) {
511 return false;
512 }
513
514 /* Are we dragging within the same user folder? */
515 if (dest->getUserFolderName() !=
516 QString::fromUtf8(dragobj->getStr("folder").c_str())) {
517 dragIsNoop = false;
518 }
519 } else {
520 /* OK to drag onto parent itself, or object that shares parent */
521 if (dragobj->getParent() != dest->getFWObject() &&
522 dragobj->getParent() != dest->getFWObject()->getParent()) {
523 return false;
524 }
525
526 /* Are we dragging to a new place? */
527 if ((FWBTree().isSystem(dest->getFWObject()) &&
528 dragobj->getStr("folder") != "") ||
529 (dest->getFWObject()->getStr("folder") !=
530 dragobj->getStr("folder"))) {
531 dragIsNoop = false;
532 }
533 }
534 }
535
536 return !dragIsNoop;
537 }
538
539
dragMoveEvent(QDragMoveEvent * ev)540 void ObjectTreeView::dragMoveEvent(QDragMoveEvent *ev)
541 {
542 /* Call the parent so that auto-scrolling works properly */
543 QTreeWidget::dragMoveEvent(ev);
544
545 list<FWObject*> objs;
546 if (ev->source() != this || !FWObjectDrag::decode(ev, objs) ||
547 !isValidDropTarget(itemAt(ev->pos()), objs)) {
548 ev->setAccepted(false);
549 return;
550 }
551
552 ev->setDropAction(Qt::MoveAction);
553 ev->setAccepted(true);
554 }
555
556
dropEvent(QDropEvent * ev)557 void ObjectTreeView::dropEvent(QDropEvent *ev)
558 {
559 // only accept drops from the same instance of fwbuilder
560 if (ev->source() == NULL) return;
561
562 ObjectTreeViewItem *dest =
563 dynamic_cast<ObjectTreeViewItem *>(itemAt(ev->pos()));
564 if (dest == 0) {
565 notWanted:
566 ev->setAccepted(false);
567 return;
568 }
569
570 list<FWObject*> objs;
571 if (!FWObjectDrag::decode(ev, objs)) goto notWanted;
572
573 /* Make sure the drop event is on an object that can handle it */
574 if (ev->source() != this || !isValidDropTarget(dest, objs)) goto notWanted;
575
576 emit moveItems_sign(dest, objs);
577 ev->setAccepted(true);
578 }
579
dragLeaveEvent(QDragLeaveEvent * ev)580 void ObjectTreeView::dragLeaveEvent( QDragLeaveEvent *ev)
581 {
582 QTreeWidget::dragLeaveEvent(ev);
583
584 clearSelection();
585 }
586
mouseMoveEvent(QMouseEvent * e)587 void ObjectTreeView::mouseMoveEvent( QMouseEvent * e )
588 {
589 /* This stops highlighting of stuff in the tree when the user
590 clicks and tries to drag something non-draggable. */
591 if (state() == DragSelectingState) return;
592 QTreeWidget::mouseMoveEvent(e);
593 if (e==NULL) return;
594 }
595
mousePressEvent(QMouseEvent * e)596 void ObjectTreeView::mousePressEvent( QMouseEvent *e )
597 {
598 if (fwbdebug) qDebug("ObjectTreeView::mousePressEvent");
599
600 second_click = false;
601 process_mouse_release_event = true;
602
603 if (fwbdebug)
604 {
605 qDebug() << "ObjectTreeView::mousePressEvent :: currentItem="
606 << ((currentItem())?currentItem()->text(0):"nil");
607 qDebug() << "ObjectTreeView::mousePressEvent :: lastSelected="
608 << ((lastSelected)?lastSelected->text(0):"nil");
609 }
610
611 lastSelected = currentItem();
612
613 QTreeWidget::mousePressEvent(e);
614
615 if (e->button() == Qt::LeftButton)
616 {
617 startingDrag = true;
618 }
619
620 if (e->button() == Qt::RightButton)
621 emit contextMenuRequested_sign(e->pos());
622 }
623
624 /*
625 * Two modes of operation of this widget:
626 *
627 * 1. this widget can intercept single mouse click and return
628 * selection back to the object that was current before it. If user
629 * double ckicks mouse button, then this reset is not done and new
630 * object is selected. This is done using timer.
631 *
632 * 2. this widget can act as usual QListView does, that is, select an object
633 * on a single click.
634 *
635 * uncomment the line that starts timer for mode #1.
636 *
637 *
638 * we use mouseReleaseEvent event to switch object opened in the
639 * editor panel (i.e. we open new object when mouse button is released
640 * rather than when it is pressed). This allows us to start drag
641 * without switching object in the editor. The problem is that
642 * mouseReleaseEvent is received in this widget after the d&d ends
643 * with a drop somewhere else, which triggers call to
644 * mouseReleaseEvent which switches object in the editor. This
645 * is undesired when the editor shows a group and we try to drag and
646 * drop an object into that group. Flag process_mouse_release_event is
647 * used to suppress object switching when mouseReleaseEvent is
648 * called after successfull drop.
649 *
650 */
mouseReleaseEvent(QMouseEvent * e)651 void ObjectTreeView::mouseReleaseEvent( QMouseEvent *e )
652 {
653 if (fwbdebug)
654 qDebug("ObjectTreeView::mouseReleaseEvent 1 this=%p process_mouse_release_event=%d",
655 this,process_mouse_release_event);
656
657 QTreeWidget::mouseReleaseEvent(e);
658
659 if (!process_mouse_release_event)
660 {
661 // just do not switch object in the editor, otherwise
662 // process this event as usual
663 process_mouse_release_event = true;
664 return;
665 }
666
667 if (fwbdebug)
668 qDebug("ObjectTreeView::mouseReleaseEvent 2 selectedObjects.size()=%d getCurrentObject()=%p current object %s",
669 int(selectedObjects.size()),
670 getCurrentObject(),
671 (getCurrentObject()!=NULL)?getCurrentObject()->getName().c_str():"nil");
672
673 if (expandOrCollapse) return; // user expanded or collapsed subtree,
674 // no need to change object in the editor
675
676 // Experiment: single click on the object in the tree should not open
677 // it in the editor
678 #if 0
679 if (selectedObjects.size()==1)
680 emit switchObjectInEditor_sign( getCurrentObject() );
681 else
682 {
683 // user selected multiple objects
684 // do not let them if editor has unsaved changes
685 //
686 if (mw->isEditorVisible() && mw->isEditorModified())
687 emit switchObjectInEditor_sign( getCurrentObject() );
688 else
689 mw->blankEditor();
690 }
691 #endif
692 }
693
694 /*
695 * normally QAbstractItemView::edit starts in-place editing. We use
696 * double click to open object in a separate editor panel
697 */
edit(const QModelIndex & index,EditTrigger trigger,QEvent * event)698 bool ObjectTreeView::edit(const QModelIndex &index,
699 EditTrigger trigger, QEvent *event)
700 {
701 if (fwbdebug) qDebug("ObjectTreeView::edit");
702 if (trigger==QAbstractItemView::DoubleClicked) editCurrentObject();
703 return QTreeWidget::edit(index, trigger, event);
704 }
705
706 /*
707 * sends signal that should be connected to a slot in
708 * ObjectManipulator which opens editor panel if it is closed and then
709 * opens current object in it
710 */
editCurrentObject()711 void ObjectTreeView::editCurrentObject()
712 {
713 if (fwbdebug) qDebug("ObjectTreeView::editCurrentObject");
714 emit editCurrentObject_sign();
715 if (fwbdebug) qDebug("ObjectTreeView::editCurrentObject done");
716 }
717
keyPressEvent(QKeyEvent * ev)718 void ObjectTreeView::keyPressEvent( QKeyEvent* ev )
719 {
720 FWObject *obj = getCurrentObject();
721 if (obj)
722 {
723 if (ev->key()==Qt::Key_Enter || ev->key()==Qt::Key_Return)
724 {
725 if (fwbdebug)
726 qDebug() << "ObjectTreeView::keyPressEvent Qt::Key_Enter";
727 editCurrentObject();
728 ev->accept();
729 return;
730 }
731 if (ev->key()==Qt::Key_Delete)
732 {
733 emit deleteObject_sign(obj);
734 ev->accept();
735 return;
736 }
737 }
738 QTreeWidget::keyPressEvent(ev);
739 }
740
keyReleaseEvent(QKeyEvent * ev)741 void ObjectTreeView::keyReleaseEvent( QKeyEvent* ev )
742 {
743 QTreeWidget::keyReleaseEvent(ev);
744 }
745
itemOpened()746 void ObjectTreeView::itemOpened ()
747 {
748 if (fwbdebug) qDebug("ObjectTreeView::itemOpened");
749 editCurrentObject();
750 }
751
clearLastSelected()752 void ObjectTreeView::clearLastSelected()
753 {
754 lastSelected = NULL;
755 }
756
757
resetSelection()758 void ObjectTreeView::resetSelection()
759 {
760 if (lastSelected)
761 {
762 if (fwbdebug)
763 qDebug() << "ObjectTreeView::resetSelection :: lastSelected="
764 << lastSelected->text(0);
765
766 setCurrentItem(lastSelected);
767 lastSelected->setSelected(true);
768 }
769 }
770
itemSelectionChanged()771 void ObjectTreeView::itemSelectionChanged()
772 {
773 if (fwbdebug)
774 qDebug("ObjectTreeView::itemSelectionChanged selectionFrozen=%d",
775 selectionFrozen);
776
777 if (selectionFrozen) return;
778
779 /* in extended selection mode there may be several selected items */
780
781 selectedObjects.clear();
782
783 QList<QTreeWidgetItem*> selected = selectedItems();
784 QList<QTreeWidgetItem*>::Iterator it;
785 for (it=selected.begin(); it!=selected.end(); it++)
786 {
787 QTreeWidgetItem *itm = (*it);
788 ObjectTreeViewItem *otvi = dynamic_cast<ObjectTreeViewItem*>(itm);
789
790 FWObject *obj = otvi->getFWObject();
791 if (obj == 0) continue;
792
793 selectedObjects.push_back(otvi->getFWObject());
794
795 if (fwbdebug) qDebug(
796 "ObjectTreeView::selectionChanged: selected otvi=%p object %s",
797 otvi, otvi->getFWObject()->getName().c_str());
798 }
799
800 if (fwbdebug)
801 qDebug("ObjectTreeView::itemSelectionChanged completed");
802 /* now list selectedObjects holds all selected items */
803 }
804
isSelected(FWObject * obj)805 bool ObjectTreeView::isSelected(FWObject* obj)
806 {
807 for (vector<FWObject*>::iterator i=selectedObjects.begin();
808 i!=selectedObjects.end(); ++i)
809 {
810 if ( (*i)==obj) return true;
811 }
812 return false;
813 }
814
getNumSelected()815 int ObjectTreeView::getNumSelected()
816 {
817 return selectedObjects.size();
818 }
819
updateAfterPrefEdit()820 void ObjectTreeView::updateAfterPrefEdit()
821 {
822 setFont(st->getTreeFont());
823 }
824
ExpandTreeItems(const set<int> & ids)825 void ObjectTreeView::ExpandTreeItems(const set<int> &ids)
826 {
827 if (fwbdebug)
828 qDebug() << "ObjectTreeView::ExpandTreeItems()";
829
830 QTreeWidgetItemIterator it(this);
831 for ( ; *it; ++it)
832 {
833 QTreeWidgetItem *itm = *it;
834 ObjectTreeViewItem *otvi=dynamic_cast<ObjectTreeViewItem*>(itm);
835 FWObject *obj = otvi->getFWObject();
836 if (obj == 0) continue;
837 if (ids.count(obj->getId()))
838 itm->setExpanded(true);
839 }
840 }
841
showOrHideAttributesColumn()842 void ObjectTreeView::showOrHideAttributesColumn()
843 {
844 if (st->getBool("UI/ShowObjectsAttributesInTree"))
845 setColumnCount(2);
846 else
847 setColumnCount(1);
848 }
849
resolveChildren(QTreeWidgetItem * parent)850 QSet<QTreeWidgetItem*> ObjectTreeView::resolveChildren(QTreeWidgetItem *parent)
851 {
852 QSet<QTreeWidgetItem*> children;
853 children.insert(parent);
854 if (parent->childCount() == 0) return children;
855 for (int i=0; i<parent->childCount(); i++)
856 children.unite(resolveChildren(parent->child(i)));
857 return children;
858 }
859
resolveParents(QTreeWidgetItem * child)860 QSet<QTreeWidgetItem*> ObjectTreeView::resolveParents(QTreeWidgetItem *child)
861 {
862 QSet<QTreeWidgetItem*> parents;
863 parents.insert(child);
864 if (child->parent() == NULL) return parents;
865 parents.unite(resolveParents(child->parent()));
866 return parents;
867 }
868
updateFilter()869 void ObjectTreeView::updateFilter()
870 {
871 if (filter.isEmpty()) return;
872 setFilter(filter);
873 }
874
filterMatchesPortRange(const QStringList & args,FWObject * obj)875 static bool filterMatchesPortRange(const QStringList &args,
876 FWObject *obj)
877 {
878 if (!obj) return false;
879
880 // We traverse the service group. If the children are references
881 // they may be pointing to ports
882 if (obj->getTypeName() == ServiceGroup::TYPENAME) {
883 for (list<FWObject*>::const_iterator it=obj->begin(); it!=obj->end(); ++it) {
884 FWServiceReference *ref = FWServiceReference::cast(*it);
885 if (ref && filterMatchesPortRange(args, ref->getPointer()))
886 return true;
887 }
888 }
889
890 TCPUDPService *service = dynamic_cast<TCPUDPService*>(obj);
891 if (!service) return false;
892
893 QRegExp rx("\\s*([><]?)\\s*(\\d*)(?:-(\\d*))?");
894
895 foreach (const QString &arg, args) {
896
897 if (!rx.exactMatch(arg)) continue;
898
899 int lowerBound = rx.cap(2).toInt(), upperBound = lowerBound;
900
901 if (rx.pos(3) != -1) {
902 upperBound = rx.cap(3).toInt();
903 }
904
905 if (rx.pos(1) != -1) {
906 if (rx.pos(3) != -1) // [><] cannot be combined with range
907 continue;
908
909 if (rx.cap(1) == ">") {
910 upperBound = 65535;
911 ++lowerBound; // Adjust for using >= below
912 } else {// "<"
913 lowerBound = 1;
914 --upperBound; // Adjust for using <= below
915 }
916 }
917
918 if (lowerBound > upperBound) continue;
919
920 int ds = service->getDstRangeStart(), de = service->getDstRangeEnd(),
921 ss = service->getSrcRangeStart(), se = service->getSrcRangeEnd();
922
923 if (ds && de && (lowerBound <= ds) && (de <= upperBound)) return true;
924 if (ss && se && (lowerBound <= ss) && (se <= upperBound)) return true;
925 } // End foreach
926
927 return false;
928 }
929
filterMatchesIpAddress(const QStringList & args,FWObject * obj)930 static bool filterMatchesIpAddress(const QStringList &args,
931 FWObject *obj)
932 {
933 if (!obj) return false;
934
935 // We traverse the object group. If the children are references
936 // they may be pointing to adresses
937 if (obj->getTypeName() == ObjectGroup::TYPENAME) {
938 for (list<FWObject*>::const_iterator it=obj->begin(); it!=obj->end(); ++it) {
939 FWObjectReference *ref = FWObjectReference::cast(*it);
940 if (ref && filterMatchesIpAddress(args, ref->getPointer()))
941 return true;
942 }
943 }
944
945 Address *addr = dynamic_cast<Address*>(obj);
946 if (!addr) return false;
947
948 QRegExp rx("\\s*([.:0-9a-fA-F]+)(?:/([.:0-9a-fA-F]+))?");
949
950 InetAddrMask searchAddrAndMask;
951 foreach (const QString &arg, args) {
952
953 if (!rx.exactMatch(arg)) continue;
954
955 try {
956 std::string netmask = rx.cap(2).isEmpty() ? "32" : rx.cap(2).toStdString();
957 InetAddr ipv4addr(rx.cap(1).toStdString());
958 InetAddr ipv4mask(netmask);
959 searchAddrAndMask = InetAddrMask(ipv4addr, ipv4mask);
960 } catch (const FWException) { // Could not create IPv4 object. Trying IPv6.
961 try {
962 int netmask = rx.cap(2).isEmpty() ? 128 : rx.cap(2).toInt();
963 InetAddr ipv6addr(AF_INET6, rx.cap(1).toStdString());
964 InetAddr ipv6mask(AF_INET6, netmask);
965 searchAddrAndMask = InetAddrMask(ipv6addr, ipv6mask);
966 } catch (const FWException) { // Could not create IPv6 object.
967 // User did not submit a valid IP address
968 return false;
969 }
970 }
971
972 const InetAddr *searchAddr = searchAddrAndMask.getAddressPtr();
973
974 if (addr->getTypeName() == AddressRange::TYPENAME) {
975 AddressRange *addrRange = dynamic_cast<AddressRange*>(obj);
976 if (addrRange
977 && (searchAddr->addressFamily() == addrRange->getRangeStart().addressFamily()) ) {
978
979 if ( !(searchAddr->opLT(addrRange->getRangeStart()))
980 && !(searchAddr->opGT(addrRange->getRangeEnd())) )
981 return true;
982 }
983 continue; // Next argument
984 }
985
986 const InetAddr *inetAddr = addr->getAddressPtr();
987
988
989 if ( inetAddr && (inetAddr->addressFamily() == searchAddr->addressFamily()) ) {
990 if (addr->getTypeName() == Network::TYPENAME) {
991 if (addr->belongs(*searchAddr))
992 return true;
993 }
994 if (searchAddrAndMask.belongs(*inetAddr))
995 return true;
996 }
997 } // End foreach
998
999 return false;
1000 }
1001
filterMatchesCommand(const QString & text,ObjectTreeViewItem * item)1002 static bool filterMatchesCommand(const QString &text,
1003 ObjectTreeViewItem *item)
1004 {
1005 QRegExp rx("(?:(port)|(ip)):(.*)", Qt::CaseInsensitive);
1006 if (!rx.exactMatch(text)) return false;
1007
1008 QStringList args = rx.cap(3).split(",", QString::SkipEmptyParts);
1009
1010 if (rx.pos(1) != -1)
1011 return (filterMatchesPortRange(args, item->getFWObject()));
1012 else
1013 return (filterMatchesIpAddress(args, item->getFWObject()));
1014 }
1015
filterMatches(const QString & text,ObjectTreeViewItem * item)1016 static bool filterMatches(const QString &text,
1017 ObjectTreeViewItem *item)
1018 {
1019 if (text.isEmpty()) return true;
1020 if (item->text(0).contains(text, Qt::CaseInsensitive)) return true;
1021
1022 // Support for port and ip search
1023 if (filterMatchesCommand(text, item)) return true;
1024
1025 if (item->getUserFolderParent() != 0) return false;
1026 FWObject *obj = item->getFWObject();
1027
1028 QByteArray utf8 = text.toUtf8();
1029 set<string> keys = obj->getKeywords();
1030 set<string>::const_iterator iter;
1031 for (iter = keys.begin(); iter != keys.end(); ++iter) {
1032 QString keyword = QString::fromUtf8((*iter).c_str());
1033 if (keyword.contains(text, Qt::CaseInsensitive)) return true;
1034 }
1035
1036 return false;
1037 }
1038
qHash(const QStringList & list)1039 static uint qHash(const QStringList &list)
1040 {
1041 uint ret = 0;
1042 for (int ii = 0; ii < list.size(); ii++) {
1043 ret += qHash(list.at(ii));
1044 }
1045 return ret;
1046 }
1047
doExpandedState(bool save,QStringList & list,QTreeWidgetItem * item)1048 void ObjectTreeView::doExpandedState(bool save, QStringList &list,
1049 QTreeWidgetItem *item)
1050 {
1051 list.append(item->text(0));
1052 if (save) {
1053 if (item->isExpanded()) expandedState.insert(list);
1054 } else {
1055 if (expandedState.contains(list)) item->setExpanded(true);
1056 }
1057
1058 for (int ii = 0; ii < item->childCount(); ii++) {
1059 doExpandedState(save, list, item->child(ii));
1060 }
1061 list.removeLast();
1062 }
1063
1064
setFilter(QString text)1065 void ObjectTreeView::setFilter(QString text)
1066 {
1067 if (filter.isEmpty() && !text.isEmpty()) {
1068 QStringList list;
1069 for (int ii = 0; ii < topLevelItemCount(); ii++) {
1070 doExpandedState(true, list, topLevelItem(ii));
1071 }
1072 } else if (text.isEmpty() && !filter.isEmpty()) {
1073 QStringList list;
1074 for (int ii = 0; ii < topLevelItemCount(); ii++) {
1075 doExpandedState(false, list, topLevelItem(ii));
1076 }
1077 expandedState.clear();
1078 }
1079 filter = text;
1080
1081 if (fwbdebug)
1082 qDebug() << "ObjectTreeView::setFilter " << text;
1083
1084 list<QTreeWidgetItem *> expand;
1085 for (QTreeWidgetItemIterator wit(this); *wit; ++wit) {
1086 ObjectTreeViewItem *otvi = dynamic_cast<ObjectTreeViewItem *>(*wit);
1087
1088 if (filterMatches(text, otvi)) {
1089 (*wit)->setHidden(false);
1090
1091 if (Firewall::cast(otvi->getFWObject()) != 0) {
1092 expand.push_back(otvi);
1093 }
1094
1095 QTreeWidgetItem *parent = (*wit)->parent();
1096 while (parent != 0) {
1097 parent->setHidden(false);
1098 parent = parent->parent();
1099 }
1100 } else {
1101 (*wit)->setHidden(true);
1102 }
1103 }
1104
1105 list<QTreeWidgetItem *>::const_iterator iter;
1106 for (iter = expand.begin(); iter != expand.end(); ++iter) {
1107 QTreeWidgetItem *item = *iter;
1108 item->setHidden(false);
1109 for (int ii = 0; ii < item->childCount(); ii++) {
1110 expand.push_back(item->child(ii));
1111 }
1112 }
1113
1114 if (!text.isEmpty()) this->expandAll();
1115 }
1116