/***************************** LICENSE START ***********************************

 Copyright 2016 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "uPlotBase.h"

#include <fstream>
#include <sstream>
#include <string>

#include "fstream_mars_fix.h"

// Qt include files
#include <QtGlobal>
#include <QAbstractPrintDialog>
#include <QAction>
#include <QComboBox>
#include <QDebug>
#include <QFileSystemWatcher>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QHBoxLayout>
#include <QMenu>
#include <QMessageBox>
#include <QPdfWriter>
#include <QPrinter>
#include <QPrintDialog>
#include <QPushButton>
#include <QStringList>
#include <QTimer>
#include <QToolButton>
#include <QUndoStack>
#include <QVBoxLayout>
#include <QWidgetAction>

#if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)
#include <QPageLayout>
#endif

// My Qt include files
#include "MgQPlotScene.h"
#include "MgQRootItem.h"
#include "MgQSceneItem.h"

#include "ExportDialog.h"

#include "MvQAbout.h"
#include "MvQApplication.h"
#include "MvQEcChartsDialog.h"
#include "MvQMethods.h"
#include "MvQProgressManager.h"

#ifdef METVIEW_WEATHER_ROOM
#include "MvQWeatherRoomDialog.h"
#endif

#include "MvQPlotView.h"
#include "MvQZoomStackWidget.h"

#if defined(METVIEW_ODB)
#include <odc/api/odc.h>
#endif


#include "MagPlusService.h"
#include "../libMetview/Path.h"  // path given to disambiguate from Magics Path.h
#include "MvMiscellaneous.h"
#include "MvPath.hpp"
#include "MvQPalette.h"
#include "MvQTheme.h"
#include "ObjectList.h"
#include "PlotMod.h"
#include "PlotModView.h"
#include "Root.h"
#include "MvLog.h"

#define PLACEMARK

Presentable* uPlotBase::owner_ = nullptr;
string uPlotBase::uPlotTempDir_ = "";


ExportMessageBox::ExportMessageBox(uPlotBase* owner) :
    QMessageBox(owner),
    owner_(owner)
{
    setText(
        "<big><center>Generating plot - please wait...</big></center>"
        "<br>This window will disappear as soon as the output file"
        " is generated");
    setStandardButtons(QMessageBox::NoButton);
    setWindowModality(Qt::WindowModal);

    // here we need to wait long enough so that the dialog could appear
    QTimer::singleShot(100, this, SLOT(doDelayedProc()));
}

void ExportMessageBox::doDelayedProc()
{
    // process the pending export request
    owner_->processExportDialogRequest();
    QDialog::reject();
}


uPlotBase::uPlotBase(QWidget* parent) :
    MvQMainWindow(parent)
{
#ifdef METVIEW_WEATHER_ROOM
    wroomSync_ = false;
#endif

    // Find out screen resolution
    int res = screenResolutionInDpi();

    // resize(1020,720);

    oriPlotWidthInCm_ = -1.;
    oriPlotHeightInCm_ = -1.;

    //---------------------
    // The scene
    //---------------------

    plotScene_ = new MgQPlotScene;
    plotScene_->setSceneRect(QRectF());
    plotScene_->setDpiResolution(res);
    plotScene_->setHighlightStyle(QBrush(Qt::NoBrush),
                                  MvQTheme::pen("uplot", "scene_highlight_border", 3.));
    plotScene_->setHighlightBriefStyle(MvQTheme::brush("uplot", "scene_highlight_brief"),
                                       MvQTheme::pen("uplot", "scene_highlight_brief_border", 3., Qt::SolidLine));

    activeScene_ = 0;

    //---------------------
    // The view
    //---------------------

    plotView_ = new MvQPlotView(plotScene_);
    plotView_->setMouseTracking(true);
    plotView_->setBackgroundBrush(MvQTheme::colour("uplot", "view_bg"));

    connect(plotView_, SIGNAL(contextMenuEventHappened(const QPoint&, const QPointF&)),
            this, SLOT(slotBuildContextMenu(const QPoint&, const QPointF&)));

    connect(plotView_, SIGNAL(resizeEventHappened(const QSize&)),
            this, SLOT(slotResizeEvent(const QSize&)));

    connect(plotView_, SIGNAL(zoomRectangleIsDefined(const std::string&, const std::string&)),
            this, SLOT(slotPerformZoom(const std::string&, const std::string&)));

    connect(plotView_, SIGNAL(iconDropped(const MvQDrop&, QPoint)),
            this, SLOT(slotProcessDropInView(const MvQDrop&, QPoint)));

    connect(plotView_, SIGNAL(addRibbonEditor(QWidget*)),
            this, SLOT(slotAddRibbonEditor(QWidget*)));

    // Setup Drag-Drop connection
    connect(this, SIGNAL(sendDropRequest(MvRequest*)),
            QApplication::instance(), SLOT(processDropRequest(MvRequest*)));

    // Create a temporary directory related to this session
    std::string errText;
    if (!metview::createWorkDir("uplot", uPlotTempDir_, errText)) {
        uPlotTempDir_ = getenv("METVIEW_TMPDIR");  // fallback position
    }
    uPlotTempDir_ += "/";
    Path path(uPlotTempDir_);
    path.mkdir();

    setupContextMenuActions();
}

uPlotBase::~uPlotBase()
{
    owner_ = nullptr;

    // Delete temporary directory
    Path path(tempDir());
    path.remove();
}

void uPlotBase::setupViewActions()
{
    QAction* action = nullptr;

    //------------------
    // Resize
    //------------------

    // Plot size combo
    sizeCombo_ = new QComboBox;

    sizeMinValue_ = 10;
    sizeMaxValue_ = 200;
    sizeValueStep_ = 10;

    predefSizeValues_ << uPlotBase::FitToWindow << uPlotBase::FitToWidth << uPlotBase::FitToHeight << 50 << 75 << 100 << 150 << 200;

    customSizeItemIndex_ = predefSizeValues_.count();

    foreach (int val, predefSizeValues_) {
        if (val == uPlotBase::FitToWindow) {
            sizeCombo_->addItem(tr("Fit to Window"));
        }
        else if (val == uPlotBase::FitToWidth) {
            sizeCombo_->addItem(tr("Fit to Width"));
        }
        else if (val == uPlotBase::FitToHeight) {
            sizeCombo_->addItem(tr("Fit to Height"));
        }
        else {
            QString s = QString::number(val) + "%";
            sizeCombo_->addItem(s);
        }

        sizeCombo_->setItemData(sizeCombo_->count() - 1, val, Qt::UserRole);
    }

    // sizeCombo_->setCurrentIndex(4);

    action = new QAction(this);
    action->setText(tr("Decrease size"));
    action->setIcon(QIcon(QPixmap(QString::fromUtf8(":/uPlot/size_down.svg"))));
    actionSizeDown_ = action;

    action = new QAction(this);
    action->setText(tr("Increase size"));
    action->setIcon(QIcon(QPixmap(QString::fromUtf8(":/uPlot/size_up.svg"))));
    actionSizeUp_ = action;

    connect(sizeCombo_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotSizeChanged(int)));

    connect(actionSizeDown_, SIGNAL(triggered()),
            this, SLOT(slotSizeDown()));

    connect(actionSizeUp_, SIGNAL(triggered()),
            this, SLOT(slotSizeUp()));

    auto* sep = new QAction(this);
    sep->setSeparator(true);

    MvQMainWindow::MenuType menuType = MvQMainWindow::ViewMenu;

    menuItems_[menuType].push_back(new MvQMenuItem(sizeCombo_));
    menuItems_[menuType].push_back(new MvQMenuItem(actionSizeDown_));
    menuItems_[menuType].push_back(new MvQMenuItem(actionSizeUp_));
}

void uPlotBase::setupZoomActions()
{
    // Zoom
    auto* actionZoom = new QAction(this);
    actionZoom->setObjectName(QString::fromUtf8("actionZoom"));
    actionZoom->setCheckable(true);
    QIcon icon;
    icon.addPixmap(QPixmap(QString::fromUtf8(":/uPlot/zoom.svg")), QIcon::Normal, QIcon::Off);
    actionZoom->setIcon(icon);
    actionZoom->setText(tr("&Zoom"));
    // controlActions_.push_back(actionZoom);

    // Up
    actionZoomUp_ = new QAction(this);
    actionZoomUp_->setObjectName(QString::fromUtf8("actionZoomStackUp"));
    QIcon iconUp;
    iconUp.addPixmap(QPixmap(QString::fromUtf8(":/uPlot/zoom_out.svg")), QIcon::Normal, QIcon::Off);
    actionZoomUp_->setIcon(iconUp);
    // actionUp->setIcon(QApplication::style()->standardIcon(QStyle::SP_ArrowUp));
    actionZoomUp_->setText(tr("Zoom &out"));
    actionZoomUp_->setShortcut(tr("Ctrl+Up"));

    // Down
    actionZoomDown_ = new QAction(this);
    actionZoomDown_->setObjectName(QString::fromUtf8("actionZoomStackUp"));
    QIcon iconDown;
    iconDown.addPixmap(QPixmap(QString::fromUtf8(":/uPlot/zoom_in.svg")), QIcon::Normal, QIcon::Off);
    actionZoomDown_->setIcon(iconDown);
    // actionDown->setIcon(QApplication::style()->standardIcon(QStyle::SP_ArrowDown));
    actionZoomDown_->setText(tr("Zoom &in"));
    actionZoomDown_->setShortcut(tr("Ctrl+Down"));

    // Zoomstack
    auto* zoomStackPb = new QToolButton;
    QIcon icon11;
    icon11.addPixmap(QPixmap(QString::fromUtf8(":/uPlot/zoom_stack.svg")), QIcon::Normal, QIcon::Off);
    zoomStackPb->setIcon(icon11);
    zoomStackPb->setToolTip(tr("Zoom levels"));
    zoomStackPb->setPopupMode(QToolButton::InstantPopup);
    zoomStackPb->setObjectName("zoomStackPb");

    // QMenu *zoomStackMenu = new QMenu("&ZoomStack");

    zoomStackWidget_ = new MvQZoomStackWidget(plotScene_);
    auto wa = new QWidgetAction(this);
    wa->setDefaultWidget(zoomStackWidget_);
    zoomStackPb->addAction(wa);

    // zoomStackPb->setMenu(zoomStackWidget_);

    // QWidgetAction *wa = new QWidgetAction(zoomStackMenu);
    // wa->setDefaultWidget(zoomStackWidget_);
    // zoomStackMenu->addAction(wa);

    // Zoomstack combo
    // zoomStackWidget_ = new MvQZoomStackWidget(plotScene_);

    // Separator
    // QAction *actionSep=new QAction(this);
    // actionSep->setSeparator(true);

    // Zoom button
    connect(actionZoom, SIGNAL(toggled(bool)),
            plotView_, SLOT(slotSetEnableZoom(bool)));

    connect(plotView_, SIGNAL(zoomIsEnabledProgramatically(bool)),
            actionZoom, SLOT(setChecked(bool)));


    connect(zoomStackWidget_, SIGNAL(actLevelChanged(QString, int)),
            this, SLOT(slotZoomStackLevelChanged(QString, int)));

    connect(actionZoomUp_, SIGNAL(triggered()),
            zoomStackWidget_, SLOT(slotStepUp()));

    connect(actionZoomDown_, SIGNAL(triggered()),
            zoomStackWidget_, SLOT(slotStepDown()));

    MvQMainWindow::MenuType menuType = MvQMainWindow::ZoomMenu;

    menuItems_[menuType].push_back(new MvQMenuItem(actionZoom));
    menuItems_[menuType].push_back(new MvQMenuItem(actionZoomUp_));    //!!!
    menuItems_[menuType].push_back(new MvQMenuItem(actionZoomDown_));  //!!!
    // menuItems_["zoom"].push_back(new MvQMenuItem(actionSep));
    menuItems_[menuType].push_back(new MvQMenuItem(zoomStackPb));
}

void uPlotBase::setupHelpActions()
{
    // About
    auto* actionAbout = new QAction(this);
    actionAbout->setObjectName(QString::fromUtf8("actionAbout"));
    actionAbout->setText(tr("&About uPlot"));

    connect(actionAbout, SIGNAL(triggered()), this, SLOT(slotShowAboutBox()));

    MvQMainWindow::MenuType menuType = MvQMainWindow::HelpMenu;

    menuItems_[menuType].push_back(new MvQMenuItem(actionAbout, MvQMenuItem::MenuTarget));
}

void uPlotBase::setupContextMenuActions()
{
    QAction* action = nullptr;

    // Up
    action = new QAction(this);
    action->setObjectName(QString::fromUtf8("actionContextZoomStackUp"));
    QIcon iconUp;
    iconUp.addPixmap(QPixmap(QString::fromUtf8(":/uPlot/zoom_out.svg")), QIcon::Normal, QIcon::Off);
    action->setIcon(iconUp);
    action->setText(tr("Zoom &out"));
    action->setShortcut(tr("Ctrl+Up"));
    actionContextZoomUp_ = action;

    // Down
    action = new QAction(this);
    action->setObjectName(QString::fromUtf8("actionContextZoomStackUp"));
    QIcon iconDown;
    iconDown.addPixmap(QPixmap(QString::fromUtf8(":/uPlot/zoom_in.svg")), QIcon::Normal, QIcon::Off);
    action->setIcon(iconDown);
    action->setText(tr("Zoom &in"));
    action->setShortcut(tr("Ctrl+Down"));
    actionContextZoomDown_ = action;

    action = new QAction(this);
    action->setObjectName(QString::fromUtf8("actionContextSelectScene"));
    QIcon icon;
    icon.addPixmap(QPixmap(QString::fromUtf8(":/uPlot/player_play.svg")), QIcon::Normal, QIcon::Off);
    // action->setIcon(icon);
    action->setText(tr("&Select as active scene"));
    actionContextSelectScene_ = action;
}


void uPlotBase::SetupResources()
{
    // Find uPlot resources file and read it
    Cached path = ObjectList::Find("visual_resources", "uPlot", "path");
    MvRequest requPlot;
    requPlot.read(path);

    int i = 0;
    requPlot.rewind();
    while (requPlot) {
        i++;
        requPlot.advance();
    }

    MvRequest tt;
    tt.read("/tmp/dummy-user/data/setup.txt");

    i = 0;
    tt.rewind();
    while (tt) {
        i++;
        tt.advance();
    }
    // COUT << "Number total resources = " << i << std::endl;
}

void uPlotBase::setPlot(float hor, float ver)
{
    if (oriPlotWidthInCm_ < 0) {
        oriPlotWidthInCm_ = hor;
        oriPlotHeightInCm_ = ver;
    }

    // Set drawing area size
    // float hsize = floorf((75.*1./2.54) * hor);
    // float vsize = floorf((75.*1./2.54) * ver);

    // plotScene_->setSceneRect(0,0,hsize,vsize);
    // plotView_->setMvPlotSize(hsize,vsize);
}

void uPlotBase::slotProcessDropInView(const MvQDrop& drop, QPoint pos)
{
    setDropTargetInView(pos);

    // IMPORTANT: THIS CODE WILL BE REPLACED BY:
    //	sPageId = activeScene_->layout().id();
    // WE WILL ASK SYLVIE TO IMPLEMENT THIS FUNCTION

    std::string sPageId;

    if (activeScene_) {
        sPageId = activeScene_->layout().id();
        if (sPageId.empty()) {
            MgQLayoutItem* projItem = activeScene_->firstProjectorItem();
            sPageId = projItem->layout().id();
        }
    }

    // Build request to be sent back to source
    int pageId = 0;
    std::istringstream sst(sPageId);
    sst >> pageId;

    MvRequest req("DROP_REQUEST");
    //	req("BATCH") = drop->batchNumber();

    req("ACTION") = 3;

    MvRequest subreq("DROP");
    subreq("DROP_ID") = pageId;
    subreq("DROP_X") = 12.;

    if (drop.iconNum() > 0) {
        subreq.setValue("ICON_NAME", drop.iconPath(0).toStdString().c_str());

        for (int i = 1; i < drop.iconNum(); i++) {
            subreq.addValue("ICON_NAME", drop.iconPath(i).toStdString().c_str());
        }
    }

    subreq("DROP_Y") = 4.2;
    subreq("_REGISTER_TRAILER") = 1;

    req("HEADER") = subreq;

    // Add View request if it exists
    if (owner_) {
        Presentable* pres = owner_->FindBranch(pageId);
        if (pres) {
            MvRequest reqView = pres->GetView().ViewRequest();
            req("_CONTEXT") = reqView;
        }
        // Safety solution
        else {
            MvRequest reqView = owner_->Request()("PAGES");
            reqView = reqView("VIEW");
            req("_CONTEXT") = reqView;
        }
    }

    // qDebug() << "DROP REQUEST";
    // req.print();

    // Send drop request to the appropriate function
    emit sendDropRequest(&req);
}

void uPlotBase::processContentsRequest(MvRequest& iconReq)
{
    owner_->contentsRequest(iconReq);
}


#if 0
void uPlotBase::dataReceived()
{
	const QMetaObject *tt = metaObject ();
}
#endif


void uPlotBase::slotEnableAntialias(bool status)
{
    plotScene_->setEnableAntialias(status);
}

//===================================================
//
//  Zoom slots
//
//===================================================

void uPlotBase::slotZoomStackLevelChanged(QString layoutId, int actLevel)
{
    owner_->ZoomRequestByLevel(layoutId.toInt(), actLevel);
}

void uPlotBase::updateZoomActionState()
{
    int levelNum = zoomStackWidget_->levelNum();
    int actLevel = zoomStackWidget_->currentLevel();

    if (levelNum <= 1) {
        actionZoomUp_->setEnabled(false);
        actionZoomDown_->setEnabled(false);
    }
    else if (actLevel == 0) {
        actionZoomUp_->setEnabled(false);
        actionZoomDown_->setEnabled(true);
    }
    else if (actLevel == levelNum - 1) {
        actionZoomUp_->setEnabled(true);
        actionZoomDown_->setEnabled(false);
    }
    else {
        actionZoomUp_->setEnabled(true);
        actionZoomDown_->setEnabled(true);
    }
}

void uPlotBase::slotPerformZoom(const std::string& sid, const std::string& def)
{
    // Location stores the bottom-left,top-right corners.
    // Its constructor is requeres the following order:
    // top,left,bottom,right

    // Change active scene if needed before performing the zoom
    if (activeScene_) {
        MgQLayoutItem* projItem = activeScene_->firstProjectorItem();
        if (projItem && projItem->layout().id() != sid) {
            foreach (MgQSceneItem* item, plotScene_->sceneItems()) {
                projItem = item->firstProjectorItem();
                if (projItem && sid == projItem->layout().id()) {
                    activeScene_ = item;
                    break;
                }
            }
        }
    }

    // Location zoomCoord(tr_y,bl_x,bl_y,tr_x);
    int id = QString(sid.c_str()).toInt();
    owner_->ZoomRequestByDef(id, def);
}


void uPlotBase::slotStepCacheStateChanged()
{
    // Layer model will be reset since as new animation step is cached
    // new layers might have added to the scene
    // layerWidget_->model()->layersAreAboutToChange();

    // Notify the scene about the unique layers's status. This will set the
    // status of all the new layers to the correct values
    // plotScene_->updateLayers(layerWidget_->model()->layers());

    // Pass the new list of unique layers to the layer model andplacemark_
    // finish its resetting
    // layerWidget_->model()->setLayers(plotScene_->uniqueLayers());
}


void uPlotBase::slotLoadExportDialog()
{
    // we cannot start a new expor dialog until an export request
    // is being processed
    if (delayedExportDialogRequest_) {
        return;
    }

    // Get current working directory
    std::string sdir;
    if (owner_)
        sdir = ObjectInfo::ObjectFullPath(owner_->Request());

    // Define export method to be applied
    export_qt_ = plotView_->hasFeatures();
    ExportDialog exportDialog(currentStep(), stepNum(), sdir, export_qt_);
    if (exportDialog.exec() == QDialog::Accepted) {
        delayedExportDialogRequest_ = exportDialog.reqOut();
        QTimer::singleShot(0, this, SLOT(startExportDialogMsgBox()));
    }
}

void uPlotBase::startExportDialogMsgBox()
{
    // Pops up a messagebox, which will start the execution of the
    // export request
    if (delayedExportDialogRequest_) {
        // delayedExportDialogRequest_.print();
        ExportMessageBox msgBox(this);
        msgBox.exec();
        delayedExportDialogRequest_ = MvRequest();
    }
}

// TODO: Is it used at all?
void uPlotBase::slotLoadEcChartsDialog()
{
    // Is there anything plotted?
    if (!owner_) {
        MvLog().popup().warn() << "Display Window empty";
        return;
    }

    // Ask and wait for user options
    MvQEcChartsDialog dialog(currentStep(), stepNum());
    if (dialog.exec() != QDialog::Accepted)
        return;

    // Get information from the dialog window
    //   std::string ecCharts_layername = dialog.getLayerName();
    std::string outputFileName = dialog.getOutputFileName();

    // Build request
    MvRequest plotReq = owner_->ExportToEcCharts();

    // Save request
    plotReq.save(outputFileName.c_str());

    /*
   if ( owner_->ExportPlot(req1) == false )
   {
      COUT << "CREATE A ERROR WINDOW MESSAGE" << std::endl;
   }*/
}

