<commit_before>#include "AppLauncherPlugin.h"
#include "../../LSession.h"
#include "OutlineToolButton.h"

#define OUTMARGIN 10 //special margin for fonts due to the outlining effect from the OutlineToolbutton

AppLauncherPlugin::AppLauncherPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID){
  QVBoxLayout *lay = new QVBoxLayout();
  this->setLayout(lay);
    lay->setContentsMargins(0,0,0,0);
  button = new OutlineToolButton(this);
    button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
    button->setAutoRaise(true);
    button->setText("...\n..."); //Need to set something here so that initial sizing works properly
    button->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
  lay->addWidget(button, 0, Qt::AlignCenter);
	connect(button, SIGNAL(DoubleClicked()), this, SLOT(buttonClicked()) );
  button->setContextMenuPolicy(Qt::NoContextMenu);
  watcher = new QFileSystemWatcher(this);
	connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT( loadButton()) );

  connect(this, SIGNAL(PluginActivated()), this, SLOT(buttonClicked()) ); //in case they use the context menu to launch it.
  loadButton();
  //QTimer::singleShot(0,this, SLOT(loadButton()) );
}

void AppLauncherPlugin::Cleanup(){
  //This is run only when the plugin was forcibly closed/removed

}

void AppLauncherPlugin::loadButton(){
  QString def = this->ID().section("::",1,50).section("---",0,0).simplified();
  QString path = this->readSetting("applicationpath",def).toString(); //use the default if necessary
  //qDebug() << "Default Application Launcher:" << def << path;
  bool ok = QFile::exists(path);
  if(!ok){ emit RemovePlugin(this->ID()); return;}
  int icosize = this->height()-4 - 2.2*button->fontMetrics().height();
  button->setFixedSize( this->width()-4, this->height()-4);
  button->setIconSize( QSize(icosize,icosize) );
  button->setToolTip("");
  QString txt;
  if(path.endsWith(".desktop") && ok){
    XDGDesktop file(path);
    ok = !file.name.isEmpty();
    if(!ok){
      button->setWhatsThis("");
      button->setIcon( QIcon(LXDG::findIcon("quickopen-file","").pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) );
      txt = tr("Click to Set");
      if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); }
    }else{
      button->setWhatsThis(file.filePath);
      button->setIcon( QIcon(LXDG::findIcon(file.icon,"system-run").pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) );
      if(!file.comment.isEmpty()){button->setWhatsThis(file.comment); }
      txt = file.name;
      if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); }
      watcher->addPath(file.filePath); //make sure to update this shortcut if the file changes
    }
  }else if(ok){
    QFileInfo info(path);
    button->setWhatsThis(info.absoluteFilePath());
    if(info.isDir()){
	if(path.startsWith("/media/")){
          //Could add device ID parsing here to determine what "type" of device it is - will be OS-specific though
	  button->setIcon( LXDG::findIcon("drive-removable-media","") );
	}
        else{ button->setIcon( LXDG::findIcon("folder","") ); }
    }else if(LUtils::imageExtensions().contains(info.suffix().toLower()) ){
      QPixmap pix;
      if(pix.load(path)){ button->setIcon( QIcon(pix.scaled(256,256)) ); } //max size for thumbnails in memory
      else{ button->setIcon( LXDG::findIcon("dialog-cancel","") ); }
    }else{
      button->setIcon( QIcon(LXDG::findMimeIcon(path).pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) );
    }
    txt = info.fileName();
    if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); }
    watcher->addPath(path); //make sure to update this shortcut if the file changes
  }else{
    //InValid File
    button->setWhatsThis("");
    button->setIcon( QIcon(LXDG::findIcon("quickopen","dialog-cancel").pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) );
    button->setText( tr("Click to Set") );
    if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); }
  }
  //If the file is a symlink, put the overlay on the icon
  if(QFileInfo(path).isSymLink()){
    QImage img = button->icon().pixmap(QSize(icosize,icosize)).toImage();
    int oSize = icosize/3; //overlay size
    QPixmap overlay = LXDG::findIcon("emblem-symbolic-link").pixmap(oSize,oSize).scaled(oSize,oSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
    QPainter painter(&img);
      painter.drawPixmap(icosize-oSize,icosize-oSize,overlay); //put it in the bottom-right corner
    button->setIcon( QIcon(QPixmap::fromImage(img)) );
  }
  //Now adjust the visible text as necessary based on font/grid sizing
  if(button->toolTip().isEmpty()){ button->setToolTip(txt); }
  //Double check that the visual icon size matches the requested size - otherwise upscale the icon
    if(button->fontMetrics().width(txt) > (button->width()-OUTMARGIN) ){
      //Text too long, try to show it on two lines
      //txt = button->fontMetrics().elidedText(txt, Qt::ElideRight, 2*(button->width()-OUTMARGIN), Qt::TextWordWrap);
      txt =txt.section(" ",0,2).replace(" ","\n"); //First take care of any natural breaks
      //Go through and combine any lines
       if(txt.contains("\n")){
        //need to check each line
	QStringList txtL = txt.split("\n");
	for(int i=0; i<txtL.length(); i++){
	  if(( i+1<txtL.length()) && (button->fontMetrics().width(txtL[i]) < button->width()/2) ){
	    txtL[i] = txtL[i]+" "+txtL[i+1];
	    txtL.removeAt(i+1);
	  }
	}
	txt = txtL.join("\n").section("\n",0,2);
      }

      if(txt.contains("\n")){
        //need to check each line
	QStringList txtL = txt.split("\n");
	for(int i=0; i<txtL.length(); i++){
	  if(i>1){ txtL.removeAt(i); i--; } //Only take the first two lines
	  else{ txtL[i] = button->fontMetrics().elidedText(txtL[i], Qt::ElideRight, (button->width()-OUTMARGIN) );  }
	}
	txt = txtL.join("\n");
      }else{
        txt = this->fontMetrics().elidedText(txt,Qt::ElideRight, 2*(button->width()-OUTMARGIN));
        //Now split the line in half for the two lines
        txt.insert( ((txt.count())/2), "\n");
      }
    }
    if(!txt.contains("\n")){ txt.append("\n "); } //always use two lines
    //qDebug() << " - Setting Button Text:" << txt;
    button->setText(txt);

  QTimer::singleShot(100, this, SLOT(update()) ); //Make sure to re-draw the image in a moment
}

void AppLauncherPlugin::buttonClicked(){
  QString path = button->whatsThis();
  if(path.isEmpty() || !QFile::exists(path) ){
    //prompt for the user to select an application
    QList<XDGDesktop*> apps = LSession::handle()->applicationMenu()->currentAppHash()->value("All"); //LXDG::sortDesktopNames( LXDG::systemDesktopFiles() );
    QStringList names;
    for(int i=0; i<apps.length(); i++){ names << apps[i]->name; }
    bool ok = false;
    QString app = QInputDialog::getItem(this, tr("Select Application"), tr("Name:"), names, 0, false, &ok);
    if(!ok || names.indexOf(app)<0){ return; } //cancelled
    this->saveSetting("applicationpath", apps[ names.indexOf(app) ]->filePath);
    QTimer::singleShot(0,this, SLOT(loadButton()));
  }else{
    LSession::LaunchApplication("lumina-open \""+path+"\"");
  }

}
<commit_msg>Oops - Fix a typo in the desktop applauncher plugin: The comment field should have been put into the tooltip - not replace the "whatsThis" value. This will fix the desktop autmounter entries getting launched appropriately again.<commit_after>#include "AppLauncherPlugin.h"
#include "../../LSession.h"
#include "OutlineToolButton.h"

#define OUTMARGIN 10 //special margin for fonts due to the outlining effect from the OutlineToolbutton

AppLauncherPlugin::AppLauncherPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID){
  QVBoxLayout *lay = new QVBoxLayout();
  this->setLayout(lay);
    lay->setContentsMargins(0,0,0,0);
  button = new OutlineToolButton(this);
    button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
    button->setAutoRaise(true);
    button->setText("...\n..."); //Need to set something here so that initial sizing works properly
    button->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
  lay->addWidget(button, 0, Qt::AlignCenter);
	connect(button, SIGNAL(DoubleClicked()), this, SLOT(buttonClicked()) );
  button->setContextMenuPolicy(Qt::NoContextMenu);
  watcher = new QFileSystemWatcher(this);
	connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT( loadButton()) );

  connect(this, SIGNAL(PluginActivated()), this, SLOT(buttonClicked()) ); //in case they use the context menu to launch it.
  loadButton();
  //QTimer::singleShot(0,this, SLOT(loadButton()) );
}

void AppLauncherPlugin::Cleanup(){
  //This is run only when the plugin was forcibly closed/removed

}

void AppLauncherPlugin::loadButton(){
  QString def = this->ID().section("::",1,50).section("---",0,0).simplified();
  QString path = this->readSetting("applicationpath",def).toString(); //use the default if necessary
  //qDebug() << "Default Application Launcher:" << def << path;
  bool ok = QFile::exists(path);
  if(!ok){ emit RemovePlugin(this->ID()); return;}
  int icosize = this->height()-4 - 2.2*button->fontMetrics().height();
  button->setFixedSize( this->width()-4, this->height()-4);
  button->setIconSize( QSize(icosize,icosize) );
  button->setToolTip("");
  QString txt;
  if(path.endsWith(".desktop") && ok){
    XDGDesktop file(path);
    ok = !file.name.isEmpty();
    if(!ok){
      button->setWhatsThis("");
      button->setIcon( QIcon(LXDG::findIcon("quickopen-file","").pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) );
      txt = tr("Click to Set");
      if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); }
    }else{
      button->setWhatsThis(file.filePath);
      button->setIcon( QIcon(LXDG::findIcon(file.icon,"system-run").pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) );
      if(!file.comment.isEmpty()){button->setToolTip(file.comment); }
      txt = file.name;
      if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); }
      watcher->addPath(file.filePath); //make sure to update this shortcut if the file changes
    }
  }else if(ok){
    QFileInfo info(path);
    button->setWhatsThis(info.absoluteFilePath());
    if(info.isDir()){
	if(path.startsWith("/media/")){
          //Could add device ID parsing here to determine what "type" of device it is - will be OS-specific though
	  button->setIcon( LXDG::findIcon("drive-removable-media","") );
	}
        else{ button->setIcon( LXDG::findIcon("folder","") ); }
    }else if(LUtils::imageExtensions().contains(info.suffix().toLower()) ){
      QPixmap pix;
      if(pix.load(path)){ button->setIcon( QIcon(pix.scaled(256,256)) ); } //max size for thumbnails in memory
      else{ button->setIcon( LXDG::findIcon("dialog-cancel","") ); }
    }else{
      button->setIcon( QIcon(LXDG::findMimeIcon(path).pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) );
    }
    txt = info.fileName();
    if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); }
    watcher->addPath(path); //make sure to update this shortcut if the file changes
  }else{
    //InValid File
    button->setWhatsThis("");
    button->setIcon( QIcon(LXDG::findIcon("quickopen","dialog-cancel").pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) );
    button->setText( tr("Click to Set") );
    if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); }
  }
  //If the file is a symlink, put the overlay on the icon
  if(QFileInfo(path).isSymLink()){
    QImage img = button->icon().pixmap(QSize(icosize,icosize)).toImage();
    int oSize = icosize/3; //overlay size
    QPixmap overlay = LXDG::findIcon("emblem-symbolic-link").pixmap(oSize,oSize).scaled(oSize,oSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
    QPainter painter(&img);
      painter.drawPixmap(icosize-oSize,icosize-oSize,overlay); //put it in the bottom-right corner
    button->setIcon( QIcon(QPixmap::fromImage(img)) );
  }
  //Now adjust the visible text as necessary based on font/grid sizing
  if(button->toolTip().isEmpty()){ button->setToolTip(txt); }
  //Double check that the visual icon size matches the requested size - otherwise upscale the icon
    if(button->fontMetrics().width(txt) > (button->width()-OUTMARGIN) ){
      //Text too long, try to show it on two lines
      //txt = button->fontMetrics().elidedText(txt, Qt::ElideRight, 2*(button->width()-OUTMARGIN), Qt::TextWordWrap);
      txt =txt.section(" ",0,2).replace(" ","\n"); //First take care of any natural breaks
      //Go through and combine any lines
       if(txt.contains("\n")){
        //need to check each line
	QStringList txtL = txt.split("\n");
	for(int i=0; i<txtL.length(); i++){
	  if(( i+1<txtL.length()) && (button->fontMetrics().width(txtL[i]) < button->width()/2) ){
	    txtL[i] = txtL[i]+" "+txtL[i+1];
	    txtL.removeAt(i+1);
	  }
	}
	txt = txtL.join("\n").section("\n",0,2);
      }

      if(txt.contains("\n")){
        //need to check each line
	QStringList txtL = txt.split("\n");
	for(int i=0; i<txtL.length(); i++){
	  if(i>1){ txtL.removeAt(i); i--; } //Only take the first two lines
	  else{ txtL[i] = button->fontMetrics().elidedText(txtL[i], Qt::ElideRight, (button->width()-OUTMARGIN) );  }
	}
	txt = txtL.join("\n");
      }else{
        txt = this->fontMetrics().elidedText(txt,Qt::ElideRight, 2*(button->width()-OUTMARGIN));
        //Now split the line in half for the two lines
        txt.insert( ((txt.count())/2), "\n");
      }
    }
    if(!txt.contains("\n")){ txt.append("\n "); } //always use two lines
    //qDebug() << " - Setting Button Text:" << txt;
    button->setText(txt);

  QTimer::singleShot(100, this, SLOT(update()) ); //Make sure to re-draw the image in a moment
}

