Compare commits

..

No commits in common. "master" and "hostdev" have entirely different histories.

253 changed files with 1811 additions and 17021 deletions

View File

@ -1,76 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at support@ostinato.org. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@ -1,16 +0,0 @@
# Contributing Guidelines
Please use a Pull Request to contribute code. Very small fixes (< 10 lines) can provide the diff via an issue instead of a PR.
In either case, you agree to the below legal terms and you indicate your acceptance by explicitly adding a comment to the issue or PR stating -
```
I have read the contributing guidelines (CONTRIBUTING.md) and I hereby assign the copyrights of these
changes to [Srivats P](https://github.com/pstavirs).
```
## Legal
By submitting a Pull Request, you disavow any rights or claims to any changes submitted to the Ostinato project and assign the copyright of those changes to [Srivats P](https://github.com/pstavirs).
If you cannot or do not want to reassign those rights (your employment contract for your employer may not allow this), you should not submit a PR. Open an issue and someone else can do the work.
This is a legal way of saying "_If you submit a PR to us, that code becomes ours_". 99.9% of the time that's what you intend anyways; we hope it doesn't scare you away from contributing.

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
github: pstavirs

View File

@ -1,5 +1,4 @@
language: cpp language: cpp
osx_image: xcode11.3
os: os:
- linux - linux
@ -14,14 +13,17 @@ matrix:
- os: osx - os: osx
compiler: gcc compiler: gcc
before_script: before_install:
- "if [ $TRAVIS_OS_NAME = 'osx' ]; then \ - "if [ $TRAVIS_OS_NAME = 'osx' ]; then \
brew update && \
brew install qt5 && \
brew link qt5 --force && \
brew install protobuf && \
ls -lR /usr/local/include/google/protobuf; \ ls -lR /usr/local/include/google/protobuf; \
which clang++; \ which clang++; \
clang++ -E -x c++ - -v < /dev/null; \ clang++ -E -x c++ - -v < /dev/null; \
export CPLUS_INCLUDE_PATH=/usr/local/include; \ export CPLUS_INCLUDE_PATH=/usr/local/include; \
export LIBRARY_PATH=/usr/local/lib; \ export LIBRARY_PATH=/usr/local/lib; \
export PATH=/usr/local/opt/qt/bin:$PATH; \
fi" fi"
addons: addons:
@ -29,16 +31,12 @@ addons:
packages: packages:
- qtbase5-dev - qtbase5-dev
- qtscript5-dev - qtscript5-dev
- libqt5svg5-dev
- libpcap-dev - libpcap-dev
- libprotobuf-dev - libprotobuf-dev
- protobuf-compiler - protobuf-compiler
- libnl-3-dev - libnl-3-dev
- libnl-route-3-dev - libnl-route-3-dev
homebrew:
packages:
- qt5
- protobuf
script: script:
- QT_SELECT=qt5 qmake -config debug - QT_SELECT=qt5 qmake -config debug
- make - make

View File

@ -1,23 +1,9 @@
# Ostinato # Ostinato
[![Build Status](https://app.travis-ci.com/pstavirs/ostinato.svg?branch=master)](https://app.travis-ci.com/pstavirs/ostinato) [![Build Status](https://travis-ci.org/pstavirs/ostinato.svg?branch=master)](https://travis-ci.org/pstavirs/ostinato)
This is the code repository for the Ostinato network packet crafter and traffic generator This is the code repository for the Ostinato network packet crafter and traffic generator
Visit https://ostinato.org for demo video and details Visit https://ostinato.org for demo video and details
Source License: GPLv3+ (see [COPYING](https://raw.githubusercontent.com/pstavirs/ostinato/master/COPYING)) License: GPLv3+ (see [COPYING](https://raw.githubusercontent.com/pstavirs/ostinato/master/COPYING))
## Author's note
I have been developing and maintaining Ostinato [single-handedly](https://github.com/pstavirs/ostinato/graphs/contributors) for more than 12 years. And during this time I have grudgingly come around to the view that open source cannot survive and thrive without money. Mixing money with open-source is messy, but I don't see a way forward unless we as a community become open to the idea of talking about it and changing our culture so that money is no longer a bad word.
I sell binary licenses on [ostinato.org](https://ostinato.org/downloads) to try and cover the costs of development. Please consider buying those - they are priced low enough that you can afford it or you could just as easily expense them to your organisation.
If you build Ostinato from source and find it useful, please sponsor to keep the lights on and sustain the project.
[![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86&style=for-the-badge)](https://github.com/sponsors/pstavirs)
Read the Ostinato [origin story](https://ostinato.org/about).
Srivats P<br/>
Author, Ostinato

View File

@ -94,7 +94,7 @@
<item> <item>
<widget class="QLabel" name="CopyrightLabel" > <widget class="QLabel" name="CopyrightLabel" >
<property name="text" > <property name="text" >
<string>Copyright (c) 2007-2023 Srivats P.</string> <string>Copyright (c) 2007-2018 Srivats P.</string>
</property> </property>
<property name="alignment" > <property name="alignment" >
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>

View File

@ -1,86 +0,0 @@
/*
Copyright (C) 2019 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _APPLY_MESSAGE_H
#define _APPLY_MESSAGE_H
#include <QDialog>
#include <QMainWindow>
#include <QProgressBar>
#include <QTimer>
extern QMainWindow *mainWindow;
class ApplyMessage: public QDialog
{
public:
ApplyMessage(QWidget *parent = Q_NULLPTR);
public slots:
void show();
virtual void done(int r);
private:
QLabel *help_;
QTimer *helpTimer_;
};
ApplyMessage::ApplyMessage(QWidget *parent)
: QDialog(parent)
{
auto layout = new QVBoxLayout(this);
auto message = new QLabel(tr("Pushing configuration changes to agent "
"and re-building packets ..."), this);
auto progress = new QProgressBar(this);
progress->setRange(0, 0);
progress->setTextVisible(false);
help_ = new QLabel(tr("<b>This may take some time</b>"), this);
help_->setAlignment(Qt::AlignCenter);
layout->addWidget(message);
layout->addWidget(progress);
layout->addWidget(help_);
helpTimer_ = new QTimer(this);
helpTimer_->setSingleShot(true);
helpTimer_->setInterval(4000);
connect(helpTimer_, SIGNAL(timeout()), help_, SLOT(show()));
}
void ApplyMessage::show()
{
help_->hide(); // shown when helpTimer_ expires
QWidget *parent = parentWidget();
if (!parent)
parent = mainWindow;
move(parent->frameGeometry().center() - rect().center());
setModal(true);
QDialog::show();
helpTimer_->start();
}
void ApplyMessage::done(int r)
{
helpTimer_->stop();
QDialog::done(r);
}
#endif

View File

@ -136,11 +136,6 @@ QVariant ArpStatusModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
Qt::DropActions ArpStatusModel::supportedDropActions() const
{
return Qt::IgnoreAction; // read-only model, doesn't accept any data
}
void ArpStatusModel::setDeviceIndex(Port *port, int deviceIndex) void ArpStatusModel::setDeviceIndex(Port *port, int deviceIndex)
{ {
beginResetModel(); beginResetModel();

View File

@ -40,8 +40,6 @@ public:
int role = Qt::DisplayRole) const; int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
Qt::DropActions supportedDropActions() const;
void setDeviceIndex(Port *port, int deviceIndex); void setDeviceIndex(Port *port, int deviceIndex);
public slots: public slots:

View File

@ -1,221 +0,0 @@
/*
Copyright (C) 2020 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "clipboardhelper.h"
#include "xtableview.h"
#include <QAction>
#include <QApplication>
#include <QClipboard>
#include <QIcon>
#include <QItemSelection>
#if 0 // change 0 to 1 for debug
#define xDebug(...) qDebug(__VA_ARGS__)
#else
#define xDebug(...)
#endif
ClipboardHelper::ClipboardHelper(QObject *parent)
: QObject(parent)
{
actionCut_ = new QAction(tr("&Cut"), this);
actionCut_->setObjectName(QStringLiteral("actionCut"));
actionCut_->setIcon(QIcon(QString::fromUtf8(":/icons/cut.png")));
actionCopy_ = new QAction(tr("Cop&y"), this);
actionCopy_->setObjectName(QStringLiteral("actionCopy"));
actionCopy_->setIcon(QIcon(QString::fromUtf8(":/icons/copy.png")));
actionPaste_ = new QAction(tr("&Paste"), this);
actionPaste_->setObjectName(QStringLiteral("actionPaste"));
actionPaste_->setIcon(QIcon(QString::fromUtf8(":/icons/paste.png")));
connect(actionCut_, SIGNAL(triggered()), SLOT(actionTriggered()));
connect(actionCopy_, SIGNAL(triggered()), SLOT(actionTriggered()));
connect(actionPaste_, SIGNAL(triggered()), SLOT(actionTriggered()));
// XXX: failsafe in case updation of cut/copy/status causes issues
// Temporary for 1 release - will be removed after that
if (qEnvironmentVariableIsSet("X-OSTINATO-CCP-STATUS")) {
qWarning("FAILSAFE: Cut-Copy-Paste action status will not be updated");
return;
}
connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)),
SLOT(updateCutCopyStatus(QWidget*, QWidget*)));
connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)),
SLOT(updatePasteStatus()));
connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()),
SLOT(updatePasteStatus()));
}
QList<QAction*> ClipboardHelper::actions()
{
QList<QAction*> actionList({actionCut_, actionCopy_, actionPaste_});
return actionList;
}
void ClipboardHelper::actionTriggered()
{
QWidget *focusWidget = qApp->focusWidget();
if (!focusWidget)
return;
// single slot to handle cut/copy/paste - find which action was triggered
QString action = sender()->objectName()
.remove("action").append("()").toLower();
if (focusWidget->metaObject()->indexOfSlot(qPrintable(action)) < 0) {
xDebug("%s slot not found for object %s:%s ",
qPrintable(action),
qPrintable(focusWidget->objectName()),
focusWidget->metaObject()->className());
return;
}
action.remove("()");
QMetaObject::invokeMethod(focusWidget, qPrintable(action),
Qt::DirectConnection);
}
void ClipboardHelper::updateCutCopyStatus(QWidget *old, QWidget *now)
{
xDebug("In %s", __FUNCTION__);
const XTableView *view = dynamic_cast<XTableView*>(old);
if (view) {
disconnect(view->selectionModel(),
SIGNAL(selectionChanged(const QItemSelection&,
const QItemSelection&)),
this,
SLOT(focusWidgetSelectionChanged(const QItemSelection&,
const QItemSelection&)));
disconnect(view->model(), SIGNAL(modelReset()),
this, SLOT(focusWidgetModelReset()));
}
if (!now) {
xDebug("No focus widget to copy from");
actionCut_->setEnabled(false);
actionCopy_->setEnabled(false);
return;
}
const QMetaObject *meta = now->metaObject();
if (meta->indexOfSlot("copy()") < 0) {
xDebug("Focus Widget (%s) doesn't have a copy slot",
qPrintable(now->objectName()));
actionCut_->setEnabled(false);
actionCopy_->setEnabled(false);
return;
}
view = dynamic_cast<XTableView*>(now);
if (view) {
connect(view->selectionModel(),
SIGNAL(selectionChanged(const QItemSelection&,
const QItemSelection&)),
SLOT(focusWidgetSelectionChanged(const QItemSelection&,
const QItemSelection&)));
connect(view->model(), SIGNAL(modelReset()),
this, SLOT(focusWidgetModelReset()));
if (!view->hasSelection()) {
xDebug("%s doesn't have anything selected to copy",
qPrintable(view->objectName()));
actionCut_->setEnabled(false);
actionCopy_->setEnabled(false);
return;
}
xDebug("%s model can cut: %d", qPrintable(view->objectName()),
view->canCut());
actionCut_->setEnabled(view->canCut());
}
xDebug("%s has a selection and copy slot: copy possible",
qPrintable(now->objectName()));
actionCopy_->setEnabled(true);
}
void ClipboardHelper::focusWidgetSelectionChanged(
const QItemSelection &selected, const QItemSelection &/*deselected*/)
{
xDebug("In %s", __FUNCTION__);
// Selection changed in the XTableView that has focus
const XTableView *view = dynamic_cast<XTableView*>(qApp->focusWidget());
xDebug("canCut:%d empty:%d", view->canCut(), selected.indexes().isEmpty());
actionCut_->setEnabled(!selected.indexes().isEmpty()
&& view && view->canCut());
actionCopy_->setEnabled(!selected.indexes().isEmpty());
}
void ClipboardHelper::updatePasteStatus()
{
xDebug("In %s", __FUNCTION__);
QWidget *focusWidget = qApp->focusWidget();
if (!focusWidget) {
xDebug("No focus widget to paste into");
actionPaste_->setEnabled(false);
return;
}
const QMimeData *item = QGuiApplication::clipboard()->mimeData();
if (!item || item->formats().isEmpty()) {
xDebug("Nothing on clipboard to paste");
actionPaste_->setEnabled(false);
return;
}
const QMetaObject *meta = focusWidget->metaObject();
if (meta->indexOfSlot("paste()") < 0) {
xDebug("Focus Widget (%s) doesn't have a paste slot",
qPrintable(focusWidget->objectName()));
actionPaste_->setEnabled(false);
return;
}
const XTableView *view = dynamic_cast<XTableView*>(focusWidget);
if (view && !view->canPaste(item)) {
xDebug("%s cannot accept this item (%s)",
qPrintable(view->objectName()),
qPrintable(item->formats().join("|")));
actionPaste_->setEnabled(false);
return;
}
xDebug("%s can accept this item (%s): paste possible",
qPrintable(focusWidget->objectName()),
qPrintable(item->formats().join("|")));
actionPaste_->setEnabled(true);
}
void ClipboardHelper::focusWidgetModelReset()
{
xDebug("In %s", __FUNCTION__);
QWidget *focusWidget = qApp->focusWidget();
updateCutCopyStatus(focusWidget, focusWidget); // re-eval cut/copy status
}
#undef xDebug

View File

@ -1,51 +0,0 @@
/*
Copyright (C) 2020 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _CLIPBOARD_HELPER_H
#define _CLIPBOARD_HELPER_H
#include <QObject>
#include <QList>
class QAction;
class QItemSelection;
class ClipboardHelper : public QObject
{
Q_OBJECT
public:
ClipboardHelper(QObject *parent=nullptr);
QList<QAction*> actions();
private slots:
void actionTriggered();
void updateCutCopyStatus(QWidget *old, QWidget *now);
void focusWidgetSelectionChanged(const QItemSelection &selected,
const QItemSelection &deselected);
void focusWidgetModelReset();
void updatePasteStatus();
private:
QAction *actionCut_{nullptr};
QAction *actionCopy_{nullptr};
QAction *actionPaste_{nullptr};
};
#endif

View File

@ -209,7 +209,7 @@ void DeviceGroupDialog::updateTotalDeviceCount()
void DeviceGroupDialog::updateIp4Gateway() void DeviceGroupDialog::updateIp4Gateway()
{ {
quint32 net = ip4Address->value() & (~0UL << (32 - ip4PrefixLength->value())); quint32 net = ip4Address->value() & (~0 << (32 - ip4PrefixLength->value()));
ip4Gateway->setValue(net | 0x01); ip4Gateway->setValue(net | 0x01);
} }

View File

@ -25,10 +25,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "uint128.h" #include "uint128.h"
#include <QHostAddress> #include <QHostAddress>
#include <QMimeData>
const QLatin1String kDeviceGroupsMimeType(
"application/vnd.ostinato.devicegroups");
enum { enum {
kName, kName,
@ -209,85 +205,6 @@ bool DeviceGroupModel::setData(
return false; return false;
} }
QStringList DeviceGroupModel::mimeTypes() const
{
return QStringList() << kDeviceGroupsMimeType;
}
QMimeData* DeviceGroupModel::mimeData(const QModelIndexList &indexes) const
{
using ::google::protobuf::uint8;
if (indexes.isEmpty())
return nullptr;
// indexes may include multiple columns for a row - but we are only
// interested in rows 'coz we have a single data for all columns
// XXX: use QMap instead of QSet to keep rows in sorted order
QMap<int, int> rows;
foreach(QModelIndex index, indexes)
rows.insert(index.row(), index.row());
OstProto::DeviceGroupConfigList dgList;
dgList.mutable_port_id()->set_id(port_->id());
foreach(int row, rows) {
OstProto::DeviceGroup *devGrp = dgList.add_device_group();
devGrp->CopyFrom(*port_->deviceGroupByIndex(row));
}
QByteArray data;
data.resize(dgList.ByteSize());
dgList.SerializeWithCachedSizesToArray((uint8*)data.data());
//qDebug("copy %s", dgList.DebugString().c_str());
//TODO: copy DebugString as text/plain?
QMimeData *mimeData = new QMimeData();
mimeData->setData(kDeviceGroupsMimeType, data);
return mimeData; // XXX: caller is expected to take ownership and free!
}
bool DeviceGroupModel::dropMimeData(const QMimeData *data,
Qt::DropAction action, int row, int /*column*/,
const QModelIndex &parent)
{
if (!data)
return false;
if (!data->hasFormat(kDeviceGroupsMimeType))
return false;
if (action != Qt::CopyAction)
return false;
OstProto::DeviceGroupConfigList dgList;
QByteArray ba(data->data(kDeviceGroupsMimeType));
dgList.ParseFromArray((void*)ba.constData(), ba.size());
//qDebug("paste %s", dgList.DebugString().c_str());
if ((row < 0) || (row > rowCount(parent)))
row = rowCount(parent);
// Delete rows that we are going to overwrite
int c = 0, count = dgList.device_group_size();
if (row < rowCount(parent))
removeRows(row, qMin(rowCount() - row, count));
beginInsertRows(parent, row, row+count-1);
for (int i = 0; i < count; i++) {
if (port_->newDeviceGroupAt(row+i, &dgList.device_group(i)))
c++;
}
endInsertRows();
if (c != count) {
qWarning("failed to copy rows in DeviceGroupModel at row %d; "
"requested = %d, actual = %d", row, count, c);
return false;
}
return true;
}
bool DeviceGroupModel::insertRows( bool DeviceGroupModel::insertRows(
int row, int row,
int count, int count,

View File

@ -42,12 +42,6 @@ public:
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole); int role = Qt::EditRole);
QStringList mimeTypes() const;
QMimeData* mimeData(const QModelIndexList &indexes) const;
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent);
bool insertRows (int row, int count, bool insertRows (int row, int count,
const QModelIndex &parent = QModelIndex()); const QModelIndex &parent = QModelIndex());
bool removeRows (int row, int count, bool removeRows (int row, int count,

View File

@ -242,11 +242,6 @@ QVariant DeviceModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
Qt::DropActions DeviceModel::supportedDropActions() const
{
return Qt::IgnoreAction; // read-only model, doesn't accept any data
}
void DeviceModel::setPort(Port *port) void DeviceModel::setPort(Port *port)
{ {
beginResetModel(); beginResetModel();

View File

@ -39,8 +39,6 @@ public:
int role = Qt::DisplayRole) const; int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
Qt::DropActions supportedDropActions() const;
void setPort(Port *port); void setPort(Port *port);
QAbstractItemModel* detailModel(const QModelIndex &index); QAbstractItemModel* detailModel(const QModelIndex &index);

View File

@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "deviceswidget.h" #include "deviceswidget.h"
#include "clipboardhelper.h"
#include "devicegroupdialog.h" #include "devicegroupdialog.h"
#include "port.h" #include "port.h"
#include "portgrouplist.h" #include "portgrouplist.h"
@ -27,8 +26,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <QHeaderView> #include <QHeaderView>
#include <QKeyEvent> #include <QKeyEvent>
extern ClipboardHelper *clipboardHelper;
DevicesWidget::DevicesWidget(QWidget *parent) DevicesWidget::DevicesWidget(QWidget *parent)
: QWidget(parent), portGroups_(NULL) : QWidget(parent), portGroups_(NULL)
{ {
@ -52,14 +49,6 @@ DevicesWidget::DevicesWidget(QWidget *parent)
// DevicesWidget's actions is an aggegate of all sub-widget's actions // DevicesWidget's actions is an aggegate of all sub-widget's actions
addActions(deviceGroupList->actions()); addActions(deviceGroupList->actions());
// Add the clipboard actions to the context menu of deviceGroupList
// but not to DeviceWidget's actions since they are already available
// in the global Edit Menu
QAction *sep = new QAction("Clipboard", this);
sep->setSeparator(true);
deviceGroupList->addAction(sep);
deviceGroupList->addActions(clipboardHelper->actions());
} }
void DevicesWidget::setPortGroupList(PortGroupList *portGroups) void DevicesWidget::setPortGroupList(PortGroupList *portGroups)

View File

@ -158,8 +158,8 @@ To emulate a device, click on Configuration and create a device group</string>
<property name="whatsThis"> <property name="whatsThis">
<string>IP neighbor cache is empty</string> <string>IP neighbor cache is empty</string>
</property> </property>
<property name="selectionBehavior"> <property name="selectionMode">
<enum>QAbstractItemView::SelectRows</enum> <enum>QAbstractItemView::SingleSelection</enum>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -262,10 +262,8 @@ void DumpView::paintEvent(QPaintEvent* /*event*/)
// FIXME(LOW): unable to set the self widget's font in constructor // FIXME(LOW): unable to set the self widget's font in constructor
painter.setFont(QFont("Courier")); painter.setFont(QFont("Courier"));
// Qt automatically clears the background before we are called // set a white background
// QWidget::paintEvent doc: painter.fillRect(rect(), QBrush(QColor(Qt::white)));
// When the paint event occurs, the update region has normally
// been erased, so you are painting on the widget's background.
if (model()) if (model())
{ {

View File

@ -1,98 +0,0 @@
/*
Copyright (C) 2021 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "fieldedit.h"
FieldEdit::FieldEdit(QWidget *parent)
: QLineEdit(parent)
{
setType(kUInt64);
}
void FieldEdit::setType(FieldType type)
{
// clear existing contents before changing the validator
clear();
setPlaceholderText("");
type_ = type;
switch (type_) {
case kUInt64:
setValidator(&uint64Validator_);
if (isMaskMode_)
setText("0xFFFFFFFFFFFFFFFF");
break;
case kMacAddress:
setValidator(&macValidator_);
setPlaceholderText("00:00:00:00:00:00");
if (isMaskMode_)
setText("FF:FF:FF:FF:FF:FF");
break;
case kIp4Address:
setValidator(&ip4Validator_);
setPlaceholderText("0.0.0.0");
if (isMaskMode_)
setText("255.255.255.255");
break;
case kIp6Address:
setValidator(&ip6Validator_);
setPlaceholderText("::");
if (isMaskMode_)
setText("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF");
break;
default:
setValidator(nullptr);
break;
}
}
// Applicable only if type is kUInt64
void FieldEdit::setRange(quint64 min, quint64 max)
{
uint64Validator_.setRange(min, max);
if (type_ == kUInt64) {
setPlaceholderText(QString("%1 - %2").arg(min).arg(max));
if (isMaskMode_)
setText(QString::number(max, 16).toUpper().prepend("0x"));
}
}
void FieldEdit::setMaskMode(bool maskMode)
{
isMaskMode_ = maskMode;
}
QString FieldEdit::text() const
{
QString str = QLineEdit::text();
switch (type_) {
case kMacAddress:
str.remove(QRegularExpression("[:-]"));
str.prepend("0x");
break;
case kIp4Address:
str = QString::number(QHostAddress(str).toIPv4Address());
break;
default:
break;
}
return str;
}

View File

@ -1,60 +0,0 @@
/*
Copyright (C) 2021 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _FIELD_EDIT_H
#define _FIELD_EDIT_H
#include "ipv4addressvalidator.h"
#include "ipv6addressvalidator.h"
#include "macaddressvalidator.h"
#include "ulonglongvalidator.h"
#include <QLineEdit>
class FieldEdit: public QLineEdit
{
Q_OBJECT
public:
enum FieldType {
kUInt64,
kMacAddress,
kIp4Address,
kIp6Address
};
FieldEdit(QWidget *parent = 0);
void setType(FieldType type);
void setRange(quint64 min, quint64 max);
void setMaskMode(bool maskMode);
QString text() const;
private:
FieldType type_{kUInt64};
bool isMaskMode_{false};
IPv4AddressValidator ip4Validator_;
IPv6AddressValidator ip6Validator_;
MacAddressValidator macValidator_;
ULongLongValidator uint64Validator_;
};
#endif

View File

@ -1,230 +0,0 @@
/*
Copyright (C) 2021 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "findreplace.h"
#include "abstractprotocol.h"
#include "iputils.h"
#include "mandatoryfieldsgroup.h"
#include "protocolmanager.h"
#include "stream.h"
#include "uint128.h"
#include <QPushButton>
extern ProtocolManager *OstProtocolManager;
// TODO: It might be useful for this dialog to support a "preview"
// of the replacements
FindReplaceDialog::FindReplaceDialog(Action *action, QWidget *parent)
: QDialog(parent), action_(action)
{
setupUi(this);
findMask->setMaskMode(true);
replaceMask->setMaskMode(true);
// Keep things simple and don't use mask(s) (default)
useFindMask->setChecked(false);
useReplaceMask->setChecked(false);
// TODO: remove combo protocols - see note in StreamBase::findReplace
QStringList protocolList = OstProtocolManager->protocolDatabase();
protocolList.sort();
protocol->addItems(protocolList);
// Enable this setting if we have streams selected on input
selectedStreamsOnly->setEnabled(action->selectedStreamsOnly);
// Reset for user input
action->selectedStreamsOnly = false;
QPushButton *ok = buttonBox->button(QDialogButtonBox::Ok);
ok->setText(tr("Replace All"));
ok->setDisabled(true);
mandatoryFields_ = new MandatoryFieldsGroup(this);
mandatoryFields_->add(protocol);
mandatoryFields_->add(field);
mandatoryFields_->add(findValue);
mandatoryFields_->add(findMask);
mandatoryFields_->add(replaceValue);
mandatoryFields_->add(replaceMask);
mandatoryFields_->setSubmitButton(ok);
}
FindReplaceDialog::~FindReplaceDialog()
{
delete mandatoryFields_;
}
void FindReplaceDialog::on_protocol_currentIndexChanged(const QString &name)
{
field->clear();
fieldAttrib_.clear();
Stream stream;
AbstractProtocol *protocol = OstProtocolManager->createProtocol(
name, &stream);
int count = protocol->fieldCount();
for (int i = 0; i < count; i++) {
// XXX: It might be useful to support meta fields too, later!
if (!protocol->fieldFlags(i).testFlag(AbstractProtocol::FrameField))
continue;
int bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize)
.toInt();
if (bitSize <= 0) // skip optional fields
continue;
FieldAttrib fieldAttrib;
fieldAttrib.index = i; // fieldIndex
fieldAttrib.bitSize = bitSize;
// field and fieldAttrib_ have same count and order of fields
fieldAttrib_.append(fieldAttrib);
field->addItem(protocol->fieldData(i, AbstractProtocol::FieldName)
.toString());
}
protocolId_ = protocol->protocolNumber();
delete protocol;
}
void FindReplaceDialog::on_field_currentIndexChanged(int index)
{
if (index < 0)
return;
QString fieldName = field->currentText();
FieldAttrib fieldAttrib = fieldAttrib_.at(index);
// Use heuristics to determine field type
if (fieldAttrib.bitSize == 48) {
findMask->setType(FieldEdit::kMacAddress);
findValue->setType(FieldEdit::kMacAddress);
replaceMask->setType(FieldEdit::kMacAddress);
replaceValue->setType(FieldEdit::kMacAddress);
} else if ((fieldAttrib.bitSize == 32)
&& (fieldName.contains(QRegularExpression(
"address|source|destination",
QRegularExpression::CaseInsensitiveOption)))) {
findMask->setType(FieldEdit::kIp4Address);
findValue->setType(FieldEdit::kIp4Address);
replaceMask->setType(FieldEdit::kIp4Address);
replaceValue->setType(FieldEdit::kIp4Address);
} else if ((fieldAttrib.bitSize == 128)
&& (fieldName.contains(QRegularExpression(
"address|source|destination",
QRegularExpression::CaseInsensitiveOption)))) {
findMask->setType(FieldEdit::kIp6Address);
findValue->setType(FieldEdit::kIp6Address);
replaceMask->setType(FieldEdit::kIp6Address);
replaceValue->setType(FieldEdit::kIp6Address);
} else {
quint64 max = quint64(~0) >> (64-fieldAttrib.bitSize);
qDebug("XXXXXX %s bitSize %d max %llx",
qPrintable(field->currentText()),
fieldAttrib.bitSize, max);
findMask->setType(FieldEdit::kUInt64);
findMask->setRange(0, max);
findValue->setType(FieldEdit::kUInt64);
findValue->setRange(0, max);
replaceMask->setType(FieldEdit::kUInt64);
replaceMask->setRange(0, max);
replaceValue->setType(FieldEdit::kUInt64);
replaceValue->setRange(0, max);
}
}
void FindReplaceDialog::on_matchAny_toggled(bool checked)
{
if (checked) {
findValueLabel->setHidden(true);
findValue->setHidden(true);
useFindMask->setHidden(true);
findMask->setHidden(true);
findMaskHint->setHidden(true);
} else {
findValueLabel->setVisible(true);
findValue->setVisible(true);
useFindMask->setVisible(true);
if (useFindMask->isChecked()) {
findMask->setVisible(true);
findMaskHint->setVisible(true);
}
}
}
void FindReplaceDialog::on_buttonBox_accepted()
{
FieldAttrib fieldAttrib = fieldAttrib_.at(field->currentIndex());
action_->protocolField = QString("%1 %2")
.arg(protocol->currentText())
.arg(field->currentText());
action_->protocolNumber = protocolId_;
action_->fieldIndex = fieldAttrib.index;
action_->fieldBitSize = fieldAttrib.bitSize;
if (fieldAttrib.bitSize == 128) { // IPv6 address
if (matchAny->isChecked()) {
action_->findMask.setValue(UInt128(0));
action_->findValue.setValue(UInt128(0));
} else {
action_->findMask.setValue(
useFindMask->isChecked() ?
ipUtils::ip6StringToUInt128(findMask->text()) :
~UInt128(0));
action_->findValue.setValue(
ipUtils::ip6StringToUInt128(findValue->text()));
}
action_->replaceMask.setValue(
useReplaceMask->isChecked() ?
ipUtils::ip6StringToUInt128(replaceMask->text()) :
~UInt128(0));
action_->replaceValue.setValue(
ipUtils::ip6StringToUInt128(replaceValue->text()));
} else { // everything else
if (matchAny->isChecked()) {
action_->findMask.setValue(0);
action_->findValue.setValue(0);
} else {
action_->findMask.setValue(
useFindMask->isChecked() ?
findMask->text().toULongLong(nullptr, 0) :
~quint64(0));
action_->findValue.setValue(
findValue->text().toULongLong(nullptr, 0));
}
action_->replaceMask.setValue(
useReplaceMask->isChecked() ?
replaceMask->text().toULongLong(nullptr, 0) :
~quint64(0));
action_->replaceValue.setValue(QString::number(
replaceValue->text().toULongLong(nullptr, 0)));
}
action_->selectedStreamsOnly = selectedStreamsOnly->isChecked();
}

View File

@ -1,71 +0,0 @@
/*
Copyright (C) 2021 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _FIND_REPLACE_H
#define _FIND_REPLACE_H
#include "ui_findreplace.h"
class MandatoryFieldsGroup;
class FindReplaceDialog: public QDialog, public Ui::FindReplace
{
Q_OBJECT
public:
struct Action;
FindReplaceDialog(Action *action, QWidget *parent = 0);
~FindReplaceDialog();
private slots:
void on_protocol_currentIndexChanged(const QString &name);
void on_field_currentIndexChanged(int index);
void on_matchAny_toggled(bool checked);
void on_buttonBox_accepted();
private:
struct FieldAttrib;
quint32 protocolId_{0};
Action *action_{nullptr};
QList<FieldAttrib> fieldAttrib_;
MandatoryFieldsGroup *mandatoryFields_{nullptr};
};
struct FindReplaceDialog::Action
{
QString protocolField;
quint32 protocolNumber;
quint32 fieldIndex;
int fieldBitSize;
QVariant findValue;
QVariant findMask;
QVariant replaceValue;
QVariant replaceMask;
bool selectedStreamsOnly; // in-out param
};
struct FindReplaceDialog::FieldAttrib
{
quint32 index;
int bitSize;
};
#endif

View File

@ -1,265 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FindReplace</class>
<widget class="QDialog" name="FindReplace">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>361</width>
<height>309</height>
</rect>
</property>
<property name="windowTitle">
<string>Find &amp; Replace</string>
</property>
<property name="windowIcon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/find.png</normaloff>:/icons/find.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="find">
<property name="title">
<string>Find</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0">
<item row="0" column="0">
<widget class="QLabel" name="protocolLabel">
<property name="text">
<string>Protocol</string>
</property>
<property name="buddy">
<cstring>protocol</cstring>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="protocol"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="fieldLabel">
<property name="text">
<string>Field</string>
</property>
<property name="buddy">
<cstring>field</cstring>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="field"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="findValueLabel">
<property name="text">
<string>Value</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="FieldEdit" name="findValue"/>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="useFindMask">
<property name="text">
<string>Mask</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="FieldEdit" name="findMask"/>
</item>
<item row="3" column="2">
<widget class="QLabel" name="findMaskHint">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Matches a field only if &lt;span style=&quot;white-space:nowrap&quot;&gt;(FieldValue &amp;amp; FindMask) = FindValue&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="pixmap">
<pixmap resource="ostinato.qrc">:/icons/info.png</pixmap>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QCheckBox" name="matchAny">
<property name="text">
<string>Match any value</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="replace">
<property name="title">
<string>Replace with</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1,0">
<item row="0" column="0">
<widget class="QLabel" name="replaceValueLabel">
<property name="text">
<string>Value</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="FieldEdit" name="replaceValue"/>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="useReplaceMask">
<property name="text">
<string>Mask</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="FieldEdit" name="replaceMask"/>
</item>
<item row="1" column="2">
<widget class="QLabel" name="replaceMaskHint">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;New field value = &lt;span style=&quot;white-space:nowrap&quot;&gt;(OldFieldValue &amp;amp; ~ReplaceMask) | (ReplaceValue &amp;amp; ReplaceMask)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="pixmap">
<pixmap resource="ostinato.qrc">:/icons/info.png</pixmap>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="selectedStreamsOnly">
<property name="text">
<string>Selected Streams Only</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FieldEdit</class>
<extends>QLineEdit</extends>
<header>fieldedit.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="ostinato.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>FindReplace</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>277</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>FindReplace</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>283</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>useFindMask</sender>
<signal>toggled(bool)</signal>
<receiver>findMask</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>60</x>
<y>115</y>
</hint>
<hint type="destinationlabel">
<x>76</x>
<y>119</y>
</hint>
</hints>
</connection>
<connection>
<sender>useReplaceMask</sender>
<signal>toggled(bool)</signal>
<receiver>replaceMask</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>56</x>
<y>228</y>
</hint>
<hint type="destinationlabel">
<x>73</x>
<y>227</y>
</hint>
</hints>
</connection>
<connection>
<sender>useReplaceMask</sender>
<signal>toggled(bool)</signal>
<receiver>replaceMaskHint</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>50</x>
<y>230</y>
</hint>
<hint type="destinationlabel">
<x>333</x>
<y>233</y>
</hint>
</hints>
</connection>
<connection>
<sender>useFindMask</sender>
<signal>toggled(bool)</signal>
<receiver>findMaskHint</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>46</x>
<y>116</y>
</hint>
<hint type="destinationlabel">
<x>335</x>
<y>122</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -31,13 +31,9 @@ class IconOnlyDelegate : public QStyledItemDelegate
{ {
QStyleOptionViewItem opt = option; QStyleOptionViewItem opt = option;
opt.decorationPosition = QStyleOptionViewItem::Top; opt.decorationPosition = QStyleOptionViewItem::Top;
opt.features &= ~QStyleOptionViewItem::HasDisplay;
QStyledItemDelegate::paint(painter, opt, index); QStyledItemDelegate::paint(painter, opt, index);
} }
QString displayText(const QVariant&, const QLocale&) const
{
return QString();
}
}; };
#endif #endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 648 B

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="31.44mm" height="31.44mm" version="1.1" viewBox="0 0 31.44 31.44" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="translate(-37.525 -210.5)">
<circle cx="53.245" cy="226.22" r="15.72" fill="#e96c59" opacity=".8"/>
<path d="m53.419 212.28c-2.0736-0.0217-4.1616 0.41684-6.0884 1.3285-4.4585 2.1095-7.4154 6.2656-7.9501 11.175-0.50808 4.6643 1.5269 9.4967 5.2044 12.359 2.066 1.6079 4.6873 2.6928 7.1183 2.9462 2.643 0.2755 5.2282-0.17492 7.6011-1.3243 1.9456-0.94242 3.6302-2.2856 4.9044-3.9102 3.2672-4.1658 3.8994-9.8451 1.6275-14.62-1.0934-2.298-2.6916-4.1431-4.8639-5.6154-2.245-1.5216-4.8872-2.3101-7.5533-2.338zm-0.0276 2.2048c2.2445 0.0235 4.4688 0.68733 6.3588 1.9683 1.8288 1.2395 3.1743 2.7928 4.0948 4.7274 1.9126 4.0198 1.3803 8.8011-1.3702 12.308-1.0727 1.3677-2.4908 2.4985-4.1288 3.2919-1.9976 0.96764-4.1741 1.3468-6.3992 1.1148-2.0466-0.21332-4.2533-1.1267-5.9926-2.4803-3.096-2.4094-4.8092-6.4777-4.3814-10.404 0.45017-4.1327 2.9394-7.6316 6.6928-9.4075 1.6221-0.76747 3.38-1.1367 5.1257-1.1184z" fill="#f9bbab" opacity=".8"/>
<g fill="#fff">
<rect x="50.933" y="217.52" width="4.6236" height="12.862" opacity=".8"/>
<rect transform="scale(1,-1)" x="50.933" y="-234.92" width="4.6236" height="3.5749" opacity=".8"/>
<rect x="50.933" y="217.52" width="4.6236" height="12.862" opacity=".8"/>
<rect transform="scale(1,-1)" x="50.933" y="-234.92" width="4.6236" height="3.5749" opacity=".8"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="221.25mm" height="193.24mm" version="1.1" viewBox="0 0 221.25 193.24" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="linearGradient879" x1="193.48" x2="101.09" y1="198.47" y2="254.08" gradientTransform="matrix(1.7412 0 0 1.7412 -267.69 -212.02)" gradientUnits="userSpaceOnUse">
<stop stop-color="#c16602" stop-opacity=".99608" offset="0"/>
<stop stop-color="#e8b839" offset="1"/>
</linearGradient>
</defs>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="translate(97.776 -43.224)">
<path d="m12.851 49.318 104.53 181.06h-209.07z" fill="#fff" opacity=".8" stroke="url(#linearGradient879)" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".99216" stroke-width="12.188"/>
<path d="m12.851 101.1 62.098 107.56h-124.2z" fill="#f5db61" opacity=".8"/>
<rect x=".23388" y="113.27" width="25.235" height="54.405" fill="#c28832" opacity=".8"/>
<rect transform="scale(1,-1)" x=".23388" y="-196.48" width="25.235" height="14.793" fill="#c28832" opacity=".8"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -108,11 +108,6 @@ QVariant LogsModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
Qt::DropActions LogsModel::supportedDropActions() const
{
return Qt::IgnoreAction; // read-only model, doesn't accept any data
}
// --------------------------------------------- // // --------------------------------------------- //
// Slots // Slots
// --------------------------------------------- // // --------------------------------------------- //

View File

@ -44,8 +44,6 @@ public:
int role = Qt::DisplayRole) const; int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
Qt::DropActions supportedDropActions() const;
public slots: public slots:
void clear(); void clear();
void setLogLevel(int level); void setLogLevel(int level);

View File

@ -26,7 +26,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <QHeaderView> #include <QHeaderView>
#include <QMainWindow> #include <QMainWindow>
#include <QMovie> #include <QMovie>
#include <QPropertyAnimation>
extern QMainWindow *mainWindow; extern QMainWindow *mainWindow;
@ -50,14 +49,6 @@ LogsWindow::LogsWindow(LogsModel *model, QWidget *parent)
warnAnime_ = new QMovie(":/icons/anime_warn.gif", QByteArray(), this); warnAnime_ = new QMovie(":/icons/anime_warn.gif", QByteArray(), this);
errorAnime_ = new QMovie(":/icons/anime_error.gif", QByteArray(), this); errorAnime_ = new QMovie(":/icons/anime_error.gif", QByteArray(), this);
alert_ = new QLabel("ALERT!", this,
Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
alert_->setScaledContents(true);
alert_->hide();
alertAnime_ = new QPropertyAnimation(alert_, "geometry", this);
alertAnime_->setDuration(2000);
alertAnime_->setEasingCurve(QEasingCurve::InOutExpo);
connect(level, SIGNAL(currentIndexChanged(int)), connect(level, SIGNAL(currentIndexChanged(int)),
model, SLOT(setLogLevel(int))); model, SLOT(setLogLevel(int)));
connect(clear, SIGNAL(clicked()), model, SLOT(clear())); connect(clear, SIGNAL(clicked()), model, SLOT(clear()));
@ -84,17 +75,12 @@ LogsWindow::~LogsWindow()
delete logsModelTest_; delete logsModelTest_;
} }
void LogsWindow::clearCurrentSelection()
{
logs->selectionModel()->clearCurrentIndex();
logs->clearSelection();
}
void LogsWindow::when_visibilityChanged(bool visible) void LogsWindow::when_visibilityChanged(bool visible)
{ {
if (visible) { if (visible) {
logs->resizeRowsToContents(); logs->resizeRowsToContents();
setState(kInfo); state_ = kInfo;
notify();
} }
isVisible_ = visible; isVisible_ = visible;
@ -104,26 +90,29 @@ void LogsWindow::when_visibilityChanged(bool visible)
void LogsWindow::when_rowsInserted(const QModelIndex &parent, void LogsWindow::when_rowsInserted(const QModelIndex &parent,
int first, int last) int first, int last)
{ {
if (isVisible_)
logs->resizeRowsToContents();
State incrementalState = kInfo; if (isVisible_) {
logs->resizeRowsToContents();
return;
}
if (state_ == kError)
return;
for (int i = first; i <= last; i++) { for (int i = first; i <= last; i++) {
// FIXME: use a user-role instead, so we don't need to know column and // FIXME: use a user-role instead, so we don't need to know column and
// have to compare strings? // have to compare strings?
QString level = logs->model()->data(logs->model()->index(i, 1, parent)) QString level = logs->model()->data(logs->model()->index(i, 1, parent))
.toString(); .toString();
if (level == "Error") { if (level == "Error") {
incrementalState = kError; state_ = kError;
break; // Highest level - no need to look further break; // Highest level - no need to look further
} }
else if (level == "Warning") { else if (level == "Warning") {
incrementalState = kWarning; state_ = kWarning;
} }
} }
alert(incrementalState); notify();
if (incrementalState > state())
setState(incrementalState);
} }
void LogsWindow::on_autoScroll_toggled(bool checked) void LogsWindow::on_autoScroll_toggled(bool checked)
@ -140,19 +129,6 @@ void LogsWindow::on_autoScroll_toggled(bool checked)
} }
} }
LogsWindow::State LogsWindow::state()
{
return state_;
}
void LogsWindow::setState(State state)
{
if (isVisible_)
return;
state_ = state;
notify();
}
QLabel* LogsWindow::tabIcon() QLabel* LogsWindow::tabIcon()
{ {
QList<QTabBar*> tabBars = mainWindow->findChildren<QTabBar*>(); QList<QTabBar*> tabBars = mainWindow->findChildren<QTabBar*>();
@ -173,57 +149,6 @@ QLabel* LogsWindow::tabIcon()
return nullptr; return nullptr;
} }
//! Popup and animate a big icon
void LogsWindow::alert(State state)
{
if (state == kInfo)
return;
// start - center of main window
QRect start;
QWidget *view = mainWindow;
if (!view)
return;
alert_->setParent(view);
alert_->raise();
start.setSize(QSize(256, 256).scaled(view->size()/2, Qt::KeepAspectRatio));
start.moveCenter(QPoint(view->size().width()/2,
view->size().height()/2));
// end - center of logs window if visible, tab icon otherwise
QPoint c;
QLabel *icon = tabIcon();
view = isVisible_ ? dynamic_cast<QWidget*>(this) : mainWindow;
if (icon && !isVisible_) {
c = icon->geometry().center(); // in icon's parent (tabBar) coords
c = icon->mapFromParent(c); // in icon's own coords
c = icon->mapTo(view, c); // in mainWindow's coords
} else {
c = view->geometry().center();
c = view->mapTo(mainWindow, c); // in mainWindow's coords
}
QRect end;
end.moveCenter(c);
switch (state) {
case kError:
alert_->setPixmap(QPixmap(":/icons/error.svg"));
break;
case kWarning:
alert_->setPixmap(QPixmap(":/icons/warn.svg"));
break;
default:
Q_UNREACHABLE();
break;
}
alertAnime_->setStartValue(start);
alertAnime_->setEndValue(end);
alert_->show(); // ensure it's visible before starting animation
alertAnime_->start();
}
//! Show tab icon
void LogsWindow::notify() void LogsWindow::notify()
{ {
QString annotation; QString annotation;
@ -233,7 +158,7 @@ void LogsWindow::notify()
warnAnime_->stop(); warnAnime_->stop();
errorAnime_->stop(); errorAnime_->stop();
switch (state()) { switch (state_) {
case kError: case kError:
anime = errorAnime_; anime = errorAnime_;
annotation = " - Error(s)"; annotation = " - Error(s)";

View File

@ -26,7 +26,6 @@ class LogsModel;
class QDockWidget; class QDockWidget;
class QShowEvent; class QShowEvent;
class QMovie; class QMovie;
class QPropertyAnimation;
class LogsWindow: public QWidget, private Ui::LogsWindow class LogsWindow: public QWidget, private Ui::LogsWindow
{ {
@ -35,9 +34,6 @@ public:
LogsWindow(LogsModel *model, QWidget *parent = 0); LogsWindow(LogsModel *model, QWidget *parent = 0);
~LogsWindow(); ~LogsWindow();
public slots:
void clearCurrentSelection();
private slots: private slots:
void when_visibilityChanged(bool visible); void when_visibilityChanged(bool visible);
void when_rowsInserted(const QModelIndex &parent, int first, int last); void when_rowsInserted(const QModelIndex &parent, int first, int last);
@ -47,24 +43,15 @@ private:
enum State {kInfo, kWarning, kError}; enum State {kInfo, kWarning, kError};
QLabel* tabIcon(); QLabel* tabIcon();
State state();
void setState(State state);
void alert(State state);
void notify(); void notify();
State state_{kInfo}; State state_{kInfo};
QDockWidget *parentDock_; QDockWidget *parentDock_;
QMovie *warnAnime_{nullptr}; QMovie *warnAnime_{nullptr};
QMovie *errorAnime_{nullptr}; QMovie *errorAnime_{nullptr};
QLabel *alert_{nullptr};
QPropertyAnimation *alertAnime_{nullptr};
QString windowTitle_; QString windowTitle_;
bool isVisible_{false}; // see XXX below bool isVisible_{false};
QObject *logsModelTest_; QObject *logsModelTest_;
// XXX: We cannot use isVisible() instead of isVisible_ since
// LogsWindow::isVisible() returns true even when the parent LogsDock
// is tabified but not the selected tab
}; };
#endif #endif

View File

@ -24,7 +24,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "params.h" #include "params.h"
#include "preferences.h" #include "preferences.h"
#include "settings.h" #include "settings.h"
#include "thememanager.h"
#include <QApplication> #include <QApplication>
#include <QDateTime> #include <QDateTime>
@ -43,9 +42,6 @@ Params appParams;
QSettings *appSettings; QSettings *appSettings;
QMainWindow *mainWindow; QMainWindow *mainWindow;
void NoMsgHandler(QtMsgType type, const QMessageLogContext &context,
const QString &msg);
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
QApplication app(argc, argv); QApplication app(argc, argv);
@ -58,16 +54,9 @@ int main(int argc, char* argv[])
appParams.parseCommandLine(argc, argv); appParams.parseCommandLine(argc, argv);
#ifndef QT_DEBUG // Release mode
if (appParams.optLogsDisabled())
qInstallMessageHandler(NoMsgHandler);
#endif
OstProtocolManager = new ProtocolManager(); OstProtocolManager = new ProtocolManager();
OstProtocolWidgetFactory = new ProtocolWidgetFactory(); OstProtocolWidgetFactory = new ProtocolWidgetFactory();
Preferences::initDefaults();
/* (Portable Mode) If we have a .ini file in the same directory as the /* (Portable Mode) If we have a .ini file in the same directory as the
executable, we use that instead of the platform specific location executable, we use that instead of the platform specific location
and format for the settings */ and format for the settings */
@ -77,7 +66,6 @@ int main(int argc, char* argv[])
appSettings = new QSettings(portableIni, QSettings::IniFormat); appSettings = new QSettings(portableIni, QSettings::IniFormat);
else else
appSettings = new QSettings(); appSettings = new QSettings();
qDebug("Settings: %s", qPrintable(appSettings->fileName()));
OstProtoLib::setExternalApplicationPaths( OstProtoLib::setExternalApplicationPaths(
appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(),
@ -85,8 +73,7 @@ int main(int argc, char* argv[])
appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(),
appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString());
ThemeManager::instance()->setTheme(appSettings->value(kThemeKey).toString()); Preferences::initDefaults();
qsrand(QDateTime::currentDateTime().toTime_t()); qsrand(QDateTime::currentDateTime().toTime_t());
mainWindow = new MainWindow; mainWindow = new MainWindow;
@ -100,14 +87,3 @@ int main(int argc, char* argv[])
return exitCode; return exitCode;
} }
void NoMsgHandler(QtMsgType type, const QMessageLogContext &/*context*/,
const QString &msg)
{
if (type == QtFatalMsg) {
fprintf(stderr, "%s\n", qPrintable(msg));
fflush(stderr);
abort();
}
}

View File

@ -23,8 +23,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "dbgthread.h" #include "dbgthread.h"
#endif #endif
#include "clipboardhelper.h"
#include "fileformatoptions.h"
#include "jumpurl.h" #include "jumpurl.h"
#include "logsmodel.h" #include "logsmodel.h"
#include "logswindow.h" #include "logswindow.h"
@ -40,7 +38,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "fileformat.pb.h" #include "fileformat.pb.h"
#include <QDate>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDockWidget> #include <QDockWidget>
#include <QFileDialog> #include <QFileDialog>
@ -62,7 +59,6 @@ extern const char* revision;
PortGroupList *pgl; PortGroupList *pgl;
LogsModel *appLogs; LogsModel *appLogs;
ClipboardHelper *clipboardHelper;
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow (parent) : QMainWindow (parent)
@ -86,14 +82,8 @@ MainWindow::MainWindow(QWidget *parent)
localServer_ = new QProcess(this); localServer_ = new QProcess(this);
connect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)), connect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)),
SLOT(onLocalServerFinished(int, QProcess::ExitStatus))); SLOT(onLocalServerFinished(int, QProcess::ExitStatus)));
#if QT_VERSION >= 0x050600
connect(localServer_, SIGNAL(errorOccurred(QProcess::ProcessError)),
SLOT(onLocalServerError(QProcess::ProcessError)));
#else
connect(localServer_, SIGNAL(error(QProcess::ProcessError)), connect(localServer_, SIGNAL(error(QProcess::ProcessError)),
SLOT(onLocalServerError(QProcess::ProcessError))); SLOT(onLocalServerError(QProcess::ProcessError)));
#endif
localServer_->setProcessChannelMode(QProcess::ForwardedChannels); localServer_->setProcessChannelMode(QProcess::ForwardedChannels);
localServer_->start(serverApp, QStringList()); localServer_->start(serverApp, QStringList());
QTimer::singleShot(5000, this, SLOT(stopLocalServerMonitor())); QTimer::singleShot(5000, this, SLOT(stopLocalServerMonitor()));
@ -103,7 +93,6 @@ MainWindow::MainWindow(QWidget *parent)
pgl = new PortGroupList; pgl = new PortGroupList;
appLogs = new LogsModel(this); appLogs = new LogsModel(this);
clipboardHelper = new ClipboardHelper(this);
portsWindow = new PortsWindow(pgl, this); portsWindow = new PortsWindow(pgl, this);
statsWindow = new PortStatsWindow(pgl, this); statsWindow = new PortStatsWindow(pgl, this);
@ -126,11 +115,7 @@ MainWindow::MainWindow(QWidget *parent)
setupUi(this); setupUi(this);
menuFile->insertActions(menuFile->actions().at(3), menuFile->insertActions(menuFile->actions().at(3), portsWindow->actions());
portsWindow->portActions());
menuEdit->addActions(clipboardHelper->actions());
menuStreams->addActions(portsWindow->streamActions());
menuDevices->addActions(portsWindow->deviceActions());
statsDock->setWidget(statsWindow); statsDock->setWidget(statsWindow);
addDockWidget(Qt::BottomDockWidgetArea, statsDock); addDockWidget(Qt::BottomDockWidgetArea, statsDock);
@ -143,13 +128,6 @@ MainWindow::MainWindow(QWidget *parent)
portsDock->setWidget(portsWindow); portsDock->setWidget(portsWindow);
addDockWidget(Qt::TopDockWidgetArea, portsDock); addDockWidget(Qt::TopDockWidgetArea, portsDock);
#if QT_VERSION >= 0x050600
// Set top and bottom docks to equal height
resizeDocks({portsDock, statsDock}, {height()/2, height()/2}, Qt::Vertical);
#endif
portsWindow->setFocus();
// Save the default window geometry and layout ... // Save the default window geometry and layout ...
defaultGeometry_ = geometry(); defaultGeometry_ = geometry();
defaultLayout_ = saveState(0); defaultLayout_ = saveState(0);
@ -175,17 +153,10 @@ MainWindow::MainWindow(QWidget *parent)
this, SLOT(onNewVersion(QString))); this, SLOT(onNewVersion(QString)));
updater->checkForNewVersion(); updater->checkForNewVersion();
// TODO: If session file specified (and valid?), don't add local drone PG
// Add the "Local" Port Group
if (appParams.optLocalDrone()) {
PortGroup *pg = new PortGroup;
pgl->addPortGroup(*pg);
}
if (appParams.argumentCount()) { if (appParams.argumentCount()) {
QString fileName = appParams.argument(0); QString fileName = appParams.argument(0);
if (QFile::exists(fileName)) if (QFile::exists(fileName))
openSession(fileName); on_actionOpenSession_triggered(fileName);
else else
QMessageBox::information(NULL, qApp->applicationName(), QMessageBox::information(NULL, qApp->applicationName(),
QString("File not found: " + fileName)); QString("File not found: " + fileName));
@ -230,7 +201,7 @@ MainWindow::~MainWindow()
} }
} }
void MainWindow::openSession(QString fileName) void MainWindow::on_actionOpenSession_triggered(QString fileName)
{ {
qDebug("Open Session Action (%s)", qPrintable(fileName)); qDebug("Open Session Action (%s)", qPrintable(fileName));
@ -282,11 +253,6 @@ _exit:
return; return;
} }
void MainWindow::on_actionOpenSession_triggered()
{
openSession();
}
void MainWindow::on_actionSaveSession_triggered() void MainWindow::on_actionSaveSession_triggered()
{ {
qDebug("Save Session Action"); qDebug("Save Session Action");
@ -379,10 +345,6 @@ void MainWindow::on_actionViewRestoreDefaults_triggered()
statsDock->raise(); statsDock->raise();
actionViewShowMyReservedPortsOnly->setChecked(false); actionViewShowMyReservedPortsOnly->setChecked(false);
portsWindow->clearCurrentSelection();
statsWindow->clearCurrentSelection();
logsWindow_->clearCurrentSelection();
} }
void MainWindow::on_actionHelpOnline_triggered() void MainWindow::on_actionHelpOnline_triggered()
@ -395,14 +357,6 @@ void MainWindow::on_actionDonate_triggered()
QDesktopServices::openUrl(QUrl(jumpUrl("donate", "app", "menu"))); QDesktopServices::openUrl(QUrl(jumpUrl("donate", "app", "menu")));
} }
void MainWindow::on_actionCheckForUpdates_triggered()
{
Updater *updater = new Updater();
connect(updater, SIGNAL(latestVersion(QString)),
this, SLOT(onLatestVersion(QString)));
updater->checkForNewVersion();
}
void MainWindow::on_actionHelpAbout_triggered() void MainWindow::on_actionHelpAbout_triggered()
{ {
QDialog *aboutDialog = new QDialog; QDialog *aboutDialog = new QDialog;
@ -420,13 +374,8 @@ void MainWindow::on_actionHelpAbout_triggered()
void MainWindow::stopLocalServerMonitor() void MainWindow::stopLocalServerMonitor()
{ {
// We are only interested in startup errors // We are only interested in startup errors
#if QT_VERSION >= 0x050600
disconnect(localServer_, SIGNAL(errorOccurred(QProcess::ProcessError)),
this, SLOT(onLocalServerError(QProcess::ProcessError)));
#else
disconnect(localServer_, SIGNAL(error(QProcess::ProcessError)), disconnect(localServer_, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(onLocalServerError(QProcess::ProcessError))); this, SLOT(onLocalServerError(QProcess::ProcessError)));
#endif
disconnect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)), disconnect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(onLocalServerFinished(int, QProcess::ExitStatus))); this, SLOT(onLocalServerFinished(int, QProcess::ExitStatus)));
} }
@ -467,8 +416,8 @@ void MainWindow::reportLocalServerError()
if (localServer_->exitCode() == STATUS_DLL_NOT_FOUND) if (localServer_->exitCode() == STATUS_DLL_NOT_FOUND)
errorStr.append(tr("<p>This is most likely because Packet.dll " errorStr.append(tr("<p>This is most likely because Packet.dll "
"was not found - make sure you have " "was not found - make sure you have "
"<a href='%1'>npcap installed and accessible</a>." "<a href='%1'>WinPcap"
"</p>") "</a> installed.</p>")
.arg(jumpUrl("winpcap"))); .arg(jumpUrl("winpcap")));
#endif #endif
msgBox.setText(errorStr); msgBox.setText(errorStr);
@ -486,52 +435,12 @@ void MainWindow::reportLocalServerError()
void MainWindow::onNewVersion(QString newVersion) void MainWindow::onNewVersion(QString newVersion)
{ {
QDate today = QDate::currentDate(); QLabel *msg = new QLabel(tr("New Ostinato version %1 available. Visit "
QDate lastChecked = QDate::fromString( "<a href='%2'>ostinato.org</a> to download")
appSettings->value(kLastUpdateCheck).toString(),
Qt::ISODate);
if (lastChecked.daysTo(today) >= 5) {
QMessageBox::information(this, tr("Update check"),
tr("<p><b>Ostinato version %1 is now available</b> (you have %2). "
"See <a href='%3'>change log</a>.</p>"
"<p>Visit <a href='%4'>ostinato.org</a> to download.</p>")
.arg(newVersion)
.arg(version)
.arg(jumpUrl("changelog", "app", "status", "update"))
.arg(jumpUrl("download", "app", "status", "update")));
}
else {
QLabel *msg = new QLabel(tr("New Ostinato version %1 available. Visit "
"<a href='%2'>ostinato.org</a> to download")
.arg(newVersion) .arg(newVersion)
.arg(jumpUrl("download", "app", "status", "update"))); .arg(jumpUrl("download", "app", "status", "update")));
msg->setOpenExternalLinks(true); msg->setOpenExternalLinks(true);
statusBar()->addPermanentWidget(msg); statusBar()->addPermanentWidget(msg);
}
appSettings->setValue(kLastUpdateCheck, today.toString(Qt::ISODate));
sender()->deleteLater();
}
void MainWindow::onLatestVersion(QString latestVersion)
{
if (version != latestVersion) {
QMessageBox::information(this, tr("Update check"),
tr("<p><b>Ostinato version %1 is now available</b> (you have %2). "
"See <a href='%3'>change log</a>.</p>"
"<p>Visit <a href='%4'>ostinato.org</a> to download.</p>")
.arg(latestVersion)
.arg(version)
.arg(jumpUrl("changelog", "app", "status", "update"))
.arg(jumpUrl("download", "app", "status", "update")));
}
else {
QMessageBox::information(this, tr("Update check"),
tr("You are already running the latest Ostinato version - %1")
.arg(version));
}
sender()->deleteLater();
} }
//! Returns true on success (or user cancel) and false on failure //! Returns true on success (or user cancel) and false on failure
@ -548,7 +457,7 @@ bool MainWindow::openSession(QString fileName, QString &error)
goto _fail; goto _fail;
} }
if ((optDialog = FileFormatOptions::openOptionsDialog(fmt))) if ((optDialog = fmt->openOptionsDialog()))
{ {
int ret; int ret;
optDialog->setParent(this, Qt::Dialog); optDialog->setParent(this, Qt::Dialog);

View File

@ -36,7 +36,6 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
Q_OBJECT Q_OBJECT
private: private:
void openSession(QString fileName = QString());
bool openSession(QString fileName, QString &error); bool openSession(QString fileName, QString &error);
bool saveSession(QString fileName, QString fileType, QString &error); bool saveSession(QString fileName, QString fileType, QString &error);
@ -56,13 +55,12 @@ public:
~MainWindow(); ~MainWindow();
public slots: public slots:
void on_actionOpenSession_triggered(); void on_actionOpenSession_triggered(QString fileName = QString());
void on_actionSaveSession_triggered(); void on_actionSaveSession_triggered();
void on_actionPreferences_triggered(); void on_actionPreferences_triggered();
void on_actionViewRestoreDefaults_triggered(); void on_actionViewRestoreDefaults_triggered();
void on_actionHelpOnline_triggered(); void on_actionHelpOnline_triggered();
void on_actionDonate_triggered(); void on_actionDonate_triggered();
void on_actionCheckForUpdates_triggered();
void on_actionHelpAbout_triggered(); void on_actionHelpAbout_triggered();
private slots: private slots:
@ -71,7 +69,6 @@ private slots:
void onLocalServerError(QProcess::ProcessError error); void onLocalServerError(QProcess::ProcessError error);
void reportLocalServerError(); void reportLocalServerError();
void onNewVersion(QString version); void onNewVersion(QString version);
void onLatestVersion(QString version);
}; };
#endif #endif

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1024</width> <width>1024</width>
<height>700</height> <height>600</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle" >
@ -25,33 +25,9 @@
<addaction name="actionOpenSession" /> <addaction name="actionOpenSession" />
<addaction name="actionSaveSession" /> <addaction name="actionSaveSession" />
<addaction name="separator" /> <addaction name="separator" />
<addaction name="separator" />
<addaction name="actionPreferences" /> <addaction name="actionPreferences" />
<addaction name="actionFileExit" /> <addaction name="actionFileExit" />
</widget> </widget>
<widget class="QMenu" name="menuEdit" >
<property name="title" >
<string>&amp;Edit</string>
</property>
</widget>
<widget class="QMenu" name="menuView" >
<property name="title" >
<string>&amp;View</string>
</property>
<addaction name="actionViewShowMyReservedPortsOnly" />
<addaction name="actionViewRestoreDefaults" />
</widget>
<widget class="QMenu" name="menuStreams" >
<property name="title" >
<string>&amp;Streams</string>
</property>
<addaction name="actionTest" />
</widget>
<widget class="QMenu" name="menuDevices" >
<property name="title" >
<string>&amp;Devices</string>
</property>
</widget>
<widget class="QMenu" name="menuHelp" > <widget class="QMenu" name="menuHelp" >
<property name="title" > <property name="title" >
<string>&amp;Help</string> <string>&amp;Help</string>
@ -59,16 +35,19 @@
<addaction name="actionHelpOnline" /> <addaction name="actionHelpOnline" />
<addaction name="separator" /> <addaction name="separator" />
<addaction name="actionDonate" /> <addaction name="actionDonate" />
<addaction name="actionCheckForUpdates" />
<addaction name="separator" /> <addaction name="separator" />
<addaction name="actionHelpAbout" /> <addaction name="actionHelpAbout" />
<addaction name="actionAboutQt" /> <addaction name="actionAboutQt" />
</widget> </widget>
<widget class="QMenu" name="menuView" >
<property name="title" >
<string>&amp;View</string>
</property>
<addaction name="actionViewShowMyReservedPortsOnly" />
<addaction name="actionViewRestoreDefaults" />
</widget>
<addaction name="menuFile" /> <addaction name="menuFile" />
<addaction name="menuEdit" />
<addaction name="menuView" /> <addaction name="menuView" />
<addaction name="menuStreams" />
<addaction name="menuDevices" />
<addaction name="menuHelp" /> <addaction name="menuHelp" />
</widget> </widget>
<widget class="QStatusBar" name="statusbar" /> <widget class="QStatusBar" name="statusbar" />
@ -143,11 +122,6 @@
<string>Donate</string> <string>Donate</string>
</property> </property>
</action> </action>
<action name="actionCheckForUpdates" >
<property name="text" >
<string>Check for Updates...</string>
</property>
</action>
</widget> </widget>
<resources> <resources>
<include location="ostinato.qrc" /> <include location="ostinato.qrc" />