// the Weather Room-specific slots need to be defined even if
// METVIEW_WEATHER_ROOM is not defined. This is because the Qt MOC file is
// shared between all the uPlot executables. For uPlot, METVIEW_WEATHER_ROOM is
// defined, but not for GeoTool. But they both link with the same MOC file
// which has to have the slots defined in order for the feature to work
// in uPlot.

void uPlotBase::slotLoadWeatherRoomDialog()
{
#ifdef METVIEW_WEATHER_ROOM
    // Is there anything plotted?
    if (!owner_) {
        MvLog().warn() << "Weather Room-> Nothing to be exported";
        return;
    }

    // Ask and wait for user options
    MvQWeatherRoomDialog dialog(currentStep(), stepNum());
    if (dialog.exec() != QDialog::Accepted)
        return;

    // Get information from the dialog window
    wroomCell_ = dialog.getCell();
    wroomWall_ = dialog.getWallId();

    // Build plot request and upload it to the weather room
    if (!this->plotWeatherRoom())
        return;

#endif
}


#ifdef METVIEW_WEATHER_ROOM
bool uPlotBase::plotWeatherRoom(bool checkSync)
{
    // Nothing to be plotted (ask to check the synchronisation flag
    // and the video wall is not synchronised with uPlot)
    if (checkSync && syncWeatherRoom() == false)
        return true;

    // Define output device. Create a temporary file.
    MvRequest devReq;
    this->outputDeviceWeatherRoom(devReq);

    // Ask my owner to recreate the plot and save it accordingly
    // Set the syncronous flag, e.g. make sure the file is created
    // before uploading it
    if (owner_->ExportPlot(&devReq, true) == false) {
        MvLog().warn() << "Weather Room-> Failed to upload plot";
        return false;
    }

    // Upload file to the weather room
    std::string fName = (const char*)devReq("OUTPUT_FULLNAME");
    this->uploadWeatherRoom(fName);

    return true;
}
#endif