void AppLauncherPlugin::buttonClicked(){
  QString path = button->whatsThis();
  if(path.isEmpty() || !QFile::exists(path) ){
    //prompt for the user to select an application
    QList<XDGDesktop*> apps = LSession::handle()->applicationMenu()->currentAppHash()->value("All"); //LXDG::sortDesktopNames( LXDG::systemDesktopFiles() );
    QStringList names;
    for(int i=0; i<apps.length(); i++){ names << apps[i]->name; }
    bool ok = false;
    QString app = QInputDialog::getItem(this, tr("Select Application"), tr("Name:"), names, 0, false, &ok);
    if(!ok || names.indexOf(app)<0){ return; } //cancelled
    this->saveSetting("applicationpath", apps[ names.indexOf(app) ]->filePath);
    QTimer::singleShot(0,this, SLOT(loadButton()));
  }else{
    LSession::LaunchApplication("lumina-open \""+path+"\"");
  }

}
<|endoftext|>


<commit_before>#include "imagewidget.h"
#include "renderers.h"
#include <QResizeEvent>
#include <QPainter>
#include <QElapsedTimer>

ImageWidget::ImageWidget(QWidget *parent) : QWidget(parent), mode(AnglaphFull), zoom(0.0), swapLR(false), panButtons(Qt::LeftButton | Qt::MidButton),
    mouseTimer(this), hBar(Qt::Horizontal, this), vBar(Qt::Vertical, this), continuousPan(true), scrollbarsVisible(true), smoothTransform(false),
    maskInterlacedHorizontal(":/masks/interlacedH.pbm"), maskInterlacedVertical(":/masks/interlacedV.pbm"), maskCheckerboard(":/masks/checkerboard.pbm") {

    setMouseTracking(true);
    recalculatescroolmax();

    connect(&hBar, SIGNAL(valueChanged(int)), this, SLOT(update()));
    connect(&vBar, SIGNAL(valueChanged(int)), this, SLOT(update()));

    mouseTimer.setSingleShot(true);
    connect(&mouseTimer, SIGNAL(timeout()), this, SLOT(hideCursor()));
}

void ImageWidget::resizeEvent(QResizeEvent *e){
    hBar.resize(e->size().width() - vBar.sizeHint().width(), hBar.sizeHint().height());
    hBar.move(0, e->size().height() - hBar.sizeHint().height());

    vBar.resize(vBar.sizeHint().width(), e->size().height() - hBar.sizeHint().height());
    vBar.move(e->size().width() - vBar.sizeHint().width(), 0);

    recalculatescroolmax();
}

void ImageWidget::paintEvent(QPaintEvent *e){
    QPainter painter(this);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, smoothTransform);
    painter.setBrush(QBrush(Qt::black));
    painter.drawRect(e->rect());

    if(imgL.isNull() && imgR.isNull()){
        painter.setPen(Qt::gray);
        QFont font;
        font.setPointSize(32);
        painter.setFont(font);
        painter.drawText(rect(), Qt::AlignCenter | Qt::TextWordWrap, tr("No Image Loaded"));
    }else if(parentWidget() && !parentWidget()->isFullScreen() && (mode == InterlacedHorizontal || mode == InterlacedVertical || mode == Checkerboard)){
        painter.setPen(Qt::gray);
        QFont font;
        font.setPointSize(24);
        painter.setFont(font);
        painter.drawText(rect(), Qt::AlignCenter | Qt::TextWordWrap, tr("%1 Display Must Be Fullscreen").arg(mode == Checkerboard ? "Checkerboard" : "Interlaced"));

        /* keep scrollbars hidden. */
        zoom = 0.0f;
        recalculatescroolmax();
    }else{
        QElapsedTimer time;
        time.start();

        const QPixmap &L = swapLR ? pixmapR : pixmapL;
        const QPixmap &R = swapLR ? pixmapL : pixmapR;

        switch(mode){
        case AnglaphFull:
        case AnglaphHalf:
        case AnglaphGreyscale:
            drawSingle(anglaph, -hBar.value(), -vBar.value(), painter, zoom);
            break;
        case SidebySide:
            drawSideBySide(L, R, -hBar.value(), -vBar.value(), painter, zoom);
            break;
        case SidebySideMLeft:
            drawSideBySide(L, R, -hBar.value(), -vBar.value(), painter, zoom, true);
            break;
        case SidebySideMRight:
            drawSideBySide(L, R, -hBar.value(), -vBar.value(), painter, zoom, false, true);
            break;
        case SidebySideMBoth:
            drawSideBySide(L, R, -hBar.value(), -vBar.value(), painter, zoom, true, true);
            break;
        case TopBottom:
            drawTopBottom(L, R, -hBar.value(), -vBar.value(), painter, zoom);
            break;
        case TopBottomMTop:
            drawTopBottom(L, R, -hBar.value(), -vBar.value(), painter, zoom, true);
            break;
        case TopBottomMBottom:
            drawTopBottom(L, R, -hBar.value(), -vBar.value(), painter, zoom, false, true);
            break;
        case TopBottomMBoth:
            drawTopBottom(L, R, -hBar.value(), -vBar.value(), painter, zoom, true, true);
            break;
        case InterlacedHorizontal:
            drawInterlaced(L, R, -hBar.value(), -vBar.value(), painter, maskInterlacedHorizontal, zoom);
            break;
        case InterlacedVertical:
            drawInterlaced(L, R, -hBar.value(), -vBar.value(), painter, maskInterlacedVertical, zoom);
            break;
        case Checkerboard:
            drawInterlaced(L, R, -hBar.value(), -vBar.value(), painter, maskCheckerboard, zoom);
            break;
        case MonoLeft:
            drawSingle(L, -hBar.value(), -vBar.value(), painter, zoom);
            break;
        case MonoRight:
            drawSingle(R, -hBar.value(), -vBar.value(), painter, zoom);
            break;
        }

        qDebug("Draw time: %fms", time.nsecsElapsed() * 1.0e-6);
    }
}

bool ImageWidget::loadStereoImage(const QString &filename){
    QImage img(filename);

    imgR = img.copy(0,               0, img.width() / 2, img.height());
    imgL = img.copy(img.width() / 2, 0, img.width() / 2, img.height());
    updateImages();

    return !img.isNull();
}

void ImageWidget::updateImages(){
    pixmapL = QPixmap::fromImage(imgL);
    pixmapR = QPixmap::fromImage(imgR);
    recalculatescroolmax();
    updateAnglaph();
    repaint();
}

void ImageWidget::mouseMoveEvent(QMouseEvent *e){
    if((e->buttons() & panButtons) && (vBar.maximum() > 0 || hBar.maximum() > 0)){
        vBar.setValue(vBar.value() + lastmousepos.y() - e->y());
        hBar.setValue(hBar.value() + lastmousepos.x() - e->x());

        if(continuousPan){
            QPoint warpto = e->pos();

            if(e->x() <= 0){
                warpto.setX(width() - 3);
            }else if(e->x() >= width() - 1){
                warpto.setX(2);
            }
            if(e->y() <= 0){
                warpto.setY(height() - 3);
            }else if(e->y() >= height() - 1){
                warpto.setY(2);
            }

            if(warpto != e->pos()){
                QCursor::setPos(mapToGlobal(warpto));
            }

            lastmousepos = warpto;
        }else{
            lastmousepos = e->pos();
        }

        setCursor(Qt::SizeAllCursor);
    }else{
        setCursor(Qt::ArrowCursor);

        lastmousepos = e->pos();
    }
    mouseTimer.start(4000);
}

void ImageWidget::mouseReleaseEvent(QMouseEvent *e){
    if(panButtons.testFlag(e->button())){
        setCursor(Qt::ArrowCursor);
    }
}

void ImageWidget::wheelEvent(QWheelEvent *e){
    addZoom(e->delta() / 1000.0);
}

void ImageWidget::recalculatescroolmax(){
    bool isSidebySide = (mode == SidebySide || mode == SidebySideMLeft || mode == SidebySideMRight || mode == SidebySideMBoth);
    bool isTopBottom  = (mode == TopBottom  || mode == TopBottomMTop   || mode == TopBottomMBottom || mode == TopBottomMBoth);

    int hmax = qMax(int((imgL.width() << isSidebySide) * zoom - width()) >> (isSidebySide + 1), 0);
    hBar.setRange(-hmax, hmax);

    int vmax = qMax(int((imgL.height() << isTopBottom) * zoom - height()) >> (isTopBottom + 1), 0);
    vBar.setRange(-vmax, vmax);

    hBar.setVisible(scrollbarsVisible && hmax != 0);
    vBar.setVisible(scrollbarsVisible && vmax != 0);
}

void ImageWidget::setZoom(qreal val){
    zoom = val;
    recalculatescroolmax();
    repaint();
}

void ImageWidget::zoomIn(){
    addZoom( 0.1);
}

void ImageWidget::zoomOut(){
    addZoom(-0.1);
}

