VirtualBox

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

Last change on this file was 104358, checked in by vboxsync, 5 weeks ago

FE/Qt. bugref:10622. More refactoring around the retranslation functionality.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use