#ifdef METVIEW_WEATHER_ROOM
void uPlotBase::outputDeviceWeatherRoom(MvRequest& devReq)
{
    // Define output device. Create a temporary file.
    wroomFormat_ = "pdf";
    devReq.setVerb("PDFOUTPUT");
    std::string fName = marstmp();
    devReq("OUTPUT_FULLNAME") = fName.c_str();
}
#endif

#ifdef METVIEW_WEATHER_ROOM
void uPlotBase::uploadWeatherRoom(const std::string& fName)
{
    // Get data unit information used to create the plot
    MvRequest req("DataInfo");
    owner_->GetDataUnitInfo(req);

    // Create json file
    // Write fixed info
    const char* jName = marstmp();
    ofstream jFile;
    jFile.open(jName);
    jFile << "{\n";
    jFile << "   \"page_format\" : \"a4\",\n";
    jFile << "   \"file_format\" : \"" << wroomFormat_ << "\",\n";
    //   jFile << "      \"rotate\"    : 90,\n";
    jFile << "   \"title\"       : \"title\",\n";
    jFile << "   \"file\"        : \"" << fName.c_str() << "\",\n";
    jFile << "   \"pages\"       : {\n";

    // Write data unit info BASE_TIME
    char cbuf[512];
    const char* expver;
    int i, step;
    double ddate, dtime;
    int count = req.countValues("DATE");
    jFile << "      \"base_time\" : [";
    for (i = 0; i < count; i++) {
        req.getValue(ddate, "DATE", i);
        req.getValue(dtime, "TIME", i);  // time format: HHMM
        ddate += dtime / 2400;           // fraction of 24hs
        MvDate date(ddate);
        date.Format("yyyy-mm-ddTHH:MM:SSZ", cbuf);
        jFile << "\"" << cbuf << "\"";
        if (i < (count - 1))
            jFile << ",";
    }
    jFile << "],\n";

    // Write data unit info STEP
    jFile << "      \"step\"      : [";
    for (i = 0; i < count; i++) {
        req.getValue(step, "STEP", i);
        jFile << step;
        if (i < (count - 1))
            jFile << ",";
    }
    jFile << "],\n";

    // Write data unit info EXPVER
    jFile << "      \"expver\"    : [";
    for (i = 0; i < count; i++) {
        req.getValue(expver, "EXPVER", i);
        jFile << "\"" << expver << "\"";
        if (i < (count - 1))
            jFile << ",";
    }
    jFile << "]\n";

    // Close file
    jFile << "     }\n}";
    jFile.close();

    // Upload file by calling a python script
    const char* upload = getenv("MV_WEATHER_ROOM_UPLOAD");
    sprintf(cbuf, "%s --index %s --wall %s --cell %d", upload, jName, wroomWall_.c_str(), wroomCell_);
    system(cbuf);

    // Delete the temporary files: ps and json
    sprintf(cbuf, "rm -f %s", fName.c_str());
    system(cbuf);

    sprintf(cbuf, "rm -f %s", jName);
    system(cbuf);
    //#endif
}
#endif