void ImageWidget::addZoom(qreal amount){
    if(zoom <= 0.0){
        bool isSidebySide = (mode == SidebySide || mode == SidebySideMLeft || mode == SidebySideMRight || mode == SidebySideMBoth);
        bool isTopBottom  = (mode == TopBottom  || mode == TopBottomMTop   || mode == TopBottomMBottom || mode == TopBottomMBoth);

        zoom = qMin(qreal(width()) / qreal(imgL.width() << isSidebySide), qreal(height()) / qreal(imgL.height() << isTopBottom));
    }
    qreal zoomorig = zoom;
    zoom += amount * zoom;
    zoom = qBound(0.2, zoom, 4.0);
    recalculatescroolmax();
    vBar.setValue(vBar.value() * zoom / zoomorig);
    hBar.setValue(hBar.value() * zoom / zoomorig);
    repaint();
}

void ImageWidget::enableSwapLR(bool enable){
    swapLR = enable;
    updateAnglaph();
    repaint();
}

void ImageWidget::showScrollbars(bool show){
    scrollbarsVisible = show;
    recalculatescroolmax();
}

void ImageWidget::enableContinuousPan(bool enable){
    continuousPan = enable;
}

void ImageWidget::enableSmoothTransform(bool enable){
    smoothTransform = enable;
    repaint();
}

void ImageWidget::hideCursor(){
    setCursor(Qt::BlankCursor);
}

void ImageWidget::setRenderMode(DrawMode m){
    mode = m;
    recalculatescroolmax();
    updateAnglaph();
    repaint();
}

void ImageWidget::setPanButtons(Qt::MouseButtons buttons){
    panButtons = buttons;
}

void ImageWidget::updateAnglaph(){
    const QImage &L = swapLR ? imgR : imgL;
    const QImage &R = swapLR ? imgL : imgR;
    switch(mode){
    case AnglaphFull:
        anglaph = QPixmap::fromImage(drawAnglaph(L, R));
        break;
    case AnglaphHalf:
        anglaph = QPixmap::fromImage(drawAnglaphHalf(L, R));
        break;
    case AnglaphGreyscale:
        anglaph = QPixmap::fromImage(drawAnglaphGrey(L, R));
        break;
    default: break;
    }
}

QMap<QString, ImageWidget::DrawMode> initDrawModeList(){
    QMap<QString, ImageWidget::DrawMode> list;
    list.insert("Anglaph, Full Color",          ImageWidget::AnglaphFull);
    list.insert("Anglaph, Half Color",          ImageWidget::AnglaphHalf);
    list.insert("Anglaph, Greyscale",           ImageWidget::AnglaphGreyscale);
    list.insert("Side by Side, No Mirror",      ImageWidget::SidebySide);
    list.insert("Side by Side, Mirror Left",    ImageWidget::SidebySideMLeft);
    list.insert("Side by Side, Mirror Right",   ImageWidget::SidebySideMRight);
    list.insert("Side by Side, Mirror Both",    ImageWidget::SidebySideMBoth);
    list.insert("Top/Bottom, No Mirror",        ImageWidget::TopBottom);
    list.insert("Top/Bottom, Mirror Top",       ImageWidget::TopBottomMTop);
    list.insert("Top/Bottom, Mirror Bottom",    ImageWidget::TopBottomMBottom);
    list.insert("Top/Bottom, Mirror Both",      ImageWidget::TopBottomMBoth);
    list.insert("Interlaced, Horizontal",       ImageWidget::InterlacedHorizontal);
    list.insert("Interlaced, Vertical",         ImageWidget::InterlacedVertical);
    list.insert("Checkerboard",                 ImageWidget::Checkerboard);
    list.insert("Mono, Left",                   ImageWidget::MonoLeft);
    list.insert("Mono, Right",                  ImageWidget::MonoRight);
    return list;
}

const QMap<QString, ImageWidget::DrawMode> ImageWidget::drawModeNames = initDrawModeList();
<commit_msg>fixed warning about unsafe use of bool in bit shift.<commit_after>#include "imagewidget.h"
#include "renderers.h"
#include <QResizeEvent>
#include <QPainter>
#include <QElapsedTimer>

ImageWidget::ImageWidget(QWidget *parent) : QWidget(parent), mode(AnglaphFull), zoom(0.0), swapLR(false), panButtons(Qt::LeftButton | Qt::MidButton),
    mouseTimer(this), hBar(Qt::Horizontal, this), vBar(Qt::Vertical, this), continuousPan(true), scrollbarsVisible(true), smoothTransform(false),
    maskInterlacedHorizontal(":/masks/interlacedH.pbm"), maskInterlacedVertical(":/masks/interlacedV.pbm"), maskCheckerboard(":/masks/checkerboard.pbm") {

    setMouseTracking(true);
    recalculatescroolmax();

    connect(&hBar, SIGNAL(valueChanged(int)), this, SLOT(update()));
    connect(&vBar, SIGNAL(valueChanged(int)), this, SLOT(update()));

    mouseTimer.setSingleShot(true);
    connect(&mouseTimer, SIGNAL(timeout()), this, SLOT(hideCursor()));
}

void ImageWidget::resizeEvent(QResizeEvent *e){
    hBar.resize(e->size().width() - vBar.sizeHint().width(), hBar.sizeHint().height());
    hBar.move(0, e->size().height() - hBar.sizeHint().height());

    vBar.resize(vBar.sizeHint().width(), e->size().height() - hBar.sizeHint().height());
    vBar.move(e->size().width() - vBar.sizeHint().width(), 0);

    recalculatescroolmax();
}

void ImageWidget::paintEvent(QPaintEvent *e){
    QPainter painter(this);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, smoothTransform);
    painter.setBrush(QBrush(Qt::black));
    painter.drawRect(e->rect());

    if(imgL.isNull() && imgR.isNull()){
        painter.setPen(Qt::gray);
        QFont font;
        font.setPointSize(32);
        painter.setFont(font);
        painter.drawText(rect(), Qt::AlignCenter | Qt::TextWordWrap, tr("No Image Loaded"));
    }else if(parentWidget() && !parentWidget()->isFullScreen() && (mode == InterlacedHorizontal || mode == InterlacedVertical || mode == Checkerboard)){
        painter.setPen(Qt::gray);
        QFont font;
        font.setPointSize(24);
        painter.setFont(font);
        painter.drawText(rect(), Qt::AlignCenter | Qt::TextWordWrap, tr("%1 Display Must Be Fullscreen").arg(mode == Checkerboard ? "Checkerboard" : "Interlaced"));

        /* keep scrollbars hidden. */
        zoom = 0.0f;
        recalculatescroolmax();
    }else{
        QElapsedTimer time;
        time.start();

        const QPixmap &L = swapLR ? pixmapR : pixmapL;
        const QPixmap &R = swapLR ? pixmapL : pixmapR;

        switch(mode){
        case AnglaphFull:
        case AnglaphHalf:
        case AnglaphGreyscale:
            drawSingle(anglaph, -hBar.value(), -vBar.value(), painter, zoom);
            break;
        case SidebySide:
            drawSideBySide(L, R, -hBar.value(), -vBar.value(), painter, zoom);
            break;
        case SidebySideMLeft:
            drawSideBySide(L, R, -hBar.value(), -vBar.value(), painter, zoom, true);
            break;
        case SidebySideMRight:
            drawSideBySide(L, R, -hBar.value(), -vBar.value(), painter, zoom, false, true);
            break;
        case SidebySideMBoth:
            drawSideBySide(L, R, -hBar.value(), -vBar.value(), painter, zoom, true, true);
            break;
        case TopBottom:
            drawTopBottom(L, R, -hBar.value(), -vBar.value(), painter, zoom);
            break;
        case TopBottomMTop:
            drawTopBottom(L, R, -hBar.value(), -vBar.value(), painter, zoom, true);
            break;
        case TopBottomMBottom:
            drawTopBottom(L, R, -hBar.value(), -vBar.value(), painter, zoom, false, true);
            break;
        case TopBottomMBoth:
            drawTopBottom(L, R, -hBar.value(), -vBar.value(), painter, zoom, true, true);
            break;
        case InterlacedHorizontal:
            drawInterlaced(L, R, -hBar.value(), -vBar.value(), painter, maskInterlacedHorizontal, zoom);
            break;
        case InterlacedVertical:
            drawInterlaced(L, R, -hBar.value(), -vBar.value(), painter, maskInterlacedVertical, zoom);
            break;
        case Checkerboard:
            drawInterlaced(L, R, -hBar.value(), -vBar.value(), painter, maskCheckerboard, zoom);
            break;
        case MonoLeft:
            drawSingle(L, -hBar.value(), -vBar.value(), painter, zoom);
            break;
        case MonoRight:
            drawSingle(R, -hBar.value(), -vBar.value(), painter, zoom);
            break;
        }

        qDebug("Draw time: %fms", time.nsecsElapsed() * 1.0e-6);
    }
}

bool ImageWidget::loadStereoImage(const QString &filename){
    QImage img(filename);

    imgR = img.copy(0,               0, img.width() / 2, img.height());
    imgL = img.copy(img.width() / 2, 0, img.width() / 2, img.height());
    updateImages();

    return !img.isNull();
}

void ImageWidget::updateImages(){
    pixmapL = QPixmap::fromImage(imgL);
    pixmapR = QPixmap::fromImage(imgR);
    recalculatescroolmax();
    updateAnglaph();
    repaint();
}

void ImageWidget::mouseMoveEvent(QMouseEvent *e){
    if((e->buttons() & panButtons) && (vBar.maximum() > 0 || hBar.maximum() > 0)){
        vBar.setValue(vBar.value() + lastmousepos.y() - e->y());
        hBar.setValue(hBar.value() + lastmousepos.x() - e->x());

        if(continuousPan){
            QPoint warpto = e->pos();

            if(e->x() <= 0){
                warpto.setX(width() - 3);
            }else if(e->x() >= width() - 1){
                warpto.setX(2);
            }
            if(e->y() <= 0){
                warpto.setY(height() - 3);
            }else if(e->y() >= height() - 1){
                warpto.setY(2);
            }

            if(warpto != e->pos()){
                QCursor::setPos(mapToGlobal(warpto));
            }

            lastmousepos = warpto;
        }else{
            lastmousepos = e->pos();
        }

        setCursor(Qt::SizeAllCursor);
    }else{
        setCursor(Qt::ArrowCursor);

        lastmousepos = e->pos();
    }
    mouseTimer.start(4000);
}

void ImageWidget::mouseReleaseEvent(QMouseEvent *e){
    if(panButtons.testFlag(e->button())){
        setCursor(Qt::ArrowCursor);
    }
}

void ImageWidget::wheelEvent(QWheelEvent *e){
    addZoom(e->delta() / 1000.0);
}

void ImageWidget::recalculatescroolmax(){
    int isSidebySide = (mode == SidebySide || mode == SidebySideMLeft || mode == SidebySideMRight || mode == SidebySideMBoth);
    int isTopBottom  = (mode == TopBottom  || mode == TopBottomMTop   || mode == TopBottomMBottom || mode == TopBottomMBoth);

    int hmax = qMax(int((imgL.width() << isSidebySide) * zoom - width()) >> (isSidebySide + 1), 0);
    hBar.setRange(-hmax, hmax);

    int vmax = qMax(int((imgL.height() << isTopBottom) * zoom - height()) >> (isTopBottom + 1), 0);
    vBar.setRange(-vmax, vmax);

    hBar.setVisible(scrollbarsVisible && hmax != 0);
    vBar.setVisible(scrollbarsVisible && vmax != 0);
}

