Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/extensions/source/scanner/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 50 kB image not shown  

Quelle  sanedlg.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <stdlib.h>
#include <o3tl/sprintf.hxx>
#include <tools/config.hxx>
#include <unotools/resmgr.hxx>
#include <vcl/bitmapex.hxx>
#include <vcl/customweld.hxx>
#include <vcl/dibtools.hxx>
#include <vcl/lineinfo.hxx>
#include <vcl/weld.hxx>
#include <vcl/svapp.hxx>
#include <vcl/event.hxx>
#include "sanedlg.hxx"
#include "grid.hxx"
#include <math.h>
#include <sal/macros.h>
#include <sal/log.hxx>
#include <rtl/strbuf.hxx>
#include <memory>
#include <strings.hrc>

#define PREVIEW_WIDTH       113
#define PREVIEW_HEIGHT      160

#define RECT_SIZE_PIX 7

namespace {

void DrawRect(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    tools::Rectangle aRect(rRect);
    rRenderContext.SetFillColor(COL_BLACK);
    rRenderContext.SetLineColor();
    rRenderContext.DrawRect(aRect);
    aRect.Move(1, 1);
    aRect.AdjustRight(-2);
    aRect.AdjustBottom(-2);
    rRenderContext.SetFillColor();
    rRenderContext.SetLineColor(COL_WHITE);
    rRenderContext.DrawRect(aRect);
}

void DrawRectangles(vcl::RenderContext& rRenderContext, Point const & rUL, Poinconst & rBR)
{
    Point aUR(rBR.X(), rUL.Y());
    Point aBL(rUL.X(), rBR.Y());
    int nMiddleX = (rBR.X() - rUL.X()) / 2 + rUL.X();
    int nMiddleY = (rBR.Y() - rUL.Y()) / 2 + rUL.Y();

    rRenderContext.SetLineColor(COL_WHITE);
    rRenderContext.DrawLine(rUL, aBL);
    rRenderContext.DrawLine(aBL, rBR);
    rRenderContext.DrawLine(rBR, aUR);
    rRenderContext.DrawLine(aUR, rUL);

    rRenderContext.SetLineColor(COL_BLACK);
    LineInfo aInfo(LineStyle::Dash, 1);
    aInfo.SetDistance(8);
    aInfo.SetDotLen(4);
    aInfo.SetDotCount(1);
    rRenderContext.DrawLine(rUL, aBL, aInfo);
    rRenderContext.DrawLine(aBL, rBR, aInfo);
    rRenderContext.DrawLine(rBR, aUR, aInfo);
    rRenderContext.DrawLine(aUR, rUL, aInfo);

    Size aSize(RECT_SIZE_PIX, RECT_SIZE_PIX);
    DrawRect(rRenderContext, tools::Rectangle(rUL, aSize));
    DrawRect(rRenderContext, tools::Rectangle(Point(aBL.X(), aBL.Y() - RECT_SIZE_PIX), aSize));
    DrawRect(rRenderContext, tools::Rectangle(Point(rBR.X() - RECT_SIZE_PIX, rBR.Y() - RECT_SIZE_PIX), aSize));
    DrawRect(rRenderContext, tools::Rectangle(Point(aUR.X() - RECT_SIZE_PIX, aUR.Y()), aSize));
    DrawRect(rRenderContext, tools::Rectangle(Point(nMiddleX - RECT_SIZE_PIX / 2, rUL.Y()), aSize));
    DrawRect(rRenderContext, tools::Rectangle(Point(nMiddleX - RECT_SIZE_PIX / 2, rBR.Y() - RECT_SIZE_PIX), aSize));
    DrawRect(rRenderContext, tools::Rectangle(Point(rUL.X(), nMiddleY - RECT_SIZE_PIX / 2), aSize));
    DrawRect(rRenderContext, tools::Rectangle(Point(rBR.X() - RECT_SIZE_PIX, nMiddleY - RECT_SIZE_PIX / 2), aSize));
}

}

class ScanPreview : public weld::CustomWidgetController
{
private:
    enum DragDirection { TopLeft, Top, TopRight, Right, BottomRight, Bottom,
                         BottomLeft, Left };

    BitmapEx  maPreviewBitmapEx;
    tools::Rectangle maPreviewRect;
    Point     maTopLeft, maBottomRight;
    Point     maMinTopLeft, maMaxBottomRight;
    SaneDlg*  mpParentDialog;
    DragDirection meDragDirection;
    bool      mbDragEnable;
    bool      mbIsDragging;

public:
    ScanPreview()
        : maMaxBottomRight(PREVIEW_WIDTH,  PREVIEW_HEIGHT)
        , mpParentDialog(nullptr)
        , meDragDirection(TopLeft)
        , mbDragEnable(false)
        , mbIsDragging(false)
    {
    }

    void Init(SaneDlg *pParent)
    {
        mpParentDialog = pParent;
    }

    void ResetForNewScanner()
    {
        maTopLeft = Point();
        maBottomRight = Point();
        maMinTopLeft = Point();
        maMaxBottomRight = Point(PREVIEW_WIDTH,  PREVIEW_HEIGHT);
    }

    void EnableDrag()
    {
        mbDragEnable = true;
    }

    void DisableDrag()
    {
        mbDragEnable = false;
    }

    bool IsDragEnabled() const
    {
        return mbDragEnable;
    }

    virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& ;rRect) override;
    virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
    virtual bool MouseMove(const MouseEvent& rMEvt) override;
    virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
    Point GetPixelPos(const Point& rIn) const;
    Point GetLogicPos(const Point& rIn) const;

    void GetPreviewLogicRect(Point& rTopLeft, Point &rBottomRight) const
    {
        rTopLeft = GetLogicPos(maTopLeft);
        rBottomRight = GetLogicPos(maBottomRight);
    }
    void GetMaxLogicRect(Point& rTopLeft, Point &rBottomRight) const
    {
        rTopLeft = maMinTopLeft;
        rBottomRight = maMaxBottomRight;

    }
    void ChangePreviewLogicTopLeftY(tools::Long Y)
    {
        Point aPoint(0, Y);
        aPoint = GetPixelPos(aPoint);
        maTopLeft.setY( aPoint.Y() );
    }
    void ChangePreviewLogicTopLeftX(tools::Long X)
    {
        Point aPoint(X, 0);
        aPoint = GetPixelPos(aPoint);
        maTopLeft.setX( aPoint.X() );
    }
    void ChangePreviewLogicBottomRightY(tools::Long Y)
    {
        Point aPoint(0, Y);
        aPoint = GetPixelPos(aPoint);
        maBottomRight.setY( aPoint.Y() );
    }
    void ChangePreviewLogicBottomRightX(tools::Long X)
    {
        Point aPoint(X, 0);
        aPoint = GetPixelPos(aPoint);
        maBottomRight.setX( aPoint.X() );
    }
    void SetPreviewLogicRect(const Point& rTopLeft, const Point &rBottomRight)
    {
        maTopLeft = GetPixelPos(rTopLeft);
        maBottomRight = GetPixelPos(rBottomRight);
        maPreviewRect = tools::Rectangle(maTopLeft,
                                  Size(maBottomRight.X() - maTopLeft.X(),
                                       maBottomRight.Y() - maTopLeft.Y()));
    }
    void SetPreviewMaxRect(const Point& rTopLeft, const Point &rBottomRight)
    {
        maMinTopLeft = rTopLeft;
        maMaxBottomRight = rBottomRight;
    }
    void DrawDrag(vcl::RenderContext& rRenderContext);
    void UpdatePreviewBounds();
    void SetBitmap(SvStream &rStream)
    {
        ReadDIBBitmapEx(maPreviewBitmapEx, rStream, true);
    }
    virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override
    {
        Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(PREVIEW_WIDTH, PREVIEW_HEIGHT), MapMode(MapUnit::MapAppFont)));
        aSize.setWidth(aSize.getWidth()+1);
        aSize.setHeight(aSize.getHeight()+1);
        pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
        CustomWidgetController::SetDrawingArea(pDrawingArea);
        SetOutputSizePixel(aSize);
    }
};