View File

@ -1,126 +0,0 @@
/*
Copyright (C) 2021 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "mandatoryfieldsgroup.h"
// No need for QDateEdit, QSpinBox, etc., since these always return values
#include <QCheckBox>
#include <QComboBox>
#include <QLineEdit>
#include <QPushButton>
#include <QWidget>
void MandatoryFieldsGroup::add(QWidget *widget)
{
if (!widgets_.contains(widget)) {
if (widget->inherits("QCheckBox"))
connect(qobject_cast<QCheckBox*>(widget),
SIGNAL(clicked()),
this, SLOT(changed()));
else if (widget->inherits("QComboBox"))
connect(qobject_cast<QComboBox*>(widget),
SIGNAL(highlighted(int)),
this, SLOT(changed()));
else if (widget->inherits("QLineEdit"))
connect(qobject_cast<QLineEdit*>(widget),
SIGNAL(textChanged(const QString&)),
this, SLOT(changed()));
else {
qWarning("MandatoryFieldsGroup: unsupported class %s",
widget->metaObject()->className());
return;
}
widgets_.append(widget);
changed();
}
}
void MandatoryFieldsGroup::remove(QWidget *widget)
{
widgets_.removeAll(widget);
changed();
}
void MandatoryFieldsGroup::setSubmitButton(QPushButton *button)
{
if (submitButton_ && submitButton_ != button)
submitButton_->setEnabled(true);
submitButton_ = button;
changed();
}
void MandatoryFieldsGroup::changed()
{
if (!submitButton_)
return;
bool enable = true;
for (auto widget : widgets_) {
// Invisible mandatory widgets are treated as non-mandatory
if (!widget->isVisible())
continue;
if (widget->inherits("QCheckBox")) {
// Makes sense only for tristate checkbox
auto checkBox = qobject_cast<const QCheckBox*>(widget);
if (checkBox->checkState() == Qt::PartiallyChecked) {
enable = false;
break;
} else
continue;
}
if (widget->inherits("QComboBox")) {
auto comboBox = qobject_cast<const QComboBox*>(widget);
if (comboBox->currentText().isEmpty()) {
enable = false;
break;
} else
continue;
}
if (widget->inherits("QLineEdit")) {
auto lineEdit = qobject_cast<const QLineEdit*>(widget);
if (lineEdit->text().isEmpty()
|| !lineEdit->hasAcceptableInput()) {
enable = false;
break;
} else
continue;
}
}
submitButton_->setEnabled(enable);
}
void MandatoryFieldsGroup::clear()
{
widgets_.clear();
if (submitButton_) {
submitButton_->setEnabled(true);
submitButton_ = nullptr;
}
}

View File

@ -1,59 +0,0 @@
/*
Copyright (C) 2021 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _MANDATORY_FIELDS_GROUP_H
#define _MANDATORY_FIELDS_GROUP_H
#include <QObject>
#include <QList>
class QPushButton;
class QWidget;
// Adapted from https://doc.qt.io/archives/qq/qq11-mandatoryfields.html
// and improved
class MandatoryFieldsGroup : public QObject
{
Q_OBJECT
public:
MandatoryFieldsGroup(QObject *parent)
: QObject(parent)
{
}
void add(QWidget *widget);
void remove(QWidget *widget);
void setSubmitButton(QPushButton *button);
public slots:
void clear();
private slots:
void changed();
private:
QList<const QWidget*> widgets_;
QPushButton *submitButton_{nullptr};
};
#endif

View File

@ -140,11 +140,6 @@ QVariant NdpStatusModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
Qt::DropActions NdpStatusModel::supportedDropActions() const
{
return Qt::IgnoreAction; // read-only model, doesn't accept any data
}
void NdpStatusModel::setDeviceIndex(Port *port, int deviceIndex) void NdpStatusModel::setDeviceIndex(Port *port, int deviceIndex)
{ {
beginResetModel(); beginResetModel();

View File

@ -40,8 +40,6 @@ public:
int role = Qt::DisplayRole) const; int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
Qt::DropActions supportedDropActions() const;
void setDeviceIndex(Port *port, int deviceIndex); void setDeviceIndex(Port *port, int deviceIndex);
public slots: public slots:

View File

@ -3,46 +3,47 @@ CONFIG += qt ver_info
macx: TARGET = Ostinato macx: TARGET = Ostinato
win32:RC_FILE = ostinato.rc win32:RC_FILE = ostinato.rc
macx:ICON = icons/logo.icns macx:ICON = icons/logo.icns
QT += widgets network script xml svg QT += widgets network script xml
INCLUDEPATH += "../rpc/" "../common/" INCLUDEPATH += "../rpc/" "../common/"
OBJDIR = .
win32 { win32 {
QMAKE_LFLAGS += -static QMAKE_LFLAGS += -static
CONFIG(debug, debug|release) { CONFIG(debug, debug|release) {
OBJDIR = debug LIBS += -L"../common/debug" -lostprotogui -lostproto
LIBS += -L"../rpc/debug" -lpbrpc
POST_TARGETDEPS += \
"../common/debug/libostprotogui.a" \
"../common/debug/libostproto.a" \
"../rpc/debug/libpbrpc.a"
} else { } else {
OBJDIR = release LIBS += -L"../common/release" -lostprotogui -lostproto
LIBS += -L"../rpc/release" -lpbrpc
POST_TARGETDEPS += \
"../common/release/libostprotogui.a" \
"../common/release/libostproto.a" \
"../rpc/release/libpbrpc.a"
} }
} else {
LIBS += -L"../common" -lostprotogui -lostproto
LIBS += -L"../rpc" -lpbrpc
POST_TARGETDEPS += \
"../common/libostprotogui.a" \
"../common/libostproto.a" \
"../rpc/libpbrpc.a"
} }
LIBS += -L"../common/$$OBJDIR" -lostfile -lostfilegui
LIBS += -L"../common/$$OBJDIR" -lostprotogui -lostproto
LIBS += -L"../rpc/$$OBJDIR" -lpbrpc
POST_TARGETDEPS += \
"../common/$$OBJDIR/libostfilegui.a" \
"../common/$$OBJDIR/libostfile.a" \
"../common/$$OBJDIR/libostprotogui.a" \
"../common/$$OBJDIR/libostproto.a" \
"../rpc/$$OBJDIR/libpbrpc.a"
LIBS += -lprotobuf LIBS += -lprotobuf
LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2
RESOURCES += ostinato.qrc RESOURCES += ostinato.qrc
HEADERS += \ HEADERS += \
arpstatusmodel.h \ arpstatusmodel.h \
clipboardhelper.h \
devicegroupdialog.h \ devicegroupdialog.h \
devicegroupmodel.h \ devicegroupmodel.h \
devicemodel.h \ devicemodel.h \
deviceswidget.h \ deviceswidget.h \
dumpview.h \ dumpview.h \
fieldedit.h \
hexlineedit.h \ hexlineedit.h \
logsmodel.h \ logsmodel.h \
logswindow.h \ logswindow.h \
findreplace.h \
mainwindow.h \ mainwindow.h \
mandatoryfieldsgroup.h \
ndpstatusmodel.h \ ndpstatusmodel.h \
packetmodel.h \ packetmodel.h \
port.h \ port.h \
@ -55,7 +56,6 @@ HEADERS += \
portstatsproxymodel.h \ portstatsproxymodel.h \
portstatswindow.h \ portstatswindow.h \
portswindow.h \ portswindow.h \
portwidget.h \
preferences.h \ preferences.h \
settings.h \ settings.h \
streamconfigdialog.h \ streamconfigdialog.h \
@ -64,31 +64,25 @@ HEADERS += \
streamstatsfiltermodel.h \ streamstatsfiltermodel.h \
streamstatsmodel.h \ streamstatsmodel.h \
streamstatswindow.h \ streamstatswindow.h \
streamswidget.h \ variablefieldswidget.h
variablefieldswidget.h \
xtableview.h
FORMS += \ FORMS += \
about.ui \ about.ui \
devicegroupdialog.ui \ devicegroupdialog.ui \
deviceswidget.ui \ deviceswidget.ui \
findreplace.ui \
logswindow.ui \ logswindow.ui \
mainwindow.ui \ mainwindow.ui \
portconfigdialog.ui \ portconfigdialog.ui \
portstatsfilter.ui \ portstatsfilter.ui \
portstatswindow.ui \ portstatswindow.ui \
portswindow.ui \ portswindow.ui \
portwidget.ui \
preferences.ui \ preferences.ui \
streamconfigdialog.ui \ streamconfigdialog.ui \
streamstatswindow.ui \ streamstatswindow.ui \
streamswidget.ui \
variablefieldswidget.ui variablefieldswidget.ui
SOURCES += \ SOURCES += \
arpstatusmodel.cpp \ arpstatusmodel.cpp \
clipboardhelper.cpp \
devicegroupdialog.cpp \ devicegroupdialog.cpp \
devicegroupmodel.cpp \ devicegroupmodel.cpp \
devicemodel.cpp \ devicemodel.cpp \
@ -98,11 +92,8 @@ SOURCES += \
hexlineedit.cpp \ hexlineedit.cpp \
logsmodel.cpp \ logsmodel.cpp \
logswindow.cpp \ logswindow.cpp \
fieldedit.cpp \
findreplace.cpp \
main.cpp \ main.cpp \
mainwindow.cpp \ mainwindow.cpp \
mandatoryfieldsgroup.cpp \
ndpstatusmodel.cpp \ ndpstatusmodel.cpp \
packetmodel.cpp \ packetmodel.cpp \
params.cpp \ params.cpp \
@ -115,31 +106,18 @@ SOURCES += \
portstatsfilterdialog.cpp \ portstatsfilterdialog.cpp \
portstatswindow.cpp \ portstatswindow.cpp \
portswindow.cpp \ portswindow.cpp \
portwidget.cpp \
preferences.cpp \ preferences.cpp \
streamconfigdialog.cpp \ streamconfigdialog.cpp \
streamlistdelegate.cpp \ streamlistdelegate.cpp \
streammodel.cpp \ streammodel.cpp \
streamstatsmodel.cpp \ streamstatsmodel.cpp \
streamstatswindow.cpp \ streamstatswindow.cpp \
streamswidget.cpp \
thememanager.cpp \
variablefieldswidget.cpp variablefieldswidget.cpp
THEMES += \
themes/material-dark.qss \
themes/material-dark.rcc \
themes/material-light.qss \
themes/material-light.rcc \
themes/qds-dark.qss \
themes/qds-dark.rcc \
themes/qds-light.qss \
themes/qds-light.rcc \
QMAKE_DISTCLEAN += object_script.* QMAKE_DISTCLEAN += object_script.*
include(../install.pri) include(../install.pri)
include(../shared.pri)
include(../version.pri) include(../version.pri)
include(../options.pri) include(../options.pri)

View File

@ -1,7 +1,5 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>icons/find.png</file>
<file>icons/info.png</file>
<file>icons/about.png</file> <file>icons/about.png</file>
<file>icons/add.png</file> <file>icons/add.png</file>
<file>icons/anime_error.gif</file> <file>icons/anime_error.gif</file>
@ -16,16 +14,13 @@
<file>icons/bullet_red.png</file> <file>icons/bullet_red.png</file>
<file>icons/bullet_white.png</file> <file>icons/bullet_white.png</file>
<file>icons/bullet_yellow.png</file> <file>icons/bullet_yellow.png</file>
<file>icons/copy.png</file>
<file>icons/control_play.png</file> <file>icons/control_play.png</file>
<file>icons/control_stop.png</file> <file>icons/control_stop.png</file>
<file>icons/cut.png</file>
<file>icons/delete.png</file> <file>icons/delete.png</file>
<file>icons/devicegroup_add.png</file> <file>icons/devicegroup_add.png</file>
<file>icons/devicegroup_delete.png</file> <file>icons/devicegroup_delete.png</file>
<file>icons/devicegroup_edit.png</file> <file>icons/devicegroup_edit.png</file>
<file>icons/donate.png</file> <file>icons/donate.png</file>
<file>icons/error.svg</file>
<file>icons/exit.png</file> <file>icons/exit.png</file>
<file>icons/frag_capture.png</file> <file>icons/frag_capture.png</file>
<file>icons/frag_exclusive.png</file> <file>icons/frag_exclusive.png</file>
@ -40,7 +35,6 @@
<file>icons/name.png</file> <file>icons/name.png</file>
<file>icons/neighbor_clear.png</file> <file>icons/neighbor_clear.png</file>
<file>icons/neighbor_resolve.png</file> <file>icons/neighbor_resolve.png</file>
<file>icons/paste.png</file>
<file>icons/portgroup_add.png</file> <file>icons/portgroup_add.png</file>
<file>icons/portgroup_connect.png</file> <file>icons/portgroup_connect.png</file>
<file>icons/portgroup_delete.png</file> <file>icons/portgroup_delete.png</file>
@ -59,6 +53,5 @@
<file>icons/stream_edit.png</file> <file>icons/stream_edit.png</file>
<file>icons/stream_stats.png</file> <file>icons/stream_stats.png</file>
<file>icons/transmit_on.png</file> <file>icons/transmit_on.png</file>
<file>icons/warn.svg</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -243,9 +243,3 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
Qt::DropActions PacketModel::supportedDropActions() const
{
return Qt::IgnoreAction; // read-only model, doesn't accept any data
}

View File

@ -42,7 +42,6 @@ public:
QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const;
QModelIndex parent(const QModelIndex &index) const; QModelIndex parent(const QModelIndex &index) const;
Qt::DropActions supportedDropActions() const;
private: private:
typedef union _IndexId typedef union _IndexId
{ {

View File

@ -21,13 +21,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <unistd.h> #include <unistd.h>
extern char *version;
extern char *revision;
Params::Params() Params::Params()
{ {
localDrone_ = true; localDrone_ = true;
logsDisabled_ = true;
} }
int Params::parseCommandLine(int argc, char* argv[]) int Params::parseCommandLine(int argc, char* argv[])
@ -35,22 +31,14 @@ int Params::parseCommandLine(int argc, char* argv[])
int c, n = 0; int c, n = 0;
opterr = 0; opterr = 0;
while ((c = getopt (argc, argv, "cdhv")) != -1) { while ((c = getopt (argc, argv, "c")) != -1) {
switch (c) switch (c)
{ {
case 'c': case 'c':
localDrone_ = false; localDrone_ = false;
break; break;
case 'd':
logsDisabled_ = false;
break;
case 'v':
printf("Ostinato %s rev %s\n", version, revision);
exit(0);
case 'h':
default: default:
printf("usage: %s [-cdhv]\n", argv[0]); qDebug("ignoring unrecognized option (%c)", c);
exit(1);
} }
n++; n++;
} }
@ -66,11 +54,6 @@ bool Params::optLocalDrone()
return localDrone_; return localDrone_;
} }
bool Params::optLogsDisabled()
{
return logsDisabled_;
}
int Params::argumentCount() int Params::argumentCount()
{ {
return args_.size(); return args_.size();

View File

@ -28,14 +28,12 @@ public:
int parseCommandLine(int argc, char* argv[]); int parseCommandLine(int argc, char* argv[]);
bool optLocalDrone(); bool optLocalDrone();
bool optLogsDisabled();
int argumentCount(); int argumentCount();
QString argument(int index); QString argument(int index);
private: private:
bool localDrone_; bool localDrone_;
bool logsDisabled_;
QStringList args_; QStringList args_;
}; };

View File

@ -20,7 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "port.h" #include "port.h"
#include "emulation.h" #include "emulation.h"
#include "fileformatoptions.h"
#include "streamfileformat.h" #include "streamfileformat.h"
#include <QApplication> #include <QApplication>
@ -29,7 +28,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <QVariant> #include <QVariant>
#include <google/protobuf/descriptor.h> #include <google/protobuf/descriptor.h>
#include <vector> #include <vector>
#include <cmath>
extern QMainWindow *mainWindow; extern QMainWindow *mainWindow;
@ -89,10 +87,7 @@ void Port::updatePortConfig(OstProto::Port *port)
setAlias(QString("if%1").arg(id())); setAlias(QString("if%1").arg(id()));
if (recalc) if (recalc)
{
recalculateAverageRates(); recalculateAverageRates();
emit streamListChanged(mPortGroupId, mPortId); // show/hide 'next' col
}
} }
void Port::updateStreamOrdinalsFromIndex() void Port::updateStreamOrdinalsFromIndex()
@ -103,7 +98,7 @@ void Port::updateStreamOrdinalsFromIndex()
void Port::reorderStreamsByOrdinals() void Port::reorderStreamsByOrdinals()
{ {
std::sort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan);
} }
void Port::setDirty(bool dirty) void Port::setDirty(bool dirty)
@ -192,11 +187,6 @@ void Port::setAveragePacketRate(double packetsPerSec)
Q_ASSERT(false); // Unreachable!! Q_ASSERT(false); // Unreachable!!
} }
// if old avgPps is 0, new rate will be calculated as nan (infinity)
// because of divide by 0 (old avgPps) above - fix that
if (std::isnan(rate))
rate = packetsPerSec;
qDebug("cur stream pps = %g", s->averagePacketRate()); qDebug("cur stream pps = %g", s->averagePacketRate());
s->setAveragePacketRate(rate); s->setAveragePacketRate(rate);
@ -271,11 +261,6 @@ void Port::setAverageBitRate(double bitsPerSec)
Q_ASSERT(false); // Unreachable!! Q_ASSERT(false); // Unreachable!!
} }
// if old avgBps is 0, new rate will be calculated as nan (infinity)
// because of divide by 0 (old avgBps) above - fix that
if (std::isnan(rate))
rate = bitsPerSec/((s->frameLenAvg()+kEthOverhead)*8);
qDebug("cur stream pps = %g", s->averagePacketRate()); qDebug("cur stream pps = %g", s->averagePacketRate());
s->setAveragePacketRate(rate); s->setAveragePacketRate(rate);
@ -318,12 +303,6 @@ void Port::setAverageBitRate(double bitsPerSec)
emit portRateChanged(mPortGroupId, mPortId); emit portRateChanged(mPortGroupId, mPortId);
} }
void Port::setAverageLoadRate(double load)
{
Q_ASSERT(d.speed() > 0);
setAverageBitRate(load*d.speed()*1e6);
}
bool Port::newStreamAt(int index, OstProto::Stream const *stream) bool Port::newStreamAt(int index, OstProto::Stream const *stream)
{ {
Stream *s = new Stream; Stream *s = new Stream;
@ -516,11 +495,6 @@ bool Port::modifiablePortConfig(OstProto::Port &config) const
modCfg.set_user_name(config.user_name()); modCfg.set_user_name(config.user_name());
change = true; change = true;
} }
if (config.is_tracking_stream_stats() != d.is_tracking_stream_stats()) {
modCfg.set_is_tracking_stream_stats(config.is_tracking_stream_stats());
change = true;
}
if (change) { if (change) {
modCfg.mutable_port_id()->set_id(id()); modCfg.mutable_port_id()->set_id(id());
@ -605,11 +579,12 @@ bool Port::openStreams(QString fileName, bool append, QString &error)
goto _fail; goto _fail;
} }
if ((optDialog = FileFormatOptions::openOptionsDialog(fmt))) if ((optDialog = fmt->openOptionsDialog()))
{ {
int ret; int ret;
optDialog->setParent(mainWindow, Qt::Dialog); optDialog->setParent(mainWindow, Qt::Dialog);
ret = optDialog->exec(); ret = optDialog->exec();
optDialog->setParent(0, Qt::Dialog);
if (ret == QDialog::Rejected) if (ret == QDialog::Rejected)
goto _user_opt_cancel; goto _user_opt_cancel;
} }

View File

@ -88,15 +88,8 @@ public:
{ return d.port_id().id(); } { return d.port_id().id(); }
const QString name() const const QString name() const
{ return QString().fromStdString(d.name()); } { return QString().fromStdString(d.name()); }
const QString systemDescription() const const QString description() const
{ return QString().fromStdString(d.description()); } { return QString().fromStdString(d.description()); }
const QString userDescription() const
{ return QString().fromStdString(d.user_description()); }
const QString description() const
{
return userDescription().isEmpty() ?
systemDescription() : userDescription();
}
const QString notes() const const QString notes() const
{ return QString().fromStdString(d.notes()); } { return QString().fromStdString(d.notes()); }
const QString userName() const const QString userName() const
@ -109,10 +102,6 @@ public:
{ return d.transmit_mode(); } { return d.transmit_mode(); }
bool trackStreamStats() const bool trackStreamStats() const
{ return d.is_tracking_stream_stats(); } { return d.is_tracking_stream_stats(); }
double speed() const
{ return d.speed(); }
double averageLoadRate() const
{ return d.speed() ? avgBitsPerSec_/(d.speed()*1e6) : 0; }
double averagePacketRate() const double averagePacketRate() const
{ return avgPacketsPerSec_; } { return avgPacketsPerSec_; }
double averageBitRate() const double averageBitRate() const
@ -194,7 +183,6 @@ public:
void setAveragePacketRate(double packetsPerSec); void setAveragePacketRate(double packetsPerSec);
void setAverageBitRate(double bitsPerSec); void setAverageBitRate(double bitsPerSec);
void setAverageLoadRate(double loadPercent);
// FIXME(MED): Bad Hack! port should not need an external trigger to // FIXME(MED): Bad Hack! port should not need an external trigger to
// recalculate - refactor client side domain objects and model objects // recalculate - refactor client side domain objects and model objects
void recalculateAverageRates(); void recalculateAverageRates();

View File

@ -32,8 +32,6 @@ PortConfigDialog::PortConfigDialog(
setupUi(this); setupUi(this);
description->setPlaceholderText(portConfig_.description().c_str());
description->setText(portConfig_.user_description().c_str());
switch(portConfig_.transmit_mode()) switch(portConfig_.transmit_mode())
{ {
case OstProto::kSequentialTransmit: case OstProto::kSequentialTransmit:
@ -82,8 +80,6 @@ void PortConfigDialog::accept()
{ {
OstProto::Port pc; OstProto::Port pc;
pc.set_user_description(description->text().toStdString());
if (sequentialStreamsButton->isChecked()) if (sequentialStreamsButton->isChecked())
pc.set_transmit_mode(OstProto::kSequentialTransmit); pc.set_transmit_mode(OstProto::kSequentialTransmit);
else if (interleavedStreamsButton->isChecked()) else if (interleavedStreamsButton->isChecked())
@ -91,7 +87,6 @@ void PortConfigDialog::accept()
else else
Q_ASSERT(false); // Unreachable!!! Q_ASSERT(false); // Unreachable!!!
pc.set_user_name(portConfig_.user_name());
switch (reservedBy_) { switch (reservedBy_) {
case kSelf: case kSelf:
if (!reserveButton->isChecked()) if (!reserveButton->isChecked())
@ -113,11 +108,6 @@ void PortConfigDialog::accept()
pc.set_is_tracking_stream_stats(streamStatsButton->isChecked()); pc.set_is_tracking_stream_stats(streamStatsButton->isChecked());
// Update fields that have changed, clear the rest // Update fields that have changed, clear the rest
if (pc.user_description() != portConfig_.user_description())
portConfig_.set_user_description(pc.user_description());
else
portConfig_.clear_user_description();
if (pc.transmit_mode() != portConfig_.transmit_mode()) if (pc.transmit_mode() != portConfig_.transmit_mode())
portConfig_.set_transmit_mode(pc.transmit_mode()); portConfig_.set_transmit_mode(pc.transmit_mode());
else else

View File

@ -1,51 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0" >
<ui version="4.0">
<class>PortConfigDialog</class> <class>PortConfigDialog</class>
<widget class="QDialog" name="PortConfigDialog"> <widget class="QDialog" name="PortConfigDialog" >
<property name="geometry"> <property name="geometry" >
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>248</width> <width>244</width>
<height>292</height> <height>257</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle" >
<string>Port Config</string> <string>Port Config</string>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout" >
<item> <item>
<widget class="QLabel" name="label"> <widget class="QGroupBox" name="transmitModeBox" >
<property name="text"> <property name="title" >
<string>Description</string>
</property>
<property name="buddy">
<cstring>description</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="description"/>
</item>
<item>
<widget class="QGroupBox" name="transmitModeBox">
<property name="title">
<string>Transmit Mode</string> <string>Transmit Mode</string>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout" >
<item> <item>
<widget class="QRadioButton" name="sequentialStreamsButton"> <widget class="QRadioButton" name="sequentialStreamsButton" >
<property name="text"> <property name="text" >
<string>Sequential Streams</string> <string>Sequential Streams</string>
</property> </property>
<property name="checked"> <property name="checked" >
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QRadioButton" name="interleavedStreamsButton"> <widget class="QRadioButton" name="interleavedStreamsButton" >
<property name="text"> <property name="text" >
<string>Interleaved Streams</string> <string>Interleaved Streams</string>
</property> </property>
</widget> </widget>
@ -54,21 +40,21 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox" >
<property name="title"> <property name="title" >
<string>Reservation</string> <string>Reservation</string>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout" >
<item> <item>
<widget class="QLabel" name="reservedBy"> <widget class="QLabel" name="reservedBy" >
<property name="text"> <property name="text" >
<string>Reserved by: </string> <string>Reserved by: </string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="reserveButton"> <widget class="QCheckBox" name="reserveButton" >
<property name="text"> <property name="text" >
<string>Reserve</string> <string>Reserve</string>
</property> </property>
</widget> </widget>
@ -77,25 +63,25 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="exclusiveControlButton"> <widget class="QCheckBox" name="exclusiveControlButton" >
<property name="text"> <property name="text" >
<string>Exclusive Control</string> <string>Exclusive Control</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="streamStatsButton"> <widget class="QCheckBox" name="streamStatsButton" >
<property name="text"> <property name="text" >
<string>Stream Statistics</string> <string>Stream Statistics</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer> <spacer>
<property name="orientation"> <property name="orientation" >
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" >
<size> <size>
<width>226</width> <width>226</width>
<height>71</height> <height>71</height>
@ -104,25 +90,17 @@
</spacer> </spacer>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox" >
<property name="orientation"> <property name="orientation" >
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<tabstops>
<tabstop>description</tabstop>
<tabstop>sequentialStreamsButton</tabstop>
<tabstop>interleavedStreamsButton</tabstop>
<tabstop>reserveButton</tabstop>
<tabstop>exclusiveControlButton</tabstop>
<tabstop>streamStatsButton</tabstop>
</tabstops>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>
@ -131,11 +109,11 @@
<receiver>PortConfigDialog</receiver> <receiver>PortConfigDialog</receiver>
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel" >
<x>234</x> <x>234</x>
<y>205</y> <y>205</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel" >
<x>157</x> <x>157</x>
<y>214</y> <y>214</y>
</hint> </hint>
@ -147,11 +125,11 @@
<receiver>PortConfigDialog</receiver> <receiver>PortConfigDialog</receiver>
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel" >
<x>234</x> <x>234</x>
<y>205</y> <y>205</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel" >
<x>243</x> <x>243</x>
<y>214</y> <y>214</y>
</hint> </hint>

View File

@ -206,7 +206,6 @@ void PortGroup::processVersionCompatibility(PbRpcController *controller)
logError(id(), QString("checkVersion failed: %1") logError(id(), QString("checkVersion failed: %1")
.arg(QString::fromStdString(verCompat->notes()))); .arg(QString::fromStdString(verCompat->notes())));
compat = kIncompatible; compat = kIncompatible;
reconnect = false;
emit portGroupDataChanged(mPortGroupId); emit portGroupDataChanged(mPortGroupId);
QMessageBox msgBox; QMessageBox msgBox;
@ -242,8 +241,7 @@ _error_exit:
void PortGroup::on_rpcChannel_disconnected() void PortGroup::on_rpcChannel_disconnected()
{ {
qDebug("disconnected %s:%u", qDebug("disconnected\n");
qPrintable(rpcChannel->serverName()), rpcChannel->serverPort());
logError(id(), "PortGroup disconnected"); logError(id(), "PortGroup disconnected");
emit portListAboutToBeChanged(mPortGroupId); emit portListAboutToBeChanged(mPortGroupId);
@ -254,16 +252,6 @@ void PortGroup::on_rpcChannel_disconnected()
emit portListChanged(mPortGroupId); emit portListChanged(mPortGroupId);
emit portGroupDataChanged(mPortGroupId); emit portGroupDataChanged(mPortGroupId);
// Disconnected during apply? Restore UI.
if (applyTimer_.isValid()) {
applyTimer_.invalidate();
emit applyFinished();
mainWindow->setEnabled(true);
QApplication::restoreOverrideCursor();
}
isGetStatsPending_ = false; isGetStatsPending_ = false;
if (reconnect) if (reconnect)
@ -277,24 +265,11 @@ void PortGroup::on_rpcChannel_disconnected()
void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError)
{ {
qDebug("%s: error %d %s:%u", __FUNCTION__, socketError, qDebug("%s: error %d", __FUNCTION__, socketError);
qPrintable(rpcChannel->serverName()), rpcChannel->serverPort());
emit portGroupDataChanged(mPortGroupId); emit portGroupDataChanged(mPortGroupId);
switch(socketError) if (socketError == QAbstractSocket::RemoteHostClosedError)
{
case QAbstractSocket::SslInvalidUserDataError: // actually abort()
logWarn(id(), QString("Bad data received from portgroup, "
"aborting connection; "
"who is listening on %1:%2 "
" - is it drone or some other process?")
.arg(rpcChannel->serverName())
.arg(rpcChannel->serverPort()));
reconnect = false; reconnect = false;
break;
default:
break;
}
qDebug("%s: state %d", __FUNCTION__, rpcChannel->state()); qDebug("%s: state %d", __FUNCTION__, rpcChannel->state());
if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect) if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect)
@ -527,7 +502,6 @@ void PortGroup::when_configApply(int portIndex)
{ {
OstProto::StreamIdList *streamIdList; OstProto::StreamIdList *streamIdList;
OstProto::StreamConfigList *streamConfigList; OstProto::StreamConfigList *streamConfigList;
OstProto::BuildConfig *buildConfig;
OstProto::Ack *ack; OstProto::Ack *ack;
PbRpcController *controller; PbRpcController *controller;
@ -539,7 +513,14 @@ void PortGroup::when_configApply(int portIndex)
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
mainWindow->setDisabled(true); mainWindow->setDisabled(true);
applyTimer_.start(); // FIXME: as currently written this code will make unnecessary RPCs
// even if the request contains no data; the fix will need to take
// care to identify when sync is complete
// NOTE: DeviceGroup RPCs are no longer called unnecessarily;
// Stream RPCs need to be fixed similarly
// Also, drone currently updates its packet list at the end of
// modifyStream() implicitly assuming that will be the last API
// called - this will also need to be fixed
// //
// Update/Sync DeviceGroups // Update/Sync DeviceGroups
@ -549,12 +530,12 @@ void PortGroup::when_configApply(int portIndex)
bool refreshReqd = false; bool refreshReqd = false;
qDebug("applying 'deleted deviceGroups' ..."); qDebug("applying 'deleted deviceGroups' ...");
logInfo(id(), mPorts[portIndex]->id(),
QString("Deleting old DeviceGroups"));
deviceGroupIdList = new OstProto::DeviceGroupIdList; deviceGroupIdList = new OstProto::DeviceGroupIdList;
deviceGroupIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); deviceGroupIdList->mutable_port_id()->set_id(mPorts[portIndex]->id());
mPorts[portIndex]->getDeletedDeviceGroupsSinceLastSync(*deviceGroupIdList); mPorts[portIndex]->getDeletedDeviceGroupsSinceLastSync(*deviceGroupIdList);
if (deviceGroupIdList->device_group_id_size()) { if (deviceGroupIdList->device_group_id_size()) {
logInfo(id(), mPorts[portIndex]->id(),
QString("Deleting old DeviceGroups"));
ack = new OstProto::Ack; ack = new OstProto::Ack;
controller = new PbRpcController(deviceGroupIdList, ack); controller = new PbRpcController(deviceGroupIdList, ack);
serviceStub->deleteDeviceGroup(controller, deviceGroupIdList, ack, serviceStub->deleteDeviceGroup(controller, deviceGroupIdList, ack,
@ -566,12 +547,12 @@ void PortGroup::when_configApply(int portIndex)
delete deviceGroupIdList; delete deviceGroupIdList;
qDebug("applying 'new deviceGroups' ..."); qDebug("applying 'new deviceGroups' ...");
logInfo(id(), mPorts[portIndex]->id(),
QString("Creating new DeviceGroups"));
deviceGroupIdList = new OstProto::DeviceGroupIdList; deviceGroupIdList = new OstProto::DeviceGroupIdList;
deviceGroupIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); deviceGroupIdList->mutable_port_id()->set_id(mPorts[portIndex]->id());
mPorts[portIndex]->getNewDeviceGroupsSinceLastSync(*deviceGroupIdList); mPorts[portIndex]->getNewDeviceGroupsSinceLastSync(*deviceGroupIdList);
if (deviceGroupIdList->device_group_id_size()) { if (deviceGroupIdList->device_group_id_size()) {
logInfo(id(), mPorts[portIndex]->id(),
QString("Creating new DeviceGroups"));
ack = new OstProto::Ack; ack = new OstProto::Ack;
controller = new PbRpcController(deviceGroupIdList, ack); controller = new PbRpcController(deviceGroupIdList, ack);
serviceStub->addDeviceGroup(controller, deviceGroupIdList, ack, serviceStub->addDeviceGroup(controller, deviceGroupIdList, ack,
@ -583,13 +564,13 @@ void PortGroup::when_configApply(int portIndex)
delete deviceGroupIdList; delete deviceGroupIdList;
qDebug("applying 'modified deviceGroups' ..."); qDebug("applying 'modified deviceGroups' ...");
logInfo(id(), mPorts[portIndex]->id(),
QString("Modifying changed DeviceGroups"));
deviceGroupConfigList = new OstProto::DeviceGroupConfigList; deviceGroupConfigList = new OstProto::DeviceGroupConfigList;
deviceGroupConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); deviceGroupConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id());
mPorts[portIndex]->getModifiedDeviceGroupsSinceLastSync( mPorts[portIndex]->getModifiedDeviceGroupsSinceLastSync(
*deviceGroupConfigList); *deviceGroupConfigList);
if (deviceGroupConfigList->device_group_size()) { if (deviceGroupConfigList->device_group_size()) {
logInfo(id(), mPorts[portIndex]->id(),
QString("Modifying changed DeviceGroups"));
ack = new OstProto::Ack; ack = new OstProto::Ack;
controller = new PbRpcController(deviceGroupConfigList, ack); controller = new PbRpcController(deviceGroupConfigList, ack);
serviceStub->modifyDeviceGroup(controller, deviceGroupConfigList, ack, serviceStub->modifyDeviceGroup(controller, deviceGroupConfigList, ack,
@ -607,72 +588,43 @@ void PortGroup::when_configApply(int portIndex)
// Update/Sync Streams // Update/Sync Streams
// //
qDebug("applying 'deleted streams' ..."); qDebug("applying 'deleted streams' ...");
logInfo(id(), mPorts[portIndex]->id(), QString("Deleting old Streams"));
streamIdList = new OstProto::StreamIdList; streamIdList = new OstProto::StreamIdList;
ack = new OstProto::Ack;
controller = new PbRpcController(streamIdList, ack);
streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id());
mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList); mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList);
if (streamIdList->stream_id_size()) {
logInfo(id(), mPorts[portIndex]->id(), QString("Deleting old Streams")); serviceStub->deleteStream(controller, streamIdList, ack,
ack = new OstProto::Ack; NewCallback(this, &PortGroup::processDeleteStreamAck, controller));
controller = new PbRpcController(streamIdList, ack);
serviceStub->deleteStream(controller, streamIdList, ack,
NewCallback(this, &PortGroup::processDeleteStreamAck, controller));
}
else
delete streamIdList;
qDebug("applying 'new streams' ..."); qDebug("applying 'new streams' ...");
logInfo(id(), mPorts[portIndex]->id(), QString("Creating new Streams"));
streamIdList = new OstProto::StreamIdList; streamIdList = new OstProto::StreamIdList;
ack = new OstProto::Ack;
controller = new PbRpcController(streamIdList, ack);
streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id());
mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList); mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList);
if (streamIdList->stream_id_size()) {
logInfo(id(), mPorts[portIndex]->id(), QString("Creating new Streams")); serviceStub->addStream(controller, streamIdList, ack,
ack = new OstProto::Ack; NewCallback(this, &PortGroup::processAddStreamAck, controller));
controller = new PbRpcController(streamIdList, ack);
serviceStub->addStream(controller, streamIdList, ack,
NewCallback(this, &PortGroup::processAddStreamAck, controller));
}
else
delete streamIdList;
qDebug("applying 'modified streams' ..."); qDebug("applying 'modified streams' ...");
logInfo(id(), mPorts[portIndex]->id(),
QString("Modifying changed Streams"));
streamConfigList = new OstProto::StreamConfigList; streamConfigList = new OstProto::StreamConfigList;
ack = new OstProto::Ack;
controller = new PbRpcController(streamConfigList, ack);
streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id());
mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList); mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList);
if (streamConfigList->stream_size()) {
logInfo(id(), mPorts[portIndex]->id(),
QString("Modifying changed Streams"));
ack = new OstProto::Ack;
controller = new PbRpcController(streamConfigList, ack);
serviceStub->modifyStream(controller, streamConfigList, ack,
NewCallback(this, &PortGroup::processModifyStreamAck,
portIndex, controller));
}
else
delete streamConfigList;
qDebug("resolve neighbors before building ..."); serviceStub->modifyStream(controller, streamConfigList, ack,
logInfo(id(), mPorts[portIndex]->id(), NewCallback(this, &PortGroup::processModifyStreamAck,
QString("Resolving device neighbors"));
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
OstProto::PortId *portId = portIdList->add_port_id();
portId->set_id(mPorts[portIndex]->id());
ack = new OstProto::Ack;
controller = new PbRpcController(portIdList, ack);
serviceStub->resolveDeviceNeighbors(controller, portIdList, ack,
NewCallback(this, &PortGroup::processResolveDeviceNeighborsAck,
controller));
qDebug("finish apply by building ...");
logInfo(id(), mPorts[portIndex]->id(),
QString("Re-building packets"));
buildConfig = new OstProto::BuildConfig;
ack = new OstProto::Ack;
controller = new PbRpcController(buildConfig, ack);
buildConfig->mutable_port_id()->set_id(mPorts[portIndex]->id());
serviceStub->build(controller, buildConfig, ack,
NewCallback(this, &PortGroup::processApplyBuildAck,
portIndex, controller)); portIndex, controller));
} }
void PortGroup::processAddDeviceGroupAck(PbRpcController *controller) void PortGroup::processAddDeviceGroupAck(PbRpcController *controller)
@ -800,33 +752,8 @@ void PortGroup::processModifyStreamAck(int portIndex,
OstProto::Ack *ack = static_cast<OstProto::Ack*>(controller->response()); OstProto::Ack *ack = static_cast<OstProto::Ack*>(controller->response());
if (controller->Failed())
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
logError(id(), mPorts[portIndex]->id(), controller->ErrorString());
goto _error_exit;
}
if (ack->status())
logError(id(), mPorts[portIndex]->id(),
QString::fromStdString(ack->notes()));
_error_exit:
delete controller;
}
void PortGroup::processApplyBuildAck(int portIndex, PbRpcController *controller)
{
qDebug("In %s", __FUNCTION__);
OstProto::Ack *ack = static_cast<OstProto::Ack*>(controller->response());
qDebug("apply completed"); qDebug("apply completed");
logInfo(id(), mPorts[portIndex]->id(), logInfo(id(), mPorts[portIndex]->id(), QString("All port changes applied"));
QString("All port changes applied - in %1s")
.arg(applyTimer_.elapsed()/1e3));
applyTimer_.invalidate();
if (controller->Failed()) if (controller->Failed())
{ {
@ -842,7 +769,6 @@ void PortGroup::processApplyBuildAck(int portIndex, PbRpcController *controller)
_error_exit: _error_exit:
mPorts[portIndex]->when_syncComplete(); mPorts[portIndex]->when_syncComplete();
emit applyFinished();
mainWindow->setEnabled(true); mainWindow->setEnabled(true);
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();
@ -963,47 +889,18 @@ void PortGroup::modifyPort(int portIndex, OstProto::Port portConfig)
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
mainWindow->setDisabled(true); mainWindow->setDisabled(true);
logInfo(id(), mPorts[portIndex]->id(),
QString("Modifying port configuration"));
OstProto::Port *port = portConfigList->add_port(); OstProto::Port *port = portConfigList->add_port();
port->CopyFrom(portConfig); port->CopyFrom(portConfig);
port->mutable_port_id()->set_id(mPorts[portIndex]->id()); port->mutable_port_id()->set_id(mPorts[portIndex]->id());
PbRpcController *controller = new PbRpcController(portConfigList, ack); PbRpcController *controller = new PbRpcController(portConfigList, ack);
serviceStub->modifyPort(controller, portConfigList, ack, serviceStub->modifyPort(controller, portConfigList, ack,
NewCallback(this, &PortGroup::processModifyPortAck, controller)); NewCallback(this, &PortGroup::processModifyPortAck, true, controller));
logInfo(id(), mPorts[portIndex]->id(),
QString("Re-building packets"));
OstProto::BuildConfig *buildConfig = new OstProto::BuildConfig;
ack = new OstProto::Ack;
controller = new PbRpcController(buildConfig, ack);
buildConfig->mutable_port_id()->set_id(mPorts[portIndex]->id());
serviceStub->build(controller, buildConfig, ack,
NewCallback(this, &PortGroup::processModifyPortBuildAck,
true, controller));
} }
void PortGroup::processModifyPortAck(PbRpcController *controller) void PortGroup::processModifyPortAck(bool restoreUi,PbRpcController *controller)
{ {
qDebug("In %s", __FUNCTION__);
if (controller->Failed())
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
logError(id(), controller->ErrorString());
}
OstProto::Ack *ack = static_cast<OstProto::Ack*>(controller->response());
if (ack->status())
logError(id(), QString::fromStdString(ack->notes()));
delete controller;
}
void PortGroup::processModifyPortBuildAck(bool restoreUi, PbRpcController *controller)
{
qDebug("In %s", __FUNCTION__); qDebug("In %s", __FUNCTION__);
if (controller->Failed()) if (controller->Failed())
@ -1111,11 +1008,8 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
// * modify (new) deviceGroups // * modify (new) deviceGroups
// * add (new) stream ids // * add (new) stream ids
// * modify (new) streams // * modify (new) streams
// * resolve neighbors
// * build packets
// XXX: This assumes getDeviceGroupIdList() was invoked before // XXX: This assumes getDeviceGroupIdList() was invoked before
// getStreamIdList() - if the order changes this code will break! // getStreamIdList() - if the order changes this code will break!
// XXX: See resolve/build notes below
// XXX: same name as input param, but shouldn't cause any problem // XXX: same name as input param, but shouldn't cause any problem
PbRpcController *controller; PbRpcController *controller;
@ -1152,11 +1046,10 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
serviceStub->modifyPort(controller, portConfigList, ack, serviceStub->modifyPort(controller, portConfigList, ack,
NewCallback(this, &PortGroup::processModifyPortAck, NewCallback(this, &PortGroup::processModifyPortAck,
controller)); false, controller));
} }
// add/modify deviceGroups // add/modify deviceGroups
bool resolve = false;
if (newPortContent->device_groups_size()) if (newPortContent->device_groups_size())
{ {
OstProto::DeviceGroupIdList *deviceGroupIdList OstProto::DeviceGroupIdList *deviceGroupIdList
@ -1188,7 +1081,6 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
deviceGroupConfigList, ack, deviceGroupConfigList, ack,
NewCallback(this, &PortGroup::processModifyDeviceGroupAck, NewCallback(this, &PortGroup::processModifyDeviceGroupAck,
portIndex, controller)); portIndex, controller));
resolve = true;
} }
// add/modify streams // add/modify streams
@ -1219,37 +1111,8 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
serviceStub->modifyStream(controller, streamConfigList, ack, serviceStub->modifyStream(controller, streamConfigList, ack,
NewCallback(this, &PortGroup::processModifyStreamAck, NewCallback(this, &PortGroup::processModifyStreamAck,
portIndex, controller)); portIndex, controller));
resolve = true;
} }
// XXX: Ideally resolve and build should be called after **all**
// ports and portgroups are configured. As of now, any resolve
// replied to by ports/portgroups configured later in the open
// session sequence will fail.
// However, to do that, we may need to rethink the open session
// implementation - so going with this for now
if (resolve)
{
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
portIdList->add_port_id()->set_id(portId);
OstProto::Ack *ack = new OstProto::Ack;
controller = new PbRpcController(portIdList, ack);
serviceStub->resolveDeviceNeighbors(controller, portIdList, ack,
NewCallback(this,
&PortGroup::processResolveDeviceNeighborsAck,
controller));
resolve = false;
}
// build packets using the new config
OstProto::BuildConfig *buildConfig = new OstProto::BuildConfig;
OstProto::Ack *ack = new OstProto::Ack;
controller = new PbRpcController(buildConfig, ack);
buildConfig->mutable_port_id()->set_id(mPorts[portIndex]->id());
serviceStub->build(controller, buildConfig, ack,
NewCallback(this, &PortGroup::processModifyPortBuildAck,
false, controller));
// delete newPortConfig // delete newPortConfig
atConnectPortConfig_[portIndex] = NULL; atConnectPortConfig_[portIndex] = NULL;
@ -2052,12 +1915,9 @@ bool PortGroup::getStreamStats(QList<uint> *portList)
if (portList == NULL) if (portList == NULL)
guidList->mutable_port_id_list()->CopyFrom(*portIdList_); guidList->mutable_port_id_list()->CopyFrom(*portIdList_);
else else
for (int i = 0; i < portList->size(); i++) { for (int i = 0; i < portList->size(); i++)
guidList->mutable_port_id_list()->add_port_id() guidList->mutable_port_id_list()->add_port_id()
->set_id(portList->at(i)); ->set_id(portList->at(i));
if (mPorts.at(i)->isTransmitting())
logWarn(id(), i, "Port is still transmitting - stream stats may be unavailable or incomplete");
}
serviceStub->getStreamStats(controller, guidList, statsList, serviceStub->getStreamStats(controller, guidList, statsList,
NewCallback(this, &PortGroup::processStreamStatsList, controller)); NewCallback(this, &PortGroup::processStreamStatsList, controller));

View File

@ -21,7 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#define _PORT_GROUP_H #define _PORT_GROUP_H
#include "port.h" #include "port.h"
#include <QElapsedTimer>
#include <QHostAddress> #include <QHostAddress>
#include <QTcpSocket> #include <QTcpSocket>
@ -63,7 +62,6 @@ private:
PbRpcChannel *rpcChannel; PbRpcChannel *rpcChannel;
PbRpcController *statsController; PbRpcController *statsController;
bool isGetStatsPending_; bool isGetStatsPending_;
QElapsedTimer applyTimer_;
OstProto::OstService::Stub *serviceStub; OstProto::OstService::Stub *serviceStub;
@ -120,7 +118,6 @@ public:
void processAddStreamAck(PbRpcController *controller); void processAddStreamAck(PbRpcController *controller);
void processDeleteStreamAck(PbRpcController *controller); void processDeleteStreamAck(PbRpcController *controller);
void processModifyStreamAck(int portIndex, PbRpcController *controller); void processModifyStreamAck(int portIndex, PbRpcController *controller);
void processApplyBuildAck(int portIndex, PbRpcController *controller);
void processAddDeviceGroupAck(PbRpcController *controller); void processAddDeviceGroupAck(PbRpcController *controller);
void processDeleteDeviceGroupAck(PbRpcController *controller); void processDeleteDeviceGroupAck(PbRpcController *controller);
@ -130,8 +127,7 @@ public:
void processDeviceNeighbors(int portIndex, PbRpcController *controller); void processDeviceNeighbors(int portIndex, PbRpcController *controller);
void modifyPort(int portId, OstProto::Port portConfig); void modifyPort(int portId, OstProto::Port portConfig);
void processModifyPortAck(PbRpcController *controller); void processModifyPortAck(bool restoreUi, PbRpcController *controller);
void processModifyPortBuildAck(bool restoreUi, PbRpcController *controller);
void processUpdatedPortConfig(PbRpcController *controller); void processUpdatedPortConfig(PbRpcController *controller);
void getStreamIdList(); void getStreamIdList();
@ -175,7 +171,6 @@ public:
void processStreamStatsList(PbRpcController *controller); void processStreamStatsList(PbRpcController *controller);
signals: signals:
void applyFinished();
void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); void portGroupDataChanged(int portGroupId, int portId = 0xFFFF);
void portListAboutToBeChanged(quint32 portGroupId); void portListAboutToBeChanged(quint32 portGroupId);
void portListChanged(quint32 portGroupId); void portListChanged(quint32 portGroupId);

View File

@ -43,6 +43,12 @@ PortGroupList::PortGroupList()
deviceGroupModelTester_ = new ModelTest(getDeviceGroupModel()); deviceGroupModelTester_ = new ModelTest(getDeviceGroupModel());
deviceModelTester_ = new ModelTest(getDeviceModel()); deviceModelTester_ = new ModelTest(getDeviceModel());
#endif #endif
// Add the "Local" Port Group
if (appParams.optLocalDrone()) {
PortGroup *pg = new PortGroup;
addPortGroup(*pg);
}
} }
PortGroupList::~PortGroupList() PortGroupList::~PortGroupList()
@ -68,7 +74,6 @@ bool PortGroupList::isPort(const QModelIndex& index)
PortGroup& PortGroupList::portGroup(const QModelIndex& index) PortGroup& PortGroupList::portGroup(const QModelIndex& index)
{ {
Q_ASSERT(index.isValid());
Q_ASSERT(mPortGroupListModel.isPortGroup(index)); Q_ASSERT(mPortGroupListModel.isPortGroup(index));
return *(mPortGroups[index.row()]); return *(mPortGroups[index.row()]);
@ -76,8 +81,6 @@ PortGroup& PortGroupList::portGroup(const QModelIndex& index)
Port& PortGroupList::port(const QModelIndex& index) Port& PortGroupList::port(const QModelIndex& index)
{ {
Q_ASSERT(index.isValid());
Q_ASSERT(index.parent().isValid());
Q_ASSERT(mPortGroupListModel.isPort(index)); Q_ASSERT(mPortGroupListModel.isPort(index));
return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]);
} }
@ -100,9 +103,6 @@ void PortGroupList::addPortGroup(PortGroup &portGroup)
connect(&portGroup, SIGNAL(portListChanged(quint32)), connect(&portGroup, SIGNAL(portListChanged(quint32)),
&mPortStatsModel, SLOT(when_portListChanged())); &mPortStatsModel, SLOT(when_portListChanged()));
connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)),
&mPortStatsModel, SLOT(when_portGroupDataChanged(int, int)));
connect(&portGroup, SIGNAL(statsChanged(quint32)), connect(&portGroup, SIGNAL(statsChanged(quint32)),
&mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32)));
@ -116,10 +116,8 @@ void PortGroupList::addPortGroup(PortGroup &portGroup)
void PortGroupList::removePortGroup(PortGroup &portGroup) void PortGroupList::removePortGroup(PortGroup &portGroup)
{ {
// Disconnect before removing from list
portGroup.disconnectFromHost();
mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup);
PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup));
qDebug("after takeAt()"); qDebug("after takeAt()");
mPortGroupListModel.portGroupRemoved(); mPortGroupListModel.portGroupRemoved();
@ -136,12 +134,11 @@ void PortGroupList::removeAllPortGroups()
do { do {
PortGroup *pg = mPortGroups.at(0); PortGroup *pg = mPortGroups.at(0);
pg->disconnectFromHost();
mPortGroupListModel.portGroupAboutToBeRemoved(pg); mPortGroupListModel.portGroupAboutToBeRemoved(pg);
mPortGroups.removeFirst(); mPortGroups.removeFirst();
delete pg; delete pg;
mPortGroupListModel.portGroupRemoved();
} while (!mPortGroups.isEmpty()); } while (!mPortGroups.isEmpty());
mPortGroupListModel.portGroupRemoved();
mPortGroupListModel.when_portListChanged(); mPortGroupListModel.when_portListChanged();
mPortStatsModel.when_portListChanged(); mPortStatsModel.when_portListChanged();

View File

@ -200,11 +200,6 @@ QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int
return QString("Name"); return QString("Name");
} }
Qt::DropActions PortModel::supportedDropActions() const
{
return Qt::IgnoreAction; // read-only model, doesn't accept any data
}
QModelIndex PortModel::index (int row, int col, QModelIndex PortModel::index (int row, int col,
const QModelIndex & parent) const const QModelIndex & parent) const
{ {

View File

@ -45,8 +45,6 @@ public:
const QModelIndex &parent = QModelIndex()) const; const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const; QModelIndex parent(const QModelIndex &index) const;
Qt::DropActions supportedDropActions() const;
bool isPortGroup(const QModelIndex& index); bool isPortGroup(const QModelIndex& index);
bool isPort(const QModelIndex& index); bool isPort(const QModelIndex& index);
quint32 portGroupId(const QModelIndex& index); quint32 portGroupId(const QModelIndex& index);

View File

@ -49,8 +49,7 @@ QList<uint> PortStatsFilterDialog::getItemList(bool* ok,
{ {
QStandardItem *item; QStandardItem *item;
item = new QStandardItem(model->headerData(i, orientation) item = new QStandardItem(model->headerData(i, orientation).toString());
.toString().replace('\n', ' '));
item->setData(i, kLogicalIndex); item->setData(i, kLogicalIndex);
item->setData(initial.indexOf(i), kVisualIndex); item->setData(initial.indexOf(i), kVisualIndex);
item->setFlags(Qt::ItemIsSelectable item->setFlags(Qt::ItemIsSelectable
@ -88,7 +87,7 @@ void PortStatsFilterDialog::on_tbSelectIn_clicked()
foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes()) foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes())
rows.append(idx.row()); rows.append(idx.row());
std::sort(rows.begin(), rows.end(), qGreater<int>()); qSort(rows.begin(), rows.end(), qGreater<int>());
QModelIndex idx = lvSelected->selectionModel()->currentIndex(); QModelIndex idx = lvSelected->selectionModel()->currentIndex();
int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount();
@ -106,7 +105,7 @@ void PortStatsFilterDialog::on_tbSelectOut_clicked()
foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes()) foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes())
rows.append(idx.row()); rows.append(idx.row());
std::sort(rows.begin(), rows.end(), qGreater<int>()); qSort(rows.begin(), rows.end(), qGreater<int>());
foreach(int row, rows) foreach(int row, rows)
{ {

View File

@ -20,9 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "portstatsmodel.h" #include "portstatsmodel.h"
#include "portgrouplist.h" #include "portgrouplist.h"
#include <QApplication>
#include <QPainter> #include <QPainter>
#include <QPalette>
#include <QPixmapCache> #include <QPixmapCache>
#include <QTimer> #include <QTimer>
@ -137,10 +135,7 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
// States // States
case e_COMBO_STATE: case e_COMBO_STATE:
return QString("Link %1%2%3") return QVariant();
.arg(LinkStateName.at(stats.state().link_state()))
.arg(stats.state().is_transmit_on() ? ";Tx On" : "")
.arg(stats.state().is_capture_on() ? ";Cap On" : "");
// Statistics // Statistics
case e_STAT_FRAMES_RCVD: case e_STAT_FRAMES_RCVD:
@ -161,13 +156,11 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
case e_STAT_BYTES_SENT: case e_STAT_BYTES_SENT:
return QString("%L1").arg(quint64(stats.tx_bytes())); return QString("%L1").arg(quint64(stats.tx_bytes()));
#if 0
case e_STAT_BYTE_SEND_RATE: case e_STAT_BYTE_SEND_RATE:
return QString("%L1").arg(quint64(stats.tx_bps())); return QString("%L1").arg(quint64(stats.tx_bps()));
case e_STAT_BYTE_RECV_RATE: case e_STAT_BYTE_RECV_RATE:
return QString("%L1").arg(quint64(stats.rx_bps())); return QString("%L1").arg(quint64(stats.rx_bps()));
#endif
case e_STAT_BIT_SEND_RATE: case e_STAT_BIT_SEND_RATE:
return QString("%L1").arg(quint64( return QString("%L1").arg(quint64(
@ -277,14 +270,6 @@ QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, in
return QVariant(); return QVariant();
} }
if ((role == Qt::BackgroundRole) && (orientation == Qt::Vertical)
&& qApp->styleSheet().isEmpty())
{
QPalette palette = QApplication::palette();
return section & 0x1 ?
palette.alternateBase() : palette.base();
}
if (role != Qt::DisplayRole) if (role != Qt::DisplayRole)
return QVariant(); return QVariant();
@ -295,23 +280,16 @@ QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, in
if (numPorts.isEmpty() || section >= numPorts.last()) if (numPorts.isEmpty() || section >= numPorts.last())
return QVariant(); return QVariant();
getDomainIndexes(index(0, section), portGroupIdx, portIdx); getDomainIndexes(index(0, section), portGroupIdx, portIdx);
PortGroup *portGroup = pgl->mPortGroups.at(portGroupIdx);
Port *port = portGroup->mPorts.at(portIdx);
portName = QString("Port %1-%2") portName = QString("Port %1-%2")
.arg(portGroup->id()) .arg(pgl->mPortGroups.at(portGroupIdx)->id())
.arg(port->id()); .arg(pgl->mPortGroups.at(portGroupIdx)->mPorts.at(portIdx)->id());
if (portGroupIdx < (uint) pgl->mPortGroups.size() if (portGroupIdx < (uint) pgl->mPortGroups.size()
&& portIdx < (uint) portGroup->mPorts.size()) && portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size())
{ {
if (!port->notes().isEmpty()) if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes()
.isEmpty())
portName += " *"; portName += " *";
portName += "\n";
portName += port->userDescription().isEmpty() ?
port->userAlias() : port->userDescription();
} }
return portName; return portName;
} }
@ -319,11 +297,6 @@ QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, in
return PortStatName.at(section); return PortStatName.at(section);
} }
Qt::DropActions PortStatsModel::supportedDropActions() const
{
return Qt::IgnoreAction; // read-only model, doesn't accept any data
}
void PortStatsModel::portListFromIndex(QModelIndexList indices, void PortStatsModel::portListFromIndex(QModelIndexList indices,
QList<PortGroupAndPortList> &portList) QList<PortGroupAndPortList> &portList)
{ {
@ -385,16 +358,6 @@ void PortStatsModel::when_portListChanged()
endResetModel(); endResetModel();
} }
void PortStatsModel::when_portGroupDataChanged(int /*portGroupId*/, int /*portId*/)
{
if (!columnCount())
return;
// Port (user) description may have changed - update column headers
// TODO: update only the changed ports, not all
emit headerDataChanged(Qt::Horizontal, 0, columnCount()-1);
}
// FIXME: unused? if used, the index calculation row/column needs to be swapped // FIXME: unused? if used, the index calculation row/column needs to be swapped
#if 0 #if 0
void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/) void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/)