void ImageWidget::setZoom(qreal val){
    zoom = val;
    recalculatescroolmax();
    repaint();
}

void ImageWidget::zoomIn(){
    addZoom( 0.1);
}

void ImageWidget::zoomOut(){
    addZoom(-0.1);
}

void ImageWidget::addZoom(qreal amount){
    if(zoom <= 0.0){
        int isSidebySide = (mode == SidebySide || mode == SidebySideMLeft || mode == SidebySideMRight || mode == SidebySideMBoth);
        int isTopBottom  = (mode == TopBottom  || mode == TopBottomMTop   || mode == TopBottomMBottom || mode == TopBottomMBoth);

        zoom = qMin(qreal(width()) / qreal(imgL.width() << isSidebySide), qreal(height()) / qreal(imgL.height() << isTopBottom));
    }
    qreal zoomorig = zoom;
    zoom += amount * zoom;
    zoom = qBound(0.2, zoom, 4.0);
    recalculatescroolmax();
    vBar.setValue(vBar.value() * zoom / zoomorig);
    hBar.setValue(hBar.value() * zoom / zoomorig);
    repaint();
}

void ImageWidget::enableSwapLR(bool enable){
    swapLR = enable;
    updateAnglaph();
    repaint();
}

void ImageWidget::showScrollbars(bool show){
    scrollbarsVisible = show;
    recalculatescroolmax();
}

void ImageWidget::enableContinuousPan(bool enable){
    continuousPan = enable;
}

void ImageWidget::enableSmoothTransform(bool enable){
    smoothTransform = enable;
    repaint();
}

void ImageWidget::hideCursor(){
    setCursor(Qt::BlankCursor);
}

void ImageWidget::setRenderMode(DrawMode m){
    mode = m;
    recalculatescroolmax();
    updateAnglaph();
    repaint();
}

void ImageWidget::setPanButtons(Qt::MouseButtons buttons){
    panButtons = buttons;
}

void ImageWidget::updateAnglaph(){
    const QImage &L = swapLR ? imgR : imgL;
    const QImage &R = swapLR ? imgL : imgR;
    switch(mode){
    case AnglaphFull:
        anglaph = QPixmap::fromImage(drawAnglaph(L, R));
        break;
    case AnglaphHalf:
        anglaph = QPixmap::fromImage(drawAnglaphHalf(L, R));
        break;
    case AnglaphGreyscale:
        anglaph = QPixmap::fromImage(drawAnglaphGrey(L, R));
        break;
    default: break;
    }
}

QMap<QString, ImageWidget::DrawMode> initDrawModeList(){
    QMap<QString, ImageWidget::DrawMode> list;
    list.insert("Anglaph, Full Color",          ImageWidget::AnglaphFull);
    list.insert("Anglaph, Half Color",          ImageWidget::AnglaphHalf);
    list.insert("Anglaph, Greyscale",           ImageWidget::AnglaphGreyscale);
    list.insert("Side by Side, No Mirror",      ImageWidget::SidebySide);
    list.insert("Side by Side, Mirror Left",    ImageWidget::SidebySideMLeft);
    list.insert("Side by Side, Mirror Right",   ImageWidget::SidebySideMRight);
    list.insert("Side by Side, Mirror Both",    ImageWidget::SidebySideMBoth);
    list.insert("Top/Bottom, No Mirror",        ImageWidget::TopBottom);
    list.insert("Top/Bottom, Mirror Top",       ImageWidget::TopBottomMTop);
    list.insert("Top/Bottom, Mirror Bottom",    ImageWidget::TopBottomMBottom);
    list.insert("Top/Bottom, Mirror Both",      ImageWidget::TopBottomMBoth);
    list.insert("Interlaced, Horizontal",       ImageWidget::InterlacedHorizontal);
    list.insert("Interlaced, Vertical",         ImageWidget::InterlacedVertical);
    list.insert("Checkerboard",                 ImageWidget::Checkerboard);
    list.insert("Mono, Left",                   ImageWidget::MonoLeft);
    list.insert("Mono, Right",                  ImageWidget::MonoRight);
    return list;
}

const QMap<QString, ImageWidget::DrawMode> ImageWidget::drawModeNames = initDrawModeList();
<|endoftext|>


<commit_before>#include "init-shared.h"

#include "debug.h"
#include "persistentmodel/archive.h"
#include "persistentmodel/job.h"
#include "persistentmodel/journal.h"
#include "scheduling.h"
#include "tarsnaperror.h"
#include "taskstatus.h"
#include "translator.h"

#include <QCoreApplication>
#include <QFile>
#include <QList>
#include <QMetaType>
#include <QUrl>
#include <QVector>

#include <TSettings.h>

/*
 * It's unnecessary from a programming standpoint to put these in a separate
 * function, but it helps me keep track of how the various Qt layers work.
 */
static void init_no_app()
{
    qRegisterMetaType<TaskStatus>("TaskStatus");
    qRegisterMetaType<QList<QUrl>>("QList<QUrl>");
    qRegisterMetaType<BackupTaskPtr>("BackupTaskPtr");
    qRegisterMetaType<QList<ArchivePtr>>("QList<ArchivePtr >");
    qRegisterMetaType<ArchivePtr>("ArchivePtr");
    qRegisterMetaType<ArchiveRestoreOptions>("ArchiveRestoreOptions");
    qRegisterMetaType<QSqlQuery>("QSqlQuery");
    qRegisterMetaType<JobPtr>("JobPtr");
    qRegisterMetaType<QMap<QString, JobPtr>>("QMap<QString, JobPtr>");
    qRegisterMetaType<TarsnapError>("TarsnapError");
    qRegisterMetaType<LogEntry>("LogEntry");
    qRegisterMetaType<QVector<LogEntry>>("QVector<LogEntry>");
    qRegisterMetaType<QVector<File>>("QVector<File>");
}

static void init_no_explicit_app()
{
#ifndef QT_TESTLIB_LIB
    // Don't do this for the tests, since we want them to have unique names.
    QCoreApplication::setOrganizationName(QLatin1String("Tarsnap Backup Inc."));
    QCoreApplication::setOrganizationDomain(QLatin1String("tarsnap.com"));
    QCoreApplication::setApplicationName(QLatin1String("Tarsnap"));
    QCoreApplication::setApplicationVersion(APP_VERSION);
#endif

    // In order to avoid a memory leak (?), must be done after setting up
    // the application and/or organization name.
    ConsoleLog::instance().initializeConsoleLog();
}

/**
 * Constructor initialization shared between GUI and non-GUI.  Cannot fail.
 */
void init_shared(QCoreApplication *app)
{
    init_no_app();
    init_no_explicit_app();

    app->setQuitLockEnabled(false);
    app->setAttribute(Qt::AA_UseHighDpiPixmaps);
}

static QString migrateSettings(QSettings *settingsOld, QSettings *settingsNew)
{
    // Copy old settings to new, by group.  (On OSX, QSettings contains a whole
    // bunch of system-wide settings which we don't want to copy.)
    QStringList groups = {"app", "tarsnap"};
    for(const QString &groupname : groups)
    {
        settingsOld->beginGroup(groupname);
        settingsNew->beginGroup(groupname);
        QStringList keys = settingsOld->childKeys();
        for(const QString &key : keys)
        {
            settingsNew->setValue(key, settingsOld->value(key));
        }
        settingsOld->endGroup();
        settingsNew->endGroup();
    }
    settingsNew->sync();

    // Rename old settings to prevent migrating it again.
    QFile   fileOld(settingsOld->fileName());
    QString renamed = settingsOld->fileName()
                      + QDate::currentDate().toString(".yyyy-MMM-dd.bak");

    // Close the Setings to prevent it from re-writing the file.
    delete settingsOld;
    fileOld.rename(renamed);
    return renamed;
}

static QString check_migrateSettings()
{
    // Get default settings file.
    QSettings::setDefaultFormat(QSettings::NativeFormat);
    QSettings *settingsOld = new QSettings();

    // Shouldn't be necessary, but just in case.
    TSettings::destroy();

    // Get new settings file.  Must be done after getting the default one!
    TSettings  tsettings;
    QSettings *settingsNew = tsettings.getQSettings();

    // Bail if we don't need to migrate anything.
    if(QFileInfo::exists(settingsNew->fileName())
       || !QFileInfo::exists(settingsOld->fileName()))
    {
        delete settingsOld;
        return "";
    }

    return migrateSettings(settingsOld, settingsNew);
}

/**
 * Configures the app-wide Settings.  Can fail and report messages.
 */
struct init_info init_shared_settings(QString configDir)
{
    struct init_info info = {INIT_OK, "", ""};

    // Handle soon-to-be-deprecated --appdata argument.  Must be done before
    // instantiating any settings object.
    if(!configDir.isEmpty())
    {
        QSettings::setPath(QSettings::IniFormat, QSettings::UserScope,
                           configDir);
        QSettings::setDefaultFormat(QSettings::IniFormat);
        // Make sure that TSettings uses the right filename in this case.
        QSettings defaultSettings;
        TSettings::setFilename(defaultSettings.fileName());
    }

    // Migrate settings from old settings file to new (if applicable).
    QString renamedOldSettings;
    if(configDir.isEmpty())
    {
        renamedOldSettings = check_migrateSettings();
    }

    TSettings settings;

    if(!renamedOldSettings.isEmpty())
    {
        info.status = INIT_SETTINGS_RENAMED;
        info.message =
            QString("Updated config file, new location:\n\n%1\n\nThe old file "
                    "was renamed to:\n\n%2")
                .arg(settings.getQSettings()->fileName(), renamedOldSettings);
    }

    return info;
}

/**
 * Initialization shared between GUI and non-GUI.  Can fail and report messages.
 */
struct init_info init_shared_core(QCoreApplication *app)
{
    struct init_info info = {INIT_OK, "", ""};
    TSettings        settings;

    // Set up the translator.
    Translator &translator = Translator::instance();
    translator.translateApp(app,
                            settings.value("app/language", LANG_AUTO).toString());

    // Run the setup wizard (if necessary).  This uses the translator, and
    // can be tested with:
    //    $ LANGUAGE=ro ./tarsnap-gui
    bool wizardDone = settings.value("app/wizard_done", false).toBool();
    if(!wizardDone)
    {
        info.status = INIT_NEEDS_SETUP;
        return (info);
    }

    // Initialize the persistentstore.  Must be after setup wizard!
    PersistentStore &store = PersistentStore::instance();
    if(!store.init())
    {
        info.status = INIT_DB_FAILED;
        return (info);
    }

    // Warn about --dry-run before trying to run --jobs.
    if(settings.value("tarsnap/dry_run", false).toBool())
    {
        info.status  = INIT_DRY_RUN;
        info.message = QObject::tr("Simulation mode is enabled.  Archives will"
                                   " not be uploaded to the Tarsnap server."
                                   "  Disable in Settings -> Backup.");
        return (info);
    }

    // Make sure we have the path to the current Tarsnap-GUI binary
    struct scheduleinfo correctedPath = correctedSchedulingPath();

    if(correctedPath.status == SCHEDULE_OK)
    {
        info.status  = INIT_SCHEDULE_OK;
        info.message = correctedPath.message;
    }
    if(correctedPath.status == SCHEDULE_ERROR)
    {
        info.status  = INIT_SCHEDULE_ERROR;
        info.message = correctedPath.message;
        info.extra   = correctedPath.extra;
    }