SaneDlg::SaneDlg(weld::Window* pParent, Sane& rSane, bool bScanEnabled)
    : GenericDialogController(pParent, u"modules/scanner/ui/sanedialog.ui"_ustr, u"SaneDialog"_ustr)
    , mpParent(pParent)
    , mrSane(rSane)
    , mbScanEnabled(bScanEnabled)
    , mnCurrentOption(0)
    , mnCurrentElement(0)
    , mfMin(0.0)
    , mfMax(0.0)
    , doScan(false)
    , mxCancelButton(m_xBuilder->weld_button(u"cancel"_ustr))
    , mxDeviceInfoButton(m_xBuilder->weld_button(u"deviceInfoButton"_ustr))
    , mxPreviewButton(m_xBuilder->weld_button(u"previewButton"_ustr))
    , mxScanButton(m_xBuilder->weld_button(u"ok"_ustr))
    , mxButtonOption(m_xBuilder->weld_button(u"optionsButton"_ustr))
    , mxOptionTitle(m_xBuilder->weld_label(u"optionTitleLabel"_ustr))
    , mxOptionDescTxt(m_xBuilder->weld_label(u"optionsDescLabel"_ustr))
    , mxVectorTxt(m_xBuilder->weld_label(u"vectorLabel"_ustr))
    , mxLeftField(m_xBuilder->weld_metric_spin_button(u"leftSpinbutton"_ustr, FieldUnit::PIXEL))
    , mxTopField(m_xBuilder->weld_metric_spin_button(u"topSpinbutton"_ustr, FieldUnit::PIXEL))
    , mxRightField(m_xBuilder->weld_metric_spin_button(u"rightSpinbutton"_ustr, FieldUnit::PIXEL))
    , mxBottomField(m_xBuilder->weld_metric_spin_button(u"bottomSpinbutton"_ustr, FieldUnit::PIXEL))
    , mxDeviceBox(m_xBuilder->weld_combo_box(u"deviceCombobox"_ustr))
    , mxReslBox(m_xBuilder->weld_combo_box(u"reslCombobox"_ustr))
    , mxAdvancedBox(m_xBuilder->weld_check_button(u"advancedCheckbutton"_ustr))
    , mxVectorBox(m_xBuilder->weld_spin_button(u"vectorSpinbutton"_ustr))
    , mxQuantumRangeBox(m_xBuilder->weld_combo_box(u"quantumRangeCombobox"_ustr))
    , mxStringRangeBox(m_xBuilder->weld_combo_box(u"stringRangeCombobox"_ustr))
    , mxBoolCheckBox(m_xBuilder->weld_check_button(u"boolCheckbutton"_ustr))
    , mxStringEdit(m_xBuilder->weld_entry(u"stringEntry"_ustr))
    , mxNumericEdit(m_xBuilder->weld_entry(u"numericEntry"_ustr))
    , mxOptionBox(m_xBuilder->weld_tree_view(u"optionSvTreeListBox"_ustr))
    , mxPreview(new ScanPreview)
    , mxPreviewWnd(new weld::CustomWeld(*m_xBuilder, u"preview"_ustr, *mxPreview))
{
    Size aSize(mxOptionBox->get_approximate_digit_width() * 32, mxOptionBox->get_height_rows(8));
    mxOptionTitle->set_size_request(aSize.Width(), aSize.Height() / 2);
    mxOptionBox->set_size_request(aSize.Width(), aSize.Height());
    mxPreview->Init(this);
    if( Sane::IsSane() )
    {
        InitDevices(); // opens first sane device
        DisableOption();
        InitFields();
    }

    mxDeviceInfoButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
    mxPreviewButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
    mxScanButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
    mxButtonOption->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
    mxDeviceBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
    mxOptionBox->connect_selection_changed(LINK(this, SaneDlg, OptionsBoxSelectHdl));
    mxCancelButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
    mxBoolCheckBox->connect_toggled( LINK( this, SaneDlg, ToggleBtnHdl ) );
    mxStringEdit->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
    mxNumericEdit->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
    mxVectorBox->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
    mxReslBox->connect_changed( LINK( this, SaneDlg, ValueModifyHdl ) );
    mxStringRangeBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
    mxQuantumRangeBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
    mxLeftField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl ) );
    mxRightField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
    mxTopField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
    mxBottomField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
    mxAdvancedBox->connect_toggled( LINK( this, SaneDlg, ToggleBtnHdl ) );

    maOldLink = mrSane.SetReloadOptionsHdl( LINK( this, SaneDlg, ReloadSaneOptionsHdl ) );
}

SaneDlg::~SaneDlg()
{
    mrSane.SetReloadOptionsHdl(maOldLink);
}

namespace {

OUString SaneResId(TranslateId aID)
{
    return Translate::get(aID, Translate::Create("pcr"));
}

}

short SaneDlg::run()
{
    if (!Sane::IsSane())
    {
        std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(mpParent,
                                                       VclMessageType::Warning, VclButtonsType::Ok,
                                                       SaneResId(STR_COULD_NOT_BE_INIT)));
        xErrorBox->run();
        return RET_CANCEL;
    }
    LoadState();
    return GenericDialogController::run();
}

void SaneDlg::InitDevices()
{
    if( ! Sane::IsSane() )
        return;

    if( mrSane.IsOpen() )
        mrSane.Close();
    mrSane.ReloadDevices();
    mxDeviceBox->clear();
    for (int i = 0; i < Sane::CountDevices(); ++i)
        mxDeviceBox->append_text(Sane::GetName(i));
    if( Sane::CountDevices() )
    {
        mrSane.Open(0);
        mxDeviceBox->set_active(0);
    }
}