// the Weather Room-sepcific slots need to be defined even if
// METVIEW_WEATHER_ROOM is not defined. This is because the Qt MOC file is
// shared between all the uPlot executables. For uPlot, METVIEW_WEATHER_ROOM is
// defined, but not for GeoTool. But they both link with the same MOC file
// which has to have the slots defined in order for the feature to work
// in uPlot.

void uPlotBase::slotEnableWroomSync(bool flag)
{
#ifdef METVIEW_WEATHER_ROOM
    wroomSync_ = flag;
#endif
}

void uPlotBase::slotLoadPrintDialog()
{
    // Is there anything plotted?
    if (!owner_) {
        MvLog().popup().warn() << "uPlot-> Nothing to be printed";
        return;
    }

    // Create the PrintDialog user interface
#if 0  // Qt printing
	QPrinter printer(QPrinter::HighResolution);
	QPrintDialog dialog( &printer );
	if ( !dialog.exec() )
		return;

	// Get printer attributes
//	printer.setResolution(300);
//	printer.setOutputFormat(QPrinter::PostScriptFormat);Enabled
//qDebug() << printer.resolution();

	// Recreate the plot and sent it to the printer
	QPainter painter(&printer);
	painter.translate(0,painter.viewport().height()); //origin moved to lower left corner
	painter.scale(1.,-1.);				  //plot directions: W->E and S->N
	plotScene_->render(&painter);

#else  // lpr printing
    QPrintDialog printDialog;
    QPrinter* pr = printDialog.printer();

    // Read and apply previous gui settings
    // The default printer name is defined by UNIX and
    // it seems that it can not be changed by QPrinter
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, "uPlotPrintDialog");
    settings.beginGroup("main");