View File

@ -26,8 +26,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
class QTimer; class QTimer;
typedef enum { typedef enum {
// Info
e_INFO_START = 0,
e_INFO_USER = e_INFO_START,
e_INFO_END = e_INFO_USER,
// State // State
e_STATE_START = 0, e_STATE_START,
e_COMBO_STATE = e_STATE_START, e_COMBO_STATE = e_STATE_START,
@ -36,16 +43,14 @@ typedef enum {
// Statistics // Statistics
e_STATISTICS_START, e_STATISTICS_START,
e_STAT_FRAMES_SENT = e_STATISTICS_START, e_STAT_FRAMES_RCVD = e_STATISTICS_START,
e_STAT_FRAMES_RCVD, e_STAT_FRAMES_SENT,
e_STAT_BYTES_SENT,
e_STAT_BYTES_RCVD,
e_STAT_FRAME_SEND_RATE, e_STAT_FRAME_SEND_RATE,
e_STAT_FRAME_RECV_RATE, e_STAT_FRAME_RECV_RATE,
#if 0 e_STAT_BYTES_RCVD,
e_STAT_BYTES_SENT,
e_STAT_BYTE_SEND_RATE, e_STAT_BYTE_SEND_RATE,
e_STAT_BYTE_RECV_RATE, e_STAT_BYTE_RECV_RATE,
#endif
e_STAT_BIT_SEND_RATE, e_STAT_BIT_SEND_RATE,
e_STAT_BIT_RECV_RATE, e_STAT_BIT_RECV_RATE,
#if 0 #if 0
@ -64,34 +69,24 @@ typedef enum {
e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS, e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS,
// Info
e_INFO_START,
// XXX: keep hidden rows at end to avoid having to recalculate rows
e_INFO_USER = e_INFO_START,
e_INFO_END = e_INFO_USER,
e_STAT_MAX e_STAT_MAX
} PortStat; } PortStat;
static const QStringList PortStatName = (QStringList() static QStringList PortStatName = (QStringList()
<< "User"
<< "Status" << "Status"
<< "Sent Frames" << "Frames Received"
<< "Received Frames" << "Frames Sent"
<< "Sent Bytes" << "Frame Send Rate (fps)"
<< "Received Bytes" << "Frame Receive Rate (fps)"
<< "Bytes Received"
<< "Send Frame Rate (fps)" << "Bytes Sent"
<< "Receive Frame Rate (fps)" << "Byte Send Rate (Bps)"
#if 0 << "Byte Receive Rate (Bps)"
<< "Send Byte Rate (Bps)" << "Bit Send Rate (bps)"
<< "Receive Byte Rate (Bps)" << "Bit Receive Rate (bps)"
#endif
<< "Send Bit Rate (bps)"
<< "Receive Bit Rate (bps)"
#if 0 #if 0
<< "Frames Received (NIC)" << "Frames Received (NIC)"
<< "Frames Sent (NIC)" << "Frames Sent (NIC)"
@ -103,8 +98,6 @@ static const QStringList PortStatName = (QStringList()
<< "Receive Errors" << "Receive Errors"
<< "Receive Fifo Errors" << "Receive Fifo Errors"
<< "Receive Frame Errors" << "Receive Frame Errors"
<< "User"
); );
static QStringList LinkStateName = (QStringList() static QStringList LinkStateName = (QStringList()
@ -130,8 +123,6 @@ class PortStatsModel : public QAbstractTableModel
QVariant headerData(int section, Qt::Orientation orientation, QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const; int role = Qt::DisplayRole) const;
Qt::DropActions supportedDropActions() const;
class PortGroupAndPortList { class PortGroupAndPortList {
public: public:
uint portGroupId; uint portGroupId;
@ -143,7 +134,6 @@ class PortStatsModel : public QAbstractTableModel
public slots: public slots:
void when_portListChanged(); void when_portListChanged();
//void on_portStatsUpdate(int port, void*stats); //void on_portStatsUpdate(int port, void*stats);
void when_portGroupDataChanged(int portGroupId, int portId);
void when_portGroup_stats_update(quint32 portGroupId); void when_portGroup_stats_update(quint32 portGroupId);
private slots: private slots:

View File

@ -26,17 +26,16 @@ class PortStatsProxyModel : public QSortFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
public: public:
PortStatsProxyModel(int userRow, QObject *parent = 0) PortStatsProxyModel(QObject *parent = 0)
: QSortFilterProxyModel(parent), userRow_(userRow) : QSortFilterProxyModel(parent)
{ {
setFilterRegExp(QRegExp(".*"));
} }
protected: protected:
bool filterAcceptsColumn(int sourceColumn, bool filterAcceptsColumn(int sourceColumn,
const QModelIndex &sourceParent) const const QModelIndex &sourceParent) const
{ {
QModelIndex index = sourceModel()->index(userRow_, sourceColumn,sourceParent); QModelIndex index = sourceModel()->index(0, sourceColumn, sourceParent);
QString user = sourceModel()->data(index).toString(); QString user = sourceModel()->data(index).toString();
return filterRegExp().exactMatch(user) ? true : false; return filterRegExp().exactMatch(user) ? true : false;
@ -44,10 +43,9 @@ protected:
bool filterAcceptsRow(int sourceRow, bool filterAcceptsRow(int sourceRow,
const QModelIndex &/*sourceParent*/) const const QModelIndex &/*sourceParent*/) const
{ {
return sourceRow == userRow_ ? false : true; // Hide row 0 - username (needed only by this filter class)
return (sourceRow > 0) ? true : false;
} }
private:
int userRow_;
}; };
#endif #endif

View File

@ -24,7 +24,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "portstatsfilterdialog.h" #include "portstatsfilterdialog.h"
#include "portstatsmodel.h" #include "portstatsmodel.h"
#include "portstatsproxymodel.h" #include "portstatsproxymodel.h"
#include "rowborderdelegate.h"
#include "streamstatsmodel.h" #include "streamstatsmodel.h"
#include "streamstatswindow.h" #include "streamstatswindow.h"
#include "settings.h" #include "settings.h"
@ -43,8 +42,7 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
this->pgl = pgl; this->pgl = pgl;
model = pgl->getPortStatsModel(); model = pgl->getPortStatsModel();
// Hide 'user' row proxyStatsModel = new PortStatsProxyModel(this);
proxyStatsModel = new PortStatsProxyModel(e_INFO_USER, this);
if (proxyStatsModel) { if (proxyStatsModel) {
proxyStatsModel->setSourceModel(model); proxyStatsModel->setSourceModel(model);
tvPortStats->setModel(proxyStatsModel); tvPortStats->setModel(proxyStatsModel);
@ -57,17 +55,6 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
tvPortStats->verticalHeader()->setDefaultSectionSize( tvPortStats->verticalHeader()->setDefaultSectionSize(
tvPortStats->verticalHeader()->minimumSectionSize()); tvPortStats->verticalHeader()->minimumSectionSize());
// XXX: Set Delegates for port stats view
// RowBorderDelegate: Group related stats using a horizontal line
// IconOnlyDelegate : For status, show only icons not icons+text
tvPortStats->setItemDelegate(
new RowBorderDelegate(
QSet<int>({
e_STAT_FRAMES_SENT,
e_STAT_FRAME_SEND_RATE,
e_STAT_RX_DROPS}),
this));
statusDelegate = new IconOnlyDelegate(this); statusDelegate = new IconOnlyDelegate(this);
#if 0 #if 0
// XXX: Ideally we should use this, but it doesn't work because in // XXX: Ideally we should use this, but it doesn't work because in
@ -81,7 +68,9 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
statusDelegate); statusDelegate);
#else #else
// ... so we use this hard-coded hack // ... so we use this hard-coded hack
tvPortStats->setItemDelegateForRow(e_COMBO_STATE, statusDelegate); tvPortStats->setItemDelegateForRow(
proxyStatsModel ? e_COMBO_STATE-1 : e_COMBO_STATE,
statusDelegate);
#endif #endif
connect(tvPortStats->selectionModel(), connect(tvPortStats->selectionModel(),
@ -100,12 +89,6 @@ PortStatsWindow::~PortStatsWindow()
/* ------------- SLOTS (public) -------------- */ /* ------------- SLOTS (public) -------------- */
void PortStatsWindow::clearCurrentSelection()
{
tvPortStats->selectionModel()->clearCurrentIndex();
tvPortStats->clearSelection();
}
void PortStatsWindow::showMyReservedPortsOnly(bool enabled) void PortStatsWindow::showMyReservedPortsOnly(bool enabled)
{ {
if (!proxyStatsModel) if (!proxyStatsModel)
@ -306,11 +289,6 @@ void PortStatsWindow::on_tbClearAll_clicked()
} }
} }
if (proxyStatsModel) {
for(QModelIndex &index : shownColumns)
index = proxyStatsModel->mapToSource(index);
}
// Get ports corresponding to the shown columns // Get ports corresponding to the shown columns
model->portListFromIndex(shownColumns, portList); model->portListFromIndex(shownColumns, portList);
@ -342,24 +320,9 @@ void PortStatsWindow::on_tbGetStreamStats_clicked()
QDockWidget *statsDock = mainWindow->findChild<QDockWidget*>( QDockWidget *statsDock = mainWindow->findChild<QDockWidget*>(
"statsDock"); "statsDock");
mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dock); mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dock);
// Add stream stats tab to the immediate right of port-stats ...
mainWindow->tabifyDockWidget(statsDock, dock); mainWindow->tabifyDockWidget(statsDock, dock);
mainWindow->splitDockWidget(statsDock, dock, Qt::Horizontal);
// ... make it the currently visible tab ...
dock->show(); dock->show();
dock->raise(); dock->raise();
// ... and set tab remove behaviour
// XXX: unfortunately, there's no direct way to get the TabBar
QList<QTabBar*> tabBars = mainWindow->findChildren<QTabBar*>();
foreach(QTabBar* tabBar, tabBars) {
if (tabBar->tabText(tabBar->currentIndex())
== dock->widget()->windowTitle())
tabBar->setSelectionBehaviorOnRemove(
QTabBar::SelectPreviousTab);
}
} }
// Get stream stats for selected ports, portgroup by portgroup // Get stream stats for selected ports, portgroup by portgroup

View File

@ -38,7 +38,6 @@ public:
~PortStatsWindow(); ~PortStatsWindow();
public slots: public slots:
void clearCurrentSelection();
void showMyReservedPortsOnly(bool enabled); void showMyReservedPortsOnly(bool enabled);
private slots: private slots:

View File

@ -165,7 +165,7 @@
<string>Stop Capture</string> <string>Stop Capture</string>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>End capture on selected port(s)</string> <string>End capture on selecteed port(s)</string>
</property> </property>
<property name="text"> <property name="text">
<string>Stop</string> <string>Stop</string>
@ -279,7 +279,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="XTableView" name="tvPortStats"> <widget class="QTableView" name="tvPortStats">
<property name="selectionBehavior"> <property name="selectionBehavior">
<enum>QAbstractItemView::SelectColumns</enum> <enum>QAbstractItemView::SelectColumns</enum>
</property> </property>
@ -287,14 +287,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>XTableView</class>
<extends>QTableView</extends>
<header>xtableview.h</header>
</customwidget>
</customwidgets>
<resources> <resources>
<include location="ostinato.qrc"/> <include location="ostinato.qrc"/>
</resources> </resources>

View File

@ -19,19 +19,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "portswindow.h" #include "portswindow.h"
#include "applymsg.h"
#include "deviceswidget.h" #include "deviceswidget.h"
#include "fileformat.pb.h"
#include "portconfigdialog.h" #include "portconfigdialog.h"
#include "portgrouplist.h"
#include "portwidget.h"
#include "settings.h" #include "settings.h"
#include "streamswidget.h" #include "streamconfigdialog.h"
#include "streamfileformat.h"
#include "streamlistdelegate.h"
#include "fileformat.pb.h"
#include "xqlocale.h"
#include <QFileInfo>
#include <QInputDialog> #include <QInputDialog>
#include <QItemSelectionModel>
#include <QMainWindow> #include <QMainWindow>
#include <QMessageBox> #include <QMessageBox>
#include <QProgressDialog>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
extern QMainWindow *mainWindow; extern QMainWindow *mainWindow;
@ -39,18 +42,25 @@ extern QMainWindow *mainWindow;
PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent)
: QWidget(parent), proxyPortModel(NULL) : QWidget(parent), proxyPortModel(NULL)
{ {
QAction *sep;
delegate = new StreamListDelegate;
proxyPortModel = new QSortFilterProxyModel(this); proxyPortModel = new QSortFilterProxyModel(this);
//slm = new StreamListModel();
//plm = new PortGroupList();
plm = pgl; plm = pgl;
setupUi(this); setupUi(this);
applyMsg_ = new ApplyMessage();
portWidget->setPortGroupList(plm);
streamsWidget->setPortGroupList(plm);
devicesWidget->setPortGroupList(plm); devicesWidget->setPortGroupList(plm);
tvPortList->header()->hide(); tvPortList->header()->hide();
tvStreamList->setItemDelegate(delegate);
tvStreamList->verticalHeader()->setDefaultSectionSize(
tvStreamList->verticalHeader()->minimumSectionSize());
// Populate PortList Context Menu Actions // Populate PortList Context Menu Actions
tvPortList->addAction(actionNew_Port_Group); tvPortList->addAction(actionNew_Port_Group);
tvPortList->addAction(actionDelete_Port_Group); tvPortList->addAction(actionDelete_Port_Group);
@ -60,18 +70,33 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent)
tvPortList->addAction(actionExclusive_Control); tvPortList->addAction(actionExclusive_Control);
tvPortList->addAction(actionPort_Configuration); tvPortList->addAction(actionPort_Configuration);
// Populate StreamList Context Menu Actions
tvStreamList->addAction(actionNew_Stream);
tvStreamList->addAction(actionEdit_Stream);
tvStreamList->addAction(actionDuplicate_Stream);
tvStreamList->addAction(actionDelete_Stream);
sep = new QAction(this);
sep->setSeparator(true);
tvStreamList->addAction(sep);
tvStreamList->addAction(actionOpen_Streams);
tvStreamList->addAction(actionSave_Streams);
// PortList, StreamList, DeviceWidget actions combined // PortList, StreamList, DeviceWidget actions combined
// make this window's actions // make this window's actions
addActions(tvPortList->actions()); addActions(tvPortList->actions());
QAction *sep = new QAction(this); sep = new QAction(this);
sep->setSeparator(true); sep->setSeparator(true);
addAction(sep); addAction(sep);
addActions(streamsWidget->actions()); addActions(tvStreamList->actions());
sep = new QAction(this); sep = new QAction(this);
sep->setSeparator(true); sep->setSeparator(true);
addAction(sep); addAction(sep);
addActions(devicesWidget->actions()); addActions(devicesWidget->actions());
tvStreamList->setModel(plm->getStreamModel());
// XXX: It would be ideal if we only needed to do the below to // XXX: It would be ideal if we only needed to do the below to
// get the proxy model to do its magic. However, the QModelIndex // get the proxy model to do its magic. However, the QModelIndex
// used by the source model and the proxy model are different // used by the source model and the proxy model are different
@ -97,35 +122,57 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent)
connect(plm->getPortModel(), SIGNAL(modelReset()), connect(plm->getPortModel(), SIGNAL(modelReset()),
SLOT(when_portModel_reset())); SLOT(when_portModel_reset()));
connect(actionPort_Configuration, SIGNAL(triggered()), connect( tvPortList->selectionModel(),
SLOT(when_actionPort_Configuration_triggered()));
connect(tvPortList, SIGNAL(activated(const QModelIndex&)),
SLOT(when_actionPort_Configuration_triggered(const QModelIndex&)));
connect(tvPortList->selectionModel(),
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
this, SLOT(when_portView_currentChanged(const QModelIndex&, this, SLOT(when_portView_currentChanged(const QModelIndex&,
const QModelIndex&))); const QModelIndex&)));
connect(this,
SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)),
portWidget,
SLOT(setCurrentPortIndex(const QModelIndex&, const QModelIndex&)));
connect(this,
SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)),
streamsWidget, SLOT(setCurrentPortIndex(const QModelIndex&)));
connect(this, connect(this,
SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)), SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)),
devicesWidget, SLOT(setCurrentPortIndex(const QModelIndex&))); devicesWidget, SLOT(setCurrentPortIndex(const QModelIndex&)));
connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)),
SLOT(updateStreamViewActions()));
connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)),
SLOT(updateStreamViewActions()));
connect(tvStreamList->selectionModel(),
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
SLOT(updateStreamViewActions()));
connect(tvStreamList->selectionModel(),
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
SLOT(updateStreamViewActions()));
tvStreamList->resizeColumnToContents(StreamModel::StreamIcon);
tvStreamList->resizeColumnToContents(StreamModel::StreamStatus);
// Initially we don't have any ports/streams/devices // Initially we don't have any ports/streams/devices
// - so send signal triggers // - so send signal triggers
when_portView_currentChanged(QModelIndex(), QModelIndex()); when_portView_currentChanged(QModelIndex(), QModelIndex());
updateStreamViewActions();
connect(plm->getStreamModel(),
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
this, SLOT(streamModelDataChanged()));
connect(plm->getStreamModel(),
SIGNAL(modelReset()),
this, SLOT(streamModelDataChanged()));
}
void PortsWindow::streamModelDataChanged()
{
QModelIndex current = tvPortList->currentIndex();
if (proxyPortModel)
current = proxyPortModel->mapToSource(current);
if (plm->isPort(current))
plm->port(current).recalculateAverageRates();
} }
PortsWindow::~PortsWindow() PortsWindow::~PortsWindow()
{ {
delete delegate;
delete proxyPortModel; delete proxyPortModel;
delete applyMsg_;
} }
int PortsWindow::portGroupCount() int PortsWindow::portGroupCount()
@ -238,27 +285,6 @@ bool PortsWindow::saveSession(
return true; return true;
} }
QList<QAction*> PortsWindow::portActions()
{
return tvPortList->actions();
}
QList<QAction*> PortsWindow::streamActions()
{
return streamsWidget->actions();
}
QList<QAction*> PortsWindow::deviceActions()
{
return devicesWidget->actions();
}
void PortsWindow::clearCurrentSelection()
{
tvPortList->selectionModel()->clearCurrentIndex();
tvPortList->clearSelection();
}
void PortsWindow::showMyReservedPortsOnly(bool enabled) void PortsWindow::showMyReservedPortsOnly(bool enabled)
{ {
if (!proxyPortModel) if (!proxyPortModel)
@ -276,6 +302,30 @@ void PortsWindow::showMyReservedPortsOnly(bool enabled)
proxyPortModel->setFilterRegExp(QRegExp("")); proxyPortModel->setFilterRegExp(QRegExp(""));
} }
void PortsWindow::on_tvStreamList_activated(const QModelIndex & index)
{
if (!index.isValid())
{
qDebug("%s: invalid index", __FUNCTION__);
return;
}
qDebug("stream list activated\n");
Port &curPort = plm->port(proxyPortModel ?
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
tvPortList->currentIndex());
QList<Stream*> streams;
streams.append(curPort.mutableStreamByIndex(index.row(), false));
StreamConfigDialog scd(streams, curPort, this);
if (scd.exec() == QDialog::Accepted) {
curPort.recalculateAverageRates();
curPort.setLocalConfigChanged(true);
}
}
void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex, void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
const QModelIndex& previousIndex) const QModelIndex& previousIndex)
{ {
@ -287,12 +337,16 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
previous = proxyPortModel->mapToSource(previous); previous = proxyPortModel->mapToSource(previous);
} }
plm->getStreamModel()->setCurrentPortIndex(current);
updatePortViewActions(currentIndex); updatePortViewActions(currentIndex);
updateStreamViewActions();
qDebug("In %s", __FUNCTION__); qDebug("In %s", __FUNCTION__);
if (previous.isValid() && plm->isPort(previous)) if (previous.isValid() && plm->isPort(previous))
{ {
disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)),
this, SLOT(updatePortRates()));
disconnect(&(plm->port(previous)), disconnect(&(plm->port(previous)),
SIGNAL(localConfigChanged(int, int, bool)), SIGNAL(localConfigChanged(int, int, bool)),
this, this,
@ -313,6 +367,9 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
else if (plm->isPort(current)) else if (plm->isPort(current))
{ {
swDetail->setCurrentIndex(2); // port detail page swDetail->setCurrentIndex(2); // port detail page
updatePortRates();
connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)),
SLOT(updatePortRates()));
connect(&(plm->port(current)), connect(&(plm->port(current)),
SIGNAL(localConfigChanged(int, int, bool)), SIGNAL(localConfigChanged(int, int, bool)),
SLOT(updateApplyHint(int, int, bool))); SLOT(updateApplyHint(int, int, bool)));
@ -320,8 +377,8 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
updateApplyHint(plm->port(current).portGroupId(), updateApplyHint(plm->port(current).portGroupId(),
plm->port(current).id(), true); plm->port(current).id(), true);
else if (plm->port(current).numStreams()) else if (plm->port(current).numStreams())
applyHint->setText("Click <img src=':/icons/control_play'/> " applyHint->setText("Use the Statistics window to transmit "
"to transmit packets"); "packets");
else else
applyHint->setText(""); applyHint->setText("");
} }
@ -370,6 +427,102 @@ void PortsWindow::when_portModel_reset()
when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex());
} }
void PortsWindow::on_averagePacketsPerSec_editingFinished()
{
QModelIndex current = tvPortList->currentIndex();
if (proxyPortModel)
current = proxyPortModel->mapToSource(current);
Q_ASSERT(plm->isPort(current));
bool isOk;
double pps = XLocale().toDouble(averagePacketsPerSec->text(), &isOk);
plm->port(current).setAveragePacketRate(pps);
}
void PortsWindow::on_averageBitsPerSec_editingFinished()
{
QModelIndex current = tvPortList->currentIndex();
if (proxyPortModel)
current = proxyPortModel->mapToSource(current);
Q_ASSERT(plm->isPort(current));
bool isOk;
double bps = XLocale().toDouble(averageBitsPerSec->text(), &isOk);
plm->port(current).setAverageBitRate(bps);
}
void PortsWindow::updatePortRates()
{
QModelIndex current = tvPortList->currentIndex();
if (proxyPortModel)
current = proxyPortModel->mapToSource(current);
if (!current.isValid())
return;
if (!plm->isPort(current))
return;
averagePacketsPerSec->setText(QString("%L1")
.arg(plm->port(current).averagePacketRate(), 0, 'f', 4));
averageBitsPerSec->setText(QString("%L1")
.arg(plm->port(current).averageBitRate(), 0, 'f', 0));
}
void PortsWindow::updateStreamViewActions()
{
QModelIndex current = tvPortList->currentIndex();
if (proxyPortModel)
current = proxyPortModel->mapToSource(current);
// For some reason hasSelection() returns true even if selection size is 0
// so additional check for size introduced
if (tvStreamList->selectionModel()->hasSelection() &&
(tvStreamList->selectionModel()->selection().size() > 0))
{
qDebug("Has selection %d",
tvStreamList->selectionModel()->selection().size());
// If more than one non-contiguous ranges selected,
// disable "New" and "Edit"
if (tvStreamList->selectionModel()->selection().size() > 1)
{
actionNew_Stream->setDisabled(true);
actionEdit_Stream->setDisabled(true);
}
else
{
actionNew_Stream->setEnabled(true);
actionEdit_Stream->setEnabled(true);
}
// Duplicate/Delete are always enabled as long as we have a selection
actionDuplicate_Stream->setEnabled(true);
actionDelete_Stream->setEnabled(true);
}
else
{
qDebug("No selection");
if (plm->isPort(current))
actionNew_Stream->setEnabled(true);
else
actionNew_Stream->setDisabled(true);
actionEdit_Stream->setDisabled(true);
actionDuplicate_Stream->setDisabled(true);
actionDelete_Stream->setDisabled(true);
}
actionOpen_Streams->setEnabled(plm->isPort(current));
actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0);
}
void PortsWindow::updateApplyHint(int /*portGroupId*/, int /*portId*/, void PortsWindow::updateApplyHint(int /*portGroupId*/, int /*portId*/,
bool configChanged) bool configChanged)
{ {
@ -377,12 +530,9 @@ void PortsWindow::updateApplyHint(int /*portGroupId*/, int /*portId*/,
applyHint->setText("Configuration has changed - " applyHint->setText("Configuration has changed - "
"<font color='red'><b>click Apply</b></font> " "<font color='red'><b>click Apply</b></font> "
"to activate the changes"); "to activate the changes");
else if (plm->getStreamModel()->rowCount() > 0)
applyHint->setText("Configuration activated - "
"click <img src=':/icons/control_play'/> "
"to transmit packets");
else else
applyHint->setText("Configuration activated"); applyHint->setText("Configuration activated. Use the Statistics "
"window to transmit packets");
} }
void PortsWindow::updatePortViewActions(const QModelIndex& currentIndex) void PortsWindow::updatePortViewActions(const QModelIndex& currentIndex)
@ -497,11 +647,6 @@ void PortsWindow::on_pbApply_clicked()
goto _exit; goto _exit;
} }
disconnect(applyMsg_);
connect(&(plm->portGroup(curPortGroup)), SIGNAL(applyFinished()),
applyMsg_, SLOT(hide()));
applyMsg_->show();
// FIXME(HI): shd this be a signal? // FIXME(HI): shd this be a signal?
//portGroup.when_configApply(port); //portGroup.when_configApply(port);
// FIXME(MED): mixing port id and index!!! // FIXME(MED): mixing port id and index!!!
@ -602,11 +747,9 @@ void PortsWindow::on_actionExclusive_Control_triggered(bool checked)
} }
} }
void PortsWindow::when_actionPort_Configuration_triggered( void PortsWindow::on_actionPort_Configuration_triggered()
const QModelIndex &portIndex)
{ {
QModelIndex current = portIndex.isValid() ? QModelIndex current = tvPortList->selectionModel()->currentIndex();
portIndex : tvPortList->selectionModel()->currentIndex();
if (proxyPortModel) if (proxyPortModel)
current = proxyPortModel->mapToSource(current); current = proxyPortModel->mapToSource(current);
@ -621,8 +764,6 @@ void PortsWindow::when_actionPort_Configuration_triggered(
// TODO: extend Port::protoDataCopyInto() to accept an optional param // TODO: extend Port::protoDataCopyInto() to accept an optional param
// which says copy only modifiable fields // which says copy only modifiable fields
//plm->port(current).protoDataCopyInto(&config); //plm->port(current).protoDataCopyInto(&config);
config.set_description(port.systemDescription().toStdString());
config.set_user_description(port.userDescription().toStdString());
config.set_transmit_mode(port.transmitMode()); config.set_transmit_mode(port.transmitMode());
config.set_is_tracking_stream_stats(port.trackStreamStats()); config.set_is_tracking_stream_stats(port.trackStreamStats());
config.set_is_exclusive_control(port.hasExclusiveControl()); config.set_is_exclusive_control(port.hasExclusiveControl());
@ -633,3 +774,258 @@ void PortsWindow::when_actionPort_Configuration_triggered(
if (dialog.exec() == QDialog::Accepted) if (dialog.exec() == QDialog::Accepted)
plm->portGroup(current.parent()).modifyPort(current.row(), config); plm->portGroup(current.parent()).modifyPort(current.row(), config);
} }
void PortsWindow::on_actionNew_Stream_triggered()
{
qDebug("New Stream Action");
QItemSelectionModel* selectionModel = tvStreamList->selectionModel();
if (selectionModel->selection().size() > 1) {
qDebug("%s: Unexpected selection size %d, can't add", __FUNCTION__,
selectionModel->selection().size());
return;
}
// In case nothing is selected, insert 1 row at the end
StreamModel *streamModel = plm->getStreamModel();
int row = streamModel->rowCount(), count = 1;
// In case we have a single range selected; insert as many rows as
// in the singe selected range before the top of the selected range
if (selectionModel->selection().size() == 1)
{
row = selectionModel->selection().at(0).top();
count = selectionModel->selection().at(0).height();
}
Port &curPort = plm->port(proxyPortModel ?
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
tvPortList->currentIndex());
QList<Stream*> streams;
for (int i = 0; i < count; i++)
streams.append(new Stream);
StreamConfigDialog scd(streams, curPort, this);
scd.setWindowTitle(tr("Add Stream"));
if (scd.exec() == QDialog::Accepted)
streamModel->insert(row, streams);
}
void PortsWindow::on_actionEdit_Stream_triggered()
{
qDebug("Edit Stream Action");
QItemSelectionModel* streamModel = tvStreamList->selectionModel();
if (!streamModel->hasSelection())
return;
Port &curPort = plm->port(proxyPortModel ?
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
tvPortList->currentIndex());
QList<Stream*> streams;
foreach(QModelIndex index, streamModel->selectedRows())
streams.append(curPort.mutableStreamByIndex(index.row(), false));
StreamConfigDialog scd(streams, curPort, this);
if (scd.exec() == QDialog::Accepted) {
curPort.recalculateAverageRates();
curPort.setLocalConfigChanged(true);
}
}
void PortsWindow::on_actionDuplicate_Stream_triggered()
{
QItemSelectionModel* model = tvStreamList->selectionModel();
QModelIndex current = tvPortList->selectionModel()->currentIndex();
qDebug("Duplicate Stream Action");
if (proxyPortModel)
current = proxyPortModel->mapToSource(current);
if (model->hasSelection())
{
bool isOk;
int count = QInputDialog::getInt(this, "Duplicate Streams",
"Count", 1, 1, 9999, 1, &isOk);
if (!isOk)
return;
QList<int> list;
foreach(QModelIndex index, model->selectedRows())
list.append(index.row());
plm->port(current).duplicateStreams(list, count);
}
else
qDebug("No selection");
}
void PortsWindow::on_actionDelete_Stream_triggered()
{
qDebug("Delete Stream Action");
QModelIndex index;
if (tvStreamList->selectionModel()->hasSelection())
{
qDebug("SelectedIndexes %d",
tvStreamList->selectionModel()->selectedRows().size());
while(tvStreamList->selectionModel()->selectedRows().size())
{
index = tvStreamList->selectionModel()->selectedRows().at(0);
plm->getStreamModel()->removeRows(index.row(), 1);
}
}
else
qDebug("No selection");
}
void PortsWindow::on_actionOpen_Streams_triggered()
{
qDebug("Open Streams Action");
QStringList fileTypes = StreamFileFormat::supportedFileTypes(
StreamFileFormat::kOpenFile);
QString fileType;
QModelIndex current = tvPortList->selectionModel()->currentIndex();
static QString dirName;
QString fileName;
QString errorStr;
bool append = true;
bool ret;
if (proxyPortModel)
current = proxyPortModel->mapToSource(current);
Q_ASSERT(plm->isPort(current));
if (fileTypes.size())
fileType = fileTypes.at(0);
fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"),
dirName, fileTypes.join(";;"), &fileType);
if (fileName.isEmpty())
goto _exit;
if (tvStreamList->model()->rowCount())
{
QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(),
tr("Append to existing streams? Or overwrite?"),
QMessageBox::NoButton, this);
QPushButton *appendBtn = msgBox.addButton(tr("Append"),
QMessageBox::ActionRole);
QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"),
QMessageBox::ActionRole);
QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel);
msgBox.exec();
if (msgBox.clickedButton() == cancelBtn)
goto _exit;
else if (msgBox.clickedButton() == appendBtn)
append = true;
else if (msgBox.clickedButton() == overwriteBtn)
append = false;
else
Q_ASSERT(false);
}
ret = plm->port(current).openStreams(fileName, append, errorStr);
if (!ret || !errorStr.isEmpty())
{
QMessageBox msgBox(this);
QStringList str = errorStr.split("\n\n\n\n");
msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical);
msgBox.setWindowTitle(qApp->applicationName());
msgBox.setText(str.at(0));
if (str.size() > 1)
msgBox.setDetailedText(str.at(1));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
}
dirName = QFileInfo(fileName).absolutePath();
_exit:
return;
}
void PortsWindow::on_actionSave_Streams_triggered()
{
qDebug("Save Streams Action");
QModelIndex current = tvPortList->selectionModel()->currentIndex();
static QString fileName;
QStringList fileTypes = StreamFileFormat::supportedFileTypes(
StreamFileFormat::kSaveFile);
QString fileType;
QString errorStr;
QFileDialog::Options options;
if (proxyPortModel)
current = proxyPortModel->mapToSource(current);
// On Mac OS with Native Dialog, getSaveFileName() ignores fileType
// which we need
#if defined(Q_OS_MAC)
options |= QFileDialog::DontUseNativeDialog;
#endif
if (fileTypes.size())
fileType = fileTypes.at(0);
Q_ASSERT(plm->isPort(current));
_retry:
fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"),
fileName, fileTypes.join(";;"), &fileType, options);
if (fileName.isEmpty())
goto _exit;
if (QFileInfo(fileName).suffix().isEmpty()) {
QString fileExt = fileType.section(QRegExp("[\\*\\)]"), 1, 1);
qDebug("Adding extension '%s' to '%s'",
qPrintable(fileExt), qPrintable(fileName));
fileName.append(fileExt);
if (QFileInfo(fileName).exists()) {
if (QMessageBox::warning(this, tr("Overwrite File?"),
QString("The file \"%1\" already exists.\n\n"
"Do you wish to overwrite it?")
.arg(QFileInfo(fileName).fileName()),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No) != QMessageBox::Yes)
goto _retry;
}
}
fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed();
if (!fileType.startsWith("Ostinato")
&& !fileType.startsWith("Python"))
{
if (QMessageBox::warning(this, tr("Ostinato"),
QString("You have chosen to save in %1 format. All stream "
"attributes may not be saved in this format.\n\n"
"It is recommended to save in native Ostinato format.\n\n"
"Continue to save in %2 format?").arg(fileType).arg(fileType),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No) != QMessageBox::Yes)
goto _retry;
}
// TODO: all or selected?
if (!plm->port(current).saveStreams(fileName, fileType, errorStr))
QMessageBox::critical(this, qApp->applicationName(), errorStr);
else if (!errorStr.isEmpty())
QMessageBox::warning(this, qApp->applicationName(), errorStr);
fileName = QFileInfo(fileName).absolutePath();
_exit:
return;
}

