VirtualBox

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

Last change on this file since 103977 was 103362, checked in by vboxsync, 10 months ago

FE/Qt: bugref:10450: Get rid of QRegExp which is a part of Qt5 legacy API; This is necessary to get rid of Qt5 support lib.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.1 KB
Line 
1/* $Id: UIPortForwardingTable.cpp 103362 2024-02-14 16:50:56Z 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 <QRegularExpression>
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 const QRegularExpression re(strTemplate.arg("(\\d+)"));
595 for (int i = 0; i < m_dataList.size(); ++i)
596 {
597 const QRegularExpressionMatch mt = re.match(m_dataList[i]->name());
598 if (mt.hasMatch())
599 {
600 const uint uFoundIndex = mt.captured(1).toUInt();
601 uMaxIndex = uFoundIndex > uMaxIndex ? uFoundIndex : uMaxIndex;
602 }
603 }
604 /* If index is valid => copy data: */
605 if (index.isValid())
606 m_dataList << new UIPortForwardingRow(parentTable(),
607 strTemplate.arg(++uMaxIndex), m_dataList[index.row()]->protocol(),
608 m_dataList[index.row()]->hostIp(), m_dataList[index.row()]->hostPort(),
609 m_dataList[index.row()]->guestIp(), m_dataList[index.row()]->guestPort());
610 /* If index is NOT valid => use default values: */
611 else
612 m_dataList << new UIPortForwardingRow(parentTable(),
613 strTemplate.arg(++uMaxIndex), KNATProtocol_TCP,
614 QString(""), 0, m_strGuestAddressHint, 0);
615 endInsertRows();
616}
617
618void UIPortForwardingModel::removeRule(const QModelIndex &index)
619{
620 if (!index.isValid())
621 return;
622 beginRemoveRows(QModelIndex(), index.row(), index.row());
623 delete m_dataList.at(index.row());
624 m_dataList.removeAt(index.row());
625 endRemoveRows();
626}
627
628void UIPortForwardingModel::setGuestAddressHint(const QString &strHint)
629{
630 m_strGuestAddressHint = strHint;
631}
632
633Qt::ItemFlags UIPortForwardingModel::flags(const QModelIndex &index) const
634{
635 /* Check index validness: */
636 if (!index.isValid())
637 return Qt::NoItemFlags;
638 /* All columns have similar flags: */
639 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
640}
641
642int UIPortForwardingModel::rowCount(const QModelIndex &) const
643{
644 return m_dataList.size();
645}
646
647int UIPortForwardingModel::columnCount(const QModelIndex &) const
648{
649 return UIPortForwardingDataType_Max;
650}
651
652QVariant UIPortForwardingModel::headerData(int iSection, Qt::Orientation enmOrientation, int iRole) const
653{
654 /* Display role for horizontal header: */
655 if (iRole == Qt::DisplayRole && enmOrientation == Qt::Horizontal)
656 {
657 /* Switch for different columns: */
658 switch (iSection)
659 {
660 case UIPortForwardingDataType_Name: return UIPortForwardingTable::tr("Name");
661 case UIPortForwardingDataType_Protocol: return UIPortForwardingTable::tr("Protocol");
662 case UIPortForwardingDataType_HostIp: return UIPortForwardingTable::tr("Host IP");
663 case UIPortForwardingDataType_HostPort: return UIPortForwardingTable::tr("Host Port");
664 case UIPortForwardingDataType_GuestIp: return UIPortForwardingTable::tr("Guest IP");
665 case UIPortForwardingDataType_GuestPort: return UIPortForwardingTable::tr("Guest Port");
666 default: break;
667 }
668 }
669 /* Return wrong value: */
670 return QVariant();
671}
672
673bool UIPortForwardingModel::setData(const QModelIndex &index, const QVariant &value, int iRole /* = Qt::EditRole */)
674{
675 /* Check index validness: */
676 if (!index.isValid() || iRole != Qt::EditRole)
677 return false;
678 /* Switch for different columns: */
679 switch (index.column())
680 {
681 case UIPortForwardingDataType_Name:
682 m_dataList[index.row()]->setName(value.value<NameData>());
683 emit dataChanged(index, index);
684 return true;
685 case UIPortForwardingDataType_Protocol:
686 m_dataList[index.row()]->setProtocol(value.value<KNATProtocol>());
687 emit dataChanged(index, index);
688 return true;
689 case UIPortForwardingDataType_HostIp:
690 m_dataList[index.row()]->setHostIp(value.value<IpData>());
691 emit dataChanged(index, index);
692 return true;
693 case UIPortForwardingDataType_HostPort:
694 m_dataList[index.row()]->setHostPort(value.value<PortData>());
695 emit dataChanged(index, index);
696 return true;
697 case UIPortForwardingDataType_GuestIp:
698 m_dataList[index.row()]->setGuestIp(value.value<IpData>());
699 emit dataChanged(index, index);
700 return true;
701 case UIPortForwardingDataType_GuestPort:
702 m_dataList[index.row()]->setGuestPort(value.value<PortData>());
703 emit dataChanged(index, index);
704 return true;
705 default: return false;
706 }
707 /* not reached! */
708}
709
710QVariant UIPortForwardingModel::data(const QModelIndex &index, int iRole) const
711{
712 /* Check index validness: */
713 if (!index.isValid())
714 return QVariant();
715 /* Switch for different roles: */
716 switch (iRole)
717 {
718 /* Display role: */
719 case Qt::DisplayRole:
720 {
721 /* Switch for different columns: */
722 switch (index.column())
723 {
724 case UIPortForwardingDataType_Name: return m_dataList[index.row()]->name();
725 case UIPortForwardingDataType_Protocol: return gpConverter->toString(m_dataList[index.row()]->protocol());
726 case UIPortForwardingDataType_HostIp: return m_dataList[index.row()]->hostIp();
727 case UIPortForwardingDataType_HostPort: return m_dataList[index.row()]->hostPort().value();
728 case UIPortForwardingDataType_GuestIp: return m_dataList[index.row()]->guestIp();
729 case UIPortForwardingDataType_GuestPort: return m_dataList[index.row()]->guestPort().value();
730 default: return QVariant();
731 }
732 }
733 /* Edit role: */
734 case Qt::EditRole:
735 {
736 /* Switch for different columns: */
737 switch (index.column())
738 {
739 case UIPortForwardingDataType_Name: return QVariant::fromValue(m_dataList[index.row()]->name());
740 case UIPortForwardingDataType_Protocol: return QVariant::fromValue(m_dataList[index.row()]->protocol());
741 case UIPortForwardingDataType_HostIp: return QVariant::fromValue(m_dataList[index.row()]->hostIp());
742 case UIPortForwardingDataType_HostPort: return QVariant::fromValue(m_dataList[index.row()]->hostPort());
743 case UIPortForwardingDataType_GuestIp: return QVariant::fromValue(m_dataList[index.row()]->guestIp());
744 case UIPortForwardingDataType_GuestPort: return QVariant::fromValue(m_dataList[index.row()]->guestPort());
745 default: return QVariant();
746 }
747 }
748 /* Alignment role: */
749 case Qt::TextAlignmentRole:
750 {
751 /* Switch for different columns: */
752 switch (index.column())
753 {
754 case UIPortForwardingDataType_Name:
755 case UIPortForwardingDataType_Protocol:
756 case UIPortForwardingDataType_HostPort:
757 case UIPortForwardingDataType_GuestPort:
758 return (int)(Qt::AlignLeft | Qt::AlignVCenter);
759 case UIPortForwardingDataType_HostIp:
760 case UIPortForwardingDataType_GuestIp:
761 return Qt::AlignCenter;
762 default: return QVariant();
763 }
764 }
765 case Qt::SizeHintRole:
766 {
767 /* Switch for different columns: */
768 switch (index.column())
769 {
770 case UIPortForwardingDataType_HostIp:
771 case UIPortForwardingDataType_GuestIp:
772 {
773 QFontMetrics fm = qobject_cast<QWidget*>(parent())->fontMetrics();
774#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
775 return QSize(fm.horizontalAdvance(" 888.888.888.888 "), fm.height());
776#else
777 return QSize(fm.width(" 888.888.888.888 "), fm.height());
778#endif
779 }
780 default: return QVariant();
781 }
782 }
783 default: break;
784 }
785 /* Return wrong value: */
786 return QVariant();
787}
788
789QITableView *UIPortForwardingModel::parentTable() const
790{
791 return qobject_cast<QITableView*>(parent());
792}
793
794
795/*********************************************************************************************************************************
796* Class UIPortForwardingView implementation. *
797*********************************************************************************************************************************/
798
799int UIPortForwardingView::childCount() const
800{
801 /* Redirect request to table model: */
802 return qobject_cast<UIPortForwardingModel*>(model())->childCount();
803}
804
805QITableViewRow *UIPortForwardingView::childItem(int iIndex) const
806{
807 /* Redirect request to table model: */
808 return qobject_cast<UIPortForwardingModel*>(model())->childItem(iIndex);
809}
810
811
812/*********************************************************************************************************************************
813* Class UIPortForwardingTable implementation. *
814*********************************************************************************************************************************/
815
816UIPortForwardingTable::UIPortForwardingTable(const UIPortForwardingDataList &rules, bool fIPv6, bool fAllowEmptyGuestIPs)
817 : m_rules(rules)
818 , m_fIPv6(fIPv6)
819 , m_fAllowEmptyGuestIPs(fAllowEmptyGuestIPs)
820 , m_fTableDataChanged(false)
821 , m_pLayout(0)
822 , m_pTableView(0)
823 , m_pToolBar(0)
824 , m_pItemEditorFactory(0)
825 , m_pTableModel(0)
826 , m_pActionAdd(0)
827 , m_pActionCopy(0)
828 , m_pActionRemove(0)
829{
830 prepare();
831}
832
833UIPortForwardingTable::~UIPortForwardingTable()
834{
835 cleanup();
836}
837
838UIPortForwardingDataList UIPortForwardingTable::rules() const
839{
840 return m_pTableModel->rules();
841}
842
843void UIPortForwardingTable::setRules(const UIPortForwardingDataList &newRules,
844 bool fHoldPosition /* = false */)
845{
846 /* Remember last chosen item: */
847 const QModelIndex currentIndex = m_pTableView->currentIndex();
848 QITableViewRow *pCurrentItem = currentIndex.isValid() ? m_pTableModel->childItem(currentIndex.row()) : 0;
849 const QString strCurrentName = pCurrentItem ? pCurrentItem->childItem(0)->text() : QString();
850
851 /* Update the list of rules: */
852 m_rules = newRules;
853 m_pTableModel->setRules(m_rules);
854 sltAdjustTable();
855
856 /* Restore last chosen item: */
857 if (fHoldPosition && !strCurrentName.isEmpty())
858 {
859 for (int i = 0; i < m_pTableModel->childCount(); ++i)
860 {
861 QITableViewRow *pItem = m_pTableModel->childItem(i);
862 const QString strName = pItem ? pItem->childItem(0)->text() : QString();
863 if (strName == strCurrentName)
864 m_pTableView->setCurrentIndex(m_pTableModel->index(i, 0));
865 }
866 }
867}
868
869void UIPortForwardingTable::setGuestAddressHint(const QString &strHint)
870{
871 m_strGuestAddressHint = strHint;
872 m_pTableModel->setGuestAddressHint(m_strGuestAddressHint);
873}
874
875bool UIPortForwardingTable::validate() const
876{
877 /* Validate table: */
878 QList<NameData> names;
879 QList<UIPortForwardingDataUnique> rules;
880 for (int i = 0; i < m_pTableModel->rowCount(); ++i)
881 {
882 /* Some of variables: */
883 const NameData strName = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_Name), Qt::EditRole).value<NameData>();
884 const KNATProtocol enmProtocol = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_Protocol), Qt::EditRole).value<KNATProtocol>();
885 const PortData hostPort = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_HostPort), Qt::EditRole).value<PortData>().value();
886 const PortData guestPort = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_GuestPort), Qt::EditRole).value<PortData>().value();
887 const IpData strHostIp = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_HostIp), Qt::EditRole).value<IpData>();
888 const IpData strGuestIp = m_pTableModel->data(m_pTableModel->index(i, UIPortForwardingDataType_GuestIp), Qt::EditRole).value<IpData>();
889
890 /* If at least one port is 'zero': */
891 if (hostPort.value() == 0 || guestPort.value() == 0)
892 return msgCenter().warnAboutIncorrectPort(window());
893 /* If at least one address is incorrect: */
894 if (!( strHostIp.trimmed().isEmpty()
895 || RTNetIsIPv4AddrStr(strHostIp.toUtf8().constData())
896 || RTNetIsIPv6AddrStr(strHostIp.toUtf8().constData())
897 || RTNetStrIsIPv4AddrAny(strHostIp.toUtf8().constData())
898 || RTNetStrIsIPv6AddrAny(strHostIp.toUtf8().constData())))
899 return msgCenter().warnAboutIncorrectAddress(window());
900 if (!( strGuestIp.trimmed().isEmpty()
901 || RTNetIsIPv4AddrStr(strGuestIp.toUtf8().constData())
902 || RTNetIsIPv6AddrStr(strGuestIp.toUtf8().constData())
903 || RTNetStrIsIPv4AddrAny(strGuestIp.toUtf8().constData())
904 || RTNetStrIsIPv6AddrAny(strGuestIp.toUtf8().constData())))
905 return msgCenter().warnAboutIncorrectAddress(window());
906 /* If empty guest address is not allowed: */
907 if ( !m_fAllowEmptyGuestIPs
908 && strGuestIp.isEmpty())
909 return msgCenter().warnAboutEmptyGuestAddress(window());
910
911 /* Make sure non of the names were previosly used: */
912 if (!names.contains(strName))
913 names << strName;
914 else
915 return msgCenter().warnAboutNameShouldBeUnique(window());
916
917 /* Make sure non of the rules were previosly used: */
918 UIPortForwardingDataUnique rule(enmProtocol, hostPort, strHostIp);
919 if (!rules.contains(rule))
920 rules << rule;
921 else
922 return msgCenter().warnAboutRulesConflict(window());
923 }
924 /* True by default: */
925 return true;
926}
927
928void UIPortForwardingTable::makeSureEditorDataCommitted()
929{
930 m_pTableView->makeSureEditorDataCommitted();
931}
932
933bool UIPortForwardingTable::eventFilter(QObject *pObject, QEvent *pEvent)
934{
935 /* Process table: */
936 if (pObject == m_pTableView)
937 {
938 /* Process different event-types: */
939 switch (pEvent->type())
940 {
941 case QEvent::Show:
942 case QEvent::Resize:
943 {
944 /* Make sure layout requests really processed first of all: */
945 QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
946 /* Adjust table: */
947 sltAdjustTable();
948 break;
949 }
950 case QEvent::FocusIn:
951 case QEvent::FocusOut:
952 {
953 /* Update actions: */
954 sltCurrentChanged();
955 break;
956 }
957 default:
958 break;
959 }
960 }
961 /* Call to base-class: */
962 return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
963}
964
965void UIPortForwardingTable::retranslateUi()
966{
967 /* Table translations: */
968 m_pTableView->setWhatsThis(tr("Contains a list of port forwarding rules."));
969
970 /* Set action's text: */
971 m_pActionAdd->setText(tr("Add New Rule"));
972 m_pActionCopy->setText(tr("Copy Selected Rule"));
973 m_pActionRemove->setText(tr("Remove Selected Rule"));
974
975 m_pActionAdd->setWhatsThis(tr("Adds new port forwarding rule."));
976 m_pActionCopy->setWhatsThis(tr("Copies selected port forwarding rule."));
977 m_pActionRemove->setWhatsThis(tr("Removes selected port forwarding rule."));
978
979 m_pActionAdd->setToolTip(m_pActionAdd->whatsThis());
980 m_pActionCopy->setToolTip(m_pActionCopy->whatsThis());
981 m_pActionRemove->setToolTip(m_pActionRemove->whatsThis());
982}
983
984void UIPortForwardingTable::sltAddRule()
985{
986 m_pTableModel->addRule(QModelIndex());
987 m_pTableView->setFocus();
988 m_pTableView->setCurrentIndex(m_pTableModel->index(m_pTableModel->rowCount() - 1, 0));
989 sltCurrentChanged();
990 sltAdjustTable();
991}
992
993void UIPortForwardingTable::sltCopyRule()
994{
995 m_pTableModel->addRule(m_pTableView->currentIndex());
996 m_pTableView->setFocus();
997 m_pTableView->setCurrentIndex(m_pTableModel->index(m_pTableModel->rowCount() - 1, 0));
998 sltCurrentChanged();
999 sltAdjustTable();
1000}
1001
1002void UIPortForwardingTable::sltRemoveRule()
1003{
1004 m_pTableModel->removeRule(m_pTableView->currentIndex());
1005 m_pTableView->setFocus();
1006 sltCurrentChanged();
1007 sltAdjustTable();
1008}
1009
1010void UIPortForwardingTable::sltTableDataChanged()
1011{
1012 m_fTableDataChanged = true;
1013 emit sigDataChanged();
1014}
1015
1016void UIPortForwardingTable::sltCurrentChanged()
1017{
1018 bool fTableFocused = m_pTableView->hasFocus();
1019 bool fTableChildFocused = m_pTableView->findChildren<QWidget*>().contains(QApplication::focusWidget());
1020 bool fTableOrChildFocused = fTableFocused || fTableChildFocused;
1021 m_pActionCopy->setEnabled(m_pTableView->currentIndex().isValid() && fTableOrChildFocused);
1022 m_pActionRemove->setEnabled(m_pTableView->currentIndex().isValid() && fTableOrChildFocused);
1023}
1024
1025void UIPortForwardingTable::sltShowTableContexMenu(const QPoint &pos)
1026{
1027 /* Prepare context menu: */
1028 QMenu menu(m_pTableView);
1029 /* If some index is currently selected: */
1030 if (m_pTableView->indexAt(pos).isValid())
1031 {
1032 menu.addAction(m_pActionCopy);
1033 menu.addAction(m_pActionRemove);
1034 }
1035 /* If no valid index selected: */
1036 else
1037 {
1038 menu.addAction(m_pActionAdd);
1039 }
1040 menu.exec(m_pTableView->viewport()->mapToGlobal(pos));
1041}
1042
1043void UIPortForwardingTable::sltAdjustTable()
1044{
1045 /* If table is NOT empty: */
1046 if (m_pTableModel->rowCount())
1047 {
1048 /* Resize table to contents size-hint and emit a spare place for first column: */
1049 m_pTableView->resizeColumnsToContents();
1050 uint uFullWidth = m_pTableView->viewport()->width();
1051 for (uint u = 1; u < UIPortForwardingDataType_Max; ++u)
1052 uFullWidth -= m_pTableView->horizontalHeader()->sectionSize(u);
1053 m_pTableView->horizontalHeader()->resizeSection(UIPortForwardingDataType_Name, uFullWidth);
1054 }
1055 /* If table is empty: */
1056 else
1057 {
1058 /* Resize table columns to be equal in size: */
1059 uint uFullWidth = m_pTableView->viewport()->width();
1060 for (uint u = 0; u < UIPortForwardingDataType_Max; ++u)
1061 m_pTableView->horizontalHeader()->resizeSection(u, uFullWidth / UIPortForwardingDataType_Max);
1062 }
1063}
1064
1065void UIPortForwardingTable::prepare()
1066{
1067 /* Prepare layout: */
1068 prepareLayout();
1069
1070 /* Apply language settings: */
1071 retranslateUi();
1072}
1073
1074void UIPortForwardingTable::prepareLayout()
1075{
1076 /* Create layout: */
1077 m_pLayout = new QHBoxLayout(this);
1078 if (m_pLayout)
1079 {
1080 /* Configure layout: */
1081#ifdef VBOX_WS_MAC
1082 /* On macOS we can do a bit of smoothness: */
1083 m_pLayout->setContentsMargins(0, 0, 0, 0);
1084 m_pLayout->setSpacing(3);
1085#else
1086 m_pLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 3);
1087#endif
1088
1089 /* Prepare table-view: */
1090 prepareTableView();
1091
1092 /* Prepare toolbar: */
1093 prepareToolbar();
1094 }
1095}
1096
1097void UIPortForwardingTable::prepareTableView()
1098{
1099 /* Create table-view: */
1100 m_pTableView = new UIPortForwardingView;
1101 if (m_pTableView)
1102 {
1103 /* Configure table-view: */
1104 m_pTableView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
1105 m_pTableView->setTabKeyNavigation(false);
1106 m_pTableView->verticalHeader()->hide();
1107 m_pTableView->verticalHeader()->setDefaultSectionSize((int)(m_pTableView->verticalHeader()->minimumSectionSize() * 1.33));
1108 m_pTableView->setSelectionMode(QAbstractItemView::SingleSelection);
1109 m_pTableView->setContextMenuPolicy(Qt::CustomContextMenu);
1110 m_pTableView->installEventFilter(this);
1111
1112 /* Prepare model: */
1113 prepareTableModel();
1114
1115 /* Finish configure table-view (after model is configured): */
1116 m_pTableView->setModel(m_pTableModel);
1117 connect(m_pTableView, &UIPortForwardingView::sigCurrentChanged, this, &UIPortForwardingTable::sltCurrentChanged);
1118 connect(m_pTableView, &UIPortForwardingView::customContextMenuRequested, this, &UIPortForwardingTable::sltShowTableContexMenu);
1119
1120 /* Prepare delegates: */
1121 prepareTableDelegates();
1122
1123 /* Add into layout: */
1124 m_pLayout->addWidget(m_pTableView);
1125 }
1126}
1127
1128void UIPortForwardingTable::prepareTableModel()
1129{
1130 /* Create table-model: */
1131 m_pTableModel = new UIPortForwardingModel(m_pTableView, m_rules);
1132 if (m_pTableModel)
1133 {
1134 /* Configure table-model: */
1135 connect(m_pTableModel, &UIPortForwardingModel::dataChanged,
1136 this, &UIPortForwardingTable::sltTableDataChanged);
1137 connect(m_pTableModel, &UIPortForwardingModel::rowsInserted,
1138 this, &UIPortForwardingTable::sltTableDataChanged);
1139 connect(m_pTableModel, &UIPortForwardingModel::rowsRemoved,
1140 this, &UIPortForwardingTable::sltTableDataChanged);
1141 }
1142}
1143
1144void UIPortForwardingTable::prepareTableDelegates()
1145{
1146 /* We certainly have abstract item delegate: */
1147 QAbstractItemDelegate *pAbstractItemDelegate = m_pTableView->itemDelegate();
1148 if (pAbstractItemDelegate)
1149 {
1150 /* But is this also styled item delegate? */
1151 QStyledItemDelegate *pStyledItemDelegate = qobject_cast<QStyledItemDelegate*>(pAbstractItemDelegate);
1152 if (pStyledItemDelegate)
1153 {
1154 /* Create new item editor factory: */
1155 m_pItemEditorFactory = new QItemEditorFactory;
1156 if (m_pItemEditorFactory)
1157 {
1158 /* Register NameEditor as the NameData editor: */
1159 int iNameId = qRegisterMetaType<NameData>();
1160 QStandardItemEditorCreator<NameEditor> *pNameEditorItemCreator = new QStandardItemEditorCreator<NameEditor>();
1161 m_pItemEditorFactory->registerEditor(iNameId, pNameEditorItemCreator);
1162
1163 /* Register ProtocolEditor as the KNATProtocol editor: */
1164 int iProtocolId = qRegisterMetaType<KNATProtocol>();
1165 QStandardItemEditorCreator<ProtocolEditor> *pProtocolEditorItemCreator = new QStandardItemEditorCreator<ProtocolEditor>();
1166 m_pItemEditorFactory->registerEditor(iProtocolId, pProtocolEditorItemCreator);
1167
1168 /* Register IPv4Editor/IPv6Editor as the IpData editor: */
1169 int iIpId = qRegisterMetaType<IpData>();
1170 if (!m_fIPv6)
1171 {
1172 QStandardItemEditorCreator<IPv4Editor> *pIPv4EditorItemCreator = new QStandardItemEditorCreator<IPv4Editor>();
1173 m_pItemEditorFactory->registerEditor(iIpId, pIPv4EditorItemCreator);
1174 }
1175 else
1176 {
1177 QStandardItemEditorCreator<IPv6Editor> *pIPv6EditorItemCreator = new QStandardItemEditorCreator<IPv6Editor>();
1178 m_pItemEditorFactory->registerEditor(iIpId, pIPv6EditorItemCreator);
1179 }
1180
1181 /* Register PortEditor as the PortData editor: */
1182 int iPortId = qRegisterMetaType<PortData>();
1183 QStandardItemEditorCreator<PortEditor> *pPortEditorItemCreator = new QStandardItemEditorCreator<PortEditor>();
1184 m_pItemEditorFactory->registerEditor(iPortId, pPortEditorItemCreator);
1185
1186 /* Set newly created item editor factory for table delegate: */
1187 pStyledItemDelegate->setItemEditorFactory(m_pItemEditorFactory);
1188 }
1189 }
1190 }
1191}
1192
1193void UIPortForwardingTable::prepareToolbar()
1194{
1195 /* Create toolbar: */
1196 m_pToolBar = new QIToolBar;
1197 if (m_pToolBar)
1198 {
1199 /* Determine icon metric: */
1200 const QStyle *pStyle = QApplication::style();
1201 const int iIconMetric = pStyle->pixelMetric(QStyle::PM_SmallIconSize);
1202
1203 /* Configure toolbar: */
1204 m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
1205 m_pToolBar->setOrientation(Qt::Vertical);
1206
1207 /* Create 'Add' action: */
1208 m_pActionAdd = new QAction(this);
1209 if (m_pActionAdd)
1210 {
1211 /* Configure action: */
1212 m_pActionAdd->setShortcut(QKeySequence("Ins"));
1213 m_pActionAdd->setIcon(UIIconPool::iconSet(":/controller_add_16px.png", ":/controller_add_disabled_16px.png"));
1214 connect(m_pActionAdd, &QAction::triggered, this, &UIPortForwardingTable::sltAddRule);
1215 m_pToolBar->addAction(m_pActionAdd);
1216 }
1217
1218 /* Create 'Copy' action: */
1219 m_pActionCopy = new QAction(this);
1220 if (m_pActionCopy)
1221 {
1222 /* Configure action: */
1223 m_pActionCopy->setIcon(UIIconPool::iconSet(":/controller_add_16px.png", ":/controller_add_disabled_16px.png"));
1224 connect(m_pActionCopy, &QAction::triggered, this, &UIPortForwardingTable::sltCopyRule);
1225 }
1226
1227 /* Create 'Remove' action: */
1228 m_pActionRemove = new QAction(this);
1229 if (m_pActionRemove)
1230 {
1231 /* Configure action: */
1232 m_pActionRemove->setShortcut(QKeySequence("Del"));
1233 m_pActionRemove->setIcon(UIIconPool::iconSet(":/controller_remove_16px.png", ":/controller_remove_disabled_16px.png"));
1234 connect(m_pActionRemove, &QAction::triggered, this, &UIPortForwardingTable::sltRemoveRule);
1235 m_pToolBar->addAction(m_pActionRemove);
1236 }
1237
1238 /* Add into layout: */
1239 m_pLayout->addWidget(m_pToolBar);
1240 }
1241}
1242
1243void UIPortForwardingTable::cleanup()
1244{
1245 delete m_pItemEditorFactory;
1246 m_pItemEditorFactory = 0;
1247}
1248
1249
1250#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