void SaneDlg::InitFields()
{
    if( ! Sane::IsSane() )
        return;

    int nOption, i, nValue;
    double fValue;
    const char *ppSpecialOptions[] = {
        "resolution",
        "tl-x",
        "tl-y",
        "br-x",
        "br-y",
        "preview"
    };

    mxPreview->EnableDrag();
    mxReslBox->clear();
    Point aTopLeft, aBottomRight;
    mxPreview->GetPreviewLogicRect(aTopLeft, aBottomRight);
    Point aMinTopLeft, aMaxBottomRight;
    mxPreview->GetMaxLogicRect(aMinTopLeft, aMaxBottomRight);
    mxScanButton->set_visible( mbScanEnabled );

    if( ! mrSane.IsOpen() )
        return;

    // set Resolution
    nOption = mrSane.GetOptionByName( "resolution" );
    if( nOption != -1 )
    {
        double fRes;

        if( mrSane.GetOptionValue( nOption, fRes ) )
        {
            mxReslBox->set_sensitive(true);

            mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fRes)));
            std::unique_ptr<double[]> pDouble;
            nValue = mrSane.GetRange( nOption, pDouble );
            if( nValue > -1 )
            {
                assert(pDouble);
                if( nValue )
                {
                    for( i=0; i<nValue; i++ )
                    {
                        if( i == 0 || i == nValue-1 || ! ( static_cast<int>(pDouble[i]) % 20) )
                            mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[i])));
                    }
                }
                else
                {
                    mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[0])));
                    // Can only select 75 and 2400 dpi in Scanner dialogue
                    // scanner allows random setting of dpi resolution, a slider might be useful
                    // support that
                    // workaround: offer at least some more standard dpi resolution between
                    // min and max value
                    int bGot300 = 0;
                    for (sal_uInt32 nRes = static_cast<sal_uInt32>(pDouble[0]) * 2; nRes < static_cast<sal_uInt32>(pDouble[1]); nRes = nRes * 2)
                    {
                        if ( !bGot300 && nRes > 300 ) {
                            nRes = 300; bGot300 = 1;
                        }
                        mxReslBox->append_text(OUString::number(nRes));
                    }
                    mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[1])));
                }
            }
            else
                mxReslBox->set_sensitive( false );
        }
    }
    else
        mxReslBox->set_sensitive( false );

    // set scan area
    for( i = 0; i < 4; i++ )
    {
        char const *pOptionName = nullptr;
        weld::MetricSpinButton* pField = nullptr;
        switch( i )
        {
            case 0:
                pOptionName = "tl-x";
                pField = mxLeftField.get();
                break;
            case 1:
                pOptionName = "tl-y";
                pField = mxTopField.get();
                break;
            case 2:
                pOptionName = "br-x";
                pField = mxRightField.get();
                break;
            case 3:
                pOptionName = "br-y";
                pField = mxBottomField.get();
        }
        nOption = pOptionName ? mrSane.GetOptionByName( pOptionName ) : -1;
        if( nOption != -1 )
        {
            if( mrSane.GetOptionValue( nOption, fValue ) )
            {
                if( mrSane.GetOptionUnit( nOption ) == SANE_UNIT_MM )
                {
                    pField->set_unit( FieldUnit::MM );
                    pField->set_value( static_cast<int>(fValue), FieldUnit::MM );
                }
                else // SANE_UNIT_PIXEL
                {
                    pField->set_unit( FieldUnit::PIXEL );
                    pField->set_value( static_cast<int>(fValue), FieldUnit::PIXEL );
                }
                switch( i ) {
                    case 0: aTopLeft.setX( static_cast<int>(fValue) );break;
                    case 1: aTopLeft.setY( static_cast<int>(fValue) );break;
                    case 2: aBottomRight.setX( static_cast<int>(fValue) );break;
                    case 3: aBottomRight.setY( static_cast<int>(fValue) );break;
                }
            }
            std::unique_ptr<double[]> pDouble;
            nValue = mrSane.GetRange( nOption, pDouble );
            if( nValue > -1 )
            {
                if( pDouble )
                {
                    pField->set_min( static_cast<tools::Long>(pDouble[0]), FieldUnit::NONE );
                    if( nValue )
                        pField->set_max( static_cast<tools::Long>(pDouble[ nValue-1 ]), FieldUnit::NONE );
                    else
                        pField->set_max( static_cast<tools::Long>(pDouble[ 1 ]), FieldUnit::NONE );
                }
                switch( i ) {
                    case 0: aMinTopLeft.setX( pField->get_min(FieldUnit::NONE) );break;
                    case 1: aMinTopLeft.setY( pField->get_min(FieldUnit::NONE) );break;
                    case 2: aMaxBottomRight.setX( pField->get_max(FieldUnit::NONE) );break;
                    case 3: aMaxBottomRight.setY( pField->get_max(FieldUnit::NONE) );break;
                }
            }
            else
            {
                switch( i ) {
                    case 0: aMinTopLeft.setX( static_cast<int>(fValue) );break;
                    case 1: aMinTopLeft.setY( static_cast<int>(fValue) );break;
                    case 2: aMaxBottomRight.setX( static_cast<int>(fValue) );break;
                    case 3: aMaxBottomRight.setY( static_cast<int>(fValue) );break;
                }
            }
            pField->set_sensitive(true);
        }
        else
        {
            mxPreview->DisableDrag();
            pField->set_min( 0, FieldUnit::NONE );
            switch( i ) {
                case 0:
                    aMinTopLeft.setX( 0 );
                    aTopLeft.setX( 0 );
                    pField->set_max( PREVIEW_WIDTH, FieldUnit::NONE );
                    pField->set_value( 0, FieldUnit::NONE );
                    break;
                case 1:
                    aMinTopLeft.setY( 0 );
                    aTopLeft.setY( 0 );
                    pField->set_max( PREVIEW_HEIGHT, FieldUnit::NONE );
                    pField->set_value( 0, FieldUnit::NONE );
                    break;
                case 2:
                    aMaxBottomRight.setX( PREVIEW_WIDTH );
                    aBottomRight.setX( PREVIEW_WIDTH );
                    pField->set_max( PREVIEW_WIDTH, FieldUnit::NONE );
                    pField->set_value( PREVIEW_WIDTH, FieldUnit::NONE );
                    break;
                case 3:
                    aMaxBottomRight.setY( PREVIEW_HEIGHT );
                    aBottomRight.setY( PREVIEW_HEIGHT );
                    pField->set_max( PREVIEW_HEIGHT, FieldUnit::NONE );
                    pField->set_value( PREVIEW_HEIGHT, FieldUnit::NONE );
                    break;
            }
            pField->set_sensitive(false);
        }
    }

    mxPreview->SetPreviewMaxRect(aMinTopLeft, aMaxBottomRight);
    mxPreview->SetPreviewLogicRect(aTopLeft, aBottomRight);
    mxPreview->Invalidate();

    // fill OptionBox
    mxOptionBox->clear();
    std::unique_ptr<weld::TreeIter> xParentEntry(mxOptionBox->make_iterator());
    bool bGroupRejected = false;
    for( i = 1; i < mrSane.CountOptions(); i++ )
    {
        OUString aOption=mrSane.GetOptionName( i );
        bool bInsertAdvanced =
            (mrSane.GetOptionCap( i ) & SANE_CAP_ADVANCED) == 0 ||
            mxAdvancedBox->get_active();
        if( mrSane.GetOptionType( i ) == SANE_TYPE_GROUP )
        {
            if( bInsertAdvanced )
            {
                aOption = mrSane.GetOptionTitle( i );
                mxOptionBox->append(xParentEntry.get());
                mxOptionBox->set_text(*xParentEntry, aOption, 0);
                bGroupRejected = false;
            }
            else
                bGroupRejected = true;
        }
        else if( !aOption.isEmpty() &&
                 ! ( mrSane.GetOptionCap( i ) &
                     (
                         SANE_CAP_HARD_SELECT |
                         SANE_CAP_INACTIVE
                         ) ) &&
                 bInsertAdvanced && ! bGroupRejected )
        {
            bool bIsSpecial = false;
            for( size_t n = 0; !bIsSpecial &&
                     n < SAL_N_ELEMENTS(ppSpecialOptions); n++ )
            {
                if( aOption == OUString::createFromAscii(ppSpecialOptions[n]) )
                    bIsSpecial=true;
            }
            if( ! bIsSpecial )
            {
                if (xParentEntry)
                    mxOptionBox->append(xParentEntry.get(), aOption);
                else
                    mxOptionBox->append_text(aOption);
            }
        }
    }
}