View File

@ -20,12 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#ifndef _PORTS_WINDOW_H #ifndef _PORTS_WINDOW_H
#define _PORTS_WINDOW_H #define _PORTS_WINDOW_H
#include "ui_portswindow.h"
#include <QWidget> #include <QWidget>
#include <QAbstractItemModel>
#include "ui_portswindow.h"
#include "portgrouplist.h"
class ApplyMessage; class QAbstractItemDelegate;
class PortGroupList;
class QProgressDialog; class QProgressDialog;
class QSortFilterProxyModel; class QSortFilterProxyModel;
@ -37,6 +37,9 @@ class PortsWindow : public QWidget, private Ui::PortsWindow
{ {
Q_OBJECT Q_OBJECT
//QAbstractItemModel *slm; // stream list model
PortGroupList *plm;
public: public:
PortsWindow(PortGroupList *pgl, QWidget *parent = 0); PortsWindow(PortGroupList *pgl, QWidget *parent = 0);
~PortsWindow(); ~PortsWindow();
@ -50,22 +53,27 @@ public:
QString &error, QString &error,
QProgressDialog *progress = NULL); QProgressDialog *progress = NULL);
QList<QAction*> portActions();
QList<QAction*> streamActions();
QList<QAction*> deviceActions();
signals: signals:
void currentPortChanged(const QModelIndex &current, void currentPortChanged(const QModelIndex &current,
const QModelIndex &previous); const QModelIndex &previous);
private:
QString lastNewPortGroup;
QAbstractItemDelegate *delegate;
QSortFilterProxyModel *proxyPortModel;
public slots: public slots:
void clearCurrentSelection();
void showMyReservedPortsOnly(bool enabled); void showMyReservedPortsOnly(bool enabled);
private slots: private slots:
void updateApplyHint(int portGroupId, int portId, bool configChanged); void updateApplyHint(int portGroupId, int portId, bool configChanged);
void updatePortViewActions(const QModelIndex& currentIndex); void updatePortViewActions(const QModelIndex& currentIndex);
void updateStreamViewActions();
void on_averagePacketsPerSec_editingFinished();
void on_averageBitsPerSec_editingFinished();
void updatePortRates();
void on_tvStreamList_activated(const QModelIndex & index);
void when_portView_currentChanged(const QModelIndex& currentIndex, void when_portView_currentChanged(const QModelIndex& currentIndex,
const QModelIndex& previousIndex); const QModelIndex& previousIndex);
void when_portModel_dataChanged(const QModelIndex& topLeft, void when_portModel_dataChanged(const QModelIndex& topLeft,
@ -80,14 +88,17 @@ private slots:
void on_actionDisconnect_Port_Group_triggered(); void on_actionDisconnect_Port_Group_triggered();
void on_actionExclusive_Control_triggered(bool checked); void on_actionExclusive_Control_triggered(bool checked);
void when_actionPort_Configuration_triggered( void on_actionPort_Configuration_triggered();
const QModelIndex &portIndex = QModelIndex());
private: void on_actionNew_Stream_triggered();
PortGroupList *plm; void on_actionEdit_Stream_triggered();
QString lastNewPortGroup; void on_actionDuplicate_Stream_triggered();
QSortFilterProxyModel *proxyPortModel; void on_actionDelete_Stream_triggered();
ApplyMessage *applyMsg_;
void on_actionOpen_Streams_triggered();
void on_actionSave_Streams_triggered();
void streamModelDataChanged();
}; };
#endif #endif