    return info;
}
<commit_msg>init-shared: explicit includes<commit_after>#include "init-shared.h"

#include "backuptask.h"
#include "debug.h"
#include "persistentmodel/archive.h"
#include "persistentmodel/job.h"
#include "persistentmodel/journal.h"
#include "scheduling.h"
#include "tarsnaperror.h"
#include "taskstatus.h"
#include "translator.h"

#include <QCoreApplication>
#include <QFile>
#include <QList>
#include <QMetaType>
#include <QUrl>
#include <QVector>

#include <TSettings.h>

/*
 * It's unnecessary from a programming standpoint to put these in a separate
 * function, but it helps me keep track of how the various Qt layers work.
 */
static void init_no_app()
{
    qRegisterMetaType<TaskStatus>("TaskStatus");
    qRegisterMetaType<QList<QUrl>>("QList<QUrl>");
    qRegisterMetaType<BackupTaskPtr>("BackupTaskPtr");
    qRegisterMetaType<QList<ArchivePtr>>("QList<ArchivePtr >");
    qRegisterMetaType<ArchivePtr>("ArchivePtr");
    qRegisterMetaType<ArchiveRestoreOptions>("ArchiveRestoreOptions");
    qRegisterMetaType<QSqlQuery>("QSqlQuery");
    qRegisterMetaType<JobPtr>("JobPtr");
    qRegisterMetaType<QMap<QString, JobPtr>>("QMap<QString, JobPtr>");
    qRegisterMetaType<TarsnapError>("TarsnapError");
    qRegisterMetaType<LogEntry>("LogEntry");
    qRegisterMetaType<QVector<LogEntry>>("QVector<LogEntry>");
    qRegisterMetaType<QVector<File>>("QVector<File>");
}

static void init_no_explicit_app()
{
#ifndef QT_TESTLIB_LIB
    // Don't do this for the tests, since we want them to have unique names.
    QCoreApplication::setOrganizationName(QLatin1String("Tarsnap Backup Inc."));
    QCoreApplication::setOrganizationDomain(QLatin1String("tarsnap.com"));
    QCoreApplication::setApplicationName(QLatin1String("Tarsnap"));
    QCoreApplication::setApplicationVersion(APP_VERSION);
#endif

    // In order to avoid a memory leak (?), must be done after setting up
    // the application and/or organization name.
    ConsoleLog::instance().initializeConsoleLog();
}

/**
 * Constructor initialization shared between GUI and non-GUI.  Cannot fail.
 */
void init_shared(QCoreApplication *app)
{
    init_no_app();
    init_no_explicit_app();

    app->setQuitLockEnabled(false);
    app->setAttribute(Qt::AA_UseHighDpiPixmaps);
}

static QString migrateSettings(QSettings *settingsOld, QSettings *settingsNew)
{
    // Copy old settings to new, by group.  (On OSX, QSettings contains a whole
    // bunch of system-wide settings which we don't want to copy.)
    QStringList groups = {"app", "tarsnap"};
    for(const QString &groupname : groups)
    {
        settingsOld->beginGroup(groupname);
        settingsNew->beginGroup(groupname);
        QStringList keys = settingsOld->childKeys();
        for(const QString &key : keys)
        {
            settingsNew->setValue(key, settingsOld->value(key));
        }
        settingsOld->endGroup();
        settingsNew->endGroup();
    }
    settingsNew->sync();

    // Rename old settings to prevent migrating it again.
    QFile   fileOld(settingsOld->fileName());
    QString renamed = settingsOld->fileName()
                      + QDate::currentDate().toString(".yyyy-MMM-dd.bak");

    // Close the Setings to prevent it from re-writing the file.
    delete settingsOld;
    fileOld.rename(renamed);
    return renamed;
}

static QString check_migrateSettings()
{
    // Get default settings file.
    QSettings::setDefaultFormat(QSettings::NativeFormat);
    QSettings *settingsOld = new QSettings();

    // Shouldn't be necessary, but just in case.
    TSettings::destroy();

    // Get new settings file.  Must be done after getting the default one!
    TSettings  tsettings;
    QSettings *settingsNew = tsettings.getQSettings();

    // Bail if we don't need to migrate anything.
    if(QFileInfo::exists(settingsNew->fileName())
       || !QFileInfo::exists(settingsOld->fileName()))
    {
        delete settingsOld;
        return "";
    }

    return migrateSettings(settingsOld, settingsNew);
}

/**
 * Configures the app-wide Settings.  Can fail and report messages.
 */
struct init_info init_shared_settings(QString configDir)
{
    struct init_info info = {INIT_OK, "", ""};

    // Handle soon-to-be-deprecated --appdata argument.  Must be done before
    // instantiating any settings object.
    if(!configDir.isEmpty())
    {
        QSettings::setPath(QSettings::IniFormat, QSettings::UserScope,
                           configDir);
        QSettings::setDefaultFormat(QSettings::IniFormat);
        // Make sure that TSettings uses the right filename in this case.
        QSettings defaultSettings;
        TSettings::setFilename(defaultSettings.fileName());
    }

    // Migrate settings from old settings file to new (if applicable).
    QString renamedOldSettings;
    if(configDir.isEmpty())
    {
        renamedOldSettings = check_migrateSettings();
    }

    TSettings settings;

    if(!renamedOldSettings.isEmpty())
    {
        info.status = INIT_SETTINGS_RENAMED;
        info.message =
            QString("Updated config file, new location:\n\n%1\n\nThe old file "
                    "was renamed to:\n\n%2")
                .arg(settings.getQSettings()->fileName(), renamedOldSettings);
    }

    return info;
}

/**
 * Initialization shared between GUI and non-GUI.  Can fail and report messages.
 */
struct init_info init_shared_core(QCoreApplication *app)
{
    struct init_info info = {INIT_OK, "", ""};
    TSettings        settings;

    // Set up the translator.
    Translator &translator = Translator::instance();
    translator.translateApp(app,
                            settings.value("app/language", LANG_AUTO).toString());

    // Run the setup wizard (if necessary).  This uses the translator, and
    // can be tested with:
    //    $ LANGUAGE=ro ./tarsnap-gui
    bool wizardDone = settings.value("app/wizard_done", false).toBool();
    if(!wizardDone)
    {
        info.status = INIT_NEEDS_SETUP;
        return (info);
    }

    // Initialize the persistentstore.  Must be after setup wizard!
    PersistentStore &store = PersistentStore::instance();
    if(!store.init())
    {
        info.status = INIT_DB_FAILED;
        return (info);
    }

    // Warn about --dry-run before trying to run --jobs.
    if(settings.value("tarsnap/dry_run", false).toBool())
    {
        info.status  = INIT_DRY_RUN;
        info.message = QObject::tr("Simulation mode is enabled.  Archives will"
                                   " not be uploaded to the Tarsnap server."
                                   "  Disable in Settings -> Backup.");
        return (info);
    }

    // Make sure we have the path to the current Tarsnap-GUI binary
    struct scheduleinfo correctedPath = correctedSchedulingPath();

    if(correctedPath.status == SCHEDULE_OK)
    {
        info.status  = INIT_SCHEDULE_OK;
        info.message = correctedPath.message;
    }
    if(correctedPath.status == SCHEDULE_ERROR)
    {
        info.status  = INIT_SCHEDULE_ERROR;
        info.message = correctedPath.message;
        info.extra   = correctedPath.extra;
    }

    return info;
}
<|endoftext|>


<commit_before>/*

 Copyright (c) 2013, Ford Motor Company
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.

 Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following
 disclaimer in the documentation and/or other materials provided with the
 distribution.

 Neither the name of the Ford Motor Company nor the names of its contributors
 may be used to endorse or promote products derived from this software
 without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */

#include <algorithm>
#include <string>
#include "application_manager/commands/mobile/dial_number_request.h"
#include "application_manager/application_manager_impl.h"
#include "application_manager/application_impl.h"

namespace application_manager {

namespace commands {

DialNumberRequest::DialNumberRequest(const MessageSharedPtr& message)
    : CommandRequestImpl(message) {
}

DialNumberRequest::~DialNumberRequest() {
}

bool DialNumberRequest::Init(){
    LOG4CXX_AUTO_TRACE(logger_);

    default_timeout_ = 0;

    return true;
}

void DialNumberRequest::Run() {
  LOG4CXX_AUTO_TRACE(logger_);

  ApplicationSharedPtr application =
      ApplicationManagerImpl::instance()->application(connection_key());

  if (!application) {
    LOG4CXX_ERROR(logger_, "NULL pointer");
    SendResponse(false, mobile_apis::Result::APPLICATION_NOT_REGISTERED);
    return;
  }
  std::string number = (*message_)[strings::msg_params][strings::number].asString();
  if (!CheckSyntax(number)) {
      LOG4CXX_ERROR(logger_, "Invalid incoming data");
      SendResponse(false, mobile_apis::Result::INVALID_DATA);
      return;
  }
  StripNumberParam(number);
  if (number.empty()) {
    LOG4CXX_WARN(logger_, "After strip number param is empty. Invalid incoming data");
    SendResponse(false, mobile_apis::Result::INVALID_DATA);
    return;
  }
  smart_objects::SmartObject msg_params = smart_objects::SmartObject(
      smart_objects::SmartType_Map);
  msg_params[strings::number] =
    (*message_)[strings::msg_params][strings::number].asString();
  msg_params[strings::app_id] = application->hmi_app_id();

  SendHMIRequest(hmi_apis::FunctionID::BasicCommunication_DialNumber,
                 &msg_params, true);
}

void DialNumberRequest::on_event(const event_engine::Event& event) {

  ApplicationSharedPtr application =
      ApplicationManagerImpl::instance()->application(connection_key());

  if (!application) {
    LOG4CXX_ERROR(logger_, "NULL pointer");
    return;
  }

  const smart_objects::SmartObject& message = event.smart_object();
  mobile_apis::Result::eType result_code = mobile_apis::Result::SUCCESS;
  switch (event.id()) {
    case hmi_apis::FunctionID::BasicCommunication_DialNumber: {
      LOG4CXX_INFO(logger_, "Received DialNumber event");
      result_code =  CommandRequestImpl::GetMobileResultCode(
        static_cast<hmi_apis::Common_Result::eType>(
          message[strings::params][hmi_response::code].asInt()));
      break;
    }
    default: {
      LOG4CXX_ERROR(logger_,"Received unknown event" << event.id());
      return;
    }
  }

  SendResponse((mobile_apis::Result::SUCCESS == result_code), result_code);
}

void DialNumberRequest::StripNumberParam(std::string &number) {
    std::size_t found = 0;
    while (std::string::npos != (found = number.find_first_not_of("+0123456789"))) {
      number.erase(number.begin() + found);
    }
    (*message_)[strings::msg_params][strings::number] = number;
}

}  // namespace commands

}  // namespace application_manager
<commit_msg>Dial number append info field to responceToMobile<commit_after>/*

 Copyright (c) 2013, Ford Motor Company
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.

 Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following
 disclaimer in the documentation and/or other materials provided with the
 distribution.

 Neither the name of the Ford Motor Company nor the names of its contributors
 may be used to endorse or promote products derived from this software
 without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */

#include <algorithm>
#include <string>
#include "application_manager/commands/mobile/dial_number_request.h"
#include "application_manager/application_manager_impl.h"
#include "application_manager/application_impl.h"