#if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)
    pr->setPageOrientation((QPageLayout::Orientation)settings.value("orientation").toInt());
#else
    pr->setOrientation((QPrinter::Orientation)settings.value("orientation").toInt());
#endif

    settings.endGroup();

    // Set print dialog default values
    printDialog.setOption(QAbstractPrintDialog::PrintToFile, false);
    printDialog.setOption(QAbstractPrintDialog::PrintCollateCopies, false);
    printDialog.setPrintRange(QAbstractPrintDialog::PageRange);
    printDialog.setMinMax(1, stepNum());
    printDialog.setFromTo(currentStep() + 1, currentStep() + 1);
    if (!printDialog.exec())
        return;

    // Get printer parameters
    std::string name = pr->printerName().toLocal8Bit().constData();

// TODO: use proper enum instead of int!
#if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)
    int orientation = pr->pageLayout().orientation();  // 0-portrait, 1-landscape
#else
    int orientation = pr->orientation();  // 0-portrait, 1-landscape
#endif
    int numCopies = pr->copyCount();

    // Create printer request
    MvRequest printerReq("PRINTER_MANAGER");
    printerReq("DESTINATION") = MVPRINTER;
    printerReq("PRINTER_NAME") = name.c_str();
    printerReq("ORIENTATION") = orientation;
    printerReq("NUMBER_COPIES") = numCopies;

    // Get print range parameters
    if (printDialog.printRange() == QAbstractPrintDialog::PageRange) {
        // Get range of values
        for (int i = printDialog.fromPage(); i <= printDialog.toPage(); i++)
            printerReq.addValue("_OUTPUT_FRAME_LIST", i);
    }

    // Ask my owner to recreate the plot and send it to the output device
    owner_->PrintFile(printerReq);

    // Save current gui settings
    settings.beginGroup("main");
    settings.setValue("orientation", orientation);
    settings.endGroup();
#endif
}

void uPlotBase::processExportDialogRequest()
{
    if (delayedExportDialogRequest_) {
        // Is there anything plotted?
        if (!owner_) {
            MvLog().popup().warn() << "uPlot-> Nothing to be exported";
            return;
        }

        // Process export calling Qt
        if (export_qt_) {
            processExportQt();
            delayedExportDialogRequest_ = MvRequest();
            return;
        }

        // Process export calling Magics
        // Set the environment to upload the result later
        MvRequest reqExp = setExportVariables(&delayedExportDialogRequest_);
        delayedExportDialogRequest_ = MvRequest();

        // Ask my owner to recreate the plot and save it accordingly
        if (!owner_->ExportPlot(&reqExp)) {
            MvLog().popup().warn() << "uPlot-> Failed to export plot";
            return;
        }

        // Upload result
        processExport();
    }
}