IMPL_LINK( SaneDlg, ClickBtnHdl, weld::Button&, rButton, void )
{
    if( mrSane.IsOpen() )
    {
        if( &rButton == mxDeviceInfoButton.get() )
        {
            OUString aString(SaneResId(STR_DEVICE_DESC));
            aString = aString.replaceFirst( "%s", Sane::GetName( mrSane.GetDeviceNumber() ) );
            aString = aString.replaceFirst( "%s", Sane::GetVendor( mrSane.GetDeviceNumber() ) );
            aString = aString.replaceFirst( "%s", Sane::GetModel( mrSane.GetDeviceNumber() ) );
            aString = aString.replaceFirst( "%s", Sane::GetType( mrSane.GetDeviceNumber() ) );
            std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
                                                          VclMessageType::Info, VclButtonsType::Ok,
                                                          aString));
            xInfoBox->run();
        }
        else if( &rButton == mxPreviewButton.get() )
            AcquirePreview();
        else if( &rButton == mxButtonOption.get() )
        {

            SANE_Value_Type nType = mrSane.GetOptionType( mnCurrentOption );
            switch( nType )
            {
                case SANE_TYPE_BUTTON:
                    mrSane.ActivateButtonOption( mnCurrentOption );
                    break;
                case SANE_TYPE_FIXED:
                case SANE_TYPE_INT:
                {
                    int nElements = mrSane.GetOptionElements( mnCurrentOption );
                    std::unique_ptr<double[]> x(new double[ nElements ]);
                    std::unique_ptr<double[]> y(new double[ nElements ]);
                    forint i = 0; i < nElements; i++ )
                        x[ i ] = static_cast<double>(i);
                    mrSane.GetOptionValue( mnCurrentOption, y.get() );

                    GridDialog aGrid(m_xDialog.get(), x.get(), y.get(), nElements);
                    aGrid.set_title( mrSane.GetOptionName( mnCurrentOption ) );
                    aGrid.setBoundings( 0, mfMin, nElements, mfMax );
                    if (aGrid.run() && aGrid.getNewYValues())
                        mrSane.SetOptionValue( mnCurrentOption, aGrid.getNewYValues() );
                }
                break;
                case SANE_TYPE_BOOL:
                case SANE_TYPE_STRING:
                case SANE_TYPE_GROUP:
                    break;
            }
        }
    }
    if (&rButton == mxScanButton.get())
    {
        double fRes = static_cast<double>(mxReslBox->get_active_text().toUInt32());
        SetAdjustedNumericalValue( "resolution", fRes );
        UpdateScanArea(true);
        SaveState();
        m_xDialog->response(mrSane.IsOpen() ? RET_OK : RET_CANCEL);
        doScan = mrSane.IsOpen();
    }
    else if( &rButton == mxCancelButton.get() )
    {
        mrSane.Close();
        m_xDialog->response(RET_CANCEL);
    }
}

IMPL_LINK( SaneDlg, ToggleBtnHdl, weld::Toggleable&, rButton, void )
{
    if( mrSane.IsOpen() )
    {
        if( &rButton == mxBoolCheckBox.get() )
        {
            mrSane.SetOptionValue( mnCurrentOption,
                                   mxBoolCheckBox->get_active() );
        }
        else if( &rButton == mxAdvancedBox.get() )
        {
            ReloadSaneOptionsHdl( mrSane );
        }
    }
}

IMPL_LINK( SaneDlg, SelectHdl, weld::ComboBox&, rListBox, void )
{
    if( &rListBox == mxDeviceBox.get() && Sane::IsSane() && Sane::CountDevices() )
    {
        int nNewNumber = mxDeviceBox->get_active();
        int nOldNumber = mrSane.GetDeviceNumber();
        if (nNewNumber != nOldNumber)
        {
            mrSane.Close();
            mrSane.Open(nNewNumber);
            mxPreview->ResetForNewScanner();
            InitFields();
        }
    }
    if( mrSane.IsOpen() )
    {
        if( &rListBox == mxQuantumRangeBox.get() )
        {
            double fValue = mxQuantumRangeBox->get_active_text().toDouble();
            mrSane.SetOptionValue(mnCurrentOption, fValue, mnCurrentElement);
        }
        else if( &rListBox == mxStringRangeBox.get() )
        {
            mrSane.SetOptionValue(mnCurrentOption, mxStringRangeBox->get_active_text());
        }
    }
}

