diff --git a/CHANGELOG.md b/CHANGELOG.md index 28212cd61..ed70d8494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,21 +3,22 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Changed `hf emrtd info`- now shows portrait, signature and other biometrics at the same time (@iceman1001) - Fixed `pm3_grabbed_output_get` - heap overflow (@jonyen) - Fixed `hf iclass sam` - removed the old implementation (@iceman1001) -- Fixed Proxmark5 (PM5/AT32) failing to re-enter the bootloader (@nemanjan00) -- Fixed `hw fpga config` on Proxmark5 (PM5/AT32) now powers on the FPGA 24MHz clock (@nemanjan00) -- Fixed `hf mfdes chk` to correctly check all provided keys instead of only checking a small portion (@corollary-de). -- Fixed `hf iclass unhash` omitting valid hash0 pre-images, which could make the subsequent hashcat DES crack unable to find the master key (@trichimtrich) -- Fixed `hf iclass unhash` returning no pre-images at all for some keys, caused by `check()` not being invertible in a single reading (@trichimtrich) -- Added `--rgb` option to `hf tune` / `lf tune` (Proxmark5/PM5): mirrors the antenna tuning level on the antenna RGB LED (@nemanjan00) -- Fixed `hw version` / `hw status` showing a bogus FPGA image (`fpga_pm3_hf.ncd image 2s30vq100`) on Proxmark5 (PM5/AT32) (@nemanjan00) +- Fixed (PM5) failing to re-enter the bootloader (@nemanjan00) +- Fixed `hw fpga config` (PM5) - now powers on the FPGA 24MHz clock (@nemanjan00) +- Fixed `hf mfdes chk` - now correctly check all provided keys instead of only checking a small portion (@corollary-de). +- Fixed `hf iclass unhash` - now omitting valid hash0 pre-images (@trichimtrich) +- Fixed `hf iclass unhash` - returning no pre-images at all for some keys, caused by `check()` not being invertible in a single reading (@trichimtrich) +- Added `--rgb` option to `hf tune` / `lf tune` (PM5) - mirrors the antenna tuning level on the antenna RGB LED (@nemanjan00) +- Fixed `hw version` / `hw status` (PM5) - now showing correct information for fpga images (@nemanjan00) - Fixed `hw status` [Model] section reporting "PM3 GENERIC" firmware on Proxmark5 (PM5/AT32); it now reports `PM5` (@nemanjan00) - Added `hw version` / `hw status` - now reports Proxmark5 (PM5/AT32) correct MCU (AT32F437) (@nemanjan00) -- Fixed `hf mf dump` preserving readable sector trailer Key B data (@oSPANNERo) +- Fixed `hf mf dump` - preserving readable sector trailer Key B data (@oSPANNERo) - Changed `magic_cards_notes.md` - documented the USCUID-UL helper scripts (`hf_mfu_uscuid` / `hf_mf_uscuid_prog`) (@c-barron) - Fixed `hf_mf_uscuid_prog.lua` - corrected the script name shown in its usage text (@c-barron) -- Added `hf felica seacauth1` command +- Added `hf felica seacauth1` command (@kormax) - Added `lf trovan` commands to support Trovan Animal ID (@iceman1001) - Added `hf mf gdmgetblk/gdmgethidblk/gdmsethidblk/gdmsetuid/gdmwipe/gdmsetsig` (@0x6r1an0y) - Changed `hf mf gdmparsecfg/gdmsetblk` (@0x6r1an0y) @@ -30,9 +31,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf 14b ctrdbl` and `hf 14b ctdump` commands for interacting with ASK CTS tags (@kormax) - Fixed `hf legic migrate` failing to parse the optional DCF argument as hex (@IdanHo) - Add support for parsing Finnish Helsinki Regional Transport (HRT) travel cards (@sanduuz) -- Added standalone mode `HF_DOEGOX_COMMIT`: DESFire suspended commit without relay (@doegox) -- Added support for Innovatron protocol detection to `hf 14b info` (@kormax) -- Improved `lf cotag reader` and `lf cotag demod`: Reimplementation and enhancement of proxmark3 COTAG support +- Added standalone mode `HF_DOEGOX_COMMIT` - DESFire suspended commit without relay (@doegox) +- Changed `hf 14b info`- now support Innovatron protocol detection (@kormax) +- Improved `lf cotag reader` and `lf cotag demod` - Reimplementation and enhancement of proxmark3 COTAG support - Added `hf felica sim` command (@kormax) - Added `mad read`, `mad write`, `mad verify`, `mad decode`, `mad encode` commands with typed struct MAD API (@AlxCzl) - Added `hf mfdes getversion` command (@kormax) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 5d0182d56..f9546aa1f 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1515,79 +1515,49 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { return PM3_SUCCESS; } -static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen) { +// Extract an image out of a data group and hand it to the picture viewer. +// The viewer keeps an array of images, so DG2 / DG5 / DG7 all end up +// side by side, each in its own tab named by "title" +static int emrtd_print_image(const char *title, uint8_t *data, size_t datalen) { - int offset = 0; + if (data == NULL || datalen < 6) { + return PM3_ESOFT; + } // This is a hacky impl that just looks for the image header. I'll improve it eventually. // based on mrpkey.py // Note: Doing datalen - 6 to account for the longest data we're checking. // Checks first byte before the rest to reduce overhead + bool found = false; + size_t offset = 0; for (offset = 0; offset < datalen - 6; offset++) { if ((data[offset] == 0xFF && memcmp(jpeg_header, data + offset, 4) == 0) || (data[offset] == 0x00 && memcmp(jpeg2k_header, data + offset, 6) == 0)) { - datalen = datalen - offset; + found = true; break; } } - // If we didn't get any data, return false. - if (datalen == 0) { + // If we didn't find any image, return false. + if (found == false) { + PrintAndLogEx(DEBUG, "No image header found in %s", title); return PM3_ESOFT; } - ShowPictureWindow(data + offset, datalen); + ShowPictureWindow(title, data + offset, (int)(datalen - offset)); return PM3_SUCCESS; } +static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen) { + return emrtd_print_image("DG2 - Encoded Face", data, datalen); +} + static int emrtd_print_ef_dg5_info(uint8_t *data, size_t datalen) { - - int offset = 0; - - // This is a hacky impl that just looks for the image header. I'll improve it eventually. - // based on mrpkey.py - // Note: Doing datalen - 6 to account for the longest data we're checking. - // Checks first byte before the rest to reduce overhead - for (offset = 0; offset < datalen - 6; offset++) { - if ((data[offset] == 0xFF && memcmp(jpeg_header, data + offset, 4) == 0) || - (data[offset] == 0x00 && memcmp(jpeg2k_header, data + offset, 6) == 0)) { - datalen = datalen - offset; - break; - } - } - - // If we didn't get any data, return false. - if (datalen == 0) { - return PM3_ESOFT; - } - - ShowPictureWindow(data + offset, datalen); - return PM3_SUCCESS; + return emrtd_print_image("DG5 - Displayed Portrait", data, datalen); } static int emrtd_print_ef_dg7_info(uint8_t *data, size_t datalen) { - - int offset = 0; - - // This is a hacky impl that just looks for the image header. I'll improve it eventually. - // based on mrpkey.py - // Note: Doing datalen - 6 to account for the longest data we're checking. - // Checks first byte before the rest to reduce overhead - for (offset = 0; offset < datalen - 6; offset++) { - if ((data[offset] == 0xFF && memcmp(jpeg_header, data + offset, 4) == 0) || - (data[offset] == 0x00 && memcmp(jpeg2k_header, data + offset, 6) == 0)) { - datalen = datalen - offset; - break; - } - } - - // If we didn't get any data, return false. - if (datalen == 0) { - return PM3_ESOFT; - } - - ShowPictureWindow(data + offset, datalen); - return PM3_SUCCESS; + return emrtd_print_image("DG7 - Signature", data, datalen); } static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { @@ -2066,6 +2036,9 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail"); } + // start with an empty picture viewer, the data groups below fill it up + ClearPictureWindow(); + // Dump all files in the file list for (int i = 0; i < filelistlen; i++) { @@ -2174,6 +2147,9 @@ int infoHF_EMRTD_offline(const char *path) { } free(data); + // start with an empty picture viewer, the data groups below fill it up + ClearPictureWindow(); + // Read files in the file list for (int i = 0; i < filelistlen; i++) { diff --git a/client/src/guidummy.cpp b/client/src/guidummy.cpp index 2882fd41d..64e9a5631 100644 --- a/client/src/guidummy.cpp +++ b/client/src/guidummy.cpp @@ -30,7 +30,7 @@ extern "C" void ShowGraphWindow(void) { extern "C" void HideGraphWindow(void) {} extern "C" void RepaintGraphWindow(void) {} -extern "C" void ShowPictureWindow(char *fn, int len) { +extern "C" void ShowPictureWindow(const char *title, unsigned char *data, int len) { static int warned = 0; if (!warned) { @@ -38,7 +38,7 @@ extern "C" void ShowPictureWindow(char *fn, int len) { warned = 1; } } -extern "C" void ShowBase64PictureWindow(char *b64) { +extern "C" void ShowBase64PictureWindow(const char *title, char *b64) { static int warned = 0; if (!warned) { @@ -46,6 +46,7 @@ extern "C" void ShowBase64PictureWindow(char *b64) { warned = 1; } } +extern "C" void ClearPictureWindow(void) {} extern "C" void HidePictureWindow(void) {} extern "C" void RepaintPictureWindow(void) {} diff --git a/client/src/nfc/ndef.c b/client/src/nfc/ndef.c index 6acf53cc0..1fab72018 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -867,7 +867,7 @@ static int ndefDecodeMime_vcard(NDEFHeader_t *ndef) { } else if (strncmp(part, "VCARD", 5) == 0) { } else { // should be in the BASE64 data part now. - ShowBase64PictureWindow(part); + ShowBase64PictureWindow("VCARD photo", part); } part = strtok(NULL, ":"); } @@ -981,7 +981,7 @@ static int ndefDecodeMime_image(NDEFHeader_t *ndef) { PrintAndLogEx(INFO, "Type............ " _YELLOW_("%.*s"), (int)ndef->TypeLen, ndef->Type); PrintAndLogEx(INFO, "Size............ " _YELLOW_("%zu"), ndef->PayloadLen); PrintAndLogEx(INFO, ""); - ShowPictureWindow(ndef->Payload, (int)ndef->PayloadLen); + ShowPictureWindow("NDEF image", ndef->Payload, (int)ndef->PayloadLen); return PM3_SUCCESS; } diff --git a/client/src/proxgui.cpp b/client/src/proxgui.cpp index d9b82abd2..b14e8100b 100644 --- a/client/src/proxgui.cpp +++ b/client/src/proxgui.cpp @@ -66,39 +66,60 @@ extern "C" void RepaintGraphWindow(void) { } +// Show a notice if X11/XQuartz isn't available +static void show_no_gui_notice(void) { +#if defined(__MACH__) && defined(__APPLE__) + PrintAndLogEx(WARNING, "You appear to be on a MacOS device without XQuartz.\nYou may need to install XQuartz (https://www.xquartz.org/) to make the plot work."); +#else + PrintAndLogEx(WARNING, "You appear to be on an environment without an X11 server or without DISPLAY environment variable set.\nPicture display may not work until you resolve these issues."); +#endif +} + // hook up picture viewer -extern "C" void ShowPictureWindow(uint8_t *data, int len) { +extern "C" void ShowPictureWindow(const char *title, uint8_t *data, int len) { // No support for jpeg2000 in Qt Image since a while... // https://doc.qt.io/qt-5/qtimageformats-index.html QImage img = QImage::fromData(data, len); if (img.isNull()) { - return; - } - if (!gui) { - // Show a notice if X11/XQuartz isn't available -#if defined(__MACH__) && defined(__APPLE__) - PrintAndLogEx(WARNING, "You appear to be on a MacOS device without XQuartz.\nYou may need to install XQuartz (https://www.xquartz.org/) to make the plot work."); -#else - PrintAndLogEx(WARNING, "You appear to be on an environment without an X11 server or without DISPLAY environment variable set.\nPicture display may not work until you resolve these issues."); -#endif + PrintAndLogEx(WARNING, "Failed to decode image ( %s ), Qt lacks support for this format (jpeg2000?)", (title) ? title : "n/a"); return; } - gui->ShowPictureWindow(img); + if (!gui) { + show_no_gui_notice(); + return; + } + + gui->ShowPictureWindow(QString::fromUtf8((title) ? title : ""), img); } -extern "C" void ShowBase64PictureWindow(char *b64) { - if (!gui) { - // Show a notice if X11/XQuartz isn't available -#if defined(__MACH__) && defined(__APPLE__) - PrintAndLogEx(WARNING, "You appear to be on a MacOS device without XQuartz.\nYou may need to install XQuartz (https://www.xquartz.org/) to make the plot work."); -#else - PrintAndLogEx(WARNING, "You appear to be on an environment without an X11 server or without DISPLAY environment variable set.\nPlot may not work until you resolve these issues."); -#endif +extern "C" void ShowBase64PictureWindow(const char *title, char *b64) { + + if (b64 == NULL) { return; } - gui->ShowBase64PictureWindow(b64); + // decode here, in the caller thread, since the b64 pointer isn't + // guaranteed to be alive once the GUI thread picks up the queued signal + QImage img; + if (img.loadFromData(QByteArray::fromBase64(QByteArray(b64))) == false) { + PrintAndLogEx(WARNING, "Failed to decode base64 image ( %s )", (title) ? title : "n/a"); + return; + } + + if (!gui) { + show_no_gui_notice(); + return; + } + + gui->ShowPictureWindow(QString::fromUtf8((title) ? title : ""), img); +} + +extern "C" void ClearPictureWindow(void) { + if (!gui) + return; + + gui->ClearPictureWindow(); } extern "C" void HidePictureWindow(void) { diff --git a/client/src/proxgui.h b/client/src/proxgui.h index 8ec934265..6abf1ab28 100644 --- a/client/src/proxgui.h +++ b/client/src/proxgui.h @@ -32,8 +32,12 @@ void HideGraphWindow(void); void RepaintGraphWindow(void); // hook up picture viewer -void ShowPictureWindow(uint8_t *data, int len); -void ShowBase64PictureWindow(char *b64); +// The picture viewer keeps an array of images, one tab per image, +// so several pictures (portrait, signature, other biometrics...) can be shown at once. +// "title" is used as tab label, NULL gives a generic "Image N" label +void ShowPictureWindow(const char *title, uint8_t *data, int len); +void ShowBase64PictureWindow(const char *title, char *b64); +void ClearPictureWindow(void); void HidePictureWindow(void); void RepaintPictureWindow(void); diff --git a/client/src/proxguiqt.cpp b/client/src/proxguiqt.cpp index 493df83af..df810aef9 100644 --- a/client/src/proxguiqt.cpp +++ b/client/src/proxguiqt.cpp @@ -32,6 +32,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -64,12 +68,12 @@ void ProxGuiQT::HideGraphWindow(void) { } // emit picture viewer signals -void ProxGuiQT::ShowPictureWindow(const QImage &img) { - emit ShowPictureWindowSignal(img); +void ProxGuiQT::ShowPictureWindow(const QString &title, const QImage &img) { + emit ShowPictureWindowSignal(title, img); } -void ProxGuiQT::ShowBase64PictureWindow(char *b64) { - emit ShowBase64PictureWindowSignal(b64); +void ProxGuiQT::ClearPictureWindow(void) { + emit ClearPictureWindowSignal(); } void ProxGuiQT::RepaintPictureWindow(void) { @@ -115,7 +119,7 @@ void ProxGuiQT::_HideGraphWindow(void) { } // picture viewer -void ProxGuiQT::_ShowPictureWindow(const QImage &img) { +void ProxGuiQT::_ShowPictureWindow(const QString &title, const QImage &img) { if (!plotapp) return; @@ -132,69 +136,16 @@ void ProxGuiQT::_ShowPictureWindow(const QImage &img) { pictureWidget = new PictureWidget(); } - QPixmap pm = QPixmap::fromImage(img); - - //QPixmap newPixmap = pm.scaled(QSize(50,50), Qt::KeepAspectRatio); - //pm = pm.scaled(pictureController->lbl_pm->size(), Qt::KeepAspectRatio); - - pictureController->lbl_pm->setPixmap(pm); - pictureController->lbl_pm->setScaledContents(false); - pictureController->lbl_pm->setAlignment(Qt::AlignCenter); - - QString s = QString("w: %1 h: %2") - .arg(pm.size().width()) - .arg(pm.size().height() - ); - pictureController->lbl_sz->setText(s); + pictureWidget->addPicture(title, img); pictureWidget->show(); - + pictureWidget->raise(); } -void ProxGuiQT::_ShowBase64PictureWindow(char *b64) { - - if (!plotapp) +void ProxGuiQT::_ClearPictureWindow(void) { + if (!plotapp || !pictureWidget) return; - if (b64 == NULL) - return; - - size_t slen = strlen(b64); - if (slen == 0) - return; - - char *myb64data = (char *)calloc(slen + 1, sizeof(uint8_t)); - if (myb64data == NULL) - return; - - memcpy(myb64data, b64, slen); - - if (!pictureWidget) { - -#if defined(__MACH__) && defined(__APPLE__) - makeFocusable(); -#endif - - pictureWidget = new PictureWidget(); - } - - QPixmap pm; - if (pm.loadFromData(QByteArray::fromBase64(myb64data), "PNG") == false) { - qWarning("Failed to read base64 data: %s", myb64data); - } - free(myb64data); - //free(b64); - - pictureController->lbl_pm->setPixmap(pm); - pictureController->lbl_pm->setScaledContents(false); - pictureController->lbl_pm->setAlignment(Qt::AlignCenter); - - QString s = QString("w: %1 h: %2") - .arg(pm.size().width()) - .arg(pm.size().height() - ); - pictureController->lbl_sz->setText(s); - pictureWidget->show(); - + pictureWidget->clearPictures(); } void ProxGuiQT::_RepaintPictureWindow(void) { @@ -230,10 +181,8 @@ void ProxGuiQT::_StartProxmarkThread(void) { void ProxGuiQT::MainLoop() { plotapp = new QApplication(argc, argv); - // Setup the picture widget + // Setup the picture widget, it builds its own UI and owns the image array pictureWidget = new PictureWidget(); - pictureController = new Ui::PictureForm(); - pictureController->setupUi(pictureWidget); // pictureWidget->setAttribute(Qt::WA_DeleteOnClose,true); // Set picture widget position if no settings. @@ -250,8 +199,8 @@ void ProxGuiQT::MainLoop() { connect(this, SIGNAL(ExitSignal()), this, SLOT(_Exit())); // hook up picture viewer signals - connect(this, SIGNAL(ShowPictureWindowSignal(const QImage &)), this, SLOT(_ShowPictureWindow(const QImage &))); - connect(this, SIGNAL(ShowBase64PictureWindowSignal(char *)), this, SLOT(_ShowBase64PictureWindow(char *))); + connect(this, SIGNAL(ShowPictureWindowSignal(const QString &, const QImage &)), this, SLOT(_ShowPictureWindow(const QString &, const QImage &))); + connect(this, SIGNAL(ClearPictureWindowSignal()), this, SLOT(_ClearPictureWindow())); connect(this, SIGNAL(RepaintPictureWindowSignal()), this, SLOT(_RepaintPictureWindow())); connect(this, SIGNAL(HidePictureWindowSignal()), this, SLOT(_HidePictureWindow())); @@ -267,17 +216,12 @@ void ProxGuiQT::MainLoop() { } ProxGuiQT::ProxGuiQT(int argc, char **argv, WorkerThread *wthread) : - plotapp(NULL), plotwidget(NULL), pictureController(NULL), pictureWidget(NULL), argc(argc), argv(argv), proxmarkThread(wthread) { + plotapp(NULL), plotwidget(NULL), pictureWidget(NULL), argc(argc), argv(argv), proxmarkThread(wthread) { } ProxGuiQT::~ProxGuiQT(void) { - if (pictureController) { - delete pictureController; - pictureController = NULL; - } - if (pictureWidget) { pictureWidget->close(); delete pictureWidget; @@ -294,12 +238,90 @@ ProxGuiQT::~ProxGuiQT(void) { // Slider Widget form based on a class to enable // Event override functions // ------------------------------------------------- -PictureWidget::PictureWidget() { +PictureWidget::PictureWidget() : m_ui(new Ui::PictureForm) { + + m_ui->setupUi(this); + // Set the initial position and size from settings // if (g_session.preferences_loaded) // setGeometry(g_session.pw.x, g_session.pw.y, g_session.pw.w, g_session.pw.h); // else resize(400, 400); + updateTitle(); +} + +PictureWidget::~PictureWidget(void) { + delete m_ui; + m_ui = NULL; +} + +void PictureWidget::updateTitle(void) { + if (m_images.isEmpty()) { + setWindowTitle(QString("Picture Viewer")); + } else { + setWindowTitle(QString("Picture Viewer (%1)").arg(m_images.size())); + } +} + +const PictureItem *PictureWidget::pictureAt(int i) const { + if (i < 0 || i >= m_images.size()) + return NULL; + + return &m_images.at(i); +} + +// Append one image to the array and give it its own tab +void PictureWidget::addPicture(const QString &title, const QImage &img) { + + if (img.isNull()) + return; + + m_images.append(PictureItem(title, img)); + + QWidget *page = new QWidget(); + QVBoxLayout *layout = new QVBoxLayout(page); + + QLabel *lbl_pm = new QLabel(); + lbl_pm->setPixmap(QPixmap::fromImage(img)); + lbl_pm->setScaledContents(false); + lbl_pm->setAlignment(Qt::AlignCenter); + + // large images (fingerprints, high res portraits) shouldn't blow up the window + QScrollArea *scroll = new QScrollArea(page); + scroll->setWidget(lbl_pm); + scroll->setWidgetResizable(true); + scroll->setAlignment(Qt::AlignCenter); + layout->addWidget(scroll); + + QLabel *lbl_sz = new QLabel(page); + lbl_sz->setText(QString("w: %1 h: %2") + .arg(img.width()) + .arg(img.height()) + ); + lbl_sz->setAlignment(Qt::AlignCenter); + layout->addWidget(lbl_sz); + + QString name = title.trimmed(); + if (name.isEmpty()) { + name = QString("Image %1").arg(m_images.size()); + } + + int idx = m_ui->tabs->addTab(page, name); + m_ui->tabs->setCurrentIndex(idx); + updateTitle(); +} + +// Drop all images, called before a new dump fills the viewer again +void PictureWidget::clearPictures(void) { + + while (m_ui->tabs->count() > 0) { + QWidget *page = m_ui->tabs->widget(0); + m_ui->tabs->removeTab(0); + delete page; + } + + m_images.clear(); + updateTitle(); } void PictureWidget::closeEvent(QCloseEvent *event) { diff --git a/client/src/proxguiqt.h b/client/src/proxguiqt.h index 6e7f8efb1..6da86e1ca 100644 --- a/client/src/proxguiqt.h +++ b/client/src/proxguiqt.h @@ -91,12 +91,35 @@ class SliderWidget : public QWidget { SliderWidget(); }; -// Added class for SliderWidget to allow move/resize event override +/** + * @brief One decoded image together with the label it is shown under + */ +class PictureItem { + public: + PictureItem(const QString &t, const QImage &i) : title(t), image(i) {} + QString title; + QImage image; +}; + +// Picture viewer window. Holds an array of images, one tab per image, so that +// several pictures of the same document (portrait, signature, other biometrics) +// can be shown side by side class PictureWidget : public QWidget { protected: void closeEvent(QCloseEvent *event); public: PictureWidget(); + ~PictureWidget(void); + + void addPicture(const QString &title, const QImage &img); + void clearPictures(void); + int pictureCount(void) const { return m_images.size(); } + const PictureItem *pictureAt(int i) const; + + private: + Ui::PictureForm *m_ui; + QVector m_images; + void updateTitle(void); }; /** @@ -156,7 +179,6 @@ class ProxGuiQT : public QObject { private: QApplication *plotapp; ProxWidget *plotwidget; - Ui::PictureForm *pictureController; PictureWidget *pictureWidget; int argc; @@ -172,8 +194,8 @@ class ProxGuiQT : public QObject { void HideGraphWindow(void); // hook up picture viewer - void ShowPictureWindow(const QImage &img); - void ShowBase64PictureWindow(char *b64); + void ShowPictureWindow(const QString &title, const QImage &img); + void ClearPictureWindow(void); void HidePictureWindow(void); void RepaintPictureWindow(void); @@ -186,8 +208,8 @@ class ProxGuiQT : public QObject { void _HideGraphWindow(void); // hook up picture viewer - void _ShowPictureWindow(const QImage &img); - void _ShowBase64PictureWindow(char *b64); + void _ShowPictureWindow(const QString &title, const QImage &img); + void _ClearPictureWindow(void); void _HidePictureWindow(void); void _RepaintPictureWindow(void); @@ -201,8 +223,8 @@ class ProxGuiQT : public QObject { void ExitSignal(void); // hook up picture viewer signals - void ShowPictureWindowSignal(const QImage &img); - void ShowBase64PictureWindowSignal(char *b64); + void ShowPictureWindowSignal(const QString &title, const QImage &img); + void ClearPictureWindowSignal(void); void HidePictureWindowSignal(void); void RepaintPictureWindowSignal(void); }; diff --git a/client/src/ui/image.ui b/client/src/ui/image.ui index cb28d7700..5bc1968bb 100644 --- a/client/src/ui/image.ui +++ b/client/src/ui/image.ui @@ -15,13 +15,12 @@ - - - - - - - Image size + + + QTabWidget::North + + + true @@ -30,4 +29,3 @@ -