namespace application_manager {

namespace commands {

DialNumberRequest::DialNumberRequest(const MessageSharedPtr& message)
    : CommandRequestImpl(message) {
}

DialNumberRequest::~DialNumberRequest() {
}

bool DialNumberRequest::Init(){
    LOG4CXX_AUTO_TRACE(logger_);

    default_timeout_ = 0;

    return true;
}

void DialNumberRequest::Run() {
  LOG4CXX_AUTO_TRACE(logger_);

  ApplicationSharedPtr application =
      ApplicationManagerImpl::instance()->application(connection_key());

  if (!application) {
    LOG4CXX_ERROR(logger_, "NULL pointer");
    SendResponse(false, mobile_apis::Result::APPLICATION_NOT_REGISTERED);
    return;
  }
  std::string number = (*message_)[strings::msg_params][strings::number].asString();
  if (!CheckSyntax(number)) {
      LOG4CXX_ERROR(logger_, "Invalid incoming data");
      SendResponse(false, mobile_apis::Result::INVALID_DATA);
      return;
  }
  StripNumberParam(number);
  if (number.empty()) {
    LOG4CXX_WARN(logger_, "After strip number param is empty. Invalid incoming data");
    SendResponse(false, mobile_apis::Result::INVALID_DATA);
    return;
  }
  smart_objects::SmartObject msg_params = smart_objects::SmartObject(
      smart_objects::SmartType_Map);
  msg_params[strings::number] =
    (*message_)[strings::msg_params][strings::number].asString();
  msg_params[strings::app_id] = application->hmi_app_id();

  SendHMIRequest(hmi_apis::FunctionID::BasicCommunication_DialNumber,
                 &msg_params, true);
}

void DialNumberRequest::on_event(const event_engine::Event& event) {

  ApplicationSharedPtr application =
      ApplicationManagerImpl::instance()->application(connection_key());

  if (!application) {
    LOG4CXX_ERROR(logger_, "NULL pointer");
    return;
  }

  const smart_objects::SmartObject& message = event.smart_object();
  mobile_apis::Result::eType result_code = mobile_apis::Result::SUCCESS;
  switch (event.id()) {
    case hmi_apis::FunctionID::BasicCommunication_DialNumber: {
      LOG4CXX_INFO(logger_, "Received DialNumber event");
      result_code =  CommandRequestImpl::GetMobileResultCode(
        static_cast<hmi_apis::Common_Result::eType>(
          message[strings::params][hmi_response::code].asInt()));
      break;
    }
    default: {
      LOG4CXX_ERROR(logger_,"Received unknown event" << event.id());
      return;
    }
  }

  const bool is_success = mobile_apis::Result::SUCCESS == result_code;
  const bool is_info_valid =
      message[strings::msg_params].keyExists(strings::info);

  if (is_info_valid) {
    const char* info_char_array =
        message[strings::msg_params][strings::info].asCharArray();
    SendResponse(is_success, result_code, info_char_array);
    return;
  }

  SendResponse(is_success, result_code);
}

void DialNumberRequest::StripNumberParam(std::string &number) {
    std::size_t found = 0;
    while (std::string::npos != (found = number.find_first_not_of("+0123456789"))) {
      number.erase(number.begin() + found);
    }
    (*message_)[strings::msg_params][strings::number] = number;
}

}  // namespace commands

}  // namespace application_manager
<|endoftext|>


<commit_before>// Copyright (C) 2016 xaizek <xaizek@openmailbox.org>
//
// This file is part of uncov.
//
// uncov is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// uncov 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with uncov.  If not, see <http://www.gnu.org/licenses/>.

#include "integration.hpp"

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>

#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/scope_exit.hpp>

#include <cstdio>
#include <cstdlib>

#include <iostream>
#include <string>
#include <utility>

#include "utils/memory.hpp"

namespace io = boost::iostreams;

/**
 * @brief Base for hidden internals of RedirectToPager class.
 */
class RedirectToPager::Impl
{
public:
    //! Virtual destructor.
    virtual ~Impl() = default;
};

/**
 * @brief Redirects standard output into a pager.
 *
 * The redirection happens only if number of lines exceeds screen height,
 * otherwise lines are just dumped onto the screen as is.
 */
class PagerRedirect : public RedirectToPager::Impl
{
public:
    /**
     * @brief Custom stream buffer that spawns pager for large outputs only.
     *
     * Collect up to terminal height lines.  If buffer is closed with this limit
     * not reached, it prints lines on std::cout.  If we hit the limit in the
     * process of output, it opens a pager and feeds it all collected output and
     * everything that comes next.
     */
    class ScreenPageBuffer
    {
    public:
        //! Type of character used by this buffer.
        using char_type = char;
        //! Category of functionality provided by this buffer implementation.
        using category = boost::iostreams::sink_tag;

    public:
        /**
         * @brief Constructs the buffer.
         *
         * @param screenHeight Height of terminal in lines.
         * @param out Storage for output stream buffer backed up by a file.
         */
        ScreenPageBuffer(unsigned int screenHeight,
                         io::stream_buffer<io::file_descriptor_sink> *out);
        /**
         * @brief Dumps output onto the screen or waits for pager to finish.
         */
        ~ScreenPageBuffer();

    public:
        /**
         * @brief Writes @p n characters from @p s.
         *
         * @param s Character buffer.
         * @param n Size of the buffer.
         *
         * @returns Number of successfully written characters.
         */
        std::streamsize write(const char s[], std::streamsize n);

    private:
        /**
         * @brief Writes single character.
         *
         * @param c Character to write.
         *
         * @returns @c true on success, @c false otherwise.
         */
        bool put(char c);
        /**
         * @brief Opens pager for output.
         */
        void openPager();

    private:
        //! Whether redirection into pager is enabled.
        bool redirectToPager = false;
        //! Number of output lines collected so far.
        unsigned int nLines = 0U;
        //! Height of terminal in lines.
        unsigned int screenHeight;
        //! Output collected so far.
        std::string buffer;
        //! Process id of a pager.
        pid_t pid;

        /**
         * @brief Pointer to buffer stored in RedirectToPager.
         *
         * This is not by value, because ScreenPageBuffer needs to be copyable.
         */
        io::stream_buffer<io::file_descriptor_sink> *out;
    };

public:
    /**
     * @brief Replaces buffer of @c std::cout with ScreenPageBuffer.
     */
    PagerRedirect() : screenPageBuffer(getTerminalSize().second, &out)
    {
        rdbuf = std::cout.rdbuf(&screenPageBuffer);
    }

    /**
     * @brief Restores original buffer of @c std::cout.
     */
    ~PagerRedirect()
    {
        // Flush the stream to make sure that we put all contents we want through
        // the custom stream buffer.
        std::cout.flush();

        std::cout.rdbuf(rdbuf);
    }

private:
    //! This is stored for ScreenPageBuffer class.
    io::stream_buffer<io::file_descriptor_sink> out;
    //! Custom buffer implementation.
    io::stream_buffer<ScreenPageBuffer> screenPageBuffer;
    //! Original buffer of @c std::cout.
    std::streambuf *rdbuf;
};

using ScreenPageBuffer = PagerRedirect::ScreenPageBuffer;

ScreenPageBuffer::ScreenPageBuffer(unsigned int screenHeight,
                               io::stream_buffer<io::file_descriptor_sink> *out)
    : screenHeight(screenHeight), out(out)
{
}

ScreenPageBuffer::~ScreenPageBuffer()
{
    if (redirectToPager) {
        out->close();
        int wstatus;
        waitpid(pid, &wstatus, 0);
    } else {
        std::cout << buffer;
    }
}

std::streamsize
ScreenPageBuffer::write(const char s[], std::streamsize n)
{
    for (std::streamsize i = 0U; i < n; ++i) {
        if (!put(s[i])) {
            return i;
        }
    }
    return n;
}

bool
ScreenPageBuffer::put(char c)
{
    if (redirectToPager) {
        return boost::iostreams::put(*out, c);
    }

    if (c == '\n') {
        ++nLines;
    }

    if (nLines > screenHeight) {
        openPager();
        redirectToPager = true;
        for (char c : buffer) {
            if (!boost::iostreams::put(*out, c)) {
                return false;
            }
        }
        return boost::iostreams::put(*out, c);
    }

    buffer.push_back(c);
    return true;
}

void
ScreenPageBuffer::openPager()
{
    int pipePair[2];
    if (pipe(pipePair) != 0) {
        throw std::runtime_error("Failed to create a pipe");
    }
    BOOST_SCOPE_EXIT_ALL(pipePair) { close(pipePair[0]); };

    pid = fork();
    if (pid == -1) {
        close(pipePair[1]);
        throw std::runtime_error("Fork has failed");
    }
    if (pid == 0) {
        close(pipePair[1]);
        if (dup2(pipePair[0], STDIN_FILENO) == -1) {
            _Exit(EXIT_FAILURE);
        }
        close(pipePair[0]);
        // XXX: hard-coded invocation of less.
        execlp("less", "less", "-R", static_cast<char *>(nullptr));
        _Exit(127);
    }

    out->open(io::file_descriptor_sink(pipePair[1],
                                       boost::iostreams::close_handle));
}

RedirectToPager::RedirectToPager()
    : impl(isOutputToTerminal() ? make_unique<PagerRedirect>() : nullptr)
{
}

RedirectToPager::~RedirectToPager()
{
    // Destroy impl with complete type.
}

bool
isOutputToTerminal()
{
    return isatty(fileno(stdout));
}

std::pair<unsigned int, unsigned int>
getTerminalSize()
{
    winsize ws;
    if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != 0) {
        return {
            std::numeric_limits<unsigned int>::max(),
            std::numeric_limits<unsigned int>::max()
        };
    }

    return { ws.ws_col, ws.ws_row };
}
<commit_msg>Wrap too long line in integration.cpp<commit_after>// Copyright (C) 2016 xaizek <xaizek@openmailbox.org>
//
// This file is part of uncov.
//
// uncov is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// uncov 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with uncov.  If not, see <http://www.gnu.org/licenses/>.

#include "integration.hpp"

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>

#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/scope_exit.hpp>

#include <cstdio>
#include <cstdlib>

#include <iostream>
#include <string>
#include <utility>

#include "utils/memory.hpp"

namespace io = boost::iostreams;

/**
 * @brief Base for hidden internals of RedirectToPager class.
 */
class RedirectToPager::Impl
{
public:
    //! Virtual destructor.
    virtual ~Impl() = default;
};

/**
 * @brief Redirects standard output into a pager.
 *
 * The redirection happens only if number of lines exceeds screen height,
 * otherwise lines are just dumped onto the screen as is.
 */
class PagerRedirect : public RedirectToPager::Impl
{
public:
    /**
     * @brief Custom stream buffer that spawns pager for large outputs only.
     *
     * Collect up to terminal height lines.  If buffer is closed with this limit
     * not reached, it prints lines on std::cout.  If we hit the limit in the
     * process of output, it opens a pager and feeds it all collected output and
     * everything that comes next.
     */
    class ScreenPageBuffer
    {
    public:
        //! Type of character used by this buffer.
        using char_type = char;
        //! Category of functionality provided by this buffer implementation.
        using category = boost::iostreams::sink_tag;