IMPL_LINK_NOARG(SaneDlg, OptionsBoxSelectHdl, weld::TreeView&, void)
{
    if (!Sane::IsSane())
        return;

    OUString aOption = mxOptionBox->get_selected_text();
    int nOption = mrSane.GetOptionByName(OUStringToOString(aOption,
        osl_getThreadTextEncoding()).getStr());
    if( nOption == -1 || nOption == mnCurrentOption )
        return;

    DisableOption();
    mnCurrentOption = nOption;
    mxOptionTitle->set_label(mrSane.GetOptionTitle(mnCurrentOption));
    SANE_Value_Type nType = mrSane.GetOptionType( mnCurrentOption );
    SANE_Constraint_Type nConstraint;
    switch( nType )
    {
        case SANE_TYPE_BOOL:    EstablishBoolOption();break;
        case SANE_TYPE_STRING:
            nConstraint = mrSane.GetOptionConstraintType( mnCurrentOption );
            if( nConstraint == SANE_CONSTRAINT_STRING_LIST )
                EstablishStringRange();
            else
                EstablishStringOption();
            break;
        case SANE_TYPE_FIXED:
        case SANE_TYPE_INT:
        {
            nConstraint = mrSane.GetOptionConstraintType( mnCurrentOption );
            int nElements = mrSane.GetOptionElements( mnCurrentOption );
            mnCurrentElement = 0;
            if( nConstraint == SANE_CONSTRAINT_RANGE ||
                nConstraint == SANE_CONSTRAINT_WORD_LIST )
                EstablishQuantumRange();
            else
            {
                mfMin = mfMax = 0.0;
                EstablishNumericOption();
            }
            if( nElements > 1 )
            {
                if( nElements <= 10 )
                {
                    mxVectorBox->set_range(1, mrSane.GetOptionElements(mnCurrentOption));
                    mxVectorBox->set_value(1);
                    mxVectorBox->show();
                    mxVectorTxt->show();
                }
                else
                {
                    DisableOption();
                    // bring up dialog only on button click
                    EstablishButtonOption();
                }
            }
        }
        break;
        case SANE_TYPE_BUTTON:
            EstablishButtonOption();
            break;
        defaultbreak;
    }
}

IMPL_LINK(SaneDlg, ModifyHdl, weld::Entry&, rEdit, void)
{
    if( !mrSane.IsOpen() )
        return;

    if (&rEdit == mxStringEdit.get())
    {
        mrSane.SetOptionValue( mnCurrentOption, mxStringEdit->get_text() );
    }
    else if (&rEdit == mxNumericEdit.get())
    {
        double fValue = mxNumericEdit->get_text().toDouble();
        if( mfMin != mfMax && ( fValue < mfMin || fValue > mfMax ) )
        {
            char pBuf[256];
            if( fValue < mfMin )
                fValue = mfMin;
            else if( fValue > mfMax )
                fValue = mfMax;
            o3tl::sprintf( pBuf, "%g", fValue );
            mxNumericEdit->set_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
        }
        mrSane.SetOptionValue( mnCurrentOption, fValue, mnCurrentElement );
    }
    else if (&rEdit == mxVectorBox.get())
    {
        mnCurrentElement = mxVectorBox->get_value() - 1;
        double fValue;
        if( mrSane.GetOptionValue( mnCurrentOption, fValue, mnCurrentElement ))
        {
            char pBuf[256];
            o3tl::sprintf( pBuf, "%g", fValue );
            OUString aValue( pBuf, strlen(pBuf), osl_getThreadTextEncoding() );
            mxNumericEdit->set_text( aValue );
            mxQuantumRangeBox->set_active_text( aValue );
        }
    }
}

IMPL_LINK(SaneDlg, ValueModifyHdl, weld::ComboBox&, rEdit, void)
{
    if( !mrSane.IsOpen() )
        return;

    if (&rEdit != mxReslBox.get())
        return;

    double fRes = static_cast<double>(mxReslBox->get_active_text().toUInt32());
    int nOption = mrSane.GetOptionByName( "resolution" );
    if( nOption == -1 )
        return;

    std::unique_ptr<double[]> pDouble;
    int nValues = mrSane.GetRange( nOption, pDouble );
    if( nValues > 0 )
    {
        int i;
        for( i = 0; i < nValues; i++ )
        {
            if( fRes == pDouble[i] )
                break;
        }
        if( i >= nValues )
            fRes = pDouble[0];
    }
    else if( nValues == 0 )
    {
        if( fRes < pDouble[ 0 ] )
            fRes = pDouble[ 0 ];
        if( fRes > pDouble[ 1 ] )
            fRes = pDouble[ 1 ];
    }
    mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fRes)));
}

IMPL_LINK(SaneDlg, MetricValueModifyHdl, weld::MetricSpinButton&, rEdit, void)
{
    if( !mrSane.IsOpen() )
        return;

    if (&rEdit == mxTopField.get())
    {
        mxPreview->ChangePreviewLogicTopLeftY(mxTopField->get_value(FieldUnit::NONE));
        mxPreview->Invalidate();
    }
    else if (&rEdit == mxLeftField.get())
    {
        mxPreview->ChangePreviewLogicTopLeftX(mxLeftField->get_value(FieldUnit::NONE));
        mxPreview->Invalidate();
    }
    else if (&rEdit == mxBottomField.get())
    {
        mxPreview->ChangePreviewLogicBottomRightY(mxBottomField->get_value(FieldUnit::NONE));
        mxPreview->Invalidate();
    }
    else if (&rEdit == mxRightField.get())
    {
        mxPreview->ChangePreviewLogicBottomRightX(mxRightField->get_value(FieldUnit::NONE));
        mxPreview->Invalidate();
    }
}

IMPL_LINK_NOARG( SaneDlg, ReloadSaneOptionsHdl, Sane&, void )
{
    mnCurrentOption = -1;
    mnCurrentElement = 0;
    DisableOption();
    InitFields();
    mxPreview->Invalidate();
}

void SaneDlg::AcquirePreview()
{
    if( ! mrSane.IsOpen() )
        return;

    UpdateScanArea( true );
    // set small resolution for preview
    double fResl = static_cast<double>(mxReslBox->get_active_text().toUInt32());
    SetAdjustedNumericalValue( "resolution", 30.0 );

    int nOption = mrSane.GetOptionByName( "preview" );
    if( nOption == -1 )
    {
        OUString aString(SaneResId(STR_SLOW_PREVIEW));
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
                                                  VclMessageType::Warning, VclButtonsType::OkCancel,
                                                  aString));
        if (xBox->run() == RET_CANCEL)
            return;
    }
    else
        mrSane.SetOptionValue( nOption, true );

    rtl::Reference<BitmapTransporter> xTransporter(new BitmapTransporter);
    if (!mrSane.Start(*xTransporter))
    {
        std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_xDialog.get(),
                                                       VclMessageType::Warning, VclButtonsType::Ok,
                                                       SaneResId(STR_ERROR_SCAN)));
        xErrorBox->run();
    }
    else
    {
#if OSL_DEBUG_LEVEL > 0
        SAL_INFO("extensions.scanner""Previewbitmapstream contains " << xTransporter->getStream().TellEnd() << "bytes");
#endif
        xTransporter->getStream().Seek( STREAM_SEEK_TO_BEGIN );
        mxPreview->SetBitmap(xTransporter->getStream());
    }

    SetAdjustedNumericalValue( "resolution", fResl );
    mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fResl)));

    mxPreview->UpdatePreviewBounds();
    mxPreview->Invalidate();
}