View File

@ -39,7 +39,7 @@
<widget class="QStackedWidget" name="swDetail"> <widget class="QStackedWidget" name="swDetail">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch> <horstretch>2</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
@ -51,7 +51,7 @@
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>&lt;p&gt;&lt;b&gt;Welcome to Ostinato!&lt;/b&gt;&lt;/p&gt; <string>&lt;p&gt;&lt;b&gt;Welcome to Ostinato&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;The port list on the left contains all the ports on which you can transmit packets.&lt;/p&gt; &lt;p&gt;The port list on the left contains all the ports on which you can transmit packets.&lt;/p&gt;
&lt;p&gt;Ports belong to a port group. Make sure the Port Group has a &lt;img src=&quot;:/icons/bullet_green.png&quot;/&gt; next to it, then double click the port group to show or hide the ports in the port group.&lt;/p&gt; &lt;p&gt;Ports belong to a port group. Make sure the Port Group has a &lt;img src=&quot;:/icons/bullet_green.png&quot;/&gt; next to it, then double click the port group to show or hide the ports in the port group.&lt;/p&gt;
&lt;p&gt;To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.&lt;/p&gt; &lt;p&gt;To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.&lt;/p&gt;
@ -126,16 +126,7 @@
</widget> </widget>
<widget class="QWidget" name="portDetail"> <widget class="QWidget" name="portDetail">
<layout class="QVBoxLayout"> <layout class="QVBoxLayout">
<property name="leftMargin"> <property name="margin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -147,16 +138,7 @@
<enum>QFrame::Raised</enum> <enum>QFrame::Raised</enum>
</property> </property>
<layout class="QHBoxLayout"> <layout class="QHBoxLayout">
<property name="leftMargin"> <property name="margin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number> <number>3</number>
</property> </property>
<item> <item>
@ -213,10 +195,80 @@
</attribute> </attribute>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout">
<item> <item>
<widget class="PortWidget" name="portWidget" native="true"/> <layout class="QHBoxLayout">
<item>
<widget class="QRadioButton" name="radioButton">
<property name="text">
<string>Avg pps</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="averagePacketsPerSec"/>
</item>
<item>
<widget class="QRadioButton" name="radioButton_2">
<property name="text">
<string>Avg bps</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="averageBitsPerSec">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item> </item>
<item> <item>
<widget class="StreamsWidget" name="streamsWidget" native="true"/> <widget class="XTableView" name="tvStreamList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="whatsThis">
<string>This is the stream list for the selected port
A stream is a sequence of one or more packets
Right-click to create a stream</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -274,6 +326,33 @@
<string>Disconnect Port Group</string> <string>Disconnect Port Group</string>
</property> </property>
</action> </action>
<action name="actionNew_Stream">
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/stream_add.png</normaloff>:/icons/stream_add.png</iconset>
</property>
<property name="text">
<string>New Stream</string>
</property>
</action>
<action name="actionDelete_Stream">
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/stream_delete.png</normaloff>:/icons/stream_delete.png</iconset>
</property>
<property name="text">
<string>Delete Stream</string>
</property>
</action>
<action name="actionEdit_Stream">
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/stream_edit.png</normaloff>:/icons/stream_edit.png</iconset>
</property>
<property name="text">
<string>Edit Stream</string>
</property>
</action>
<action name="actionExclusive_Control"> <action name="actionExclusive_Control">
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
@ -282,11 +361,30 @@
<string>Exclusive Port Control (EXPERIMENTAL)</string> <string>Exclusive Port Control (EXPERIMENTAL)</string>
</property> </property>
</action> </action>
<action name="actionOpen_Streams">
<property name="text">
<string>Open Streams ...</string>
</property>
</action>
<action name="actionSave_Streams">
<property name="text">
<string>Save Streams ...</string>
</property>
</action>
<action name="actionPort_Configuration"> <action name="actionPort_Configuration">
<property name="text"> <property name="text">
<string>Port Configuration ...</string> <string>Port Configuration ...</string>
</property> </property>
</action> </action>
<action name="actionDuplicate_Stream">
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/stream_duplicate.png</normaloff>:/icons/stream_duplicate.png</iconset>
</property>
<property name="text">
<string>Duplicate Stream</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
@ -301,20 +399,46 @@
<header>xtreeview.h</header> <header>xtreeview.h</header>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>StreamsWidget</class> <class>XTableView</class>
<extends>QWidget</extends> <extends>QTableView</extends>
<header>streamswidget.h</header> <header>xtableview.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>PortWidget</class>
<extends>QWidget</extends>
<header>portwidget.h</header>
<container>1</container>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources> <resources>
<include location="ostinato.qrc"/> <include location="ostinato.qrc"/>
</resources> </resources>
<connections/> <connections>
<connection>
<sender>radioButton</sender>
<signal>toggled(bool)</signal>
<receiver>averagePacketsPerSec</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>326</x>
<y>80</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
<y>79</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioButton_2</sender>
<signal>toggled(bool)</signal>
<receiver>averageBitsPerSec</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>523</x>
<y>80</y>
</hint>
<hint type="destinationlabel">
<x>651</x>
<y>88</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View File