void uPlotBase::processExportQt()
{
    if (!plotView_->hasFeatures()) {
        MvLog().popup().err() << "Export: Internal Error: empty Display Window";
        return;
    }

    // Get output format
    std::string verb = delayedExportDialogRequest_.getVerb();
    std::string format;
    size_t cpos = verb.find("QT_OUTPUT");
    if (cpos != std::string::npos) {
        format = verb.substr(0, cpos);
        metview::toLower(format);
    }
    else {
        format = "png";
    }

    // Get output file name
    std::string fn = (const char*)delayedExportDialogRequest_("OUTPUT_NAME");
    fn += "." + format;

    QSizeF sourceSize = plotScene_->sceneRect().size();
    auto sourceRatio = sourceSize.height() / sourceSize.width();

    // Initialise output format object
    if (format == "pdf") {
        int targetWidth = (int)delayedExportDialogRequest_("OUTPUT_WIDTH");
        QSizeF imageSize(targetWidth, static_cast<int>(
                                          targetWidth * sourceRatio));

        QPdfWriter pdfWriter(fn.c_str());
        pdfWriter.setPageSize(QPageSize(imageSize, QPageSize::Point));

        QPainter painter(&pdfWriter);
        painter.setRenderHint(QPainter::Antialiasing, true);
        painter.translate(0, painter.viewport().height());
        painter.scale(1., -1.);  // plot directions: W->E and S->N

#if 0
        plotScene_->render(&painter);
#else
        QStyleOptionGraphicsItem option;
        imageSize *= 15;
        auto targetRect = QRectF(0, 0, imageSize.width(), imageSize.height());
        auto sourceRect = plotScene_->sceneRect();
        plotScene_->renderContents(&painter, &option, targetRect, sourceRect,
                                   true);
#endif
    }
    else if (format == "png") {
        int targetWidth = (int)delayedExportDialogRequest_("OUTPUT_WIDTH");
        QPixmap pix(targetWidth, static_cast<int>(targetWidth * sourceRatio));

        // background filling
        const char* colour = (const char*)delayedExportDialogRequest_(
            "OUTPUT_BACKGROUND_COLOUR");
        pix.fill(MvQPalette::magics(colour));

        QPainter painter(&pix);
        painter.setRenderHint(QPainter::Antialiasing, true);
        painter.translate(0, pix.height());
        painter.scale(1., -1.);

        QStyleOptionGraphicsItem option;
        auto targetRect = QRectF(0, 0, pix.width(), pix.height());
        auto sourceRect = plotScene_->sceneRect();
        plotScene_->renderContents(&painter, &option, targetRect, sourceRect,
                                   true);

        pix.save(fn.c_str(), nullptr, 50);  // quality: 0-100, -1 (default)
    }
}

MvRequest uPlotBase::setExportVariables(MvRequest* preq)
{
    if (!preq) {
        MvLog().popup().err() << "Export: Internal Error: empty Display Window";
        return {};
    }

    // This version only allows the ANIMATED_GIT_OUTPUT format
    MvRequest req = *preq;
    export_format_ = (const char*)req.getVerb();
    if (export_format_ != "ANIMATED_GIF_OUTPUT")
        return req;

    // Read input parameters from the dialog
    export_fname_ = (const char*)req("OUTPUT_NAME");

    // Create a temporary directory to store the animation files
    // This directory will be deleted at the end
    std::string name = mbasename(export_fname_.c_str());
    export_tmp_path_ = CreateTmpPath(name.c_str());

#if 0   // FAMI20200319 ProgressManager removed
    // Define directories/files to be watched
    if (pm_) {
        pm_->setVisible(false);
        delete pm_;
        pm_ = 0;
    }

    // Define output gif full filename
    // To 'watch' this file Qt requires that it already exists
    std::string fullname = export_fname_ + ".gif";
    fstream fs;
    fs.open(fullname.c_str(), std::ios::out);
    fs.close();

    int ntemp_files = 1;  // only one PS file; otherwise use stepNum()
    pm_ = new MvQProgressManager(ntemp_files, progressBar_);
    QFileSystemWatcher* watcher = pm_->getWatcher();
    QStringList ldirs;
    ldirs << fullname.c_str() << export_tmp_path_.c_str();
    watcher->addPaths(ldirs);

    // Define functions to be executed after:
    // a) output file is created
    // b) delte all temporary files
    connect(watcher, SIGNAL(fileChanged(const QString&)), this,
            SLOT(fileChangedExport(const QString&)));
    connect(pm_, SIGNAL(endTempFilesCreation()), this, SLOT(uploadExport()));

    // Show progress bar
    pm_->setVisible(true);
#endif  // FAMI20200319

    // Define temporary output device
    // A postscript file will be generated first and then converted
    // to the animated gif format
    std::string psfullfname = export_tmp_path_ + "/" + name;
    MvRequest devReq("PSOUTPUT");
    devReq("OUTPUT_NAME") = (const char*)(psfullfname.c_str());
    if ((const char*)req("OUTPUT_FRAME_LIST")) {
        int cnt = req.iterInit("OUTPUT_FRAME_LIST");
        double val;
        for (int i = 0; i < cnt; ++i) {
            req.iterGetNextValue(val);
            devReq.addValue("OUTPUT_FRAME_LIST", (int)val);
        }
    }
    //   QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
    //   int err;
    //   MvApplication::waitService ( "uPlotBatch", outReq, err );
    //   QApplication::restoreOverrideCursor();   // restore cursor

    return devReq;
}

void uPlotBase::processExport()
{
    // This version only allows the ANIMATED_GIT_OUTPUT format
    if (export_format_ != "ANIMATED_GIF_OUTPUT")
        return;

    // Read the selected output format request from the Metview/Defaults
    // directory. If not found, create a default request
    MvRequest reqOut = ObjectList::UserDefaultRequest(export_format_.c_str());
    reqOut = ObjectList::ExpandRequest(reqOut, EXPAND_DEFAULTS);

    // Define parameters: output/input filenames, image looping
    std::string ofname = export_fname_ + ".gif";
    std::string ifname = export_tmp_path_ + "/" + mbasename(export_fname_.c_str()) + ".ps";
    int nloops = 1;
    if (strcmp((const char*)reqOut("OUTPUT_CONTINUOUS_LOOPING"), "YES") == 0)
        nloops = 999;

    // Check if ps files is completed
    std::string data;
    FILE* stream = nullptr;
    const int max_buffer = 256;
    char buffer[max_buffer];
    std::string ctail("tail -1 ");
    ctail += ifname + " 2>&1";
    while (true) {
        // Get result of command tail
        stream = popen(ctail.c_str(), "r");
        if (stream) {
            while (!feof(stream))
                if (fgets(buffer, max_buffer, stream) != nullptr)
                    data.append(buffer);
            pclose(stream);
        }

        // Check if ps file is completed
        if (data.substr(0, 5) == "%%EOF")
            break;
        else {
            data.clear();
            sleep(1);
        }
    }

    // TODO: move this command into a shell script
    //  Postscript file has been generated
    //  Call external function convert to create the animated gif
    std::stringstream out, err;
    std::ostringstream cmd;
    cmd << "convert -delay " << (const char*)reqOut("OUTPUT_DELAY") << " -rotate " << (int)reqOut("OUTPUT_ROTATE") << " -resize " << (int)reqOut("OUTPUT_WIDTH") << " -loop " << nloops << " "
        << "'" << ifname << "'"
        << "  "
        << "'" << ofname << "'";

    metview::shellCommand(cmd.str(), out, err);
    if (!err.str().empty()) {
        QString message = tr("<b>Export Error: </b>") + tr(err.str().c_str());
        QMessageBox::warning(nullptr, tr("Metview"), message);
        DeletePath(export_tmp_path_.c_str());  // delete temporary directory
    }

    // Delete temporary directory
    DeletePath(export_tmp_path_.c_str());
}