void ScanPreview::UpdatePreviewBounds()
{
    if( mbDragEnable )
    {
        maPreviewRect = tools::Rectangle( maTopLeft,
                                   Size( maBottomRight.X() - maTopLeft.X(),
                                         maBottomRight.Y() - maTopLeft.Y() )
                                   );
    }
    else
    {
        Size aBMSize( maPreviewBitmapEx.GetSizePixel() );
        if( aBMSize.Width() > aBMSize.Height() && aBMSize.Width() )
        {
            int nVHeight = (maBottomRight.X() - maTopLeft.X()) * aBMSize.Height() / aBMSize.Width();
            maPreviewRect = tools::Rectangle( Point( maTopLeft.X(), ( maTopLeft.Y() + maBottomRight.Y() )/2 - nVHeight/2 ),
                                       Size( maBottomRight.X() - maTopLeft.X(),
                                             nVHeight ) );
        }
        else if (aBMSize.Height())
        {
            int nVWidth = (maBottomRight.Y() - maTopLeft.Y()) * aBMSize.Width() / aBMSize.Height();
            maPreviewRect = tools::Rectangle( Point( ( maTopLeft.X() + maBottomRight.X() )/2 - nVWidth/2, maTopLeft.Y() ),
                                       Size( nVWidth,
                                             maBottomRight.Y() - maTopLeft.Y() ) );
        }
    }
}

void ScanPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
{
    rRenderContext.SetMapMode(MapMode(MapUnit::MapAppFont));
    rRenderContext.SetFillColor(COL_WHITE);
    rRenderContext.SetLineColor(COL_WHITE);
    rRenderContext.DrawRect(tools::Rectangle(Point(0, 0),
                                      Size(PREVIEW_WIDTH, PREVIEW_HEIGHT)));
    rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
    // check for sane values
    rRenderContext.DrawBitmapEx(maPreviewRect.TopLeft(), maPreviewRect.GetSize(), maPreviewBitmapEx);

    DrawDrag(rRenderContext);
}

void SaneDlg::DisableOption()
{
    mxBoolCheckBox->hide();
    mxStringEdit->hide();
    mxNumericEdit->hide();
    mxQuantumRangeBox->hide();
    mxStringRangeBox->hide();
    mxButtonOption->hide();
    mxVectorBox->hide();
    mxVectorTxt->hide();
    mxOptionDescTxt->hide();
}

void SaneDlg::EstablishBoolOption()
{
    bool bSuccess, bValue;

    bSuccess = mrSane.GetOptionValue( mnCurrentOption, bValue );
    if( bSuccess )
    {
        mxBoolCheckBox->set_label( mrSane.GetOptionName( mnCurrentOption ) );
        mxBoolCheckBox->set_active( bValue );
        mxBoolCheckBox->show();
    }
}

void SaneDlg::EstablishStringOption()
{
    bool bSuccess;
    OString aValue;

    bSuccess = mrSane.GetOptionValue( mnCurrentOption, aValue );
    if( bSuccess )
    {
        mxOptionDescTxt->set_label( mrSane.GetOptionName( mnCurrentOption ) );
        mxOptionDescTxt->show();
        mxStringEdit->set_text(OStringToOUString(aValue, osl_getThreadTextEncoding()));
        mxStringEdit->show();
    }
}

void SaneDlg::EstablishStringRange()
{
    const char** ppStrings = mrSane.GetStringConstraint( mnCurrentOption );
    mxStringRangeBox->clear();
    forint i = 0; ppStrings[i] != nullptr; i++ )
        mxStringRangeBox->append_text( OUString( ppStrings[i], strlen(ppStrings[i]), osl_getThreadTextEncoding() ) );
    OString aValue;
    mrSane.GetOptionValue( mnCurrentOption, aValue );
    mxStringRangeBox->set_active_text(OStringToOUString(aValue, osl_getThreadTextEncoding()));
    mxStringRangeBox->show();
    mxOptionDescTxt->set_label( mrSane.GetOptionName( mnCurrentOption ) );
    mxOptionDescTxt->show();
}

void SaneDlg::EstablishQuantumRange()
{
    mpRange.reset();
    int nValues = mrSane.GetRange( mnCurrentOption, mpRange );
    if( nValues == 0 )
    {
        mfMin = mpRange[ 0 ];
        mfMax = mpRange[ 1 ];
        mpRange.reset();
        EstablishNumericOption();
    }
    else if( nValues > 0 )
    {
        char pBuf[ 256 ];
        mxQuantumRangeBox->clear();
        mfMin = mpRange[ 0 ];
        mfMax = mpRange[ nValues-1 ];
        forint i = 0; i < nValues; i++ )
        {
            o3tl::sprintf( pBuf, "%g", mpRange[ i ] );
            mxQuantumRangeBox->append_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
        }
        double fValue;
        if( mrSane.GetOptionValue( mnCurrentOption, fValue, mnCurrentElement ) )
        {
            o3tl::sprintf( pBuf, "%g", fValue );
            mxQuantumRangeBox->set_active_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
        }
        mxQuantumRangeBox->show();
        OUString aText = mrSane.GetOptionName( mnCurrentOption ) + " "
            + mrSane.GetOptionUnitName( mnCurrentOption );
        mxOptionDescTxt->set_label(aText);
        mxOptionDescTxt->show();
    }
}

void SaneDlg::EstablishNumericOption()
{
    bool bSuccess;
    double fValue;

    bSuccess = mrSane.GetOptionValue( mnCurrentOption, fValue );
    if( ! bSuccess )
        return;

    char pBuf[256];
    OUString aText = mrSane.GetOptionName( mnCurrentOption ) + " "
        + mrSane.GetOptionUnitName( mnCurrentOption );
    if( mfMin != mfMax )
    {
        o3tl::sprintf( pBuf, " < %g ; %g >", mfMin, mfMax );
        aText += OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() );
    }
    mxOptionDescTxt->set_label( aText );
    mxOptionDescTxt->show();
    o3tl::sprintf( pBuf, "%g", fValue );
    mxNumericEdit->set_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
    mxNumericEdit->show();
}

void SaneDlg::EstablishButtonOption()
{
    mxOptionDescTxt->set_label(mrSane.GetOptionName(mnCurrentOption));
    mxOptionDescTxt->show();
    mxButtonOption->show();
}