@ -1,186 +0,0 @@
/*
Copyright (C) 2010 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "portwidget.h"
#include "portgrouplist.h"
#include "xqlocale.h"
#include <cfloat>
PortWidget::PortWidget(QWidget *parent)
: QWidget(parent)
{
setupUi(this);
}
void PortWidget::setPortGroupList(PortGroupList *portGroups)
{
plm = portGroups;
connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)),
SLOT(updatePortActions()));
connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)),
SLOT(updatePortActions()));
connect(plm->getStreamModel(), SIGNAL(modelReset()),
SLOT(updatePortActions()));
updatePortActions();
}
PortWidget::~PortWidget()
{
}
void PortWidget::setCurrentPortIndex(const QModelIndex &currentIndex,
const QModelIndex &previousIndex)
{
if (!plm)
return;
qDebug("In %s", __PRETTY_FUNCTION__);
// XXX: We assume indices corresponds to sourceModel, not proxyModel
// - caller/sender should ensure this
// Disconnect previous port
if (plm->isPort(previousIndex))
disconnect(&(plm->port(previousIndex)),
SIGNAL(portRateChanged(int, int)),
this, SLOT(updatePortRates()));
if (!plm->isPort(currentIndex)) {
currentPortIndex_ = QModelIndex(); // set to invalid
return;
}
currentPortIndex_ = currentIndex;
// Connect current port
connect(&(plm->port(currentPortIndex_)),
SIGNAL(portRateChanged(int, int)),
this, SLOT(updatePortRates()));
double speed = plm->port(currentPortIndex_).speed();
portSpeed->setText(QString("Max %L1 Mbps").arg(speed));
rbLoad->setVisible(speed > 0);
averageLoadPercent->setVisible(speed > 0);
speedSep->setVisible(speed > 0);
portSpeed->setVisible(speed > 0);
updatePortRates();
updatePortActions();
}
void PortWidget::on_startTx_clicked()
{
Q_ASSERT(plm->isPort(currentPortIndex_));
QModelIndex curPortGroup = plm->getPortModel()->parent(currentPortIndex_);
Q_ASSERT(curPortGroup.isValid());
Q_ASSERT(plm->isPortGroup(curPortGroup));
QList<uint> portList({plm->port(currentPortIndex_).id()});
plm->portGroup(curPortGroup).startTx(&portList);
}
void PortWidget::on_stopTx_clicked()
{
Q_ASSERT(plm->isPort(currentPortIndex_));
QModelIndex curPortGroup = plm->getPortModel()->parent(currentPortIndex_);
Q_ASSERT(curPortGroup.isValid());
Q_ASSERT(plm->isPortGroup(curPortGroup));
QList<uint> portList({plm->port(currentPortIndex_).id()});
plm->portGroup(curPortGroup).stopTx(&portList);
}
void PortWidget::on_averageLoadPercent_editingFinished()
{
Q_ASSERT(plm->isPort(currentPortIndex_));
plm->port(currentPortIndex_).setAverageLoadRate(
averageLoadPercent->value()/100);
}
void PortWidget::on_averagePacketsPerSec_editingFinished()
{
Q_ASSERT(plm->isPort(currentPortIndex_));
bool isOk;
double pps = XLocale().toPacketsPerSecond(averagePacketsPerSec->text(),
&isOk);
if (isOk)
plm->port(currentPortIndex_).setAveragePacketRate(pps);
else
updatePortRates();
}
void PortWidget::on_averageBitsPerSec_editingFinished()
{
Q_ASSERT(plm->isPort(currentPortIndex_));
bool isOk;
double bps = XLocale().toBitsPerSecond(averageBitsPerSec->text(), &isOk);
if (isOk)
plm->port(currentPortIndex_).setAverageBitRate(bps);
else
updatePortRates();
}
void PortWidget::updatePortRates()
{
if (!currentPortIndex_.isValid())
return;
if (!plm->isPort(currentPortIndex_))
return;
// XXX: pps/bps input widget is a LineEdit and not a SpinBox
// because we want users to be able to enter values in various
// units e.g. "1.5 Mbps", "1000K", "50" (bps) etc.
// XXX: It's a considered decision NOT to show frame rate in
// higher units of Kpps and Mpps as most users may not be
// familiar with those and also we want frame rate to have a
// high resolution for input e.g. if user enters 1,488,095.2381
// it should NOT be shown as 1.4881 Mpps
averagePacketsPerSec->setText(QString("%L1 pps")
.arg(plm->port(currentPortIndex_).averagePacketRate(), 0, 'f', 4));
averageBitsPerSec->setText(XLocale().toBitRateString(
plm->port(currentPortIndex_).averageBitRate()));
averageLoadPercent->setValue(
plm->port(currentPortIndex_).averageLoadRate()*100);
}
void PortWidget::updatePortActions()
{
if (!plm->isPort(currentPortIndex_))
return;
startTx->setEnabled(plm->port(currentPortIndex_).numStreams() > 0);
stopTx->setEnabled(plm->port(currentPortIndex_).numStreams() > 0);
}

View File

@ -1,61 +0,0 @@
/*
Copyright (C) 2010 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _PORT_WIDGET_H
#define _PORT_WIDGET_H
#include "ui_portwidget.h"
#include <QModelIndex>
#include <QWidget>
class PortGroupList;
class PortWidget : public QWidget, private Ui::PortWidget
{
Q_OBJECT
public:
PortWidget(QWidget *parent = 0);
~PortWidget();
void setPortGroupList(PortGroupList *portGroups);
public slots:
void setCurrentPortIndex(const QModelIndex &currentIndex,
const QModelIndex &previousIndex);
private slots:
void on_startTx_clicked();
void on_stopTx_clicked();
void on_averageLoadPercent_editingFinished();
void on_averagePacketsPerSec_editingFinished();
void on_averageBitsPerSec_editingFinished();
void updatePortActions();
void updatePortRates();
private:
PortGroupList *plm{nullptr}; // FIXME: rename to portGroups_?
QModelIndex currentPortIndex_;
};
#endif

View File

@ -1,218 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PortWidget</class>
<widget class="QWidget" name="PortWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>806</width>
<height>73</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QToolButton" name="startTx">
<property name="toolTip">
<string>Start Transmit</string>
</property>
<property name="statusTip">
<string>Start transmit on selected port</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/control_play.png</normaloff>:/icons/control_play.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="stopTx">
<property name="toolTip">
<string>Stop Transmit</string>
</property>
<property name="statusTip">
<string>Stop transmit on selected port</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/control_stop.png</normaloff>:/icons/control_stop.png</iconset>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="rateSep">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton">
<property name="text">
<string>Frame Rate</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="averagePacketsPerSec"/>
</item>
<item>
<widget class="QRadioButton" name="radioButton_2">
<property name="text">
<string>Bit Rate</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="averageBitsPerSec">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Bit rate on the line including overhead such as Preamble, IPG, FCS etc.</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbLoad">
<property name="text">
<string>Load</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="averageLoadPercent">
<property name="enabled">
<bool>false</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="decimals">
<number>4</number>
</property>
<property name="maximum">
<double>999.999900000000025</double>
</property>
</widget>
</item>
<item>
<widget class="Line" name="speedSep">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="portSpeed">
<property name="toolTip">
<string>Port Speed</string>
</property>
<property name="statusTip">
<string/>
</property>
<property name="text">
<string>Max speed</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="ostinato.qrc"/>
</resources>
<connections>
<connection>
<sender>radioButton</sender>
<signal>toggled(bool)</signal>
<receiver>averagePacketsPerSec</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>450</x>
<y>44</y>
</hint>
<hint type="destinationlabel">
<x>593</x>
<y>45</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioButton_2</sender>
<signal>toggled(bool)</signal>
<receiver>averageBitsPerSec</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>661</x>
<y>44</y>
</hint>
<hint type="destinationlabel">
<x>804</x>
<y>45</y>
</hint>
</hints>
</connection>
<connection>
<sender>rbLoad</sender>
<signal>toggled(bool)</signal>
<receiver>averageLoadPercent</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>281</x>
<y>43</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>45</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -21,11 +21,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "../common/ostprotolib.h" #include "../common/ostprotolib.h"
#include "settings.h" #include "settings.h"
#include "thememanager.h"
#include <QFileDialog> #include <QFileDialog>
#include <QtGlobal> #include <QtGlobal>
#include <QXmlStreamReader>
#if defined(Q_OS_WIN32) #if defined(Q_OS_WIN32)
QString kGzipPathDefaultValue; QString kGzipPathDefaultValue;
@ -41,7 +39,6 @@ Preferences::Preferences()
setupUi(this); setupUi(this);
// Program paths
wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey,
kWiresharkPathDefaultValue).toString()); kWiresharkPathDefaultValue).toString());
tsharkPathEdit->setText(appSettings->value(kTsharkPathKey, tsharkPathEdit->setText(appSettings->value(kTsharkPathKey,
@ -53,10 +50,6 @@ Preferences::Preferences()
awkPathEdit->setText(appSettings->value(kAwkPathKey, awkPathEdit->setText(appSettings->value(kAwkPathKey,
kAwkPathDefaultValue).toString()); kAwkPathDefaultValue).toString());
// Theme
theme->addItems(ThemeManager::instance()->themeList());
theme->setCurrentText(appSettings->value(kThemeKey).toString());
// TODO(only if required): kUserKey // TODO(only if required): kUserKey
} }
@ -84,7 +77,6 @@ void Preferences::initDefaults()
void Preferences::accept() void Preferences::accept()
{ {
// Program paths
appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text());
appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text()); appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text());
appSettings->setValue(kGzipPathKey, gzipPathEdit->text()); appSettings->setValue(kGzipPathKey, gzipPathEdit->text());
@ -97,9 +89,6 @@ void Preferences::accept()
appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(),
appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString());
// Theme
ThemeManager::instance()->setTheme(theme->currentText());
QDialog::accept(); QDialog::accept();
} }
@ -109,31 +98,6 @@ void Preferences::on_wiresharkPathButton_clicked()
path = QFileDialog::getOpenFileName(0, "Locate Wireshark", path = QFileDialog::getOpenFileName(0, "Locate Wireshark",
wiresharkPathEdit->text()); wiresharkPathEdit->text());
#ifdef Q_OS_MAC
// Find executable inside app bundle using Info.plist
if (!path.isEmpty() && path.endsWith(".app")) {
QFile plist(path+"/Contents/Info.plist");
plist.open(QIODevice::ReadOnly);
QXmlStreamReader xml(&plist);
while (!xml.atEnd()) {
xml.readNext();
if (xml.isStartElement()
&& (xml.name() == "key")
&& (xml.readElementText() == "CFBundleExecutable")) {
xml.readNext(); // </key>
xml.readNext(); // <string>
if (xml.isStartElement() && (xml.name() == "string"))
path = path+"/Contents/MacOs/"+xml.readElementText();
break;
}
if (xml.hasError())
qDebug("%lld:%lld Error reading Info.plist: %s",
xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.errorString()));
}
}
#endif
if (!path.isEmpty()) if (!path.isEmpty())
wiresharkPathEdit->setText(path); wiresharkPathEdit->setText(path);

View File

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0" >
<ui version="4.0">
<class>Preferences</class> <class>Preferences</class>
<widget class="QDialog" name="Preferences"> <widget class="QDialog" name="Preferences" >
<property name="geometry"> <property name="geometry" >
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
@ -10,149 +9,148 @@
<height>220</height> <height>220</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle" >
<string>Preferences</string> <string>Preferences</string>
</property> </property>
<property name="windowIcon"> <property name="windowIcon" >
<iconset resource="ostinato.qrc"> <iconset resource="ostinato.qrc" >:/icons/preferences.png</iconset>
<normaloff>:/icons/preferences.png</normaloff>:/icons/preferences.png</iconset>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout" >
<item> <item>
<widget class="QFrame" name="frame"> <widget class="QFrame" name="frame" >
<property name="frameShape"> <property name="frameShape" >
<enum>QFrame::Box</enum> <enum>QFrame::Box</enum>
</property> </property>
<property name="frameShadow"> <property name="frameShadow" >
<enum>QFrame::Sunken</enum> <enum>QFrame::Sunken</enum>
</property> </property>
<layout class="QGridLayout"> <layout class="QGridLayout" >
<item row="0" column="0"> <item row="0" column="0" >
<widget class="QLabel" name="label"> <widget class="QLabel" name="label" >
<property name="text"> <property name="text" >
<string>'wireshark' Path</string> <string>'wireshark' Path</string>
</property> </property>
<property name="buddy"> <property name="buddy" >
<cstring>wiresharkPathEdit</cstring> <cstring>wiresharkPathEdit</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1" >
<widget class="QLineEdit" name="wiresharkPathEdit"> <widget class="QLineEdit" name="wiresharkPathEdit" >
<property name="enabled"> <property name="enabled" >
<bool>true</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="2"> <item row="0" column="2" >
<widget class="QToolButton" name="wiresharkPathButton"> <widget class="QToolButton" name="wiresharkPathButton" >
<property name="text"> <property name="text" >
<string>...</string> <string>...</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0" >
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2" >
<property name="text"> <property name="text" >
<string>'tshark' Path</string> <string>'tshark' Path</string>
</property> </property>
<property name="buddy"> <property name="buddy" >
<cstring>tsharkPathEdit</cstring> <cstring>tsharkPathEdit</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1" >
<widget class="QLineEdit" name="tsharkPathEdit"> <widget class="QLineEdit" name="tsharkPathEdit" >
<property name="enabled"> <property name="enabled" >
<bool>true</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="2"> <item row="1" column="2" >
<widget class="QToolButton" name="tsharkPathButton"> <widget class="QToolButton" name="tsharkPathButton" >
<property name="text"> <property name="text" >
<string>...</string> <string>...</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0" >
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5" >
<property name="text"> <property name="text" >
<string>'gzip' Path</string> <string>'gzip' Path</string>
</property> </property>
<property name="buddy"> <property name="buddy" >
<cstring>diffPathEdit</cstring> <cstring>diffPathEdit</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1" >
<widget class="QLineEdit" name="gzipPathEdit"> <widget class="QLineEdit" name="gzipPathEdit" >
<property name="enabled"> <property name="enabled" >
<bool>true</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="2"> <item row="2" column="2" >
<widget class="QToolButton" name="gzipPathButton"> <widget class="QToolButton" name="gzipPathButton" >
<property name="text"> <property name="text" >
<string>...</string> <string>...</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0" >
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3" >
<property name="text"> <property name="text" >
<string>'diff' Path</string> <string>'diff' Path</string>
</property> </property>
<property name="buddy"> <property name="buddy" >
<cstring>diffPathEdit</cstring> <cstring>diffPathEdit</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1" >
<widget class="QLineEdit" name="diffPathEdit"> <widget class="QLineEdit" name="diffPathEdit" >
<property name="enabled"> <property name="enabled" >
<bool>true</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="2"> <item row="3" column="2" >
<widget class="QToolButton" name="diffPathButton"> <widget class="QToolButton" name="diffPathButton" >
<property name="text"> <property name="text" >
<string>...</string> <string>...</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="4" column="0" >
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4" >
<property name="text"> <property name="text" >
<string>'awk' Path</string> <string>'awk' Path</string>
</property> </property>
<property name="buddy"> <property name="buddy" >
<cstring>awkPathEdit</cstring> <cstring>awkPathEdit</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="4" column="1" >
<widget class="QLineEdit" name="awkPathEdit"> <widget class="QLineEdit" name="awkPathEdit" >
<property name="enabled"> <property name="enabled" >
<bool>true</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="2"> <item row="4" column="2" >
<widget class="QToolButton" name="awkPathButton"> <widget class="QToolButton" name="awkPathButton" >
<property name="text"> <property name="text" >
<string>...</string> <string>...</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="5" column="1" >
<spacer> <spacer>
<property name="orientation"> <property name="orientation" >
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" >
<size> <size>
<width>21</width> <width>21</width>
<height>61</height> <height>61</height>
@ -164,26 +162,12 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <widget class="QDialogButtonBox" name="buttonBox" >
<item> <property name="orientation" >
<widget class="QLabel" name="label_6">
<property name="text">
<string>Theme (Experimental)</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="theme"/>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -203,7 +187,7 @@
<tabstop>buttonBox</tabstop> <tabstop>buttonBox</tabstop>
</tabstops> </tabstops>
<resources> <resources>
<include location="ostinato.qrc"/> <include location="ostinato.qrc" />
</resources> </resources>
<connections> <connections>
<connection> <connection>
@ -212,11 +196,11 @@
<receiver>Preferences</receiver> <receiver>Preferences</receiver>
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel" >
<x>248</x> <x>248</x>
<y>254</y> <y>254</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel" >
<x>157</x> <x>157</x>
<y>274</y> <y>274</y>
</hint> </hint>
@ -228,11 +212,11 @@
<receiver>Preferences</receiver> <receiver>Preferences</receiver>
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel" >
<x>316</x> <x>316</x>
<y>260</y> <y>260</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel" >
<x>286</x> <x>286</x>
<y>274</y> <y>274</y>
</hint> </hint>

View File

@ -1,50 +0,0 @@
/*
Copyright (C) 2023 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _ROW_BORDER_DELEGATE
#define _ROW_BORDER_DELEGATE
#include <QStyledItemDelegate>
#include <QSet>
class RowBorderDelegate : public QStyledItemDelegate
{
public:
RowBorderDelegate(QSet<int> rows, QObject *parent = nullptr)
: QStyledItemDelegate(parent), rows_(rows)
{
}
private:
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter, option, index);
if (rows_.contains(index.row())) {
const QRect rect(option.rect);
painter->drawLine(rect.topLeft(), rect.topRight());
}
}
QSet<int> rows_;
};
#endif

View File

@ -31,7 +31,7 @@ const QString kWiresharkPathDefaultValue(
"C:/Program Files/Wireshark/wireshark.exe"); "C:/Program Files/Wireshark/wireshark.exe");
#elif defined(Q_OS_MAC) #elif defined(Q_OS_MAC)
const QString kWiresharkPathDefaultValue( const QString kWiresharkPathDefaultValue(
"/Applications/Wireshark.app/Contents/MacOS/Wireshark"); "/Applications/Wireshark.app/Contents/Resources/bin/wireshark");
#else #else
const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); const QString kWiresharkPathDefaultValue("/usr/bin/wireshark");
#endif #endif
@ -74,8 +74,6 @@ const QString kAwkPathDefaultValue("/usr/bin/awk");
const QString kAwkPathDefaultValue("/usr/bin/awk"); const QString kAwkPathDefaultValue("/usr/bin/awk");
#endif #endif
const QString kThemeKey("Theme");
const QString kUserKey("User"); const QString kUserKey("User");
extern QString kUserDefaultValue; extern QString kUserDefaultValue;
@ -84,7 +82,6 @@ extern QString kUserDefaultValue;
// //
const QString kApplicationWindowGeometryKey("LastUse/ApplicationWindowGeometry"); const QString kApplicationWindowGeometryKey("LastUse/ApplicationWindowGeometry");
const QString kApplicationWindowLayout("LastUse/ApplicationWindowLayout"); const QString kApplicationWindowLayout("LastUse/ApplicationWindowLayout");
const QString kLastUpdateCheck("LastUse/UpdateCheck");
#endif #endif

View File

@ -432,12 +432,6 @@ void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode)
lePktLenMin->setEnabled(true); lePktLenMin->setEnabled(true);
lePktLenMax->setEnabled(true); lePktLenMax->setEnabled(true);
} }
else if (mode == "IMIX")
{
lePktLen->setDisabled(true);
lePktLenMin->setDisabled(true);
lePktLenMax->setDisabled(true);
}
else else
{ {
qWarning("Unhandled/Unknown PktLenMode = %s", qPrintable(mode)); qWarning("Unhandled/Unknown PktLenMode = %s", qPrintable(mode));
@ -1171,7 +1165,12 @@ void StreamConfigDialog::on_lePacketsPerSec_textChanged(const QString &text)
{ {
bool isOk; bool isOk;
Stream *pStream = mpStream; Stream *pStream = mpStream;
uint frameLen = pStream->frameLenAvg(); uint frameLen;
if (pStream->lenMode() == Stream::e_fl_fixed)
frameLen = pStream->frameLen();
else
frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2;
if (rbSendPackets->isChecked()) if (rbSendPackets->isChecked())
{ {
@ -1190,9 +1189,13 @@ void StreamConfigDialog::on_leBurstsPerSec_textChanged(const QString &text)
bool isOk; bool isOk;
Stream *pStream = mpStream; Stream *pStream = mpStream;
uint burstSize = lePacketsPerBurst->text().toULong(&isOk); uint burstSize = lePacketsPerBurst->text().toULong(&isOk);
uint frameLen = pStream->frameLenAvg(); uint frameLen;
qDebug("start of %s(%s)", __FUNCTION__, qPrintable(text)); qDebug("start of %s(%s)", __FUNCTION__, qPrintable(text));
if (pStream->lenMode() == Stream::e_fl_fixed)
frameLen = pStream->frameLen();
else
frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2;
if (rbSendBursts->isChecked()) if (rbSendBursts->isChecked())
{ {
@ -1212,7 +1215,12 @@ void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text)
bool isOk; bool isOk;
Stream *pStream = mpStream; Stream *pStream = mpStream;
uint burstSize = lePacketsPerBurst->text().toULong(&isOk); uint burstSize = lePacketsPerBurst->text().toULong(&isOk);
uint frameLen = pStream->frameLenAvg(); uint frameLen;
if (pStream->lenMode() == Stream::e_fl_fixed)
frameLen = pStream->frameLen();
else
frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2;
if (rbSendPackets->isChecked()) if (rbSendPackets->isChecked())
{ {

View File

@ -108,11 +108,6 @@ QLineEdit:enabled[inputMask = &quot;HH HH HH HH HH HH; &quot;] { background-colo
<string>Random</string> <string>Random</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>IMIX</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
@ -675,19 +670,6 @@ QLineEdit:enabled[inputMask = &quot;HH HH HH HH HH HH; &quot;] { background-colo
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="page_2"> <widget class="QWidget" name="page_2">

View File

@ -22,10 +22,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "portgrouplist.h" #include "portgrouplist.h"
#include "qicon.h" #include "qicon.h"
#include <QMimeData>
const QLatin1String kStreamsMimeType("application/vnd.ostinato.streams");
StreamModel::StreamModel(PortGroupList *p, QObject *parent) StreamModel::StreamModel(PortGroupList *p, QObject *parent)
: QAbstractTableModel(parent) : QAbstractTableModel(parent)
{ {
@ -227,77 +223,6 @@ QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int r
return QVariant(); return QVariant();
} }
QStringList StreamModel::mimeTypes() const
{
return QStringList() << kStreamsMimeType;
}
QMimeData* StreamModel::mimeData(const QModelIndexList &indexes) const
{
using ::google::protobuf::uint8;
if (indexes.isEmpty())
return nullptr;
// indexes may include multiple columns for a row - but we are only
// interested in rows 'coz we have a single data for all columns
// XXX: use QMap instead of QSet to keep rows in sorted order
QMap<int, int> rows;
foreach(QModelIndex index, indexes)
rows.insert(index.row(), index.row());
OstProto::StreamConfigList streams;
streams.mutable_port_id()->set_id(mCurrentPort->id());
foreach(int row, rows) {
OstProto::Stream *stream = streams.add_stream();
mCurrentPort->streamByIndex(row)->protoDataCopyInto(*stream);
}
QByteArray data;
data.resize(streams.ByteSize());
streams.SerializeWithCachedSizesToArray((uint8*)data.data());
//qDebug("copy %s", streams.DebugString().c_str());
//TODO: copy DebugString as text/plain?
QMimeData *mimeData = new QMimeData();
mimeData->setData(kStreamsMimeType, data);
return mimeData; // XXX: caller is expected to take ownership and free!
}
bool StreamModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int /*column*/, const QModelIndex &parent)
{
if (!data)
return false;
if (!data->hasFormat(kStreamsMimeType))
return false;
if (action != Qt::CopyAction)
return false;
OstProto::StreamConfigList streamsData;
QByteArray ba(data->data(kStreamsMimeType));
streamsData.ParseFromArray((void*)ba.constData(), ba.size());
//qDebug("paste %s", streamsData.DebugString().c_str());
QList<Stream*> streams;
for (int i = 0; i < streamsData.stream_size(); i++) {
Stream *stream = new Stream;
stream->protoDataCopyFrom(streamsData.stream(i));
streams.append(stream);
}
if ((row < 0) || (row > rowCount(parent)))
row = rowCount(parent);
// Delete rows that we are going to overwrite
if (row < rowCount(parent))
removeRows(row, qMin(rowCount() - row, streams.size()));
return insert(row, streams); // callee will free streams after insert
}
/*! /*!
* Inserts streams before the given row * Inserts streams before the given row
* *
@ -361,13 +286,15 @@ void StreamModel::setCurrentPortIndex(const QModelIndex &current)
else else
{ {
qDebug("change to valid port"); qDebug("change to valid port");
// Disconnect any existing connection to avoid duplication
// Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible
// with earlier Qt versions
if (mCurrentPort) if (mCurrentPort)
{ {
disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)), disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)),
this, SLOT(when_mCurrentPort_streamListChanged(int, int))); this, SLOT(when_mCurrentPort_streamListChanged(int, int)));
} }
quint16 pg = current.internalId() >> 16; quint16 pg = current.internalId() >> 16;
// TODO: make mCurrentPort a smart weak pointer
mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()];
connect(mCurrentPort, SIGNAL(streamListChanged(int, int)), connect(mCurrentPort, SIGNAL(streamListChanged(int, int)),
this, SLOT(when_mCurrentPort_streamListChanged(int, int))); this, SLOT(when_mCurrentPort_streamListChanged(int, int)));

View File

@ -39,19 +39,12 @@ class StreamModel : public QAbstractTableModel
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const;
Qt::ItemFlags flags(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole); int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const; int role = Qt::DisplayRole) const;
QStringList mimeTypes() const;
QMimeData* mimeData(const QModelIndexList &indexes) const;
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent);
bool insert(int row, QList<Stream*> &streams); bool insert(int row, QList<Stream*> &streams);
bool insertRows (int row, int count, bool insertRows (int row, int count,
const QModelIndex & parent = QModelIndex()); const QModelIndex & parent = QModelIndex());

View File

@ -20,12 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "streamstatsmodel.h" #include "streamstatsmodel.h"
#include "protocol.pb.h" #include "protocol.pb.h"
#include "xqlocale.h"
#include <QApplication>
#include <QBrush> #include <QBrush>
#include <QFont>
#include <QPalette>
// XXX: Keep the enum in sync with it's string // XXX: Keep the enum in sync with it's string
enum { enum {
@ -46,26 +42,12 @@ enum {
kAggrTxPkts, kAggrTxPkts,
kAggrRxPkts, kAggrRxPkts,
kAggrPktLoss, kAggrPktLoss,
kTxDuration,
kAvgTxFrameRate,
kAvgRxFrameRate,
kAvgTxBitRate,
kAvgRxBitRate,
kAvgLatency,
kAvgJitter,
kMaxAggrStreamStats kMaxAggrStreamStats
}; };
static QStringList aggrStatTitles = QStringList() static QStringList aggrStatTitles = QStringList()
<< "Total\nTx Pkts" << "Total\nTx Pkts"
<< "Total\nRx Pkts" << "Total\nRx Pkts"
<< "Total\nPkt Loss" << "Total\nPkt Loss";
<< "Duration\n(secs)"
<< "Avg\nTx PktRate"
<< "Avg\nRx PktRate"
<< "Avg\nTx BitRate"
<< "Avg\nRx BitRate"
<< "Avg\nLatency"
<< "Avg\nJitter";
static const uint kAggrGuid = 0xffffffff; static const uint kAggrGuid = 0xffffffff;
@ -121,35 +103,20 @@ QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
return Qt::AlignRight; return Qt::AlignRight;
int portColumn = index.column() - kMaxAggrStreamStats; int portColumn = index.column() - kMaxAggrStreamStats;
if (role == Qt::BackgroundRole) {
// Stylesheets typically don't use or set palette colors, so if if (portColumn < 0)
// using one, don't use palette colors return QBrush(QColor("lavender")); // Aggregate Column
if ((role == Qt::BackgroundRole) && qApp->styleSheet().isEmpty()) { if (index.row() == (guidList_.size() - 1))
QPalette palette = QApplication::palette(); return QBrush(QColor("burlywood")); // Aggregate Row
if (index.row() == (guidList_.size() - 1)) // Aggregate Row else if ((portColumn/kMaxStreamStats) & 1)
return palette.dark(); return QBrush(QColor("beige")); // Color alternate Ports
if (portColumn < 0) // Aggregate Column
return palette.alternateBase();
if ((portColumn/kMaxStreamStats) & 1) // Color alternate Ports
return palette.alternateBase();
} }
Guid guid = guidList_.at(index.row()); Guid guid = guidList_.at(index.row());
if ((role == Qt::ForegroundRole && qApp->styleSheet().isEmpty())) { if (role == Qt::ForegroundRole) {
QPalette palette = QApplication::palette();
if ((index.column() == kAggrPktLoss) if ((index.column() == kAggrPktLoss)
&& aggrGuidStats_.value(guid).pktLoss) && aggrGuidStats_.value(guid).pktLoss)
return palette.link(); return QBrush(QColor("firebrick"));
if (index.row() == (guidList_.size() - 1)) // Aggregate Row
return palette.brightText();
}
if (role == Qt::FontRole ) {
if (index.row() == (guidList_.size() - 1)) { // Aggregate Row
QFont font;
font.setBold(true);
return font;
}
} }
if (role != Qt::DisplayRole) if (role != Qt::DisplayRole)
@ -164,42 +131,6 @@ QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
return QString("%L1").arg(aggrGuidStats_.value(guid).txPkts); return QString("%L1").arg(aggrGuidStats_.value(guid).txPkts);
case kAggrPktLoss: case kAggrPktLoss:
return QString("%L1").arg(aggrGuidStats_.value(guid).pktLoss); return QString("%L1").arg(aggrGuidStats_.value(guid).pktLoss);
case kTxDuration:
return QString("%L1").arg(aggrGuidStats_.value(guid).txDuration);
case kAvgTxFrameRate:
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
XLocale().toPktRateString(
aggrGuidStats_.value(guid).txPkts
/ aggrGuidStats_.value(guid).txDuration);
case kAvgRxFrameRate:
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
XLocale().toPktRateString(
aggrGuidStats_.value(guid).rxPkts
/ aggrGuidStats_.value(guid).txDuration);
case kAvgTxBitRate:
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
XLocale().toBitRateString(
(aggrGuidStats_.value(guid).txBytes
+ 24 * aggrGuidStats_.value(guid).txPkts) * 8
/ aggrGuidStats_.value(guid).txDuration);
case kAvgRxBitRate:
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
XLocale().toBitRateString(
(aggrGuidStats_.value(guid).rxBytes
+ 24 * aggrGuidStats_.value(guid).rxPkts) * 8
/ aggrGuidStats_.value(guid).txDuration);
case kAvgLatency:
return aggrGuidStats_.value(guid).latencyCount <= 0
|| aggrGuidStats_.value(guid).latencySum <= 0 ? QString("-") :
XLocale().toTimeIntervalString(
aggrGuidStats_.value(guid).latencySum
/ aggrGuidStats_.value(guid).latencyCount);
case kAvgJitter:
return aggrGuidStats_.value(guid).latencyCount <= 0
|| aggrGuidStats_.value(guid).latencySum <= 0 ? QString("-") :
XLocale().toTimeIntervalString(
aggrGuidStats_.value(guid).jitterSum
/ aggrGuidStats_.value(guid).latencyCount);
default: default:
break; break;
}; };
@ -225,11 +156,6 @@ QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
Qt::DropActions StreamStatsModel::supportedDropActions() const
{
return Qt::IgnoreAction; // read-only model, doesn't accept any data
}
// --------------------------------------------- // // --------------------------------------------- //
// Slots // Slots
// --------------------------------------------- // // --------------------------------------------- //
@ -274,8 +200,6 @@ void StreamStatsModel::appendStreamStatsList(
ss.txPkts = s.tx_pkts(); ss.txPkts = s.tx_pkts();
ss.rxBytes = s.rx_bytes(); ss.rxBytes = s.rx_bytes();
ss.txBytes = s.tx_bytes(); ss.txBytes = s.tx_bytes();
ss.rxLatency = s.latency();
ss.rxJitter = s.jitter();
aggrPort.rxPkts += ss.rxPkts; aggrPort.rxPkts += ss.rxPkts;
aggrPort.txPkts += ss.txPkts; aggrPort.txPkts += ss.txPkts;
@ -285,28 +209,10 @@ void StreamStatsModel::appendStreamStatsList(
aggrGuid.rxPkts += ss.rxPkts; aggrGuid.rxPkts += ss.rxPkts;
aggrGuid.txPkts += ss.txPkts; aggrGuid.txPkts += ss.txPkts;
aggrGuid.pktLoss += ss.txPkts - ss.rxPkts; aggrGuid.pktLoss += ss.txPkts - ss.rxPkts;
aggrGuid.rxBytes += ss.rxBytes;
aggrGuid.txBytes += ss.txBytes;
if (s.tx_duration() > aggrGuid.txDuration)
aggrGuid.txDuration = s.tx_duration(); // XXX: use largest or avg?
if (ss.rxLatency) {
aggrGuid.latencySum += ss.rxLatency;
aggrGuid.jitterSum += ss.rxJitter;
aggrGuid.latencyCount++;
}
aggrAggr.rxPkts += ss.rxPkts; aggrAggr.rxPkts += ss.rxPkts;
aggrAggr.txPkts += ss.txPkts; aggrAggr.txPkts += ss.txPkts;
aggrAggr.pktLoss += ss.txPkts - ss.rxPkts; aggrAggr.pktLoss += ss.txPkts - ss.rxPkts;
aggrAggr.rxBytes += ss.rxBytes;
aggrAggr.txBytes += ss.txBytes;
if (aggrGuid.txDuration > aggrAggr.txDuration)
aggrAggr.txDuration = aggrGuid.txDuration;
if (ss.rxLatency) {
aggrAggr.latencySum += ss.rxLatency;
aggrAggr.jitterSum += ss.rxJitter;
aggrAggr.latencyCount++;
}
if (!portList_.contains(pgp)) if (!portList_.contains(pgp))
portList_.append(pgp); portList_.append(pgp);
@ -314,11 +220,9 @@ void StreamStatsModel::appendStreamStatsList(
guidList_.append(guid); guidList_.append(guid);
} }
if (guidList_.size() && !guidList_.contains(kAggrGuid)) if (guidList_.size())
guidList_.append(kAggrGuid); guidList_.append(kAggrGuid);
std::sort(guidList_.begin(), guidList_.end());
#if QT_VERSION >= 0x040600 #if QT_VERSION >= 0x040600
endResetModel(); endResetModel();
#else #else

View File

@ -43,8 +43,6 @@ public:
int role = Qt::DisplayRole) const; int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
Qt::DropActions supportedDropActions() const;
public slots: public slots:
void clearStats(); void clearStats();
void appendStreamStatsList(quint32 portGroupId, void appendStreamStatsList(quint32 portGroupId,
@ -57,19 +55,11 @@ private:
quint64 txPkts; quint64 txPkts;
quint64 rxBytes; quint64 rxBytes;
quint64 txBytes; quint64 txBytes;
quint64 rxLatency;
quint64 rxJitter;
}; };
struct AggrGuidStats { struct AggrGuidStats {
quint64 rxPkts; quint64 rxPkts;
quint64 txPkts; quint64 txPkts;
quint64 rxBytes;
quint64 txBytes;
qint64 pktLoss; qint64 pktLoss;
double txDuration;
quint64 latencySum;
quint64 jitterSum;
uint latencyCount;
}; };
QList<Guid> guidList_; QList<Guid> guidList_;
QList<PortGroupPort> portList_; QList<PortGroupPort> portList_;

View File

@ -31,7 +31,7 @@ StreamStatsWindow::StreamStatsWindow(QAbstractItemModel *model, QWidget *parent)
: QWidget(parent) : QWidget(parent)
{ {
setupUi(this); setupUi(this);
streamStats->addAction(actionShowDetails); streamStats->addAction(actionShowByteCounters);
if (id) if (id)
setWindowTitle(windowTitle() + QString("(%1)").arg(id)); setWindowTitle(windowTitle() + QString("(%1)").arg(id));
@ -39,17 +39,13 @@ StreamStatsWindow::StreamStatsWindow(QAbstractItemModel *model, QWidget *parent)
count++; count++;
filterModel_ = new StreamStatsFilterModel(this); filterModel_ = new StreamStatsFilterModel(this);
filterModel_->setFilterRegExp(QRegExp(kDefaultFilter_)); filterModel_->setFilterRegExp(QRegExp(".*Pkt.*"));
filterModel_->setSourceModel(model); filterModel_->setSourceModel(model);
streamStats->setModel(filterModel_); streamStats->setModel(filterModel_);
streamStats->verticalHeader()->setHighlightSections(false); streamStats->verticalHeader()->setHighlightSections(false);
streamStats->verticalHeader()->setDefaultSectionSize( streamStats->verticalHeader()->setDefaultSectionSize(
streamStats->verticalHeader()->minimumSectionSize()); streamStats->verticalHeader()->minimumSectionSize());
// Fit all columns in window whenever data changes
connect(model, &QAbstractItemModel::modelReset,
[=]() { streamStats->resizeColumnsToContents(); });
} }
StreamStatsWindow::~StreamStatsWindow() StreamStatsWindow::~StreamStatsWindow()
@ -60,12 +56,10 @@ StreamStatsWindow::~StreamStatsWindow()
id = 0; id = 0;
} }
void StreamStatsWindow::on_actionShowDetails_triggered(bool checked) void StreamStatsWindow::on_actionShowByteCounters_triggered(bool checked)
{ {
if (checked) if (checked)
filterModel_->setFilterRegExp(QRegExp(".*")); filterModel_->setFilterRegExp(QRegExp(".*"));
else else
filterModel_->setFilterRegExp(QRegExp(kDefaultFilter_)); filterModel_->setFilterRegExp(QRegExp(".*Pkt.*"));
streamStats->resizeColumnsToContents();
} }

View File

@ -33,10 +33,9 @@ public:
~StreamStatsWindow(); ~StreamStatsWindow();
private slots: private slots:
void on_actionShowDetails_triggered(bool checked); void on_actionShowByteCounters_triggered(bool checked);
private: private:
QString kDefaultFilter_{"^(?!Port).*"};
QSortFilterProxyModel *filterModel_; QSortFilterProxyModel *filterModel_;
}; };

View File

@ -19,9 +19,6 @@
<property name="contextMenuPolicy"> <property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum> <enum>Qt::ActionsContextMenu</enum>
</property> </property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="whatsThis"> <property name="whatsThis">
<string>Oops! We don't seem to have any stream statistics for the requested port(s) <string>Oops! We don't seem to have any stream statistics for the requested port(s)
@ -30,12 +27,12 @@ Wait a little bit to see if they appear, otherwise verify your stream stats conf
</widget> </widget>
</item> </item>
</layout> </layout>
<action name="actionShowDetails"> <action name="actionShowByteCounters">
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Show Details</string> <string>Show Byte Counters</string>
</property> </property>
</action> </action>
</widget> </widget>

View File

@ -1,519 +0,0 @@
/*
Copyright (C) 2010 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "streamswidget.h"
#include "clipboardhelper.h"
#include "findreplace.h"
#include "portgrouplist.h"
#include "streamconfigdialog.h"
#include "streamfileformat.h"
#include "streamlistdelegate.h"
#include <QInputDialog>
#include <QItemSelectionModel>
#include <QMessageBox>
extern ClipboardHelper *clipboardHelper;
StreamsWidget::StreamsWidget(QWidget *parent)
: QWidget(parent)
{
setupUi(this);
delegate = new StreamListDelegate;
tvStreamList->setItemDelegate(delegate);
tvStreamList->verticalHeader()->setDefaultSectionSize(
tvStreamList->verticalHeader()->minimumSectionSize());
// Populate StreamList Context Menu Actions
tvStreamList->addAction(actionNew_Stream);
tvStreamList->addAction(actionEdit_Stream);
tvStreamList->addAction(actionDuplicate_Stream);
tvStreamList->addAction(actionDelete_Stream);
QAction *sep2 = new QAction(this);
sep2->setSeparator(true);
tvStreamList->addAction(sep2);
tvStreamList->addAction(actionFind_Replace);
QAction *sep3 = new QAction(this);
sep3->setSeparator(true);
tvStreamList->addAction(sep3);
tvStreamList->addAction(actionOpen_Streams);
tvStreamList->addAction(actionSave_Streams);
// StreamWidget's actions is an aggegate of all sub-widget's actions
addActions(tvStreamList->actions());
// Add the clipboard actions to the context menu of streamList
// but not to StreamsWidget's actions since they are already available
// in the global Edit Menu
QAction *sep4 = new QAction("Clipboard", this);
sep4->setSeparator(true);
tvStreamList->insertAction(sep2, sep4);
tvStreamList->insertActions(sep2, clipboardHelper->actions());
}
void StreamsWidget::setPortGroupList(PortGroupList *portGroups)
{
plm = portGroups;
tvStreamList->setModel(plm->getStreamModel());
connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)),
SLOT(updateStreamViewActions()));
connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)),
SLOT(updateStreamViewActions()));
connect(tvStreamList->selectionModel(),
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
SLOT(updateStreamViewActions()));
connect(tvStreamList->selectionModel(),
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
SLOT(updateStreamViewActions()));
tvStreamList->resizeColumnToContents(StreamModel::StreamIcon);
tvStreamList->resizeColumnToContents(StreamModel::StreamStatus);
updateStreamViewActions();
connect(plm->getStreamModel(),
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
this, SLOT(streamModelDataChanged()));
connect(plm->getStreamModel(),
SIGNAL(modelReset()),
this, SLOT(streamModelDataChanged()));
}
void StreamsWidget::streamModelDataChanged()
{
if (plm->isPort(currentPortIndex_))
plm->port(currentPortIndex_).recalculateAverageRates();
}
StreamsWidget::~StreamsWidget()
{
delete delegate;
}
void StreamsWidget::on_tvStreamList_activated(const QModelIndex & index)
{
if (!index.isValid())
{
qDebug("%s: invalid index", __FUNCTION__);
return;
}
qDebug("stream list activated\n");
Q_ASSERT(plm->isPort(currentPortIndex_));
Port &curPort = plm->port(currentPortIndex_);
QList<Stream*> streams;
streams.append(curPort.mutableStreamByIndex(index.row(), false));
StreamConfigDialog scd(streams, curPort, this);
if (scd.exec() == QDialog::Accepted) {
curPort.recalculateAverageRates();
curPort.setLocalConfigChanged(true);
}
}
void StreamsWidget::setCurrentPortIndex(const QModelIndex &portIndex)
{
if (!plm)
return;
// XXX: We assume portIndex corresponds to sourceModel, not proxyModel;
// caller should ensure this
qDebug("In %s", __PRETTY_FUNCTION__);
currentPortIndex_ = portIndex;
plm->getStreamModel()->setCurrentPortIndex(portIndex);
updateStreamViewActions();
}
void StreamsWidget::updateStreamViewActions()
{
// For some reason hasSelection() returns true even if selection size is 0
// so additional check for size introduced
if (tvStreamList->selectionModel()->hasSelection() &&
(tvStreamList->selectionModel()->selection().size() > 0))
{
qDebug("Has selection %d",
tvStreamList->selectionModel()->selection().size());
// If more than one non-contiguous ranges selected,
// disable "New" and "Edit"
if (tvStreamList->selectionModel()->selection().size() > 1)
{
actionNew_Stream->setDisabled(true);
actionEdit_Stream->setDisabled(true);
}
else
{
actionNew_Stream->setEnabled(true);
actionEdit_Stream->setEnabled(true);
}
// Duplicate/Delete are always enabled as long as we have a selection
actionDuplicate_Stream->setEnabled(true);
actionDelete_Stream->setEnabled(true);
}
else
{
qDebug("No selection");
if (plm->isPort(currentPortIndex_))
actionNew_Stream->setEnabled(true);
else
actionNew_Stream->setDisabled(true);
actionEdit_Stream->setDisabled(true);
actionDuplicate_Stream->setDisabled(true);
actionDelete_Stream->setDisabled(true);
}
actionFind_Replace->setEnabled(tvStreamList->model()->rowCount() > 0);
actionOpen_Streams->setEnabled(plm->isPort(currentPortIndex_));
actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0);
}
void StreamsWidget::on_actionNew_Stream_triggered()
{
qDebug("New Stream Action");
QItemSelectionModel* selectionModel = tvStreamList->selectionModel();
if (selectionModel->selection().size() > 1) {
qDebug("%s: Unexpected selection size %d, can't add", __FUNCTION__,
selectionModel->selection().size());
return;
}
// In case nothing is selected, insert 1 row at the end
StreamModel *streamModel = plm->getStreamModel();
int row = streamModel->rowCount(), count = 1;
// In case we have a single range selected; insert as many rows as
// in the singe selected range before the top of the selected range
if (selectionModel->selection().size() == 1)
{
row = selectionModel->selection().at(0).top();
count = selectionModel->selection().at(0).height();
}
Q_ASSERT(plm->isPort(currentPortIndex_));
Port &curPort = plm->port(currentPortIndex_);
QList<Stream*> streams;
for (int i = 0; i < count; i++)
streams.append(new Stream);
StreamConfigDialog scd(streams, curPort, this);
scd.setWindowTitle(tr("Add Stream"));
if (scd.exec() == QDialog::Accepted)
streamModel->insert(row, streams);
}
void StreamsWidget::on_actionEdit_Stream_triggered()
{
qDebug("Edit Stream Action");
QItemSelectionModel* streamModel = tvStreamList->selectionModel();
if (!streamModel->hasSelection())
return;
Q_ASSERT(plm->isPort(currentPortIndex_));
Port &curPort = plm->port(currentPortIndex_);
QList<Stream*> streams;
foreach(QModelIndex index, streamModel->selectedRows())
streams.append(curPort.mutableStreamByIndex(index.row(), false));
StreamConfigDialog scd(streams, curPort, this);
if (scd.exec() == QDialog::Accepted) {
curPort.recalculateAverageRates();
curPort.setLocalConfigChanged(true);
}
}
void StreamsWidget::on_actionDuplicate_Stream_triggered()
{
QItemSelectionModel* model = tvStreamList->selectionModel();
qDebug("Duplicate Stream Action");
Q_ASSERT(plm->isPort(currentPortIndex_));
if (model->hasSelection())
{
bool isOk;
int count = QInputDialog::getInt(this, "Duplicate Streams",
"Count", 1, 1, 9999, 1, &isOk);
if (!isOk)
return;
QList<int> list;
foreach(QModelIndex index, model->selectedRows())
list.append(index.row());
plm->port(currentPortIndex_).duplicateStreams(list, count);
}
else
qDebug("No selection");
}
void StreamsWidget::on_actionDelete_Stream_triggered()
{
qDebug("Delete Stream Action");
QModelIndex index;
if (tvStreamList->selectionModel()->hasSelection())
{
qDebug("SelectedIndexes %d",
tvStreamList->selectionModel()->selectedRows().size());
while(tvStreamList->selectionModel()->selectedRows().size())
{
index = tvStreamList->selectionModel()->selectedRows().at(0);
plm->getStreamModel()->removeRows(index.row(), 1);
}
}
else
qDebug("No selection");
}
void StreamsWidget::on_actionFind_Replace_triggered()
{
qDebug("Find & Replace Action");
Q_ASSERT(plm->isPort(currentPortIndex_));
QItemSelectionModel* selectionModel = tvStreamList->selectionModel();
FindReplaceDialog::Action action;
action.selectedStreamsOnly = selectionModel->selection().size() > 0 ?
true : false;
FindReplaceDialog findReplace(&action, this);
if (findReplace.exec() == QDialog::Accepted) {
QProgressDialog progress(this);
progress.setLabelText(tr("Replacing %1 ...").arg(action.protocolField));
progress.setWindowModality(Qt::WindowModal);
int c, fc = 0, sc = 0; // replace counts
Port &port = plm->port(currentPortIndex_);
if (action.selectedStreamsOnly) {
QModelIndexList selected = selectionModel->selectedRows();
int count = selected.size();
progress.setMaximum(count);
for (int i = 0; i < count; i++) {
QModelIndex index = selected.at(i);
Stream *stream = port.mutableStreamByIndex(index.row(), false);
c = stream->protocolFieldReplace(action.protocolNumber,
action.fieldIndex,
action.fieldBitSize,
action.findValue,
action.findMask,
action.replaceValue,
action.replaceMask);
if (c) {
fc += c;
sc++;
}
progress.setValue(i+1);
if (progress.wasCanceled())
break;
}
} else {
int count = tvStreamList->model()->rowCount();
progress.setMaximum(count);
for (int i = 0; i < count; i++) {
Stream *stream = port.mutableStreamByIndex(i, false);
c = stream->protocolFieldReplace(action.protocolNumber,
action.fieldIndex,
action.fieldBitSize,
action.findValue,
action.findMask,
action.replaceValue,
action.replaceMask);
if (c) {
fc += c;
sc++;
}
progress.setValue(i+1);
if (progress.wasCanceled())
break;
}
}
if (fc)
port.setLocalConfigChanged(true);
QMessageBox::information(this, tr("Find & Replace"),
tr("%1 fields replaced in %2 streams").arg(fc).arg(sc));
}
}
void StreamsWidget::on_actionOpen_Streams_triggered()
{
qDebug("Open Streams Action");
QStringList fileTypes = StreamFileFormat::supportedFileTypes(
StreamFileFormat::kOpenFile);
QString fileType;
static QString dirName;
QString fileName;
QString errorStr;
bool append = true;
bool ret;
Q_ASSERT(plm->isPort(currentPortIndex_));
if (fileTypes.size())
fileType = fileTypes.at(0);
fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"),
dirName, fileTypes.join(";;"), &fileType);
if (fileName.isEmpty())
goto _exit;
if (tvStreamList->model()->rowCount())
{
QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(),
tr("Append to existing streams? Or overwrite?"),
QMessageBox::NoButton, this);
QPushButton *appendBtn = msgBox.addButton(tr("Append"),
QMessageBox::ActionRole);
QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"),
QMessageBox::ActionRole);
QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel);
msgBox.exec();
if (msgBox.clickedButton() == cancelBtn)
goto _exit;
else if (msgBox.clickedButton() == appendBtn)
append = true;
else if (msgBox.clickedButton() == overwriteBtn)
append = false;
else
Q_ASSERT(false);
}
ret = plm->port(currentPortIndex_).openStreams(fileName, append, errorStr);
if (!ret || !errorStr.isEmpty())
{
QMessageBox msgBox(this);
QStringList str = errorStr.split("\n\n\n\n");
msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical);
msgBox.setWindowTitle(qApp->applicationName());
msgBox.setText(str.at(0));
if (str.size() > 1)
msgBox.setDetailedText(str.at(1));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
}
dirName = QFileInfo(fileName).absolutePath();
updateStreamViewActions();
_exit:
return;
}
void StreamsWidget::on_actionSave_Streams_triggered()
{
qDebug("Save Streams Action");
static QString fileName;
QStringList fileTypes = StreamFileFormat::supportedFileTypes(
StreamFileFormat::kSaveFile);
QString fileType;
QString errorStr;
QFileDialog::Options options;
// On Mac OS with Native Dialog, getSaveFileName() ignores fileType
// which we need
#if defined(Q_OS_MAC)
options |= QFileDialog::DontUseNativeDialog;
#endif
if (fileTypes.size())
fileType = fileTypes.at(0);
Q_ASSERT(plm->isPort(currentPortIndex_));
_retry:
fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"),
fileName, fileTypes.join(";;"), &fileType, options);
if (fileName.isEmpty())
goto _exit;
if (QFileInfo(fileName).suffix().isEmpty()) {
QString fileExt = fileType.section(QRegExp("[\\*\\)]"), 1, 1);
qDebug("Adding extension '%s' to '%s'",
qPrintable(fileExt), qPrintable(fileName));
fileName.append(fileExt);
if (QFileInfo(fileName).exists()) {
if (QMessageBox::warning(this, tr("Overwrite File?"),
QString("The file \"%1\" already exists.\n\n"
"Do you wish to overwrite it?")
.arg(QFileInfo(fileName).fileName()),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No) != QMessageBox::Yes)
goto _retry;
}
}
fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed();
if (!fileType.startsWith("Ostinato")
&& !fileType.startsWith("Python"))
{
if (QMessageBox::warning(this, tr("Ostinato"),
QString("You have chosen to save in %1 format. All stream "
"attributes may not be saved in this format.\n\n"
"It is recommended to save in native Ostinato format.\n\n"
"Continue to save in %2 format?").arg(fileType).arg(fileType),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No) != QMessageBox::Yes)
goto _retry;
}
// TODO: all or selected?
if (!plm->port(currentPortIndex_).saveStreams(fileName, fileType, errorStr))
QMessageBox::critical(this, qApp->applicationName(), errorStr);
else if (!errorStr.isEmpty())
QMessageBox::warning(this, qApp->applicationName(), errorStr);
fileName = QFileInfo(fileName).absolutePath();
_exit:
return;
}

View File

@ -1,67 +0,0 @@
/*
Copyright (C) 2010 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _STREAMS_WIDGET_H
#define _STREAMS_WIDGET_H
#include "ui_streamswidget.h"
#include <QWidget>
class PortGroupList;
class QAbstractItemDelegate;
class StreamsWidget : public QWidget, private Ui::StreamsWidget
{
Q_OBJECT
public:
StreamsWidget(QWidget *parent = 0);
~StreamsWidget();
void setPortGroupList(PortGroupList *portGroups);
public slots:
void setCurrentPortIndex(const QModelIndex &portIndex);
private slots:
void updateStreamViewActions();
void on_tvStreamList_activated(const QModelIndex & index);
void on_actionNew_Stream_triggered();
void on_actionEdit_Stream_triggered();
void on_actionDuplicate_Stream_triggered();
void on_actionDelete_Stream_triggered();
void on_actionFind_Replace_triggered();
void on_actionOpen_Streams_triggered();
void on_actionSave_Streams_triggered();
void streamModelDataChanged();
private:
PortGroupList *plm{nullptr}; // FIXME: rename to portGroups_?
QModelIndex currentPortIndex_;
QAbstractItemDelegate *delegate;
};
#endif

View File

@ -1,132 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StreamsWidget</class>
<widget class="QWidget" name="StreamsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>602</width>
<height>364</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="XTableView" name="tvStreamList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="whatsThis">
<string>This is the stream list for the selected port
A stream is a sequence of one or more packets
Right-click to create a stream</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
</layout>
<action name="actionNew_Stream">
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/stream_add.png</normaloff>:/icons/stream_add.png</iconset>
</property>
<property name="text">
<string>New Stream</string>
</property>
</action>
<action name="actionDelete_Stream">
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/stream_delete.png</normaloff>:/icons/stream_delete.png</iconset>
</property>
<property name="text">
<string>Delete Stream</string>
</property>
</action>
<action name="actionEdit_Stream">
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/stream_edit.png</normaloff>:/icons/stream_edit.png</iconset>
</property>
<property name="text">
<string>Edit Stream</string>
</property>
</action>
<action name="actionOpen_Streams">
<property name="text">
<string>Open Streams ...</string>
</property>
</action>
<action name="actionSave_Streams">
<property name="text">
<string>Save Streams ...</string>
</property>
</action>
<action name="actionDuplicate_Stream">
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/stream_duplicate.png</normaloff>:/icons/stream_duplicate.png</iconset>
</property>
<property name="text">
<string>Duplicate Stream</string>
</property>
</action>
<action name="actionFind_Replace">
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/find.png</normaloff>:/icons/find.png</iconset>
</property>
<property name="text">
<string>Find &amp;&amp; Replace</string>
</property>
<property name="toolTip">
<string>Find &amp; Replace protocol field values</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>XTableView</class>
<extends>QTableView</extends>
<header>xtableview.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="ostinato.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -1,130 +0,0 @@
/*
Copyright (C) 2021 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "thememanager.h"
#include "settings.h"
#include <QApplication>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QRegularExpression>
#include <QResource>
ThemeManager *ThemeManager::instance_{nullptr};
ThemeManager::ThemeManager()
{
themeDir_ = QCoreApplication::applicationDirPath() + "/themes/";
#if defined(Q_OS_MAC)
/*
* Executable and Theme directory location inside app bundle -
* Ostinato.app/Contents/MacOS/
* Ostinato.app/Contents/SharedSupport/themes/
*/
themeDir_.replace("/MacOS/", "/SharedSupport/");
#elif defined(Q_OS_UNIX)
/*
* Possible (but not comprehensive) locations for Ostinato executable
* and theme directory locations
*
* non-install-dir/
* non-install-dir/themes/
*
* /usr/[local]/bin/
* /usr/[local]/share/ostinato-controller/themes/
*
* /opt/ostinato/bin/
* /opt/ostinato/share/themes/
*/
if (themeDir_.contains(QRegularExpression("^/usr/.*/bin/")))
themeDir_.replace("/bin/", "/share/ostinato-controller/");
else if (themeDir_.contains(QRegularExpression("^/opt/.*/bin/")))
themeDir_.replace("/bin/", "/share/");
#endif
qDebug("Themes directory: %s", qPrintable(themeDir_));
}
QStringList ThemeManager::themeList()
{
QDir themeDir(themeDir_);
themeDir.setFilter(QDir::Files);
themeDir.setNameFilters(QStringList() << "*.qss");
themeDir.setSorting(QDir::Name);
QStringList themes = themeDir.entryList();
for (QString& theme : themes)
theme.remove(".qss");
themes.prepend("default");
return themes;
}
void ThemeManager::setTheme(QString theme)
{
// Remove current theme, if we have one
QString oldTheme = appSettings->value(kThemeKey).toString();
if (!oldTheme.isEmpty()) {
// Remove stylesheet first so that there are
// no references to resources when unregistering 'em
qApp->setStyleSheet("");
QString rccFile = themeDir_ + oldTheme + ".rcc";
if (QResource::unregisterResource(rccFile)) {
qDebug("Unable to unregister theme rccFile %s",
qPrintable(rccFile));
}
appSettings->setValue(kThemeKey, QVariant());
}
if (theme.isEmpty() || (theme == "default"))
return;
// Apply new theme
QFile qssFile(themeDir_ + theme + ".qss");
if (!qssFile.open(QFile::ReadOnly)) {
qDebug("Unable to open theme qssFile %s",
qPrintable(qssFile.fileName()));
return;
}
// Register theme resource before applying theme style sheet
QString rccFile = themeDir_ + theme + ".rcc";
if (!QResource::registerResource(rccFile))
qDebug("Unable to register theme rccFile %s", qPrintable(rccFile));
#if 0 // FIXME: debug only
QDirIterator it(":", QDirIterator::Subdirectories);
while (it.hasNext()) {
qDebug() << it.next();
}
#endif
QString styleSheet { qssFile.readAll() };
qApp->setStyleSheet(styleSheet);
appSettings->setValue(kThemeKey, theme);
}
ThemeManager* ThemeManager::instance()
{
if (!instance_)
instance_ = new ThemeManager();
return instance_;
}

View File

@ -1,42 +0,0 @@
/*
Copyright (C) 2021 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _THEME_MANAGER_H
#define _THEME_MANAGER_H
#include <QStringList>
class ThemeManager
{
public:
ThemeManager();
QStringList themeList();
void setTheme(QString theme);
static ThemeManager* instance();
private:
QString themeDir_;
static ThemeManager *instance_;
};
#endif

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More