diff options
Diffstat (limited to 'extensions/source/scanner/sanedlg.cxx')
-rw-r--r-- | extensions/source/scanner/sanedlg.cxx | 1479 |
1 files changed, 1479 insertions, 0 deletions
diff --git a/extensions/source/scanner/sanedlg.cxx b/extensions/source/scanner/sanedlg.cxx new file mode 100644 index 0000000000..b4f5e9a36c --- /dev/null +++ b/extensions/source/scanner/sanedlg.cxx @@ -0,0 +1,1479 @@ +/* -*- 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, Point const & 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, "modules/scanner/ui/sanedialog.ui", "SaneDialog") + , mpParent(pParent) + , mrSane(rSane) + , mbScanEnabled(bScanEnabled) + , mnCurrentOption(0) + , mnCurrentElement(0) + , mfMin(0.0) + , mfMax(0.0) + , doScan(false) + , mxCancelButton(m_xBuilder->weld_button("cancel")) + , mxDeviceInfoButton(m_xBuilder->weld_button("deviceInfoButton")) + , mxPreviewButton(m_xBuilder->weld_button("previewButton")) + , mxScanButton(m_xBuilder->weld_button("ok")) + , mxButtonOption(m_xBuilder->weld_button("optionsButton")) + , mxOptionTitle(m_xBuilder->weld_label("optionTitleLabel")) + , mxOptionDescTxt(m_xBuilder->weld_label("optionsDescLabel")) + , mxVectorTxt(m_xBuilder->weld_label("vectorLabel")) + , mxLeftField(m_xBuilder->weld_metric_spin_button("leftSpinbutton", FieldUnit::PIXEL)) + , mxTopField(m_xBuilder->weld_metric_spin_button("topSpinbutton", FieldUnit::PIXEL)) + , mxRightField(m_xBuilder->weld_metric_spin_button("rightSpinbutton", FieldUnit::PIXEL)) + , mxBottomField(m_xBuilder->weld_metric_spin_button("bottomSpinbutton", FieldUnit::PIXEL)) + , mxDeviceBox(m_xBuilder->weld_combo_box("deviceCombobox")) + , mxReslBox(m_xBuilder->weld_combo_box("reslCombobox")) + , mxAdvancedBox(m_xBuilder->weld_check_button("advancedCheckbutton")) + , mxVectorBox(m_xBuilder->weld_spin_button("vectorSpinbutton")) + , mxQuantumRangeBox(m_xBuilder->weld_combo_box("quantumRangeCombobox")) + , mxStringRangeBox(m_xBuilder->weld_combo_box("stringRangeCombobox")) + , mxBoolCheckBox(m_xBuilder->weld_check_button("boolCheckbutton")) + , mxStringEdit(m_xBuilder->weld_entry("stringEntry")) + , mxNumericEdit(m_xBuilder->weld_entry("numericEntry")) + , mxOptionBox(m_xBuilder->weld_tree_view("optionSvTreeListBox")) + , mxPreview(new ScanPreview) + , mxPreviewWnd(new weld::CustomWeld(*m_xBuilder, "preview", *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_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 ]); + for( int 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; + default: break; + } +} + +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(); + for( int 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 ]; + for( int 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; + default: break; + } + 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; + for( int 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: */ |