bool ScanPreview::MouseMove(const MouseEvent& rMEvt)
{
    if( !mbIsDragging )
        return false;

    Point aMousePos = rMEvt.GetPosPixel();
    // move into valid area
    Point aLogicPos = GetLogicPos( aMousePos );
    aMousePos = GetPixelPos( aLogicPos );
    switch( meDragDirection )
    {
        case TopLeft:       maTopLeft = aMousePos; break;
        case Top:           maTopLeft.setY( aMousePos.Y() ); break;
        case TopRight:
            maTopLeft.setY( aMousePos.Y() );
            maBottomRight.setX( aMousePos.X() );
            break;
        case Right:         maBottomRight.setX( aMousePos.X() ); break;
        case BottomRight:   maBottomRight = aMousePos; break;
        case Bottom:        maBottomRight.setY( aMousePos.Y() ); break;
        case BottomLeft:
            maTopLeft.setX( aMousePos.X() );
            maBottomRight.setY( aMousePos.Y() );
            break;
        case Left:          maTopLeft.setX( aMousePos.X() ); break;
        defaultbreak;
    }
    int nSwap;
    if( maTopLeft.X() > maBottomRight.X() )
    {
        nSwap = maTopLeft.X();
        maTopLeft.setX( maBottomRight.X() );
        maBottomRight.setX( nSwap );
    }
    if( maTopLeft.Y() > maBottomRight.Y() )
    {
        nSwap = maTopLeft.Y();
        maTopLeft.setY( maBottomRight.Y() );
        maBottomRight.setY( nSwap );
    }
    Invalidate();
    mpParentDialog->UpdateScanArea(false);
    return false;
}

bool ScanPreview::MouseButtonDown( const MouseEvent& rMEvt )
{
    if (!mbIsDragging && mbDragEnable)
    {
        Point aMousePixel = rMEvt.GetPosPixel();

        int nMiddleX = ( maBottomRight.X() - maTopLeft.X() ) / 2 - RECT_SIZE_PIX/2 + maTopLeft.X();
        int nMiddleY = ( maBottomRight.Y() - maTopLeft.Y() ) / 2 - RECT_SIZE_PIX/2 + maTopLeft.Y();
        if( aMousePixel.Y() >= maTopLeft.Y() &&
            aMousePixel.Y() < maTopLeft.Y() + RECT_SIZE_PIX )
        {
            if( aMousePixel.X() >= maTopLeft.X() &&
                aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
            {
                meDragDirection = TopLeft;
                mbIsDragging = true;
            }
            else if( aMousePixel.X() >= nMiddleX &&
                     aMousePixel.X() < nMiddleX + RECT_SIZE_PIX )
            {
                meDragDirection = Top;
                mbIsDragging = true;
            }
            else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
                     aMousePixel.X() <= maBottomRight.X() )
            {
                meDragDirection = TopRight;
                mbIsDragging = true;
            }
        }
        else if( aMousePixel.Y() >= nMiddleY &&
                 aMousePixel.Y() < nMiddleY + RECT_SIZE_PIX )
        {
            if( aMousePixel.X() >= maTopLeft.X() &&
                aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
            {
                meDragDirection = Left;
                mbIsDragging = true;
            }
            else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
                     aMousePixel.X() <= maBottomRight.X() )
            {
                meDragDirection = Right;
                mbIsDragging = true;
            }
        }
        else if( aMousePixel.Y() <= maBottomRight.Y() &&
                 aMousePixel.Y() > maBottomRight.Y() - RECT_SIZE_PIX )
        {
            if( aMousePixel.X() >= maTopLeft.X() &&
                aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
            {
                meDragDirection = BottomLeft;
                mbIsDragging = true;
            }
            else if( aMousePixel.X() >= nMiddleX &&
                     aMousePixel.X() < nMiddleX + RECT_SIZE_PIX )
            {
                meDragDirection = Bottom;
                mbIsDragging = true;
            }
            else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
                     aMousePixel.X() <= maBottomRight.X() )
            {
                meDragDirection = BottomRight;
                mbIsDragging = true;
            }
        }
    }

    if( mbIsDragging )
        Invalidate();

    return false;
}

bool ScanPreview::MouseButtonUp(const MouseEvent&)
{
    if( mbIsDragging )
        mpParentDialog->UpdateScanArea(true);
    mbIsDragging = false;

    return false;
}

void ScanPreview::DrawDrag(vcl::RenderContext& rRenderContext)
{
    if (!mbDragEnable)
        return;

    rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));

    DrawRectangles(rRenderContext, maTopLeft, maBottomRight);

    rRenderContext.SetMapMode(MapMode(MapUnit::MapAppFont));
}

Point ScanPreview::GetPixelPos( const Point& rIn) const
{
    Point aConvert(
        ( ( rIn.X() * PREVIEW_WIDTH ) /
          ( maMaxBottomRight.X() - maMinTopLeft.X() ) )
        ,
        ( ( rIn.Y() * PREVIEW_HEIGHT )
          / ( maMaxBottomRight.Y() - maMinTopLeft.Y() ) )
        );

    return GetDrawingArea()->get_ref_device().LogicToPixel(aConvert, MapMode(MapUnit::MapAppFont));
}

Point ScanPreview::GetLogicPos(const Point& rIn) const
{
    Point aConvert = GetDrawingArea()->get_ref_device().PixelToLogic(rIn, MapMode(MapUnit::MapAppFont));
    if( aConvert.X() < 0 )
        aConvert.setX( 0 );
    if( aConvert.X() >= PREVIEW_WIDTH )
        aConvert.setX( PREVIEW_WIDTH-1 );
    if( aConvert.Y() < 0 )
        aConvert.setY( 0 );
    if( aConvert.Y() >= PREVIEW_HEIGHT )
        aConvert.setY( PREVIEW_HEIGHT-1 );

    aConvert.setX( aConvert.X() * ( maMaxBottomRight.X() - maMinTopLeft.X() ) );
    aConvert.setX( aConvert.X() / ( PREVIEW_WIDTH) );
    aConvert.setY( aConvert.Y() * ( maMaxBottomRight.Y() - maMinTopLeft.Y() ) );
    aConvert.setY( aConvert.Y() / ( PREVIEW_HEIGHT) );
    return aConvert;
}

void SaneDlg::UpdateScanArea(bool bSend)
{
    if (!mxPreview->IsDragEnabled())
        return;

    Point aUL, aBR;
    mxPreview->GetPreviewLogicRect(aUL, aBR);

    mxLeftField->set_value(aUL.X(), FieldUnit::NONE);
    mxTopField->set_value(aUL.Y(), FieldUnit::NONE);
    mxRightField->set_value(aBR.X(), FieldUnit::NONE);
    mxBottomField->set_value(aBR.Y(), FieldUnit::NONE);

    if (!bSend)
        return;

    if( mrSane.IsOpen() )
    {
        SetAdjustedNumericalValue( "tl-x"static_cast<double>(aUL.X()) );
        SetAdjustedNumericalValue( "tl-y"static_cast<double>(aUL.Y()) );
        SetAdjustedNumericalValue( "br-x"static_cast<double>(aBR.X()) );
        SetAdjustedNumericalValue( "br-y"static_cast<double>(aBR.Y()) );
    }
}

bool SaneDlg::LoadState()
{
    int i;

    if( ! Sane::IsSane() )
        return false;

    const char* pEnv = getenv("HOME");
    OUString aFileName = (pEnv ? OUString(pEnv, strlen(pEnv), osl_getThreadTextEncoding() ) : OUString()) + "/.so_sane_state";
    Config aConfig( aFileName );
    if( ! aConfig.HasGroup( "SANE" ) )
        return false;

    aConfig.SetGroup( "SANE"_ostr );
    OString aString = aConfig.ReadKey( "SO_LastSaneDevice"_ostr );
    for( i = 0; i < Sane::CountDevices() && aString != OUStringToOString(Sane::GetName(i), osl_getThreadTextEncoding()); i++ ) ;
    if( i == Sane::CountDevices() )
        return false;

    mrSane.Close();
    mrSane.Open( aString.getStr() );

    DisableOption();
    InitFields();

    if( mrSane.IsOpen() )
    {
        int iMax = aConfig.GetKeyCount();
        for (i = 0; i < iMax; ++i)
        {
            aString = aConfig.GetKeyName( i );
            OString aValue = aConfig.ReadKey( i );
            int nOption = mrSane.GetOptionByName( aString.getStr() );
            if( nOption == -1 )
                continue;

            if (aValue.startsWith("BOOL="))
            {
                aValue = aValue.copy(RTL_CONSTASCII_LENGTH("BOOL="));
                bool aBOOL = aValue.toInt32() != 0;
                mrSane.SetOptionValue( nOption, aBOOL );
            }
            else if (aValue.startsWith("STRING="))
            {
                aValue = aValue.copy(RTL_CONSTASCII_LENGTH("STRING="));
                mrSane.SetOptionValue(nOption,OStringToOUString(aValue, osl_getThreadTextEncoding()) );
            }
            else if (aValue.startsWith("NUMERIC="))
            {
                aValue = aValue.copy(RTL_CONSTASCII_LENGTH("NUMERIC="));

                sal_Int32 nIndex = 0;
                int n = 0;
                do
                {
                    OString aSub = aValue.getToken(0, ':', nIndex);
                    double fValue=0.0;
                    sscanf(aSub.getStr(), "%lg", &fValue);
                    SetAdjustedNumericalValue(aString.getStr(), fValue, n++);
                }
                while ( nIndex >= 0 );
            }
        }
    }

    DisableOption();
    InitFields();

    return true;
}

void SaneDlg::SaveState()
{
    if( ! Sane::IsSane() )
        return;

    const char* pEnv = getenv( "HOME" );
    OUString aFileName;

    if( pEnv )
        aFileName = OUString::createFromAscii(pEnv) + "/.so_sane_state";
    else
        aFileName = OStringToOUString("", osl_getThreadTextEncoding()) + "/.so_sane_state";

    Config aConfig( aFileName );
    aConfig.DeleteGroup( "SANE" );
    aConfig.SetGroup( "SANE"_ostr );
    aConfig.WriteKey( "SO_LastSANEDevice"_ostr,
        OUStringToOString(mxDeviceBox->get_active_text(), RTL_TEXTENCODING_UTF8) );

    static char const* pSaveOptions[] = {
        "resolution",
        "tl-x",
        "tl-y",
        "br-x",
        "br-y"
    };
    for(const char * pSaveOption : pSaveOptions)
    {
        OString aOption = pSaveOption;
        int nOption = mrSane.GetOptionByName( pSaveOption );
        if( nOption > -1 )
        {
            SANE_Value_Type nType = mrSane.GetOptionType( nOption );
            switch( nType )
            {
                case SANE_TYPE_BOOL:
                {
                    bool bValue;
                    if( mrSane.GetOptionValue( nOption, bValue ) )
                    {
                        OString aString = "BOOL=" + OString::number(static_cast<sal_Int32>(bValue));
                        aConfig.WriteKey(aOption, aString);
                    }
                }
                break;
                case SANE_TYPE_STRING:
                {
                    OString aValue;
                    if( mrSane.GetOptionValue( nOption, aValue ) )
                    {
                        OString aString = "STRING=" + aValue;
                        aConfig.WriteKey( aOption, aString );
                    }
                }
                break;
                case SANE_TYPE_FIXED:
                case SANE_TYPE_INT:
                {
                    OStringBuffer aString("NUMERIC=");
                    double fValue;
                    char buf[256];
                    int n;

                    for( n = 0; n < mrSane.GetOptionElements( nOption ); n++ )
                    {
                        if( ! mrSane.GetOptionValue( nOption, fValue, n ) )
                            break;
                        if( n > 0 )
                            aString.append(':');
                        o3tl::sprintf( buf, "%lg", fValue );
                        aString.append(buf);
                    }
                    if( n >= mrSane.GetOptionElements( nOption ) )
                        aConfig.WriteKey( aOption, aString.makeStringAndClear() );
                }
                break;
                default:
                    break;
            }
        }
    }
}

bool SaneDlg::SetAdjustedNumericalValue(
    const char* pOption,
    double fValue,
    int nElement )
{
    if (! Sane::IsSane() || ! mrSane.IsOpen())
        return false;
    int const nOption(mrSane.GetOptionByName(pOption));
    if (nOption == -1)
        return false;

    if( nElement < 0 || nElement >= mrSane.GetOptionElements( nOption ) )
        return false;

    std::unique_ptr<double[]> pValues;
    int nValues;
    if( ( nValues = mrSane.GetRange( nOption, pValues ) ) < 0 )
    {
        return false;
    }

    SAL_INFO("extensions.scanner""SaneDlg::SetAdjustedNumericalValue(\"" << pOption << "\", " << fValue << ") ");

    if( nValues )
    {
        int nNearest = 0;
        double fNearest = 1e6;
        forint i = 0; i < nValues; i++ )
        {
            if( fabs( fValue - pValues[ i ] ) < fNearest )
            {
                fNearest = fabs( fValue - pValues[ i ] );
                nNearest = i;
            }
        }
        fValue = pValues[ nNearest ];
    }
    else
    {
        if( fValue < pValues[0] )
            fValue = pValues[0];
        if( fValue > pValues[1] )
            fValue = pValues[1];
    }
    mrSane.SetOptionValue( nOption, fValue, nElement );
    SAL_INFO("extensions.scanner""yields " << fValue);


    return true;
}


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=99 H=95 G=96

¤ Dauer der Verarbeitung: 0.16 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.