#if 0  // FAMII20200319  ProgressBar removed
void uPlotBase::fileChangedExport(const QString& fname)
{
    // Send a popup window message
    std::string stext = "The Animated GIF file has been created at:\n" + fname.toStdString();
//    PlotMod::Instance().warningMessage(stext, true);
//    PlotMod::Instance().MetviewError("Display Window empty", "Warning");

    // Remove directories/file that have been watched
    QFileSystemWatcher* watcher = pm_->getWatcher();
    QStringList ldirs;
    ldirs << fname << export_tmp_path_.c_str();
    QStringList bb = watcher->removePaths(ldirs);

    // Delete temporary directory
    DeletePath(export_tmp_path_.c_str());

    // Delete Progress Manager
    if (pm_) {
        pm_->setVisible(false);
        delete pm_;
        pm_ = 0;
    }
}
#endif

void uPlotBase::slotGenerateMacro()
{
    if (!owner_) {
        MvLog().popup().warn() << "uPlot-> Display Window empty. Nothing to be generated";
        return;
    }

    // Ask my owner to recreate the plot and save it in a file
    owner_->GenerateMacro();
}

void uPlotBase::slotShowAboutBox()
{
    std::string magicsVersionString = getMagicsVersionString();
    QString magicsVersion(QString::fromUtf8(magicsVersionString.c_str()));

    QMap<MvQAbout::Version, QString> text;
    text[MvQAbout::MagicsVersion] = magicsVersion;

#if defined(METVIEW_ODB)
    const char* odc_ver = nullptr;
    odc_version(&odc_ver);
    text[MvQAbout::OdcVersion] = odc_ver;
#endif

    MvQAbout about("uPlot", "",
                   MvQAbout::GribApiVersion | MvQAbout::MetviewVersion | MvQAbout::MagicsVersion | MvQAbout::OdcVersion,
                   text);

    about.exec();
}

//===============================
//
// Resize
//
//===============================

void uPlotBase::slotResizeEvent(const QSize&)
{
    int index = sizeCombo_->currentIndex();
    int id = sizeCombo_->itemData(index, Qt::UserRole).toInt();
    if (id == FitToWindow || id == FitToWidth || id == FitToHeight) {
        changeSize(id);
    }
}

void uPlotBase::slotSizeChanged(int index)
{
    changeSize(sizeCombo_->itemData(index, Qt::UserRole).toInt());
}

void uPlotBase::slotSizeDown()
{
    int scalingId = 0;
    int id = sizeCombo_->itemData(sizeCombo_->currentIndex(), Qt::UserRole).toInt();

    if (id == FitToWindow || id == FitToWidth || id == FitToHeight) {
        float scaling = currentSizeRatio();
        scalingId = static_cast<int>(scaling * 100.);
    }
    else {
        scalingId = id;
    }

    scalingId -= sizeValueStep_;

    if (scalingId >= sizeMinValue_ && scalingId <= sizeMaxValue_) {
        setCustomSize(scalingId);
    }
}

void uPlotBase::slotSizeUp()
{
    int scalingId = 0;
    int id = sizeCombo_->itemData(sizeCombo_->currentIndex(), Qt::UserRole).toInt();

    if (id == FitToWindow || id == FitToWidth || id == FitToHeight) {
        float scaling = currentSizeRatio();
        scalingId = static_cast<int>(scaling * 100.);
    }
    else {
        scalingId = id;
    }

    scalingId += sizeValueStep_;

    if (scalingId >= sizeMinValue_ && scalingId <= sizeMaxValue_) {
        setCustomSize(scalingId);
    }
}

void uPlotBase::setCustomSize(int sizeId)
{
    // If custom size item is not available in the combobox we add one to it
    if (customSizeItemIndex_ >= sizeCombo_->count()) {
        sizeCombo_->addItem(QString::number(sizeId) + "%", sizeId);
    }
    else if (customSizeItemIndex_ == sizeCombo_->count() - 1) {
        sizeCombo_->setItemData(customSizeItemIndex_, sizeId);
        sizeCombo_->setItemText(customSizeItemIndex_, QString::number(sizeId) + "%");
    }

    if (sizeCombo_->currentIndex() != customSizeItemIndex_) {
        sizeCombo_->setCurrentIndex(customSizeItemIndex_);
    }
    else {
        changeSize(sizeId);
    }
}

float uPlotBase::currentSizeRatio()
{
    QRectF currentRect = plotScene_->sceneRect();
    if (currentRect.isEmpty()) {
        return 1.;
    }

    QRectF oriRect = plotScene_->oriSceneRect();
    if (oriRect.isEmpty()) {
        return 1.;
    }

    return currentRect.width() / oriRect.width();
}