    public:
        /**
         * @brief Constructs the buffer.
         *
         * @param screenHeight Height of terminal in lines.
         * @param out Storage for output stream buffer backed up by a file.
         */
        ScreenPageBuffer(unsigned int screenHeight,
                         io::stream_buffer<io::file_descriptor_sink> *out);
        /**
         * @brief Dumps output onto the screen or waits for pager to finish.
         */
        ~ScreenPageBuffer();

    public:
        /**
         * @brief Writes @p n characters from @p s.
         *
         * @param s Character buffer.
         * @param n Size of the buffer.
         *
         * @returns Number of successfully written characters.
         */
        std::streamsize write(const char s[], std::streamsize n);

    private:
        /**
         * @brief Writes single character.
         *
         * @param c Character to write.
         *
         * @returns @c true on success, @c false otherwise.
         */
        bool put(char c);
        /**
         * @brief Opens pager for output.
         */
        void openPager();

    private:
        //! Whether redirection into pager is enabled.
        bool redirectToPager = false;
        //! Number of output lines collected so far.
        unsigned int nLines = 0U;
        //! Height of terminal in lines.
        unsigned int screenHeight;
        //! Output collected so far.
        std::string buffer;
        //! Process id of a pager.
        pid_t pid;

        /**
         * @brief Pointer to buffer stored in RedirectToPager.
         *
         * This is not by value, because ScreenPageBuffer needs to be copyable.
         */
        io::stream_buffer<io::file_descriptor_sink> *out;
    };

public:
    /**
     * @brief Replaces buffer of @c std::cout with ScreenPageBuffer.
     */
    PagerRedirect() : screenPageBuffer(getTerminalSize().second, &out)
    {
        rdbuf = std::cout.rdbuf(&screenPageBuffer);
    }

    /**
     * @brief Restores original buffer of @c std::cout.
     */
    ~PagerRedirect()
    {
        // Flush the stream to make sure that we put all contents we want
        // through the custom stream buffer.
        std::cout.flush();

        std::cout.rdbuf(rdbuf);
    }

private:
    //! This is stored for ScreenPageBuffer class.
    io::stream_buffer<io::file_descriptor_sink> out;
    //! Custom buffer implementation.
    io::stream_buffer<ScreenPageBuffer> screenPageBuffer;
    //! Original buffer of @c std::cout.
    std::streambuf *rdbuf;
};

using ScreenPageBuffer = PagerRedirect::ScreenPageBuffer;

ScreenPageBuffer::ScreenPageBuffer(unsigned int screenHeight,
                               io::stream_buffer<io::file_descriptor_sink> *out)
    : screenHeight(screenHeight), out(out)
{
}

ScreenPageBuffer::~ScreenPageBuffer()
{
    if (redirectToPager) {
        out->close();
        int wstatus;
        waitpid(pid, &wstatus, 0);
    } else {
        std::cout << buffer;
    }
}

std::streamsize
ScreenPageBuffer::write(const char s[], std::streamsize n)
{
    for (std::streamsize i = 0U; i < n; ++i) {
        if (!put(s[i])) {
            return i;
        }
    }
    return n;
}

bool
ScreenPageBuffer::put(char c)
{
    if (redirectToPager) {
        return boost::iostreams::put(*out, c);
    }

    if (c == '\n') {
        ++nLines;
    }

    if (nLines > screenHeight) {
        openPager();
        redirectToPager = true;
        for (char c : buffer) {
            if (!boost::iostreams::put(*out, c)) {
                return false;
            }
        }
        return boost::iostreams::put(*out, c);
    }

    buffer.push_back(c);
    return true;
}

void
ScreenPageBuffer::openPager()
{
    int pipePair[2];
    if (pipe(pipePair) != 0) {
        throw std::runtime_error("Failed to create a pipe");
    }
    BOOST_SCOPE_EXIT_ALL(pipePair) { close(pipePair[0]); };

    pid = fork();
    if (pid == -1) {
        close(pipePair[1]);
        throw std::runtime_error("Fork has failed");
    }
    if (pid == 0) {
        close(pipePair[1]);
        if (dup2(pipePair[0], STDIN_FILENO) == -1) {
            _Exit(EXIT_FAILURE);
        }
        close(pipePair[0]);
        // XXX: hard-coded invocation of less.
        execlp("less", "less", "-R", static_cast<char *>(nullptr));
        _Exit(127);
    }

    out->open(io::file_descriptor_sink(pipePair[1],
                                       boost::iostreams::close_handle));
}

RedirectToPager::RedirectToPager()
    : impl(isOutputToTerminal() ? make_unique<PagerRedirect>() : nullptr)
{
}

RedirectToPager::~RedirectToPager()
{
    // Destroy impl with complete type.
}

bool
isOutputToTerminal()
{
    return isatty(fileno(stdout));
}

std::pair<unsigned int, unsigned int>
getTerminalSize()
{
    winsize ws;
    if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != 0) {
        return {
            std::numeric_limits<unsigned int>::max(),
            std::numeric_limits<unsigned int>::max()
        };
    }

    return { ws.ws_col, ws.ws_row };
}
<|endoftext|>


<commit_before>/* Copyright (c) 2017 Brandon Pollack
* Contact @ grok3d@gmail.com
* This file is available under the MIT license included in the project
*/

#include "grok3d/grok3d.h"

#include <iostream>
#include <cmath>

using namespace Grok3d;
using namespace Grok3d::Entities;
using namespace Grok3d::Components;
using namespace Grok3d::ShaderManager;

// Normally these would be loaded from some file or something, here they're static, but I show what you would
// do if they weren't by allocating the memory anyway.
static float triangleFloats[] = {
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    0.0f, 0.5f, 0.0
};

static constexpr auto kFirstUniform = "ourColor";

class FirstUniformShader : public ShaderProgram {
 public:
  FirstUniformShader(
      const char * const vertexShader,
      const char * const fragmentShader,
      const char * const uniform) : ShaderProgram(vertexShader, fragmentShader) {
    firstUniform = static_cast<GRK_UniformID>(glGetUniformLocation(shaderProgramId, uniform));
    if (firstUniform < 0) {
      std::cout << "Error getting uniform \"" << uniform << "\" from shaders: "
                << vertexShader << " " << fragmentShader << std::endl;
      std::exit(-1);
    }
  }

  auto SetFirstUniformFloat(float r, float g, float b, float w) {
    glUniform4f(firstUniform, r, g, b, 1.0f);
  }

  auto SetFirstUniformFloat(float r, float g, float b) {
    SetFirstUniformFloat(r, g, b, 1);
  }

 private:
  GRK_UniformID firstUniform;
};

class ChangeColorBehaviour : public GRK_GameBehaviourBase {
 public:
  explicit ChangeColorBehaviour(GRK_EntityHandle entity, FirstUniformShader shader) noexcept
      : GRK_GameBehaviourBase(entity),
        renderComponent(entity.GetComponent<GRK_RenderComponent>()),
        shader(shader) {}

  auto Update(double dt) -> void override {
    time = time + dt;

    // Convert running time into sinusoid.
    float greenValue = std::sin(2 * time) / 2.0f + .5f;
    float redValue = std::cos(2*time) / 2.0f + .5f;
    float blueValue = std::rand();
    shader.SetFirstUniformFloat(redValue, greenValue, blueValue);
  }

 private:
  GRK_ComponentHandle<GRK_RenderComponent> renderComponent;
  FirstUniformShader shader;
  double time = 0;
};

// TODO clean
auto HelloChangingTriangleTest(char *args[]) -> void {
  auto engineInitialization =
      [args](GRK_EntityComponentManager &ecm) -> GRK_Result {
        auto triangleEntity = ecm.CreateEntity();

        auto vertexes = std::make_unique<float>(9);
        std::copy(triangleFloats, &triangleFloats[9], vertexes.get());

        auto shaderProgram = FirstUniformShader(args[1], args[2], kFirstUniform);

        auto rc = GRK_RenderComponent(
            std::move(vertexes),
            3,
            sizeof(float),
            GRK_GL_PrimitiveType::Unsigned_Int,
            nullptr,
            0,
            GRK_OpenGLPrimitive::GL_Triangles,
            shaderProgram.GetId());

        triangleEntity.AddComponent(std::move(rc));

        GRK_GameLogicComponent glc;
        auto changeColorBehaviour = std::make_unique<ChangeColorBehaviour>(triangleEntity, shaderProgram);
        glc.RegisterBehaviour(std::move(changeColorBehaviour));

        triangleEntity.AddComponent(std::move(glc));

        return GRK_Result::Ok;
      };

  GRK_Engine engine(engineInitialization);
  engine.Run();
};

auto main(int argc, char *argv[]) -> int {
  if (argc < 3) {
    std::cout << "Triangle test requires a vertex and frag shader passed as arguments 1 and 2" << std::endl;
    return -1;
  } else {
    HelloChangingTriangleTest(argv);
  }

  return 0;
}
<commit_msg>changed to use float for triangle colors<commit_after>/* Copyright (c) 2017 Brandon Pollack
* Contact @ grok3d@gmail.com
* This file is available under the MIT license included in the project
*/

#include "grok3d/grok3d.h"

#include <iostream>
#include <cmath>

using namespace Grok3d;
using namespace Grok3d::Entities;
using namespace Grok3d::Components;
using namespace Grok3d::ShaderManager;

// Normally these would be loaded from some file or something, here they're static, but I show what you would
// do if they weren't by allocating the memory anyway.
static float triangleFloats[] = {
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    0.0f, 0.5f, 0.0
};

static constexpr auto kFirstUniform = "ourColor";

class FirstUniformShader : public ShaderProgram {
 public:
  FirstUniformShader(
      const char * const vertexShader,
      const char * const fragmentShader,
      const char * const uniform) : ShaderProgram(vertexShader, fragmentShader) {
    firstUniform = static_cast<GRK_UniformID>(glGetUniformLocation(shaderProgramId, uniform));
    if (firstUniform < 0) {
      std::cout << "Error getting uniform \"" << uniform << "\" from shaders: "
                << vertexShader << " " << fragmentShader << std::endl;
      std::exit(-1);
    }
  }

  auto SetFirstUniformFloat(float r, float g, float b, float w) {
    glUniform4f(firstUniform, r, g, b, 1.0f);
  }

  auto SetFirstUniformFloat(float r, float g, float b) {
    SetFirstUniformFloat(r, g, b, 1);
  }

 private:
  GRK_UniformID firstUniform;
};

class ChangeColorBehaviour : public GRK_GameBehaviourBase {
 public:
  explicit ChangeColorBehaviour(GRK_EntityHandle entity, FirstUniformShader shader) noexcept
      : GRK_GameBehaviourBase(entity),
        renderComponent(entity.GetComponent<GRK_RenderComponent>()),
        shader(shader) {}

  auto Update(double dt) -> void override {
    time = time + dt;

    // Convert running time into sinusoid.
    float greenValue = std::sin(2 * time) / 2.0f + .5f;
    float redValue = std::cos(2*time) / 2.0f + .5f;
    float blueValue = static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX);
    shader.SetFirstUniformFloat(redValue, greenValue, blueValue);
  }

 private:
  GRK_ComponentHandle<GRK_RenderComponent> renderComponent;
  FirstUniformShader shader;
  double time = 0;
};

// TODO clean
auto HelloChangingTriangleTest(char *args[]) -> void {
  auto engineInitialization =
      [args](GRK_EntityComponentManager &ecm) -> GRK_Result {
        auto triangleEntity = ecm.CreateEntity();

        auto vertexes = std::make_unique<float>(9);
        std::copy(triangleFloats, &triangleFloats[9], vertexes.get());

        auto shaderProgram = FirstUniformShader(args[1], args[2], kFirstUniform);

        auto rc = GRK_RenderComponent(
            std::move(vertexes),
            3,
            sizeof(float),
            GRK_GL_PrimitiveType::Unsigned_Int,
            nullptr,
            0,
            GRK_OpenGLPrimitive::GL_Triangles,
            shaderProgram.GetId());

        triangleEntity.AddComponent(std::move(rc));

        GRK_GameLogicComponent glc;
        auto changeColorBehaviour = std::make_unique<ChangeColorBehaviour>(triangleEntity, shaderProgram);
        glc.RegisterBehaviour(std::move(changeColorBehaviour));

        triangleEntity.AddComponent(std::move(glc));

        return GRK_Result::Ok;
      };

  GRK_Engine engine(engineInitialization);
  engine.Run();
};

auto main(int argc, char *argv[]) -> int {
  if (argc < 3) {
    std::cout << "Triangle test requires a vertex and frag shader passed as arguments 1 and 2" << std::endl;
    return -1;
  } else {
    HelloChangingTriangleTest(argv);
  }

  return 0;
}
<|endoftext|>


<commit_before>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013, 2014, 2015
              Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include "MeshVisualizer.h"

#include <Corrade/Utility/Resource.h>

#include "Magnum/Context.h"
#include "Magnum/Extensions.h"
#include "Magnum/Shader.h"
#include "MagnumExternal/Optional/optional.hpp"

#include "Implementation/CreateCompatibilityShader.h"

namespace Magnum { namespace Shaders {

MeshVisualizer::MeshVisualizer(const Flags flags): flags(flags), transformationProjectionMatrixUniform(0), viewportSizeUniform(1), colorUniform(2), wireframeColorUniform(3), wireframeWidthUniform(4), smoothnessUniform(5) {
    #ifndef MAGNUM_TARGET_GLES2
    if(flags & Flag::Wireframe && !(flags & Flag::NoGeometryShader)) {
        #ifndef MAGNUM_TARGET_GLES
        MAGNUM_ASSERT_VERSION_SUPPORTED(Version::GL320);
        MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::geometry_shader4);
        #else
        MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::EXT::geometry_shader);
        #endif
    }
    #else
    if(flags & Flag::Wireframe)
        MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::OES::standard_derivatives);
    #endif

    #ifdef MAGNUM_BUILD_STATIC
    /* Import resources on static build, if not already */
    if(!Utility::Resource::hasGroup("MagnumShaders"))
        importShaderResources();
    #endif
    Utility::Resource rs("MagnumShaders");

    #ifndef MAGNUM_TARGET_GLES
    const Version version = Context::current()->supportedVersion({Version::GL320, Version::GL310, Version::GL300, Version::GL210});
    CORRADE_INTERNAL_ASSERT(!flags || flags & Flag::NoGeometryShader || version >= Version::GL320);
    #else
    const Version version = Context::current()->supportedVersion({Version::GLES310, Version::GLES300, Version::GLES200});
    CORRADE_INTERNAL_ASSERT(!flags || flags & Flag::NoGeometryShader || version >= Version::GLES310);
    #endif

    Shader vert = Implementation::createCompatibilityShader(rs, version, Shader::Type::Vertex);
    Shader frag = Implementation::createCompatibilityShader(rs, version, Shader::Type::Fragment);

    vert.addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "")
        .addSource(flags & Flag::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "")
        #ifdef MAGNUM_TARGET_WEBGL
        .addSource("#define SUBSCRIPTING_WORKAROUND\n")
        #elif defined(MAGNUM_TARGET_GLES2)
        .addSource(Context::current()->detectedDriver() & Context::DetectedDriver::ProbablyAngle ?
            "#define SUBSCRIPTING_WORKAROUND\n" : "")
        #endif
        .addSource(rs.get("generic.glsl"))
        .addSource(rs.get("MeshVisualizer.vert"));
    frag.addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "")
        .addSource(flags & Flag::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "")
        .addSource(rs.get("MeshVisualizer.frag"));

    #ifndef MAGNUM_TARGET_GLES2
    std::optional<Shader> geom;
    if(flags & Flag::Wireframe && !(flags & Flag::NoGeometryShader)) {
        geom = Implementation::createCompatibilityShader(rs, version, Shader::Type::Geometry);
        geom->addSource(rs.get("MeshVisualizer.geom"));
    }
    #endif

    #ifndef MAGNUM_TARGET_GLES2
    if(geom) CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, *geom, frag}));
    else
    #endif
        CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));

    attachShaders({vert, frag});
    #ifndef MAGNUM_TARGET_GLES2
    if(geom) attachShader(*geom);
    #endif

    #ifndef MAGNUM_TARGET_GLES
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::explicit_attrib_location>(version))
    #else
    if(!Context::current()->isVersionSupported(Version::GLES300))
    #endif
    {
        bindAttributeLocation(Position::Location, "position");

        #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2)
        #ifndef MAGNUM_TARGET_GLES
        if(!Context::current()->isVersionSupported(Version::GL310))
        #endif
        {
            bindAttributeLocation(VertexIndex::Location, "vertexIndex");
        }
        #endif
    }

    CORRADE_INTERNAL_ASSERT_OUTPUT(link());

    #ifndef MAGNUM_TARGET_GLES
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::explicit_uniform_location>(version))
    #endif
    {
        transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix");
        colorUniform = uniformLocation("color");
        if(flags & Flag::Wireframe) {
            wireframeColorUniform = uniformLocation("wireframeColor");
            wireframeWidthUniform = uniformLocation("wireframeWidth");
            smoothnessUniform = uniformLocation("smoothness");
            if(!(flags & Flag::NoGeometryShader))
                viewportSizeUniform = uniformLocation("viewportSize");
        }
    }

    /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */
    #ifdef MAGNUM_TARGET_GLES
    setColor(Color3(1.0f));
    if(flags & Flag::Wireframe) {
        setWireframeColor(Color3(0.0f));
        setWireframeWidth(1.0f);
        setSmoothness(2.0f);
    }
    #endif
}

}}
<commit_msg>Shaders: fix MeshVisualizer compilation on WebGL.<commit_after>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013, 2014, 2015
              Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include "MeshVisualizer.h"

#include <Corrade/Utility/Resource.h>

#include "Magnum/Context.h"
#include "Magnum/Extensions.h"
#include "Magnum/Shader.h"
#include "MagnumExternal/Optional/optional.hpp"

#include "Implementation/CreateCompatibilityShader.h"

namespace Magnum { namespace Shaders {

MeshVisualizer::MeshVisualizer(const Flags flags): flags(flags), transformationProjectionMatrixUniform(0), viewportSizeUniform(1), colorUniform(2), wireframeColorUniform(3), wireframeWidthUniform(4), smoothnessUniform(5) {
    #ifndef MAGNUM_TARGET_GLES2
    if(flags & Flag::Wireframe && !(flags & Flag::NoGeometryShader)) {
        #ifndef MAGNUM_TARGET_GLES
        MAGNUM_ASSERT_VERSION_SUPPORTED(Version::GL320);
        MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::geometry_shader4);
        #else
        MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::EXT::geometry_shader);
        #endif
    }
    #else
    if(flags & Flag::Wireframe)
        MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::OES::standard_derivatives);
    #endif

    #ifdef MAGNUM_BUILD_STATIC
    /* Import resources on static build, if not already */
    if(!Utility::Resource::hasGroup("MagnumShaders"))
        importShaderResources();
    #endif
    Utility::Resource rs("MagnumShaders");

    #ifndef MAGNUM_TARGET_GLES
    const Version version = Context::current()->supportedVersion({Version::GL320, Version::GL310, Version::GL300, Version::GL210});
    CORRADE_INTERNAL_ASSERT(!flags || flags & Flag::NoGeometryShader || version >= Version::GL320);
    #elif !defined(MAGNUM_TARGET_WEBGL)
    const Version version = Context::current()->supportedVersion({Version::GLES310, Version::GLES300, Version::GLES200});
    CORRADE_INTERNAL_ASSERT(!flags || flags & Flag::NoGeometryShader || version >= Version::GLES310);
    #else
    const Version version = Context::current()->supportedVersion({Version::GLES300, Version::GLES200});
    #endif

    Shader vert = Implementation::createCompatibilityShader(rs, version, Shader::Type::Vertex);
    Shader frag = Implementation::createCompatibilityShader(rs, version, Shader::Type::Fragment);

    vert.addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "")
        .addSource(flags & Flag::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "")
        #ifdef MAGNUM_TARGET_WEBGL
        .addSource("#define SUBSCRIPTING_WORKAROUND\n")
        #elif defined(MAGNUM_TARGET_GLES2)
        .addSource(Context::current()->detectedDriver() & Context::DetectedDriver::ProbablyAngle ?
            "#define SUBSCRIPTING_WORKAROUND\n" : "")
        #endif
        .addSource(rs.get("generic.glsl"))
        .addSource(rs.get("MeshVisualizer.vert"));
    frag.addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "")
        .addSource(flags & Flag::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "")
        .addSource(rs.get("MeshVisualizer.frag"));

    #ifndef MAGNUM_TARGET_GLES2
    std::optional<Shader> geom;
    if(flags & Flag::Wireframe && !(flags & Flag::NoGeometryShader)) {
        geom = Implementation::createCompatibilityShader(rs, version, Shader::Type::Geometry);
        geom->addSource(rs.get("MeshVisualizer.geom"));
    }
    #endif

    #ifndef MAGNUM_TARGET_GLES2
    if(geom) CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, *geom, frag}));
    else
    #endif
        CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));

    attachShaders({vert, frag});
    #ifndef MAGNUM_TARGET_GLES2
    if(geom) attachShader(*geom);
    #endif

    #ifndef MAGNUM_TARGET_GLES
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::explicit_attrib_location>(version))
    #else
    if(!Context::current()->isVersionSupported(Version::GLES300))
    #endif
    {
        bindAttributeLocation(Position::Location, "position");

        #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2)
        #ifndef MAGNUM_TARGET_GLES
        if(!Context::current()->isVersionSupported(Version::GL310))
        #endif
        {
            bindAttributeLocation(VertexIndex::Location, "vertexIndex");
        }
        #endif
    }

    CORRADE_INTERNAL_ASSERT_OUTPUT(link());

    #ifndef MAGNUM_TARGET_GLES
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::explicit_uniform_location>(version))
    #endif
    {
        transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix");
        colorUniform = uniformLocation("color");
        if(flags & Flag::Wireframe) {
            wireframeColorUniform = uniformLocation("wireframeColor");
            wireframeWidthUniform = uniformLocation("wireframeWidth");
            smoothnessUniform = uniformLocation("smoothness");
            if(!(flags & Flag::NoGeometryShader))
                viewportSizeUniform = uniformLocation("viewportSize");
        }
    }

    /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */
    #ifdef MAGNUM_TARGET_GLES
    setColor(Color3(1.0f));
    if(flags & Flag::Wireframe) {
        setWireframeColor(Color3(0.0f));
        setWireframeWidth(1.0f);
        setSmoothness(2.0f);
    }
    #endif
}

}}
<|endoftext|>