diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,8 +51,9 @@ endif (QWT_USE_STATIC)
# includes
include_directories(${QWT_INCLUDE_DIR})
-# wrap UI files
+# wrap UI and resource files
qt5_wrap_ui(UI_FILES mainwindow.ui portcontrol.ui about_dialog.ui)
+qt5_add_resources(RES_FILES misc/icons.qrc)
add_executable(${PROGRAM_NAME} WIN32
main.cpp
@@ -64,7 +65,9 @@ add_executable(${PROGRAM_NAME} WIN32
framebuffer.cpp
scalepicker.cpp
scalezoomer.cpp
+ portlist.cpp
${UI_FILES}
+ ${RES_FILES}
misc/windows_icon.rc
)
diff --git a/misc/bluetooth_icon.png b/misc/bluetooth_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..b9d6cd9b54fc3c88c1a55a14a5619e7ed7ab34fe
GIT binary patch
literal 2722
zc${rkX;_n27CzrcAb~(g5F|>J5QY(Cbwxxa!BE917+gRE0avJFWfiDc@`Y4k1VzWS
zV}w+n5dm2RX&aW
ISdplpJzt#pGtd0U^WN{=bKmpcdvngYf0KnT
zvay1o|2%vu~3xwFT)0+USz6g~{R%G9Kao=WZtp9lF+U*-3KKBYdcBOM>
zYr(jV$_{9y+^FrB`B)v8hSq{;BgeNk)$_2(Z{|-9&;0WvJCvEo&
z-%Sl}n({FKJ=nAd;VxANpqyX9UZmrj6k73;cBAOou#+`YipOO~1vCDE+l{jkyO4W6
ziQ_8%jFB*Af!GXbFr-u~AUe{v;cCCnp#BUCeNUmwN_vJ%bkt8rx}(Sv$y9R*S6s6f
zBPQg*hT9IvZ;iBpX0ifm4!Lh*7Lst0L`9RFK$7K1B1V!lnxp_p!f6s0hOjW0c9lT=
zX_Q0|Xjq1ZIYqD`f{kmLcrLqfZ7-MH7cvXi3h?^fajoJPmU#Ry3aI4~9gnyuV0OUQ
zx?5dt=(9zgqPum;14mw`(sb2^ybo{3?#jdE!mKo!lkG97;n;}d+19KAYCQ&9C4He@
zvm?W$|B`{1LZ1-ZeR9K(-9GEan$s96+>-X<3ftA!=R4Nsu9-!?q}2p>GOM2KFN{hq
z<109{L1bW7J^Gd%m0UAk;X@lp4zsGaO+WCL-%sDw?G~4z(m~M?Wu(r9ypLzDWve)=
z6*-K#7TU4T_omHFa=7AH9RsTF;JhwLWok?Iak6*2#WDeB^&!3ab33?{=o>nwzW&3S
z3mhUP=gHNBws&@3+nHfYT|0YDymA(UT|TKHaJbOG
zhvCG{VSQ?ithIJ4A}9d;^TE*T;<4DrzI#*BAr&2mi#hmhrL
zs^U?@2Ynj0!@-i346-)A^I{~2>YTAV7~VvD-&;Y*vS;sSY2(U#e_RLQK?jl;32g@)
z3(H8YF@y)+mHBBRH1Y}E*)%^5?a70%%3Wt?Kv546y2f>44SI%g;mau7FBSp6
zjYp~nIwe&c(EBfhd9kz39Kx8pSX8m6#kr}t$etK{Wza=3s39j;$c>wDuVo->pe14S
z35zOA%+IRHd1Mnc=pi1+L7VQmoH?f
zi#=Tj`;jAzN5xg|*1pdv9M&7-V6T~nNjda%&ciD(mEg4amy0xIX8g}w<(Qmg}Y
zH(}Jcw0S#gh5QI7~(
zhVm7I9;D7uF5t(DVE&M$B*l@`*_PT_XSqVlV5nq>OX`@V;{~>^Fn@5TWYC`ME%vq9
zRmOwl9jUTrJc>tMO8rKL_L`yRVODpGkhI}KP>uD#gAvV09G6IEXuB$vnvvg$d1<}{
z)HJeJzx%r?mUgAH6GsS>t0&&7fY`Uw*16KxwYJJyd)?V{wh-=APyWpSXN*Vdxb)>N
z4kT+0>o1WE@wE!#g_XARCdKB#e9wHm|H^o_j~E)xOuqaQo@a+;6ZHSaeF|CI-}umy
zA^y2qp|a9mxmm#EQom<%^cx_vE%KfMRE;_Kk4t$pOFxd$s~WdZ#?qGG+-u9FW`x^_
z-C#;7@xTS%A8$4;A?A#E`LVQH%Y2(xLuX&2-5z*;GUZnXGnsu!*q`V`FpshkM||~J
z1WUVpiDOIvg!Dy+2;r~gYm%@aH74GeNUT~kBno7x-4|#MhdXCPRXE;qgOK3~=T4;P
z?z_jwVl{Z2css9ClJ#BgS}X^@m6p%?r!wz<0yfH48pGnh5P}1$QKx^1`=I2l?_;
zYtY9p?o-#5!~p-}x~a@P^HrGL{<1kq&-ECt?O%P$_BakEv=gR-0r41OCn8?5R|R4|
zmu?gIjrPs-H{udR5)Pg$0
z7Gj^a(h7=t?65zJAl)xF2G?CDjw**@Oz#bPZZfvrmGI=hWg~&p-eX~sfhL-$@84
zX^Y8=mSi%GH))~0&{S+PA=j%8XXd6)x=30VZf%DH6~LF<
+
+ usb_icon.png
+ bluetooth_icon.png
+
+
diff --git a/misc/usb_icon.png b/misc/usb_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..679f4171a1072b39efa1508487542d856e143be2
GIT binary patch
literal 816
zc$@(+1JC@4P)pe5)
z%$#%QIzC@`;NE-Q^Sr2WwoH6!w>1nqH;9&JKt6?54N{jeUzC7M)e43oZ3sniM
zlIL(sf)z9?iiA
zI3#Uhlkqfu7<1pYN|aW>65KLzH9kIxk3O6jGydbM#O7oOAIf>&IV0bev?=5MIrwJG
zxX-5{yezg$T7Jf=b^{uF>q2ZO;Iy?KEW@XGyZ-$<9Fg|_nWRxf*Wec%#%|nf9r%u^
zA5tDSyMr&(>)D+A!JIYp)UB6h3S5w)lc!jm#s>u_U4+e2n)KtklyWx;`YFmVM(Uqy
zQd;iBXuH9Ai{QJ{5VTf05!UbrUYv%nJT!{nIq6I?A$hjJVyVgRc%u3g1nr77zmJ2X@6gozs;Z_2JuUsWr!T1GS%atbT)d>dP5mIk25
z_#jNhrWAl~ISJPAnN)m#@8rbY)@VFaw?$Grt(9f?w#*5;EdUKhwLh+wzH4=B0J8W8
uyE3l*+DRcn5ClOG1VIo4K@bGNk!`6
#include
+#include
#include
#include "utils.h"
@@ -41,24 +42,33 @@ PortControl::PortControl(QSerialPort* po
openAction.setToolTip("Open Port (F2)");
QObject::connect(&openAction, &QAction::triggered,
this, &PortControl::openActionTriggered);
+
+ portToolBar.addWidget(&tbPortList);
portToolBar.addAction(&openAction);
+ // setup port selection widgets
+ tbPortList.setModel(&portList);
+ ui->cbPortList->setModel(&portList);
+ QObject::connect(ui->cbPortList,
+ SELECT::OVERLOAD_OF(&QComboBox::activated),
+ this, &PortControl::onCbPortListActivated);
+ QObject::connect(&tbPortList,
+ SELECT::OVERLOAD_OF(&QComboBox::activated),
+ this, &PortControl::onTbPortListActivated);
+ QObject::connect(ui->cbPortList,
+ SELECT::OVERLOAD_OF(&QComboBox::activated),
+ this, &PortControl::selectPort);
+ QObject::connect(&tbPortList,
+ SELECT::OVERLOAD_OF(&QComboBox::activated),
+ this, &PortControl::selectPort);
+
// setup buttons
QObject::connect(ui->pbReloadPorts, &QPushButton::clicked,
this, &PortControl::loadPortList);
ui->pbOpenPort->setDefaultAction(&openAction);
- // TODO: port name coming from combobox is dirty, create a separate layer of signals
- // that will sanitize this information
- QObject::connect(ui->cbPortList,
- SELECT::OVERLOAD_OF(&QComboBox::activated),
- this, &PortControl::selectPort);
-
- QObject::connect(ui->cbPortList,
- SELECT::OVERLOAD_OF(&QComboBox::activated),
- this, &PortControl::onPortNameChanged);
-
+ // setup baud rate selection widget
QObject::connect(ui->cbBaudRate,
SELECT::OVERLOAD_OF(&QComboBox::activated),
this, &PortControl::selectBaudRate);
@@ -118,36 +128,13 @@ PortControl::~PortControl()
void PortControl::loadPortList()
{
- QString currentSelection = ui->cbPortList->currentText();
-
- ui->cbPortList->clear();
-
- discoveredPorts.clear();
- for (auto port : QSerialPortInfo::availablePorts())
+ QString currentSelection = ui->cbPortList->currentData(PortNameRole).toString();
+ portList.loadPortList();
+ int index = portList.indexOf(currentSelection);
+ if (index >= 0)
{
- QString pName = port.portName();
- if (!port.description().isEmpty()) pName += QString(" ") + port.description();
- if (port.hasProductIdentifier())
- {
- QString vID = QString("%1").arg(port.vendorIdentifier(), 4, 16, QChar('0'));
- QString pID = QString("%1").arg(port.productIdentifier(), 4, 16, QChar('0'));
- pName = pName + " [" + vID + ":" + pID + "]";
- }
- ui->cbPortList->addItem(pName);
- discoveredPorts << port.portName();
- }
-
- ui->cbPortList->addItems(userEnteredPorts);
-
- // find current selection in the new list, maybe it doesn't exist anymore?
- int currentSelectionIndex = ui->cbPortList->findText(currentSelection);
- if (currentSelectionIndex >= 0)
- {
- ui->cbPortList->setCurrentIndex(currentSelectionIndex);
- }
- else // our port doesn't exist anymore, close port if it's open
- {
- if (serialPort->isOpen()) togglePort();
+ ui->cbPortList->setCurrentIndex(index);
+ tbPortList.setCurrentIndex(index);
}
}
@@ -230,11 +217,26 @@ void PortControl::togglePort()
}
else
{
- // port name may contain description
- QString portName = ui->cbPortList->currentText().split(" ")[0];
- keepPortName(portName);
+ // we get the port name from the edit text, which may not be
+ // in the portList if user hasn't pressed Enter
+ // Also note that, portText may not be the `portName`
+ QString portText = ui->cbPortList->currentText();
+ QString portName;
+ int portIndex = portList.indexOf(portText);
+ if (portIndex < 0) // not in list, add to model and update the selections
+ {
+ portList.appendRow(new PortListItem(portText));
+ ui->cbPortList->setCurrentIndex(portList.rowCount()-1);
+ tbPortList.setCurrentIndex(portList.rowCount()-1);
+ portName = portText;
+ }
+ else
+ {
+ // get the port name from the data field
+ portName = static_cast(portList.item(portIndex))->portName();
+ }
- serialPort->setPortName(portName);
+ serialPort->setPortName(ui->cbPortList->currentData(PortNameRole).toString());
// open port
if (serialPort->open(QIODevice::ReadWrite))
@@ -276,24 +278,6 @@ void PortControl::enableSkipByte(bool en
ui->pbSkipByte->setDisabled(enabled);
}
-void PortControl::keepPortName(QString portName)
-{
- if(!discoveredPorts.contains(portName) &&
- !userEnteredPorts.contains(portName))
- {
- userEnteredPorts << portName;
- }
- if(ui->cbPortList->findText(portName) < 0)
- {
- ui->cbPortList->addItem(portName);
- }
-}
-
-void PortControl::onPortNameChanged(QString portName)
-{
- keepPortName(portName);
-}
-
QToolBar* PortControl::toolBar()
{
return &portToolBar;
@@ -303,3 +287,13 @@ void PortControl::openActionTriggered(bo
{
togglePort();
}
+
+void PortControl::onCbPortListActivated(int index)
+{
+ tbPortList.setCurrentIndex(index);
+}
+
+void PortControl::onTbPortListActivated(int index)
+{
+ ui->cbPortList->setCurrentIndex(index);
+}
diff --git a/portcontrol.h b/portcontrol.h
--- a/portcontrol.h
+++ b/portcontrol.h
@@ -26,6 +26,9 @@
#include
#include
#include
+#include
+
+#include "portlist.h"
namespace Ui {
class PortControl;
@@ -53,10 +56,8 @@ private:
QToolBar portToolBar;
QAction openAction;
-
- QStringList discoveredPorts; // list of port names returned by availablePorts
- QStringList userEnteredPorts; // list of port names entered by user
- void keepPortName(QString portName); // if it's not already in userEn. or discv. list
+ QComboBox tbPortList;
+ PortList portList;
public slots:
void loadPortList();
@@ -72,9 +73,11 @@ public slots:
void selectFlowControl(int flowControl); // flowControl must be one of QSerialPort::FlowControl
private slots:
- void onPortNameChanged(QString portName);
void openActionTriggered(bool checked);
+ void onCbPortListActivated(int index);
+ void onTbPortListActivated(int index);
+
signals:
void skipByteRequested();
void portToggled(bool open);
diff --git a/portlist.cpp b/portlist.cpp
new file mode 100644
--- /dev/null
+++ b/portlist.cpp
@@ -0,0 +1,117 @@
+/*
+ Copyright © 2015 Hasan Yavuz Özderya
+
+ This file is part of serialplot.
+
+ serialplot 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.
+
+ serialplot 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 serialplot. If not, see .
+*/
+
+#include
+#include
+#include
+
+#include "portlist.h"
+
+PortListItem::PortListItem(QString name, QString description, quint16 vid, quint16 pid)
+{
+ construct(name, description, vid, pid);
+}
+
+PortListItem::PortListItem(QSerialPortInfo* portInfo)
+{
+ if (portInfo->hasProductIdentifier())
+ {
+ construct(portInfo->portName(),
+ portInfo->description(),
+ portInfo->vendorIdentifier(),
+ portInfo->productIdentifier());
+ }
+ else
+ {
+ construct(portInfo->portName());
+ }
+}
+
+void PortListItem::construct(QString name, QString description, quint16 vid, quint16 pid)
+{
+ QString text = name;
+ if (!description.isEmpty())
+ {
+ text += QString(" ") + description;
+ }
+ if (vid && pid)
+ {
+ text += QString("[%1:").arg(vid, 4, 16, QChar('0'));
+ text += QString("%1]").arg(pid, 4, 16, QChar('0'));
+ setIcon(QIcon(":/usb_icon.png"));
+ }
+ else if (name.contains("rfcomm"))
+ {
+ setIcon(QIcon(":/bluetooth_icon.png"));
+ }
+ setText(text);
+ setData(name, PortNameRole);
+}
+
+QString PortListItem::portName()
+{
+ return data(PortNameRole).toString();
+}
+
+PortList::PortList(QObject* parent) :
+ QStandardItemModel(parent)
+{
+ loadPortList();
+
+ // we have to use macro based notation to be able to disconnect
+ QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(onRowsInserted(QModelIndex, int, int)));
+}
+
+void PortList::loadPortList()
+{
+ clear();
+
+ disconnect(this, SLOT(onRowsInserted(QModelIndex,int,int)));
+ for (auto portInfo : QSerialPortInfo::availablePorts())
+ {
+ appendRow(new PortListItem(&portInfo));
+ }
+ for (auto portName : userEnteredPorts)
+ {
+ appendRow(new PortListItem(portName));
+ }
+ QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(onRowsInserted(QModelIndex, int, int)));
+}
+
+int PortList::indexOf(QString portText)
+{
+ for (int i = 0; i < rowCount(); i++)
+ {
+ if (item(i)->text() == portText)
+ {
+ return i;
+ }
+ }
+ return -1; // not found
+}
+
+void PortList::onRowsInserted(QModelIndex parent, int start, int end)
+{
+ PortListItem* newItem = static_cast(item(start));
+ QString portName = newItem->text();
+ newItem->setData(portName, PortNameRole);
+ userEnteredPorts << portName;
+}
diff --git a/portlist.h b/portlist.h
new file mode 100644
--- /dev/null
+++ b/portlist.h
@@ -0,0 +1,62 @@
+/*
+ Copyright © 2015 Hasan Yavuz Özderya
+
+ This file is part of serialplot.
+
+ serialplot 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.
+
+ serialplot 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 serialplot. If not, see .
+*/
+
+#ifndef PORTLIST_H
+#define PORTLIST_H
+
+#include
+#include
+#include
+#include
+
+enum PortListRoles
+{
+ PortNameRole = Qt::UserRole+1 // portName as QString
+};
+
+class PortListItem : public QStandardItem
+{
+public:
+ PortListItem(QString name, QString description="", quint16 vid=0, quint16 pid=0);
+ PortListItem(QSerialPortInfo* portInfo);
+
+ QString portName(); // returns only the port name
+
+private:
+ // common constructor
+ void construct(QString name, QString description="", quint16 vid=0, quint16 pid=0);
+};
+
+class PortList : public QStandardItemModel
+{
+ Q_OBJECT
+public:
+ PortList(QObject* parent=0);
+
+ void loadPortList();
+ int indexOf(QString portText); // return -1 if not found
+
+private:
+ QStringList userEnteredPorts;
+
+private slots:
+ void onRowsInserted(QModelIndex parent, int start, int end);
+};
+
+#endif // PORTLIST_H
diff --git a/serialplot.pro b/serialplot.pro
--- a/serialplot.pro
+++ b/serialplot.pro
@@ -42,6 +42,7 @@ SOURCES += main.cpp\
framebuffer.cpp \
scalepicker.cpp \
scalezoomer.cpp
+ portlist.cpp
HEADERS += mainwindow.h \
utils.h \
@@ -53,6 +54,7 @@ HEADERS += mainwindow.h \
framebuffer.h \
scalepicker.h \
scalezoomer.h
+ portlist.h
FORMS += mainwindow.ui \
about_dialog.ui \
@@ -61,3 +63,6 @@ FORMS += mainwindow.ui \
INCLUDEPATH += qmake/
CONFIG += c++11
+
+RESOURCES += \
+ misc/icons.qrc