void uPlotBase::changeSize(int sizeId, bool renderIt)
{
    float scaling;

    if (sizeId == FitToWindow) {
        QRectF sceneRect = plotScene_->sceneRect();
        if (sceneRect.isEmpty()) {
            return;
        }

        QSize size = plotView_->maximumViewportSize();
        float wRatio = size.width() / sceneRect.width();
        float hRatio = size.height() / sceneRect.height();
        scaling = (wRatio < hRatio) ? wRatio : hRatio;

        if (scaling < 0.95 || scaling > 1.05) {
            plotScene_->setPlotScale(scaling, MgQPlotScene::RelativeToCurrentSize);
        }
    }
    else if (sizeId == FitToWidth) {
        QRectF sceneRect = plotScene_->sceneRect();
        if (sceneRect.isEmpty()) {
            return;
        }

        QSize size = plotView_->maximumViewportSize();
        float wRatio = size.width() / sceneRect.width();
        scaling = wRatio;

        if (scaling < 0.95 || scaling > 1.05) {
            plotScene_->setPlotScale(scaling, MgQPlotScene::RelativeToCurrentSize);
        }
    }
    else if (sizeId == FitToHeight) {
        QRectF sceneRect = plotScene_->sceneRect();
        if (sceneRect.isEmpty()) {
            return;
        }

        QSize size = plotView_->maximumViewportSize();
        float hRatio = size.height() / sceneRect.height();
        scaling = hRatio;

        if (scaling < 0.95 || scaling > 1.05) {
            plotScene_->setPlotScale(scaling, MgQPlotScene::RelativeToCurrentSize);
        }
    }
    else if (sizeId >= 10 && sizeId <= 200) {
        scaling = static_cast<float>(sizeId) * 0.01;

        // float w=oriPlotWidthInCm_*scaling;
        // float h=oriPlotHeightInCm_*scaling;

        plotScene_->setPlotScale(scaling, MgQPlotScene::RelativeToOriSize);

        if (renderIt) {
            plotScene_->sceneItemChanged();
        }
    }

    if (renderIt) {
        plotScene_->sceneItemChanged();
    }

    updateResizeActionState();

    emit plotScaleChanged();

    // GraphicsEngine& ge = owner_->GetGraphicsEngine();
    // owner_->ResizeRequest(w,h);
}

void uPlotBase::updateResizeActionState()
{
    int index = sizeCombo_->currentIndex();
    int id = sizeCombo_->itemData(index, Qt::UserRole).toInt();
    int sizeId = 0;

    if (index < 0)
        return;

    if (id == FitToWindow || id == FitToWidth || id == FitToHeight) {
        sizeId = static_cast<int>(plotScene_->plotScale() * 100);
    }
    else {
        sizeId = id;
    }

    if (sizeId + sizeValueStep_ <= sizeMaxValue_) {
        actionSizeUp_->setEnabled(true);
    }
    else {
        actionSizeUp_->setEnabled(false);
    }

    if (sizeId - sizeValueStep_ >= sizeMinValue_) {
        actionSizeDown_->setEnabled(true);
    }
    else {
        actionSizeDown_->setEnabled(false);
    }
}


void uPlotBase::slotBuildContextMenu(const QPoint& globalPos, const QPointF& scenePos)
{
    QList<QAction*> actions;
    MgQSceneItem* item = nullptr;

    if (actionRedo_ && actionUndo_) {
        actions << actionRedo_ << actionUndo_;
    }

    // If there is only one scene
    if (plotScene_->sceneItems().count() <= 1) {
        actions << actionZoomUp_ << actionZoomDown_;
        // More than one scene: check which scene is clicked
    }
    else {
        item = plotScene_->findSceneItem(scenePos);
        if (item) {
            // Context menu triggered from the active scene
            if (item == activeScene_) {
                actions << actionZoomUp_ << actionZoomDown_;
                // Context menu triggered from a non-active scene
            }
            else {
                actions << actionContextSelectScene_ << actionContextZoomUp_ << actionContextZoomDown_;

                // Set the status of the context zoom actions
                MvQZoomStackData* zoomData = zoomStackWidget_->pageData(item);
                if (zoomData) {
                    int levelNum = zoomData->levelNum();
                    int actLevel = zoomData->selectedLevel();

                    if (levelNum <= 1) {
                        actionContextZoomUp_->setEnabled(false);
                        actionContextZoomDown_->setEnabled(false);
                    }
                    else if (actLevel == 0) {
                        actionContextZoomUp_->setEnabled(false);
                        actionContextZoomDown_->setEnabled(true);
                    }
                    else if (actLevel == levelNum - 1) {
                        actionContextZoomUp_->setEnabled(true);
                        actionContextZoomDown_->setEnabled(false);
                    }
                    else {
                        actionContextZoomUp_->setEnabled(true);
                        actionContextZoomDown_->setEnabled(true);
                    }
                }
                else {
                    actionContextZoomUp_->setEnabled(false);
                    actionContextZoomDown_->setEnabled(false);
                }
            }
        }
    }

    if (QAction* ac = plotView_->handleContextMenu(actions, globalPos, scenePos)) {
        if (ac == actionContextSelectScene_) {
            slotSetActiveScene(item);
        }
        else if (ac == actionContextZoomUp_) {
            slotSetActiveScene(item);
            // Now this scene is active. We can use the original zoomUp action!!
            actionZoomUp_->trigger();
        }
        else if (ac == actionContextZoomDown_) {
            slotSetActiveScene(item);
            // Now this scene is active. We can use the original zoomDown action!!
            actionZoomDown_->trigger();
        }
        else if (ac == actionRedo_) {
            if (undoStack_) {
                undoStack_->redo();
            }
        }
        else if (ac == actionUndo_) {
            if (undoStack_) {
                undoStack_->undo();
            }
        }
    }
}

int uPlotBase::screenResolutionInDpi()
{
    int defaultRes = -1;

    char* mvres = getenv("METVIEW_SCREEN_RESOLUTION");
    if (mvres) {
        int res = QString(mvres).toInt();
        if (res > 50 && res < 400) {
            return res;
        }
    }

    return defaultRes;
}

#if 0
// ----------------------------------------------------------------------------
// uPlotBase::messageToLogWindow
// sends the given message to the Metview message log window
// ----------------------------------------------------------------------------

void uPlotBase::messageToLogWindow (const std::string& msg, int severity)
{
	MvRequest reqst("USER_MESSAGE");
	reqst("INFO") = msg.c_str(); 
	send_message(MvApplication::instance().getService(),(request*)reqst);


        // also send to the Mars/Metview module log
	char msg_c[1024];
	strncpy(msg_c, msg.c_str(), 1023);
	marslog(severity, msg_c);
}
#endif

void uPlotBase::setPlotWidgetSize(float hor, float ver)
{
    setPlot(hor, ver);
}

std::string uPlotBase::currentWorkDir() const
{
    if (owner_) {
        return ObjectInfo::ObjectFullPath(owner_->Request());
    }
    return {};
}
