VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/widgets/UIPortForwardingTable.cpp@ 100347

Last change on this file since 100347 was 100346, checked in by vboxsync, 15 months ago

FE/Qt: bugref:10450: Actually QItemEditorFactory was never using QVariant::Type, which is deprecated now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.0 KB
Line 
1/* $Id: UIPortForwardingTable.cpp 100346 2023-07-03 11:22:58Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIPortForwardingTable class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QAction>
30#include <QComboBox>
31#include <QHBoxLayout>
32#include <QHeaderView>
33#include <QItemEditorFactory>
34#include <QLineEdit>
35#include <QMenu>
36#include <QRegExp>
37#include <QSpinBox>
38#include <QStyledItemDelegate>
39
40/* GUI includes: */
41#include "QITableView.h"
42#include "UIConverter.h"
43#include "UIIconPool.h"
44#include "UIMessageCenter.h"
45#include "UIPortForwardingTable.h"
46#include "QIToolBar.h"
47
48/* Other VBox includes: */
49#include <iprt/cidr.h>
50
51/* External includes: */
52#include <math.h>
53
54
55/** Port Forwarding data types. */
56enum UIPortForwardingDataType
57{
58 UIPortForwardingDataType_Name,
59 UIPortForwardingDataType_Protocol,
60 UIPortForwardingDataType_HostIp,
61 UIPortForwardingDataType_HostPort,
62 UIPortForwardingDataType_GuestIp,
63 UIPortForwardingDataType_GuestPort,
64 UIPortForwardingDataType_Max
65};
66
67
68/** QLineEdit extension used as name editor. */
69class NameEditor : public QLineEdit
70{
71 Q_OBJECT;
72 Q_PROPERTY(NameData name READ name WRITE setName USER true);
73
74public:
75
76 /** Constructs name editor passing @a pParent to the base-class. */
77 NameEditor(QWidget *pParent = 0) : QLineEdit(pParent)
78 {
79 setFrame(false);
80 setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
81 setValidator(new QRegularExpressionValidator(QRegularExpression("[^,:]*"), this));
82 }
83
84private:
85
86 /** Defines the @a strName. */
87 void setName(NameData strName)
88 {
89 setText(strName);
90 }
91
92 /** Returns the name. */
93 NameData name() const
94 {
95 return text();
96 }
97};
98
99
100/** QComboBox extension used as protocol editor. */
101class ProtocolEditor : public QComboBox
102{
103 Q_OBJECT;
104 Q_PROPERTY(KNATProtocol protocol READ protocol WRITE setProtocol USER true);
105
106public:
107
108 /** Constructs protocol editor passing @a pParent to the base-class. */
109 ProtocolEditor(QWidget *pParent = 0) : QComboBox(pParent)
110 {
111 addItem(gpConverter->toString(KNATProtocol_UDP), QVariant::fromValue(KNATProtocol_UDP));
112 addItem(gpConverter->toString(KNATProtocol_TCP), QVariant::fromValue(KNATProtocol_TCP));
113 }
114
115private:
116
117 /** Defines the @a enmProtocol. */
118 void setProtocol(KNATProtocol enmProtocol)
119 {
120 for (int i = 0; i < count(); ++i)
121 {
122 if (itemData(i).value<KNATProtocol>() == enmProtocol)
123 {
124 setCurrentIndex(i);
125 break;
126 }
127 }
128 }
129
130 /** Returns the protocol. */
131 KNATProtocol protocol() const
132 {
133 return itemData(currentIndex()).value<KNATProtocol>();
134 }
135};
136
137
138/** QLineEdit extension used as IPv4 editor. */
139class IPv4Editor : public QLineEdit
140{
141 Q_OBJECT;
142 Q_PROPERTY(IpData ip READ ip WRITE setIp USER true);
143
144public:
145
146 /** Constructs IPv4-editor passing @a pParent to the base-class. */
147 IPv4Editor(QWidget *pParent = 0) : QLineEdit(pParent)
148 {
149 setFrame(false);
150 setAlignment(Qt::AlignCenter);
151 // Decided to not use it for now:
152 // setValidator(new IPv4Validator(this));
153 }
154
155private:
156
157 /** Defines the @a strIp. */
158 void setIp(IpData strIp)
159 {
160 setText(strIp);
161 }
162
163 /** Returns the ip. */
164 IpData ip() const
165 {
166 return text() == "..." ? QString() : text();
167 }
168};
169
170
171/** QLineEdit extension used as IPv6 editor. */
172class IPv6Editor : public QLineEdit
173{
174 Q_OBJECT;
175 Q_PROPERTY(IpData ip READ ip WRITE setIp USER true);
176
177public:
178
179 /** Constructs IPv6-editor passing @a pParent to the base-class. */
180 IPv6Editor(QWidget *pParent = 0) : QLineEdit(pParent)
181 {
182 setFrame(false);
183 setAlignment(Qt::AlignCenter);
184 // Decided to not use it for now:
185 // setValidator(new IPv6Validator(this));
186 }
187
188private:
189
190 /** Defines the @a strIp. */
191 void setIp(IpData strIp)
192 {
193 setText(strIp);
194 }
195
196 /** Returns the ip. */
197 IpData ip() const
198 {
199 return text() == "..." ? QString() : text();
200 }
201};
202
203
204/** QSpinBox extension used as Port editor. */
205class PortEditor : public QSpinBox
206{
207 Q_OBJECT;
208 Q_PROPERTY(PortData port READ port WRITE setPort USER true);
209
210public:
211
212 /** Constructs Port-editor passing @a pParent to the base-class. */
213 PortEditor(QWidget *pParent = 0) : QSpinBox(pParent)
214 {
215 setFrame(false);
216 setRange(0, (1 << (8 * sizeof(ushort))) - 1);
217 }
218
219private:
220
221 /** Defines the @a port. */
222 void setPort(PortData port)
223 {
224 setValue(port.value());
225 }
226
227 /** Returns the port. */
228 PortData port() const
229 {
230 return value();
231 }
232};
233
234
235/** QITableViewCell extension used as Port Forwarding table-view cell. */
236class UIPortForwardingCell : public QITableViewCell
237{
238 Q_OBJECT;
239
240public:
241
242 /** Constructs table cell passing @a pParent to the base-class.
243 * @param strName Brings the name. */
244 UIPortForwardingCell(QITableViewRow *pParent, const NameData &strName)
245 : QITableViewCell(pParent)
246 , m_strText(strName)
247 {}
248
249 /** Constructs table cell passing @a pParent to the base-class.
250 * @param enmProtocol Brings the protocol type. */
251 UIPortForwardingCell(QITableViewRow *pParent, KNATProtocol enmProtocol)
252 : QITableViewCell(pParent)
253 , m_strText(gpConverter->toString(enmProtocol))
254 {}
255
256 /** Constructs table cell passing @a pParent to the base-class.
257 * @param strIp Brings the IP address. */
258 UIPortForwardingCell(QITableViewRow *pParent, const IpData &strIp)
259 : QITableViewCell(pParent)
260 , m_strText(strIp)
261 {}
262
263 /** Constructs table cell passing @a pParent to the base-class.
264 * @param port Brings the port. */
265 UIPortForwardingCell(QITableViewRow *pParent, PortData port)
266 : QITableViewCell(pParent)
267 , m_strText(QString::number(port.value()))
268 {}
269
270 /** Returns the cell text. */
271 virtual QString text() const RT_OVERRIDE { return m_strText; }
272
273private:
274
275 /** Holds the cell text. */
276 QString m_strText;
277};
278
279
280/** QITableViewRow extension used as Port Forwarding table-view row. */
281class UIPortForwardingRow : public QITableViewRow
282{
283 Q_OBJECT;
284
285public:
286
287 /** Constructs table row passing @a pParent to the base-class.
288 * @param strName Brings the unique rule name.
289 * @param enmProtocol Brings the rule protocol type.
290 * @param strHostIp Brings the rule host IP address.
291 * @param hostPort Brings the rule host port.
292 * @param strGuestIp Brings the rule guest IP address.
293 * @param guestPort Brings the rule guest port. */
294 UIPortForwardingRow(QITableView *pParent,
295 const NameData &strName, KNATProtocol enmProtocol,
296 const IpData &strHostIp, PortData hostPort,
297 const IpData &strGuestIp, PortData guestPort)
298 : QITableViewRow(pParent)
299 , m_strName(strName), m_enmProtocol(enmProtocol)
300 , m_strHostIp(strHostIp), m_hostPort(hostPort)
301 , m_strGuestIp(strGuestIp), m_guestPort(guestPort)
302 {
303 /* Create cells: */
304 createCells();
305 }
306
307 /** Destructs table row. */
308 ~UIPortForwardingRow()
309 {
310 /* Destroy cells: */
311 destroyCells();
312 }
313
314 /** Returns the unique rule name. */
315 NameData name() const { return m_strName; }
316 /** Defines the unique rule name. */
317 void setName(const NameData &strName)
318 {
319 m_strName = strName;
320 delete m_cells[UIPortForwardingDataType_Name];
321 m_cells[UIPortForwardingDataType_Name] = new UIPortForwardingCell(this, m_strName);
322 }
323
324 /** Returns the rule protocol type. */
325 KNATProtocol protocol() const { return m_enmProtocol; }
326 /** Defines the rule protocol type. */
327 void setProtocol(KNATProtocol enmProtocol)
328 {
329 m_enmProtocol = enmProtocol;
330 delete m_cells[UIPortForwardingDataType_Protocol];
331 m_cells[UIPortForwardingDataType_Protocol] = new UIPortForwardingCell(this, m_enmProtocol);
332 }
333
334 /** Returns the rule host IP address. */
335 IpData hostIp() const { return m_strHostIp; }
336 /** Defines the rule host IP address. */
337 void setHostIp(const IpData &strHostIp)
338 {
339 m_strHostIp = strHostIp;
340 delete m_cells[UIPortForwardingDataType_HostIp];
341 m_cells[UIPortForwardingDataType_HostIp] = new UIPortForwardingCell(this, m_strHostIp);
342 }
343
344 /** Returns the rule host port. */
345 PortData hostPort() const { return m_hostPort; }
346 /** Defines the rule host port. */
347 void setHostPort(PortData hostPort)
348 {
349 m_hostPort = hostPort;
350 delete m_cells[UIPortForwardingDataType_HostPort];
351 m_cells[UIPortForwardingDataType_HostPort] = new UIPortForwardingCell(this, m_hostPort);
352 }
353
354 /** Returns the rule guest IP address. */
355 IpData guestIp() const { return m_strGuestIp; }
356 /** Defines the rule guest IP address. */
357 void setGuestIp(const IpData &strGuestIp)
358 {
359 m_strGuestIp = strGuestIp;
360 delete m_cells[UIPortForwardingDataType_GuestIp];
361 m_cells[UIPortForwardingDataType_GuestIp] = new UIPortForwardingCell(this, m_strGuestIp);
362 }
363
364 /** Returns the rule guest port. */
365 PortData guestPort() const { return m_guestPort; }
366 /** Defines the rule guest port. */
367 void setGuestPort(PortData guestPort)
368 {
369 m_guestPort = guestPort;
370 delete m_cells[UIPortForwardingDataType_GuestPort];
371 m_cells[UIPortForwardingDataType_GuestPort] = new UIPortForwardingCell(this, m_guestPort);
372 }
373
374protected:
375
376 /** Returns the number of children. */
377 virtual int childCount() const RT_OVERRIDE
378 {
379 /* Return cell count: */
380 return UIPortForwardingDataType_Max;
381 }
382
383 /** Returns the child item with @a iIndex. */
384 virtual QITableViewCell *childItem(int iIndex) const RT_OVERRIDE
385 {
386 /* Make sure index within the bounds: */
387 AssertReturn(iIndex >= 0 && iIndex < m_cells.size(), 0);
388 /* Return corresponding cell: */
389 return m_cells[iIndex];
390 }
391
392private:
393
394 /** Creates cells. */
395 void createCells()
396 {
397 /* Create cells on the basis of variables we have: */
398 m_cells.resize(UIPortForwardingDataType_Max);
399 m_cells[UIPortForwardingDataType_Name] = new UIPortForwardingCell(this, m_strName);
400 m_cells[UIPortForwardingDataType_Protocol] = new UIPortForwardingCell(this, m_enmProtocol);
401 m_cells[UIPortForwardingDataType_HostIp] = new UIPortForwardingCell(this, m_strHostIp);
402 m_cells[UIPortForwardingDataType_HostPort] = new UIPortForwardingCell(this, m_hostPort);
403 m_cells[UIPortForwardingDataType_GuestIp] = new UIPortForwardingCell(this, m_strGuestIp);
404 m_cells[UIPortForwardingDataType_GuestPort] = new UIPortForwardingCell(this, m_guestPort);
405 }
406
407 /** Destroys cells. */
408 void destroyCells()
409 {
410 /* Destroy cells: */
411 qDeleteAll(m_cells);
412 m_cells.clear();
413 }
414
415 /** Holds the unique rule name. */
416 NameData m_strName;
417 /** Holds the rule protocol type. */
418 KNATProtocol m_enmProtocol;
419 /** Holds the rule host IP address. */
420 IpData m_strHostIp;
421 /** Holds the rule host port. */
422 PortData m_hostPort;
423 /** Holds the rule guest IP address. */
424 IpData m_strGuestIp;
425 /** Holds the rule guest port. */
426 PortData m_guestPort;
427
428 /** Holds the cell instances. */
429 QVector<UIPortForwardingCell*> m_cells;
430};
431
432
433/** QAbstractTableModel subclass used as port forwarding data model. */
434class UIPortForwardingModel : public QAbstractTableModel
435{
436 Q_OBJECT;
437
438public:
439
440 /** Constructs Port Forwarding model passing @a pParent to the base-class.
441 * @param rules Brings the list of port forwarding rules to load initially. */
442 UIPortForwardingModel(QITableView *pParent, const UIPortForwardingDataList &rules = UIPortForwardingDataList());
443 /** Destructs Port Forwarding model. */
444 ~UIPortForwardingModel();
445
446 /** Returns the number of children. */
447 int childCount() const;
448 /** Returns the child item with @a iIndex. */
449 QITableViewRow *childItem(int iIndex) const;
450
451 /** Returns the list of port forwarding rules. */
452 UIPortForwardingDataList rules() const;
453 /** Defines the list of port forwarding @a newRules. */
454 void setRules(const UIPortForwardingDataList &newRules);
455 /** Adds empty port forwarding rule for certain @a index. */
456 void addRule(const QModelIndex &index);
457 /** Removes port forwarding rule with certain @a index. */
458 void removeRule(const QModelIndex &index);
459
460 /** Defines guest address @a strHint. */
461 void setGuestAddressHint(const QString &strHint);
462
463 /** Returns flags for item with certain @a index. */
464 Qt::ItemFlags flags(const QModelIndex &index) const;
465
466 /** Returns row count of certain @a parent. */
467 int rowCount(const QModelIndex &parent = QModelIndex()) const;
468
469 /** Returns column count of certain @a parent. */
470 int columnCount(const QModelIndex &parent = QModelIndex()) const;
471
472 /** Returns header data.
473 * @param iSection Brings the number of section we aquire data for.
474 * @param enmOrientation Brings the orientation of header we aquire data for.
475 * @param iRole Brings the role we aquire data for. */
476 QVariant headerData(int iSection, Qt::Orientation enmOrientation, int iRole) const;
477
478 /** Defines the @a iRole data for item with @a index as @a value. */
479 bool setData(const QModelIndex &index, const QVariant &value, int iRole = Qt::EditRole);
480 /** Returns the @a iRole data for item with @a index. */
481 QVariant data(const QModelIndex &index, int iRole) const;
482
483private:
484
485 /** Return the parent table-view reference. */
486 QITableView *parentTable() const;
487
488 /** Holds the port forwarding row list. */
489 QList<UIPortForwardingRow*> m_dataList;
490
491 /** Holds the guest address hint. */
492 QString m_strGuestAddressHint;
493};
494
495
496/** QITableView extension used as Port Forwarding table-view. */
497class UIPortForwardingView : public QITableView
498{
499 Q_OBJECT;
500
501public:
502
503 /** Constructs Port Forwarding table-view. */
504 UIPortForwardingView() {}
505
506protected:
507
508 /** Returns the number of children. */
509 virtual int childCount() const RT_OVERRIDE;
510 /** Returns the child item with @a iIndex. */
511 virtual QITableViewRow *childItem(int iIndex) const RT_OVERRIDE;
512};
513
514
515/*********************************************************************************************************************************
516* Class UIPortForwardingModel implementation. *
517*********************************************************************************************************************************/
518
519UIPortForwardingModel::UIPortForwardingModel(QITableView *pParent,
520 const UIPortForwardingDataList &rules /* = UIPortForwardingDataList() */)
521 : QAbstractTableModel(pParent)
522{
523 /* Fetch the incoming data: */
524 foreach (const UIDataPortForwardingRule &rule, rules)
525 m_dataList << new UIPortForwardingRow(pParent,
526 rule.name, rule.protocol,
527 rule.hostIp, rule.hostPort,
528 rule.guestIp, rule.guestPort);
529}
530
531UIPortForwardingModel::~UIPortForwardingModel()
532{
533 /* Delete the cached data: */
534 qDeleteAll(m_dataList);
535 m_dataList.clear();
536}
537
538int UIPortForwardingModel::childCount() const
539{
540 /* Return row count: */
541 return rowCount();
542}
543
544QITableViewRow *UIPortForwardingModel::childItem(int iIndex) const
545{
546 /* Make sure index within the bounds: */
547 AssertReturn(iIndex >= 0 && iIndex < m_dataList.size(), 0);
548 /* Return corresponding row: */
549 return m_dataList[iIndex];
550}
551
552UIPortForwardingDataList UIPortForwardingModel::rules() const
553{
554 /* Return the cached data: */
555 UIPortForwardingDataList data;
556 foreach (const UIPortForwardingRow *pRow, m_dataList)
557 data << UIDataPortForwardingRule(pRow->name(), pRow->protocol(),
558 pRow->hostIp(), pRow->hostPort(),
559 pRow->guestIp(), pRow->guestPort());
560 return data;
561}
562
563void UIPortForwardingModel::setRules(const UIPortForwardingDataList &newRules)
564{
565 /* Clear old data first of all: */
566 if (!m_dataList.isEmpty())
567 {
568 beginRemoveRows(QModelIndex(), 0, m_dataList.size() - 1);
569 foreach (const UIPortForwardingRow *pRow, m_dataList)
570 delete pRow;
571 m_dataList.clear();
572 endRemoveRows();
573 }
574
575 /* Fetch incoming data: */
576 if (!newRules.isEmpty())
577 {
578 beginInsertRows(QModelIndex(), 0, newRules.size() - 1);
579 foreach (const UIDataPortForwardingRule &rule, newRules)
580 m_dataList << new UIPortForwardingRow(qobject_cast<QITableView*>(parent()),
581 rule.name, rule.protocol,
582 rule.hostIp, rule.hostPort,
583 rule.guestIp, rule.guestPort);
584 endInsertRows();
585 }
586}
587
588void UIPortForwardingModel::addRule(const QModelIndex &index)
589{
590 beginInsertRows(QModelIndex(), m_dataList.size(), m_dataList.size());
591 /* Search for existing "Rule [NUMBER]" record: */
592 uint uMaxIndex = 0;
593 QString strTemplate("Rule %1");
594 QRegExp regExp(strTemplate.arg("(\\d+)"));
595 for (int i = 0; i < m_dataList.size(); ++i)
596 if (regExp.indexIn(m_dataList[i]->name()) > -1)
597 uMaxIndex = regExp.cap(1).toUInt() > uMaxIndex ? regExp.cap(1).toUInt() : uMaxIndex;
598 /* If index is valid => copy data: */
599 if (index.isValid())
600 m_dataList << new UIPortForwardingRow(parentTable(),
601 strTemplate.arg(++uMaxIndex), m_dataList[index.row()]->protocol(),
602 m_dataList[index.row()]->hostIp(), m_dataList[index.row()]->hostPort(),
603 m_dataList[index.row()]->guestIp(), m_dataList[index.row()]->guestPort());
604 /* If index is NOT valid => use default values: */
605 else
606 m_dataList << new UIPortForwardingRow(parentTable(),
607 strTemplate.arg(++uMaxIndex), KNATProtocol_TCP,
608 QString(""), 0, m_strGuestAddressHint, 0);
609 endInsertRows();
610}
611
612void UIPortForwardingModel::removeRule(const QModelIndex &index)
613{
614 if (!index.isValid())
615 return;
616 beginRemoveRows(QModelIndex(), index.row(), index.row());
617 delete m_dataList.at(index.row());
618 m_dataList.removeAt(index.row());
619 endRemoveRows();
620}
621
622void UIPortForwardingModel::setGuestAddressHint(const QString &strHint)
623{
624 m_strGuestAddressHint = strHint;
625}
626
627Qt::ItemFlags UIPortForwardingModel::flags(const QModelIndex &index) const
628{
629 /* Check index validness: */
630 if (!index.isValid())
631 return Qt::NoItemFlags;
632 /* All columns have similar flags: */
633 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
634}
635
636int UIPortForwardingModel::rowCount(const QModelIndex &) const
637{
638 return m_dataList.size();
639}
640
641int UIPortForwardingModel::columnCount(const QModelIndex &) const
642{
643 return UIPortForwardingDataType_Max;
644}
645
646QVariant UIPortForwardingModel::headerData(int iSection, Qt::Orientation enmOrientation, int iRole) const
647{
648 /* Display role for horizontal header: */
649 if (iRole == Qt::DisplayRole && enmOrientation == Qt::Horizontal)
650 {
651 /* Switch for different columns: */
652 switch (iSection)
653 {
654 case UIPortForwardingDataType_Name: return UIPortForwardingTable::tr("Name");
655 case UIPortForwardingDataType_Protocol: return UIPortForwardingTable::tr("Protocol");
656 case UIPortForwardingDataType_HostIp: return UIPortForwardingTable::tr("Host IP");
657 case UIPortForwardingDataType_HostPort: return UIPortForwardingTable::tr("Host Port");
658 case UIPortForwardingDataType_GuestIp: return UIPortForwardingTable::tr("Guest IP");
659 case UIPortForwardingDataType_GuestPort: return UIPortForwardingTable::tr("Guest Port");
660 default: break;
661 }
662 }
663 /* Return wrong value: */
664 return QVariant();
665}
666
667bool UIPortForwardingModel::setData(const QModelIndex &index, const QVariant &value, int iRole /* = Qt::EditRole */)
668{
669 /* Check index validness: */
670 if (!index.isValid() || iRole != Qt::EditRole)
671 return false;
672 /* Switch for different columns: */
673 switch (index.column())
674 {
675 case UIPortForwardingDataType_Name:
676 m_dataList[index.row()]->setName(value.value<NameData>());
677 emit dataChanged(index, index);
678 return true;
679 case UIPortForwardingDataType_Protocol:
680 m_dataList[index.row()]->setProtocol(value.value<KNATProtocol>());
681 emit dataChanged(index, index);
682 return true;
683 case UIPortForwardingDataType_HostIp:
684 m_dataList[index.row()]->setHostIp(value.value<IpData>());
685 emit dataChanged(index, index);
686 return true;
687 case UIPortForwardingDataType_HostPort:
688 m_dataList[index.row()]->setHostPort(value.value<PortData>());
689 emit dataChanged(index, index);
690 return true;
691 case UIPortForwardingDataType_GuestIp:
692 m_dataList[index.row()]->setGuestIp(value.value<IpData>());
693 emit dataChanged(index, index);
694 return true;
695 case UIPortForwardingDataType_GuestPort:
696 m_dataList[index.row()]->setGuestPort(value.value<PortData>());
697 emit dataChanged(index, index);
698 return true;
699 default: return false;
700 }
701 /* not reached! */
702}
703
704QVariant UIPortForwardingModel::data(const QModelIndex &index, int iRole) const
705{
706 /* Check index validness: */
707 if (!index.isValid())
708 return QVariant();
709 /* Switch for different roles: */
710 switch (iRole)
711 {
712 /* Display role: */
713 case Qt::DisplayRole:
714 {
715 /* Switch for different columns: */
716 switch (index.column())
717 {
718 case UIPortForwardingDataType_Name: return m_dataList[index.row()]->name();
719 case UIPortForwardingDataType_Protocol: return gpConverter->toString(m_dataList[index.row()]->protocol());
720 case UIPortForwardingDataType_HostIp: return m_dataList[index.row()]->hostIp();
721 case UIPortForwardingDataType_HostPort: return m_dataList[index.row()]->hostPort().value();
722 case UIPortForwardingDataType_GuestIp: return m_dataList[index.row()]->guestIp();
723 case UIPortForwardingDataType_GuestPort: return m_dataList[index.row()]->guestPort().value();
724 default: return QVariant();
725 }
726 }
727 /* Edit role: */
728 case Qt::EditRole:
729 {
730 /* Switch for different columns: */
731 switch (index.column())
732 {
733 case UIPortForwardingDataType_Name: return QVariant::fromValue(m_dataList[index.row()]->name());
734 case UIPortForwardingDataType_Protocol: return QVariant::fromValue(m_dataList[index.row()]->protocol());
735 case UIPortForwardingDataType_HostIp: return QVariant::fromValue(m_dataList[index.row()]->hostIp());
736 case UIPortForwardingDataType_HostPort: return QVariant::fromValue(m_dataList[index.row()]->hostPort());
737 case UIPortForwardingDataType_GuestIp: return QVariant::fromValue(m_dataList[index.row()]->guestIp());
738 case UIPortForwardingDataType_GuestPort: return QVariant::fromValue(m_dataList[index.row()]->guestPort());
739 default: return QVariant();
740 }
741 }
742 /* Alignment role: */
743 case Qt::TextAlignmentRole:
744 {
745 /* Switch for different columns: */
746 switch (index.column())
747 {
748 case UIPortForwardingDataType_Name:
749 case UIPortForwardingDataType_Protocol:
750 case UIPortForwardingDataType_HostPort:
751 case UIPortForwardingDataType_GuestPort:
752 return (int)(Qt::AlignLeft | Qt::AlignVCenter);
753 case UIPortForwardingDataType_HostIp:
754 case UIPortForwardingDataType_GuestIp:
755 return Qt::AlignCenter;
756 default: return QVariant();
757 }
758 }
759 case Qt::SizeHintRole:
760 {
761 /* Switch for different columns: */
762 switch (index.column())
763 {
764 case UIPortForwardingDataType_HostIp:
765 case UIPortForwardingDataType_GuestIp:
766#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
767 return QSize(QApplication::fontMetrics().horizontalAdvance(" 888.888.888.888 "),
768 QApplication::fontMetrics().height());
769#else
770 return QSize(QApplication::fontMetrics().width(" 888.888.888.888 "), QApplication::fontMetrics().height());
771#endif
772 default: return QVariant();
773 }
774 }
775 default: break;
776 }
777 /* Return wrong value: */
778 return QVariant();
779}
780
781QITableView *UIPortForwardingModel::parentTable() const
782{
783 return qobject_cast<QITableView*>(parent());
784}
785
786
787/*********************************************************************************************************************************
788* Class UIPortForwardingView implementation. *
789*********************************************************************************************************************************/
790
791int UIPortForwardingView::childCount() const
792{
793 /* Redirect request to table model: */
794 return qobject_cast<UIPortForwardingModel*>(model())->childCount();
795}
796
797QITableViewRow *UIPortForwardingView::childItem(int iIndex) const
798{
799 /* Redirect request to table model: */
800 return qobject_cast<UIPortForwardingModel*>(model())->childItem(iIndex);
801}
802
803
804/*********************************************************************************************************************************
805* Class UIPortForwardingTable implementation. *
806*********************************************************************************************************************************/
807
808UIPortForwardingTable::UIPortForwardingTable(const UIPortForwardingDataList &rules, bool fIPv6, bool fAllowEmptyGuestIPs)
809 : m_rules(rules)
810 , m_fIPv6(fIPv6)
811 , m_fAllowEmptyGuestIPs(fAllowEmptyGuestIPs)
812 , m_fTableDataChanged(false)
813 , m_pLayout(0)
814 , m_pTableView(0)
815 , m_pToolBar(0)
816 , m_pItemEditorFactory(0)
817 , m_pTableModel(0)
818 , m_pActionAdd(0)
819 , m_pActionCopy(0)
820 , m_pActionRemove(0)
821{
822 prepare();
823}
824
825UIPortForwardingTable::~UIPortForwardingTable()
826{
827 cleanup();
828}
829
830UIPortForwardingDataList UIPortForwardingTable::rules() const
831{
832 return m_pTableModel->rules();
833}
834
835void UIPortForwardingTable::setRules(const UIPortForwardingDataList &newRules,
836 bool fHoldPosition /* = false */)
837{
838 /* Remember last chosen item: */
839 const QModelIndex currentIndex = m_pTableView->currentIndex();
840 QITableViewRow *pCurrentItem = currentIndex.isValid() ? m_pTableModel->childItem(currentIndex.row()) : 0;
841 const QString strCurrentName = pCurrentItem ? pCurrentItem->childItem(0)->text() : QString();
842
843 /* Update the list of rules: */
844 m_rules = newRules;
845 m_pTableModel->setRules(m_rules);
846 sltAdjustTable();
847
848 /* Restore last chosen item: */
849 if (fHoldPosition && !strCurrentName.isEmpty())
850 {
851 for (int i = 0; i < m_pTableModel->childCount(); ++i)
852 {
853 QITableViewRow *pItem = m_pTableModel->childItem(i);
854 const QString strName = pItem ? pItem->childItem(0)->text() : QString();
855 if (strName == strCurrentName)
856 m_pTableView->setCurrentIndex(m_pTableModel->index(i, 0));
857 }
858 }
859}
860
861void UIPortForwardingTable::setGuestAddressHint(const QString &strHint)
862{
863 m_strGuestAddressHint = strHint;
864 m_pTableModel->setGuestAddressHint(m_strGuestAddressHint);
865}
866
867bool UIPortForwardingTable::validate() const
868{
869 /* Validate table: */
870 QList<NameData> names;
871 QList<UIPortForwardingDataUnique> rules;
872 for (int i = 0; i < m_pTableModel->rowCount(); ++i)
873 {
874 /* Some of variables: */
875 const NameData strName = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_Name), Qt::EditRole).value<NameData>();
876 const KNATProtocol enmProtocol = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_Protocol), Qt::EditRole).value<KNATProtocol>();
877 const PortData hostPort = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_HostPort), Qt::EditRole).value<PortData>().value();
878 const PortData guestPort = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_GuestPort), Qt::EditRole).value<PortData>().value();
879 const IpData strHostIp = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_HostIp), Qt::EditRole).value<IpData>();
880 const IpData strGuestIp = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_GuestIp), Qt::EditRole).value<IpData>();
881
882 /* If at least one port is 'zero': */
883 if (hostPort.value() == 0 || guestPort.value() == 0)
884 return msgCenter().warnAboutIncorrectPort(window());
885 /* If at least one address is incorrect: */
886 if (!( strHostIp.trimmed().isEmpty()
887 || RTNetIsIPv4AddrStr(strHostIp.toUtf8().constData())
888 || RTNetIsIPv6AddrStr(strHostIp.toUtf8().constData())
889 || RTNetStrIsIPv4AddrAny(strHostIp.toUtf8().constData())
890 || RTNetStrIsIPv6AddrAny(strHostIp.toUtf8().constData())))
891 return msgCenter().warnAboutIncorrectAddress(window());
892 if (!( strGuestIp.trimmed().isEmpty()
893 || RTNetIsIPv4AddrStr(strGuestIp.toUtf8().constData())
894 || RTNetIsIPv6AddrStr(strGuestIp.toUtf8().constData())
895 || RTNetStrIsIPv4AddrAny(strGuestIp.toUtf8().constData())
896 || RTNetStrIsIPv6AddrAny(strGuestIp.toUtf8().constData())))
897 return msgCenter().warnAboutIncorrectAddress(window());
898 /* If empty guest address is not allowed: */
899 if ( !m_fAllowEmptyGuestIPs
900 && strGuestIp.isEmpty())
901 return msgCenter().warnAboutEmptyGuestAddress(window());
902
903 /* Make sure non of the names were previosly used: */
904 if (!names.contains(strName))
905 names << strName;
906 else
907 return msgCenter().warnAboutNameShouldBeUnique(window());
908
909 /* Make sure non of the rules were previosly used: */
910 UIPortForwardingDataUnique rule(enmProtocol, hostPort, strHostIp);
911 if (!rules.contains(rule))
912 rules << rule;
913 else
914 return msgCenter().warnAboutRulesConflict(window());
915 }
916 /* True by default: */
917 return true;
918}
919
920void UIPortForwardingTable::makeSureEditorDataCommitted()
921{
922 m_pTableView->makeSureEditorDataCommitted();
923}
924
925bool UIPortForwardingTable::eventFilter(QObject *pObject, QEvent *pEvent)
926{
927 /* Process table: */
928 if (pObject == m_pTableView)
929 {
930 /* Process different event-types: */
931 switch (pEvent->type())
932 {
933 case QEvent::Show:
934 case QEvent::Resize:
935 {
936 /* Make sure layout requests really processed first of all: */
937 QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
938 /* Adjust table: */
939 sltAdjustTable();
940 break;
941 }
942 case QEvent::FocusIn:
943 case QEvent::FocusOut:
944 {
945 /* Update actions: */
946 sltCurrentChanged();
947 break;
948 }
949 default:
950 break;
951 }
952 }
953 /* Call to base-class: */
954 return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
955}
956
957void UIPortForwardingTable::retranslateUi()
958{
959 /* Table translations: */
960 m_pTableView->setWhatsThis(tr("Contains a list of port forwarding rules."));
961
962 /* Set action's text: */
963 m_pActionAdd->setText(tr("Add New Rule"));
964 m_pActionCopy->setText(tr("Copy Selected Rule"));
965 m_pActionRemove->setText(tr("Remove Selected Rule"));
966
967 m_pActionAdd->setWhatsThis(tr("Adds new port forwarding rule."));
968 m_pActionCopy->setWhatsThis(tr("Copies selected port forwarding rule."));
969 m_pActionRemove->setWhatsThis(tr("Removes selected port forwarding rule."));
970
971 m_pActionAdd->setToolTip(m_pActionAdd->whatsThis());
972 m_pActionCopy->setToolTip(m_pActionCopy->whatsThis());
973 m_pActionRemove->setToolTip(m_pActionRemove->whatsThis());
974}
975
976void UIPortForwardingTable::sltAddRule()
977{
978 m_pTableModel->addRule(QModelIndex());
979 m_pTableView->setFocus();
980 m_pTableView->setCurrentIndex(m_pTableModel->index(m_pTableModel->rowCount() - 1, 0));
981 sltCurrentChanged();
982 sltAdjustTable();
983}
984
985void UIPortForwardingTable::sltCopyRule()
986{
987 m_pTableModel->addRule(m_pTableView->currentIndex());
988 m_pTableView->setFocus();
989 m_pTableView->setCurrentIndex(m_pTableModel->index(m_pTableModel->rowCount() - 1, 0));
990 sltCurrentChanged();
991 sltAdjustTable();
992}
993
994void UIPortForwardingTable::sltRemoveRule()
995{
996 m_pTableModel->removeRule(m_pTableView->currentIndex());
997 m_pTableView->setFocus();
998 sltCurrentChanged();
999 sltAdjustTable();
1000}
1001
1002void UIPortForwardingTable::sltTableDataChanged()
1003{
1004 m_fTableDataChanged = true;
1005 emit sigDataChanged();
1006}
1007
1008void UIPortForwardingTable::sltCurrentChanged()
1009{
1010 bool fTableFocused = m_pTableView->hasFocus();
1011 bool fTableChildFocused = m_pTableView->findChildren<QWidget*>().contains(QApplication::focusWidget());
1012 bool fTableOrChildFocused = fTableFocused || fTableChildFocused;
1013 m_pActionCopy->setEnabled(m_pTableView->currentIndex().isValid() && fTableOrChildFocused);
1014 m_pActionRemove->setEnabled(m_pTableView->currentIndex().isValid() && fTableOrChildFocused);
1015}
1016
1017void UIPortForwardingTable::sltShowTableContexMenu(const QPoint &pos)
1018{
1019 /* Prepare context menu: */
1020 QMenu menu(m_pTableView);
1021 /* If some index is currently selected: */
1022 if (m_pTableView->indexAt(pos).isValid())
1023 {
1024 menu.addAction(m_pActionCopy);
1025 menu.addAction(m_pActionRemove);
1026 }
1027 /* If no valid index selected: */
1028 else
1029 {
1030 menu.addAction(m_pActionAdd);
1031 }
1032 menu.exec(m_pTableView->viewport()->mapToGlobal(pos));
1033}
1034
1035void UIPortForwardingTable::sltAdjustTable()
1036{
1037 /* If table is NOT empty: */
1038 if (m_pTableModel->rowCount())
1039 {
1040 /* Resize table to contents size-hint and emit a spare place for first column: */
1041 m_pTableView->resizeColumnsToContents();
1042 uint uFullWidth = m_pTableView->viewport()->width();
1043 for (uint u = 1; u < UIPortForwardingDataType_Max; ++u)
1044 uFullWidth -= m_pTableView->horizontalHeader()->sectionSize(u);
1045 m_pTableView->horizontalHeader()->resizeSection(UIPortForwardingDataType_Name, uFullWidth);
1046 }
1047 /* If table is empty: */
1048 else
1049 {
1050 /* Resize table columns to be equal in size: */
1051 uint uFullWidth = m_pTableView->viewport()->width();
1052 for (uint u = 0; u < UIPortForwardingDataType_Max; ++u)
1053 m_pTableView->horizontalHeader()->resizeSection(u, uFullWidth / UIPortForwardingDataType_Max);
1054 }
1055}
1056
1057void UIPortForwardingTable::prepare()
1058{
1059 /* Prepare layout: */
1060 prepareLayout();
1061
1062 /* Apply language settings: */
1063 retranslateUi();
1064}
1065
1066void UIPortForwardingTable::prepareLayout()
1067{
1068 /* Create layout: */
1069 m_pLayout = new QHBoxLayout(this);
1070 if (m_pLayout)
1071 {
1072 /* Configure layout: */
1073#ifdef VBOX_WS_MAC
1074 /* On macOS we can do a bit of smoothness: */
1075 m_pLayout->setContentsMargins(0, 0, 0, 0);
1076 m_pLayout->setSpacing(3);
1077#else
1078 m_pLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 3);
1079#endif
1080
1081 /* Prepare table-view: */
1082 prepareTableView();
1083
1084 /* Prepare toolbar: */
1085 prepareToolbar();
1086 }
1087}
1088
1089void UIPortForwardingTable::prepareTableView()
1090{
1091 /* Create table-view: */
1092 m_pTableView = new UIPortForwardingView;
1093 if (m_pTableView)
1094 {
1095 /* Configure table-view: */
1096 m_pTableView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
1097 m_pTableView->setTabKeyNavigation(false);
1098 m_pTableView->verticalHeader()->hide();
1099 m_pTableView->verticalHeader()->setDefaultSectionSize((int)(m_pTableView->verticalHeader()->minimumSectionSize() * 1.33));
1100 m_pTableView->setSelectionMode(QAbstractItemView::SingleSelection);
1101 m_pTableView->setContextMenuPolicy(Qt::CustomContextMenu);
1102 m_pTableView->installEventFilter(this);
1103
1104 /* Prepare model: */
1105 prepareTableModel();
1106
1107 /* Finish configure table-view (after model is configured): */
1108 m_pTableView->setModel(m_pTableModel);
1109 connect(m_pTableView, &UIPortForwardingView::sigCurrentChanged, this, &UIPortForwardingTable::sltCurrentChanged);
1110 connect(m_pTableView, &UIPortForwardingView::customContextMenuRequested, this, &UIPortForwardingTable::sltShowTableContexMenu);
1111
1112 /* Prepare delegates: */
1113 prepareTableDelegates();
1114
1115 /* Add into layout: */
1116 m_pLayout->addWidget(m_pTableView);
1117 }
1118}
1119
1120void UIPortForwardingTable::prepareTableModel()
1121{
1122 /* Create table-model: */
1123 m_pTableModel = new UIPortForwardingModel(m_pTableView, m_rules);
1124 if (m_pTableModel)
1125 {
1126 /* Configure table-model: */
1127 connect(m_pTableModel, &UIPortForwardingModel::dataChanged,
1128 this, &UIPortForwardingTable::sltTableDataChanged);
1129 connect(m_pTableModel, &UIPortForwardingModel::rowsInserted,
1130 this, &UIPortForwardingTable::sltTableDataChanged);
1131 connect(m_pTableModel, &UIPortForwardingModel::rowsRemoved,
1132 this, &UIPortForwardingTable::sltTableDataChanged);
1133 }
1134}
1135
1136void UIPortForwardingTable::prepareTableDelegates()
1137{
1138 /* We certainly have abstract item delegate: */
1139 QAbstractItemDelegate *pAbstractItemDelegate = m_pTableView->itemDelegate();
1140 if (pAbstractItemDelegate)
1141 {
1142 /* But is this also styled item delegate? */
1143 QStyledItemDelegate *pStyledItemDelegate = qobject_cast<QStyledItemDelegate*>(pAbstractItemDelegate);
1144 if (pStyledItemDelegate)
1145 {
1146 /* Create new item editor factory: */
1147 m_pItemEditorFactory = new QItemEditorFactory;
1148 if (m_pItemEditorFactory)
1149 {
1150 /* Register NameEditor as the NameData editor: */
1151 int iNameId = qRegisterMetaType<NameData>();
1152 QStandardItemEditorCreator<NameEditor> *pNameEditorItemCreator = new QStandardItemEditorCreator<NameEditor>();
1153 m_pItemEditorFactory->registerEditor(iNameId, pNameEditorItemCreator);
1154
1155 /* Register ProtocolEditor as the KNATProtocol editor: */
1156 int iProtocolId = qRegisterMetaType<KNATProtocol>();
1157 QStandardItemEditorCreator<ProtocolEditor> *pProtocolEditorItemCreator = new QStandardItemEditorCreator<ProtocolEditor>();
1158 m_pItemEditorFactory->registerEditor(iProtocolId, pProtocolEditorItemCreator);
1159
1160 /* Register IPv4Editor/IPv6Editor as the IpData editor: */
1161 int iIpId = qRegisterMetaType<IpData>();
1162 if (!m_fIPv6)
1163 {
1164 QStandardItemEditorCreator<IPv4Editor> *pIPv4EditorItemCreator = new QStandardItemEditorCreator<IPv4Editor>();
1165 m_pItemEditorFactory->registerEditor(iIpId, pIPv4EditorItemCreator);
1166 }
1167 else
1168 {
1169 QStandardItemEditorCreator<IPv6Editor> *pIPv6EditorItemCreator = new QStandardItemEditorCreator<IPv6Editor>();
1170 m_pItemEditorFactory->registerEditor(iIpId, pIPv6EditorItemCreator);
1171 }
1172
1173 /* Register PortEditor as the PortData editor: */
1174 int iPortId = qRegisterMetaType<PortData>();
1175 QStandardItemEditorCreator<PortEditor> *pPortEditorItemCreator = new QStandardItemEditorCreator<PortEditor>();
1176 m_pItemEditorFactory->registerEditor(iPortId, pPortEditorItemCreator);
1177
1178 /* Set newly created item editor factory for table delegate: */
1179 pStyledItemDelegate->setItemEditorFactory(m_pItemEditorFactory);
1180 }
1181 }
1182 }
1183}
1184
1185void UIPortForwardingTable::prepareToolbar()
1186{
1187 /* Create toolbar: */
1188 m_pToolBar = new QIToolBar;
1189 if (m_pToolBar)
1190 {
1191 /* Determine icon metric: */
1192 const QStyle *pStyle = QApplication::style();
1193 const int iIconMetric = pStyle->pixelMetric(QStyle::PM_SmallIconSize);
1194
1195 /* Configure toolbar: */
1196 m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
1197 m_pToolBar->setOrientation(Qt::Vertical);
1198
1199 /* Create 'Add' action: */
1200 m_pActionAdd = new QAction(this);
1201 if (m_pActionAdd)
1202 {
1203 /* Configure action: */
1204 m_pActionAdd->setShortcut(QKeySequence("Ins"));
1205 m_pActionAdd->setIcon(UIIconPool::iconSet(":/controller_add_16px.png", ":/controller_add_disabled_16px.png"));
1206 connect(m_pActionAdd, &QAction::triggered, this, &UIPortForwardingTable::sltAddRule);
1207 m_pToolBar->addAction(m_pActionAdd);
1208 }
1209
1210 /* Create 'Copy' action: */
1211 m_pActionCopy = new QAction(this);
1212 if (m_pActionCopy)
1213 {
1214 /* Configure action: */
1215 m_pActionCopy->setIcon(UIIconPool::iconSet(":/controller_add_16px.png", ":/controller_add_disabled_16px.png"));
1216 connect(m_pActionCopy, &QAction::triggered, this, &UIPortForwardingTable::sltCopyRule);
1217 }
1218
1219 /* Create 'Remove' action: */
1220 m_pActionRemove = new QAction(this);
1221 if (m_pActionRemove)
1222 {
1223 /* Configure action: */
1224 m_pActionRemove->setShortcut(QKeySequence("Del"));
1225 m_pActionRemove->setIcon(UIIconPool::iconSet(":/controller_remove_16px.png", ":/controller_remove_disabled_16px.png"));
1226 connect(m_pActionRemove, &QAction::triggered, this, &UIPortForwardingTable::sltRemoveRule);
1227 m_pToolBar->addAction(m_pActionRemove);
1228 }
1229
1230 /* Add into layout: */
1231 m_pLayout->addWidget(m_pToolBar);
1232 }
1233}
1234
1235void UIPortForwardingTable::cleanup()
1236{
1237 delete m_pItemEditorFactory;
1238 m_pItemEditorFactory = 0;
1239}
1240
1241
1242#include "UIPortForwardingTable.moc"
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette