Get the style color and number just once
[LibreOffice.git] / extensions / source / scanner / sanedlg.cxx
blob7c2af809a6abf828c43d0a348c4154b001945d93
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <stdlib.h>
21 #include <o3tl/sprintf.hxx>
22 #include <tools/config.hxx>
23 #include <unotools/resmgr.hxx>
24 #include <vcl/bitmapex.hxx>
25 #include <vcl/customweld.hxx>
26 #include <vcl/dibtools.hxx>
27 #include <vcl/lineinfo.hxx>
28 #include <vcl/weld.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/event.hxx>
31 #include "sanedlg.hxx"
32 #include "grid.hxx"
33 #include <math.h>
34 #include <sal/macros.h>
35 #include <sal/log.hxx>
36 #include <rtl/strbuf.hxx>
37 #include <memory>
38 #include <strings.hrc>
40 #define PREVIEW_WIDTH 113
41 #define PREVIEW_HEIGHT 160
43 #define RECT_SIZE_PIX 7
45 namespace {
47 void DrawRect(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
49 tools::Rectangle aRect(rRect);
50 rRenderContext.SetFillColor(COL_BLACK);
51 rRenderContext.SetLineColor();
52 rRenderContext.DrawRect(aRect);
53 aRect.Move(1, 1);
54 aRect.AdjustRight(-2);
55 aRect.AdjustBottom(-2);
56 rRenderContext.SetFillColor();
57 rRenderContext.SetLineColor(COL_WHITE);
58 rRenderContext.DrawRect(aRect);
61 void DrawRectangles(vcl::RenderContext& rRenderContext, Point const & rUL, Point const & rBR)
63 Point aUR(rBR.X(), rUL.Y());
64 Point aBL(rUL.X(), rBR.Y());
65 int nMiddleX = (rBR.X() - rUL.X()) / 2 + rUL.X();
66 int nMiddleY = (rBR.Y() - rUL.Y()) / 2 + rUL.Y();
68 rRenderContext.SetLineColor(COL_WHITE);
69 rRenderContext.DrawLine(rUL, aBL);
70 rRenderContext.DrawLine(aBL, rBR);
71 rRenderContext.DrawLine(rBR, aUR);
72 rRenderContext.DrawLine(aUR, rUL);
74 rRenderContext.SetLineColor(COL_BLACK);
75 LineInfo aInfo(LineStyle::Dash, 1);
76 aInfo.SetDistance(8);
77 aInfo.SetDotLen(4);
78 aInfo.SetDotCount(1);
79 rRenderContext.DrawLine(rUL, aBL, aInfo);
80 rRenderContext.DrawLine(aBL, rBR, aInfo);
81 rRenderContext.DrawLine(rBR, aUR, aInfo);
82 rRenderContext.DrawLine(aUR, rUL, aInfo);
84 Size aSize(RECT_SIZE_PIX, RECT_SIZE_PIX);
85 DrawRect(rRenderContext, tools::Rectangle(rUL, aSize));
86 DrawRect(rRenderContext, tools::Rectangle(Point(aBL.X(), aBL.Y() - RECT_SIZE_PIX), aSize));
87 DrawRect(rRenderContext, tools::Rectangle(Point(rBR.X() - RECT_SIZE_PIX, rBR.Y() - RECT_SIZE_PIX), aSize));
88 DrawRect(rRenderContext, tools::Rectangle(Point(aUR.X() - RECT_SIZE_PIX, aUR.Y()), aSize));
89 DrawRect(rRenderContext, tools::Rectangle(Point(nMiddleX - RECT_SIZE_PIX / 2, rUL.Y()), aSize));
90 DrawRect(rRenderContext, tools::Rectangle(Point(nMiddleX - RECT_SIZE_PIX / 2, rBR.Y() - RECT_SIZE_PIX), aSize));
91 DrawRect(rRenderContext, tools::Rectangle(Point(rUL.X(), nMiddleY - RECT_SIZE_PIX / 2), aSize));
92 DrawRect(rRenderContext, tools::Rectangle(Point(rBR.X() - RECT_SIZE_PIX, nMiddleY - RECT_SIZE_PIX / 2), aSize));
97 class ScanPreview : public weld::CustomWidgetController
99 private:
100 enum DragDirection { TopLeft, Top, TopRight, Right, BottomRight, Bottom,
101 BottomLeft, Left };
103 BitmapEx maPreviewBitmapEx;
104 tools::Rectangle maPreviewRect;
105 Point maTopLeft, maBottomRight;
106 Point maMinTopLeft, maMaxBottomRight;
107 SaneDlg* mpParentDialog;
108 DragDirection meDragDirection;
109 bool mbDragEnable;
110 bool mbIsDragging;
112 public:
113 ScanPreview()
114 : maMaxBottomRight(PREVIEW_WIDTH, PREVIEW_HEIGHT)
115 , mpParentDialog(nullptr)
116 , meDragDirection(TopLeft)
117 , mbDragEnable(false)
118 , mbIsDragging(false)
122 void Init(SaneDlg *pParent)
124 mpParentDialog = pParent;
127 void ResetForNewScanner()
129 maTopLeft = Point();
130 maBottomRight = Point();
131 maMinTopLeft = Point();
132 maMaxBottomRight = Point(PREVIEW_WIDTH, PREVIEW_HEIGHT);
135 void EnableDrag()
137 mbDragEnable = true;
140 void DisableDrag()
142 mbDragEnable = false;
145 bool IsDragEnabled() const
147 return mbDragEnable;
150 virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
151 virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
152 virtual bool MouseMove(const MouseEvent& rMEvt) override;
153 virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
154 Point GetPixelPos(const Point& rIn) const;
155 Point GetLogicPos(const Point& rIn) const;
157 void GetPreviewLogicRect(Point& rTopLeft, Point &rBottomRight) const
159 rTopLeft = GetLogicPos(maTopLeft);
160 rBottomRight = GetLogicPos(maBottomRight);
162 void GetMaxLogicRect(Point& rTopLeft, Point &rBottomRight) const
164 rTopLeft = maMinTopLeft;
165 rBottomRight = maMaxBottomRight;
168 void ChangePreviewLogicTopLeftY(tools::Long Y)
170 Point aPoint(0, Y);
171 aPoint = GetPixelPos(aPoint);
172 maTopLeft.setY( aPoint.Y() );
174 void ChangePreviewLogicTopLeftX(tools::Long X)
176 Point aPoint(X, 0);
177 aPoint = GetPixelPos(aPoint);
178 maTopLeft.setX( aPoint.X() );
180 void ChangePreviewLogicBottomRightY(tools::Long Y)
182 Point aPoint(0, Y);
183 aPoint = GetPixelPos(aPoint);
184 maBottomRight.setY( aPoint.Y() );
186 void ChangePreviewLogicBottomRightX(tools::Long X)
188 Point aPoint(X, 0);
189 aPoint = GetPixelPos(aPoint);
190 maBottomRight.setX( aPoint.X() );
192 void SetPreviewLogicRect(const Point& rTopLeft, const Point &rBottomRight)
194 maTopLeft = GetPixelPos(rTopLeft);
195 maBottomRight = GetPixelPos(rBottomRight);
196 maPreviewRect = tools::Rectangle(maTopLeft,
197 Size(maBottomRight.X() - maTopLeft.X(),
198 maBottomRight.Y() - maTopLeft.Y()));
200 void SetPreviewMaxRect(const Point& rTopLeft, const Point &rBottomRight)
202 maMinTopLeft = rTopLeft;
203 maMaxBottomRight = rBottomRight;
205 void DrawDrag(vcl::RenderContext& rRenderContext);
206 void UpdatePreviewBounds();
207 void SetBitmap(SvStream &rStream)
209 ReadDIBBitmapEx(maPreviewBitmapEx, rStream, true);
211 virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override
213 Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(PREVIEW_WIDTH, PREVIEW_HEIGHT), MapMode(MapUnit::MapAppFont)));
214 aSize.setWidth(aSize.getWidth()+1);
215 aSize.setHeight(aSize.getHeight()+1);
216 pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
217 CustomWidgetController::SetDrawingArea(pDrawingArea);
218 SetOutputSizePixel(aSize);
222 SaneDlg::SaneDlg(weld::Window* pParent, Sane& rSane, bool bScanEnabled)
223 : GenericDialogController(pParent, u"modules/scanner/ui/sanedialog.ui"_ustr, u"SaneDialog"_ustr)
224 , mpParent(pParent)
225 , mrSane(rSane)
226 , mbScanEnabled(bScanEnabled)
227 , mnCurrentOption(0)
228 , mnCurrentElement(0)
229 , mfMin(0.0)
230 , mfMax(0.0)
231 , doScan(false)
232 , mxCancelButton(m_xBuilder->weld_button(u"cancel"_ustr))
233 , mxDeviceInfoButton(m_xBuilder->weld_button(u"deviceInfoButton"_ustr))
234 , mxPreviewButton(m_xBuilder->weld_button(u"previewButton"_ustr))
235 , mxScanButton(m_xBuilder->weld_button(u"ok"_ustr))
236 , mxButtonOption(m_xBuilder->weld_button(u"optionsButton"_ustr))
237 , mxOptionTitle(m_xBuilder->weld_label(u"optionTitleLabel"_ustr))
238 , mxOptionDescTxt(m_xBuilder->weld_label(u"optionsDescLabel"_ustr))
239 , mxVectorTxt(m_xBuilder->weld_label(u"vectorLabel"_ustr))
240 , mxLeftField(m_xBuilder->weld_metric_spin_button(u"leftSpinbutton"_ustr, FieldUnit::PIXEL))
241 , mxTopField(m_xBuilder->weld_metric_spin_button(u"topSpinbutton"_ustr, FieldUnit::PIXEL))
242 , mxRightField(m_xBuilder->weld_metric_spin_button(u"rightSpinbutton"_ustr, FieldUnit::PIXEL))
243 , mxBottomField(m_xBuilder->weld_metric_spin_button(u"bottomSpinbutton"_ustr, FieldUnit::PIXEL))
244 , mxDeviceBox(m_xBuilder->weld_combo_box(u"deviceCombobox"_ustr))
245 , mxReslBox(m_xBuilder->weld_combo_box(u"reslCombobox"_ustr))
246 , mxAdvancedBox(m_xBuilder->weld_check_button(u"advancedCheckbutton"_ustr))
247 , mxVectorBox(m_xBuilder->weld_spin_button(u"vectorSpinbutton"_ustr))
248 , mxQuantumRangeBox(m_xBuilder->weld_combo_box(u"quantumRangeCombobox"_ustr))
249 , mxStringRangeBox(m_xBuilder->weld_combo_box(u"stringRangeCombobox"_ustr))
250 , mxBoolCheckBox(m_xBuilder->weld_check_button(u"boolCheckbutton"_ustr))
251 , mxStringEdit(m_xBuilder->weld_entry(u"stringEntry"_ustr))
252 , mxNumericEdit(m_xBuilder->weld_entry(u"numericEntry"_ustr))
253 , mxOptionBox(m_xBuilder->weld_tree_view(u"optionSvTreeListBox"_ustr))
254 , mxPreview(new ScanPreview)
255 , mxPreviewWnd(new weld::CustomWeld(*m_xBuilder, u"preview"_ustr, *mxPreview))
257 Size aSize(mxOptionBox->get_approximate_digit_width() * 32, mxOptionBox->get_height_rows(8));
258 mxOptionTitle->set_size_request(aSize.Width(), aSize.Height() / 2);
259 mxOptionBox->set_size_request(aSize.Width(), aSize.Height());
260 mxPreview->Init(this);
261 if( Sane::IsSane() )
263 InitDevices(); // opens first sane device
264 DisableOption();
265 InitFields();
268 mxDeviceInfoButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
269 mxPreviewButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
270 mxScanButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
271 mxButtonOption->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
272 mxDeviceBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
273 mxOptionBox->connect_selection_changed(LINK(this, SaneDlg, OptionsBoxSelectHdl));
274 mxCancelButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
275 mxBoolCheckBox->connect_toggled( LINK( this, SaneDlg, ToggleBtnHdl ) );
276 mxStringEdit->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
277 mxNumericEdit->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
278 mxVectorBox->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
279 mxReslBox->connect_changed( LINK( this, SaneDlg, ValueModifyHdl ) );
280 mxStringRangeBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
281 mxQuantumRangeBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
282 mxLeftField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl ) );
283 mxRightField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
284 mxTopField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
285 mxBottomField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
286 mxAdvancedBox->connect_toggled( LINK( this, SaneDlg, ToggleBtnHdl ) );
288 maOldLink = mrSane.SetReloadOptionsHdl( LINK( this, SaneDlg, ReloadSaneOptionsHdl ) );
291 SaneDlg::~SaneDlg()
293 mrSane.SetReloadOptionsHdl(maOldLink);
296 namespace {
298 OUString SaneResId(TranslateId aID)
300 return Translate::get(aID, Translate::Create("pcr"));
305 short SaneDlg::run()
307 if (!Sane::IsSane())
309 std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(mpParent,
310 VclMessageType::Warning, VclButtonsType::Ok,
311 SaneResId(STR_COULD_NOT_BE_INIT)));
312 xErrorBox->run();
313 return RET_CANCEL;
315 LoadState();
316 return GenericDialogController::run();
319 void SaneDlg::InitDevices()
321 if( ! Sane::IsSane() )
322 return;
324 if( mrSane.IsOpen() )
325 mrSane.Close();
326 mrSane.ReloadDevices();
327 mxDeviceBox->clear();
328 for (int i = 0; i < Sane::CountDevices(); ++i)
329 mxDeviceBox->append_text(Sane::GetName(i));
330 if( Sane::CountDevices() )
332 mrSane.Open(0);
333 mxDeviceBox->set_active(0);
337 void SaneDlg::InitFields()
339 if( ! Sane::IsSane() )
340 return;
342 int nOption, i, nValue;
343 double fValue;
344 const char *ppSpecialOptions[] = {
345 "resolution",
346 "tl-x",
347 "tl-y",
348 "br-x",
349 "br-y",
350 "preview"
353 mxPreview->EnableDrag();
354 mxReslBox->clear();
355 Point aTopLeft, aBottomRight;
356 mxPreview->GetPreviewLogicRect(aTopLeft, aBottomRight);
357 Point aMinTopLeft, aMaxBottomRight;
358 mxPreview->GetMaxLogicRect(aMinTopLeft, aMaxBottomRight);
359 mxScanButton->set_visible( mbScanEnabled );
361 if( ! mrSane.IsOpen() )
362 return;
364 // set Resolution
365 nOption = mrSane.GetOptionByName( "resolution" );
366 if( nOption != -1 )
368 double fRes;
370 if( mrSane.GetOptionValue( nOption, fRes ) )
372 mxReslBox->set_sensitive(true);
374 mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fRes)));
375 std::unique_ptr<double[]> pDouble;
376 nValue = mrSane.GetRange( nOption, pDouble );
377 if( nValue > -1 )
379 assert(pDouble);
380 if( nValue )
382 for( i=0; i<nValue; i++ )
384 if( i == 0 || i == nValue-1 || ! ( static_cast<int>(pDouble[i]) % 20) )
385 mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[i])));
388 else
390 mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[0])));
391 // Can only select 75 and 2400 dpi in Scanner dialogue
392 // scanner allows random setting of dpi resolution, a slider might be useful
393 // support that
394 // workaround: offer at least some more standard dpi resolution between
395 // min and max value
396 int bGot300 = 0;
397 for (sal_uInt32 nRes = static_cast<sal_uInt32>(pDouble[0]) * 2; nRes < static_cast<sal_uInt32>(pDouble[1]); nRes = nRes * 2)
399 if ( !bGot300 && nRes > 300 ) {
400 nRes = 300; bGot300 = 1;
402 mxReslBox->append_text(OUString::number(nRes));
404 mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[1])));
407 else
408 mxReslBox->set_sensitive( false );
411 else
412 mxReslBox->set_sensitive( false );
414 // set scan area
415 for( i = 0; i < 4; i++ )
417 char const *pOptionName = nullptr;
418 weld::MetricSpinButton* pField = nullptr;
419 switch( i )
421 case 0:
422 pOptionName = "tl-x";
423 pField = mxLeftField.get();
424 break;
425 case 1:
426 pOptionName = "tl-y";
427 pField = mxTopField.get();
428 break;
429 case 2:
430 pOptionName = "br-x";
431 pField = mxRightField.get();
432 break;
433 case 3:
434 pOptionName = "br-y";
435 pField = mxBottomField.get();
437 nOption = pOptionName ? mrSane.GetOptionByName( pOptionName ) : -1;
438 if( nOption != -1 )
440 if( mrSane.GetOptionValue( nOption, fValue ) )
442 if( mrSane.GetOptionUnit( nOption ) == SANE_UNIT_MM )
444 pField->set_unit( FieldUnit::MM );
445 pField->set_value( static_cast<int>(fValue), FieldUnit::MM );
447 else // SANE_UNIT_PIXEL
449 pField->set_unit( FieldUnit::PIXEL );
450 pField->set_value( static_cast<int>(fValue), FieldUnit::PIXEL );
452 switch( i ) {
453 case 0: aTopLeft.setX( static_cast<int>(fValue) );break;
454 case 1: aTopLeft.setY( static_cast<int>(fValue) );break;
455 case 2: aBottomRight.setX( static_cast<int>(fValue) );break;
456 case 3: aBottomRight.setY( static_cast<int>(fValue) );break;
459 std::unique_ptr<double[]> pDouble;
460 nValue = mrSane.GetRange( nOption, pDouble );
461 if( nValue > -1 )
463 if( pDouble )
465 pField->set_min( static_cast<tools::Long>(pDouble[0]), FieldUnit::NONE );
466 if( nValue )
467 pField->set_max( static_cast<tools::Long>(pDouble[ nValue-1 ]), FieldUnit::NONE );
468 else
469 pField->set_max( static_cast<tools::Long>(pDouble[ 1 ]), FieldUnit::NONE );
471 switch( i ) {
472 case 0: aMinTopLeft.setX( pField->get_min(FieldUnit::NONE) );break;
473 case 1: aMinTopLeft.setY( pField->get_min(FieldUnit::NONE) );break;
474 case 2: aMaxBottomRight.setX( pField->get_max(FieldUnit::NONE) );break;
475 case 3: aMaxBottomRight.setY( pField->get_max(FieldUnit::NONE) );break;
478 else
480 switch( i ) {
481 case 0: aMinTopLeft.setX( static_cast<int>(fValue) );break;
482 case 1: aMinTopLeft.setY( static_cast<int>(fValue) );break;
483 case 2: aMaxBottomRight.setX( static_cast<int>(fValue) );break;
484 case 3: aMaxBottomRight.setY( static_cast<int>(fValue) );break;
487 pField->set_sensitive(true);
489 else
491 mxPreview->DisableDrag();
492 pField->set_min( 0, FieldUnit::NONE );
493 switch( i ) {
494 case 0:
495 aMinTopLeft.setX( 0 );
496 aTopLeft.setX( 0 );
497 pField->set_max( PREVIEW_WIDTH, FieldUnit::NONE );
498 pField->set_value( 0, FieldUnit::NONE );
499 break;
500 case 1:
501 aMinTopLeft.setY( 0 );
502 aTopLeft.setY( 0 );
503 pField->set_max( PREVIEW_HEIGHT, FieldUnit::NONE );
504 pField->set_value( 0, FieldUnit::NONE );
505 break;
506 case 2:
507 aMaxBottomRight.setX( PREVIEW_WIDTH );
508 aBottomRight.setX( PREVIEW_WIDTH );
509 pField->set_max( PREVIEW_WIDTH, FieldUnit::NONE );
510 pField->set_value( PREVIEW_WIDTH, FieldUnit::NONE );
511 break;
512 case 3:
513 aMaxBottomRight.setY( PREVIEW_HEIGHT );
514 aBottomRight.setY( PREVIEW_HEIGHT );
515 pField->set_max( PREVIEW_HEIGHT, FieldUnit::NONE );
516 pField->set_value( PREVIEW_HEIGHT, FieldUnit::NONE );
517 break;
519 pField->set_sensitive(false);
523 mxPreview->SetPreviewMaxRect(aMinTopLeft, aMaxBottomRight);
524 mxPreview->SetPreviewLogicRect(aTopLeft, aBottomRight);
525 mxPreview->Invalidate();
527 // fill OptionBox
528 mxOptionBox->clear();
529 std::unique_ptr<weld::TreeIter> xParentEntry(mxOptionBox->make_iterator());
530 bool bGroupRejected = false;
531 for( i = 1; i < mrSane.CountOptions(); i++ )
533 OUString aOption=mrSane.GetOptionName( i );
534 bool bInsertAdvanced =
535 (mrSane.GetOptionCap( i ) & SANE_CAP_ADVANCED) == 0 ||
536 mxAdvancedBox->get_active();
537 if( mrSane.GetOptionType( i ) == SANE_TYPE_GROUP )
539 if( bInsertAdvanced )
541 aOption = mrSane.GetOptionTitle( i );
542 mxOptionBox->append(xParentEntry.get());
543 mxOptionBox->set_text(*xParentEntry, aOption, 0);
544 bGroupRejected = false;
546 else
547 bGroupRejected = true;
549 else if( !aOption.isEmpty() &&
550 ! ( mrSane.GetOptionCap( i ) &
552 SANE_CAP_HARD_SELECT |
553 SANE_CAP_INACTIVE
554 ) ) &&
555 bInsertAdvanced && ! bGroupRejected )
557 bool bIsSpecial = false;
558 for( size_t n = 0; !bIsSpecial &&
559 n < SAL_N_ELEMENTS(ppSpecialOptions); n++ )
561 if( aOption == OUString::createFromAscii(ppSpecialOptions[n]) )
562 bIsSpecial=true;
564 if( ! bIsSpecial )
566 if (xParentEntry)
567 mxOptionBox->append(xParentEntry.get(), aOption);
568 else
569 mxOptionBox->append_text(aOption);
575 IMPL_LINK( SaneDlg, ClickBtnHdl, weld::Button&, rButton, void )
577 if( mrSane.IsOpen() )
579 if( &rButton == mxDeviceInfoButton.get() )
581 OUString aString(SaneResId(STR_DEVICE_DESC));
582 aString = aString.replaceFirst( "%s", Sane::GetName( mrSane.GetDeviceNumber() ) );
583 aString = aString.replaceFirst( "%s", Sane::GetVendor( mrSane.GetDeviceNumber() ) );
584 aString = aString.replaceFirst( "%s", Sane::GetModel( mrSane.GetDeviceNumber() ) );
585 aString = aString.replaceFirst( "%s", Sane::GetType( mrSane.GetDeviceNumber() ) );
586 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
587 VclMessageType::Info, VclButtonsType::Ok,
588 aString));
589 xInfoBox->run();
591 else if( &rButton == mxPreviewButton.get() )
592 AcquirePreview();
593 else if( &rButton == mxButtonOption.get() )
596 SANE_Value_Type nType = mrSane.GetOptionType( mnCurrentOption );
597 switch( nType )
599 case SANE_TYPE_BUTTON:
600 mrSane.ActivateButtonOption( mnCurrentOption );
601 break;
602 case SANE_TYPE_FIXED:
603 case SANE_TYPE_INT:
605 int nElements = mrSane.GetOptionElements( mnCurrentOption );
606 std::unique_ptr<double[]> x(new double[ nElements ]);
607 std::unique_ptr<double[]> y(new double[ nElements ]);
608 for( int i = 0; i < nElements; i++ )
609 x[ i ] = static_cast<double>(i);
610 mrSane.GetOptionValue( mnCurrentOption, y.get() );
612 GridDialog aGrid(m_xDialog.get(), x.get(), y.get(), nElements);
613 aGrid.set_title( mrSane.GetOptionName( mnCurrentOption ) );
614 aGrid.setBoundings( 0, mfMin, nElements, mfMax );
615 if (aGrid.run() && aGrid.getNewYValues())
616 mrSane.SetOptionValue( mnCurrentOption, aGrid.getNewYValues() );
618 break;
619 case SANE_TYPE_BOOL:
620 case SANE_TYPE_STRING:
621 case SANE_TYPE_GROUP:
622 break;
626 if (&rButton == mxScanButton.get())
628 double fRes = static_cast<double>(mxReslBox->get_active_text().toUInt32());
629 SetAdjustedNumericalValue( "resolution", fRes );
630 UpdateScanArea(true);
631 SaveState();
632 m_xDialog->response(mrSane.IsOpen() ? RET_OK : RET_CANCEL);
633 doScan = mrSane.IsOpen();
635 else if( &rButton == mxCancelButton.get() )
637 mrSane.Close();
638 m_xDialog->response(RET_CANCEL);
642 IMPL_LINK( SaneDlg, ToggleBtnHdl, weld::Toggleable&, rButton, void )
644 if( mrSane.IsOpen() )
646 if( &rButton == mxBoolCheckBox.get() )
648 mrSane.SetOptionValue( mnCurrentOption,
649 mxBoolCheckBox->get_active() );
651 else if( &rButton == mxAdvancedBox.get() )
653 ReloadSaneOptionsHdl( mrSane );
658 IMPL_LINK( SaneDlg, SelectHdl, weld::ComboBox&, rListBox, void )
660 if( &rListBox == mxDeviceBox.get() && Sane::IsSane() && Sane::CountDevices() )
662 int nNewNumber = mxDeviceBox->get_active();
663 int nOldNumber = mrSane.GetDeviceNumber();
664 if (nNewNumber != nOldNumber)
666 mrSane.Close();
667 mrSane.Open(nNewNumber);
668 mxPreview->ResetForNewScanner();
669 InitFields();
672 if( mrSane.IsOpen() )
674 if( &rListBox == mxQuantumRangeBox.get() )
676 double fValue = mxQuantumRangeBox->get_active_text().toDouble();
677 mrSane.SetOptionValue(mnCurrentOption, fValue, mnCurrentElement);
679 else if( &rListBox == mxStringRangeBox.get() )
681 mrSane.SetOptionValue(mnCurrentOption, mxStringRangeBox->get_active_text());
686 IMPL_LINK_NOARG(SaneDlg, OptionsBoxSelectHdl, weld::TreeView&, void)
688 if (!Sane::IsSane())
689 return;
691 OUString aOption = mxOptionBox->get_selected_text();
692 int nOption = mrSane.GetOptionByName(OUStringToOString(aOption,
693 osl_getThreadTextEncoding()).getStr());
694 if( nOption == -1 || nOption == mnCurrentOption )
695 return;
697 DisableOption();
698 mnCurrentOption = nOption;
699 mxOptionTitle->set_label(mrSane.GetOptionTitle(mnCurrentOption));
700 SANE_Value_Type nType = mrSane.GetOptionType( mnCurrentOption );
701 SANE_Constraint_Type nConstraint;
702 switch( nType )
704 case SANE_TYPE_BOOL: EstablishBoolOption();break;
705 case SANE_TYPE_STRING:
706 nConstraint = mrSane.GetOptionConstraintType( mnCurrentOption );
707 if( nConstraint == SANE_CONSTRAINT_STRING_LIST )
708 EstablishStringRange();
709 else
710 EstablishStringOption();
711 break;
712 case SANE_TYPE_FIXED:
713 case SANE_TYPE_INT:
715 nConstraint = mrSane.GetOptionConstraintType( mnCurrentOption );
716 int nElements = mrSane.GetOptionElements( mnCurrentOption );
717 mnCurrentElement = 0;
718 if( nConstraint == SANE_CONSTRAINT_RANGE ||
719 nConstraint == SANE_CONSTRAINT_WORD_LIST )
720 EstablishQuantumRange();
721 else
723 mfMin = mfMax = 0.0;
724 EstablishNumericOption();
726 if( nElements > 1 )
728 if( nElements <= 10 )
730 mxVectorBox->set_range(1, mrSane.GetOptionElements(mnCurrentOption));
731 mxVectorBox->set_value(1);
732 mxVectorBox->show();
733 mxVectorTxt->show();
735 else
737 DisableOption();
738 // bring up dialog only on button click
739 EstablishButtonOption();
743 break;
744 case SANE_TYPE_BUTTON:
745 EstablishButtonOption();
746 break;
747 default: break;
751 IMPL_LINK(SaneDlg, ModifyHdl, weld::Entry&, rEdit, void)
753 if( !mrSane.IsOpen() )
754 return;
756 if (&rEdit == mxStringEdit.get())
758 mrSane.SetOptionValue( mnCurrentOption, mxStringEdit->get_text() );
760 else if (&rEdit == mxNumericEdit.get())
762 double fValue = mxNumericEdit->get_text().toDouble();
763 if( mfMin != mfMax && ( fValue < mfMin || fValue > mfMax ) )
765 char pBuf[256];
766 if( fValue < mfMin )
767 fValue = mfMin;
768 else if( fValue > mfMax )
769 fValue = mfMax;
770 o3tl::sprintf( pBuf, "%g", fValue );
771 mxNumericEdit->set_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
773 mrSane.SetOptionValue( mnCurrentOption, fValue, mnCurrentElement );
775 else if (&rEdit == mxVectorBox.get())
777 mnCurrentElement = mxVectorBox->get_value() - 1;
778 double fValue;
779 if( mrSane.GetOptionValue( mnCurrentOption, fValue, mnCurrentElement ))
781 char pBuf[256];
782 o3tl::sprintf( pBuf, "%g", fValue );
783 OUString aValue( pBuf, strlen(pBuf), osl_getThreadTextEncoding() );
784 mxNumericEdit->set_text( aValue );
785 mxQuantumRangeBox->set_active_text( aValue );
790 IMPL_LINK(SaneDlg, ValueModifyHdl, weld::ComboBox&, rEdit, void)
792 if( !mrSane.IsOpen() )
793 return;
795 if (&rEdit != mxReslBox.get())
796 return;
798 double fRes = static_cast<double>(mxReslBox->get_active_text().toUInt32());
799 int nOption = mrSane.GetOptionByName( "resolution" );
800 if( nOption == -1 )
801 return;
803 std::unique_ptr<double[]> pDouble;
804 int nValues = mrSane.GetRange( nOption, pDouble );
805 if( nValues > 0 )
807 int i;
808 for( i = 0; i < nValues; i++ )
810 if( fRes == pDouble[i] )
811 break;
813 if( i >= nValues )
814 fRes = pDouble[0];
816 else if( nValues == 0 )
818 if( fRes < pDouble[ 0 ] )
819 fRes = pDouble[ 0 ];
820 if( fRes > pDouble[ 1 ] )
821 fRes = pDouble[ 1 ];
823 mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fRes)));
826 IMPL_LINK(SaneDlg, MetricValueModifyHdl, weld::MetricSpinButton&, rEdit, void)
828 if( !mrSane.IsOpen() )
829 return;
831 if (&rEdit == mxTopField.get())
833 mxPreview->ChangePreviewLogicTopLeftY(mxTopField->get_value(FieldUnit::NONE));
834 mxPreview->Invalidate();
836 else if (&rEdit == mxLeftField.get())
838 mxPreview->ChangePreviewLogicTopLeftX(mxLeftField->get_value(FieldUnit::NONE));
839 mxPreview->Invalidate();
841 else if (&rEdit == mxBottomField.get())
843 mxPreview->ChangePreviewLogicBottomRightY(mxBottomField->get_value(FieldUnit::NONE));
844 mxPreview->Invalidate();
846 else if (&rEdit == mxRightField.get())
848 mxPreview->ChangePreviewLogicBottomRightX(mxRightField->get_value(FieldUnit::NONE));
849 mxPreview->Invalidate();
853 IMPL_LINK_NOARG( SaneDlg, ReloadSaneOptionsHdl, Sane&, void )
855 mnCurrentOption = -1;
856 mnCurrentElement = 0;
857 DisableOption();
858 InitFields();
859 mxPreview->Invalidate();
862 void SaneDlg::AcquirePreview()
864 if( ! mrSane.IsOpen() )
865 return;
867 UpdateScanArea( true );
868 // set small resolution for preview
869 double fResl = static_cast<double>(mxReslBox->get_active_text().toUInt32());
870 SetAdjustedNumericalValue( "resolution", 30.0 );
872 int nOption = mrSane.GetOptionByName( "preview" );
873 if( nOption == -1 )
875 OUString aString(SaneResId(STR_SLOW_PREVIEW));
876 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
877 VclMessageType::Warning, VclButtonsType::OkCancel,
878 aString));
879 if (xBox->run() == RET_CANCEL)
880 return;
882 else
883 mrSane.SetOptionValue( nOption, true );
885 rtl::Reference<BitmapTransporter> xTransporter(new BitmapTransporter);
886 if (!mrSane.Start(*xTransporter))
888 std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_xDialog.get(),
889 VclMessageType::Warning, VclButtonsType::Ok,
890 SaneResId(STR_ERROR_SCAN)));
891 xErrorBox->run();
893 else
895 #if OSL_DEBUG_LEVEL > 0
896 SAL_INFO("extensions.scanner", "Previewbitmapstream contains " << xTransporter->getStream().TellEnd() << "bytes");
897 #endif
898 xTransporter->getStream().Seek( STREAM_SEEK_TO_BEGIN );
899 mxPreview->SetBitmap(xTransporter->getStream());
902 SetAdjustedNumericalValue( "resolution", fResl );
903 mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fResl)));
905 mxPreview->UpdatePreviewBounds();
906 mxPreview->Invalidate();
909 void ScanPreview::UpdatePreviewBounds()
911 if( mbDragEnable )
913 maPreviewRect = tools::Rectangle( maTopLeft,
914 Size( maBottomRight.X() - maTopLeft.X(),
915 maBottomRight.Y() - maTopLeft.Y() )
918 else
920 Size aBMSize( maPreviewBitmapEx.GetSizePixel() );
921 if( aBMSize.Width() > aBMSize.Height() && aBMSize.Width() )
923 int nVHeight = (maBottomRight.X() - maTopLeft.X()) * aBMSize.Height() / aBMSize.Width();
924 maPreviewRect = tools::Rectangle( Point( maTopLeft.X(), ( maTopLeft.Y() + maBottomRight.Y() )/2 - nVHeight/2 ),
925 Size( maBottomRight.X() - maTopLeft.X(),
926 nVHeight ) );
928 else if (aBMSize.Height())
930 int nVWidth = (maBottomRight.Y() - maTopLeft.Y()) * aBMSize.Width() / aBMSize.Height();
931 maPreviewRect = tools::Rectangle( Point( ( maTopLeft.X() + maBottomRight.X() )/2 - nVWidth/2, maTopLeft.Y() ),
932 Size( nVWidth,
933 maBottomRight.Y() - maTopLeft.Y() ) );
938 void ScanPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
940 rRenderContext.SetMapMode(MapMode(MapUnit::MapAppFont));
941 rRenderContext.SetFillColor(COL_WHITE);
942 rRenderContext.SetLineColor(COL_WHITE);
943 rRenderContext.DrawRect(tools::Rectangle(Point(0, 0),
944 Size(PREVIEW_WIDTH, PREVIEW_HEIGHT)));
945 rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
946 // check for sane values
947 rRenderContext.DrawBitmapEx(maPreviewRect.TopLeft(), maPreviewRect.GetSize(), maPreviewBitmapEx);
949 DrawDrag(rRenderContext);
952 void SaneDlg::DisableOption()
954 mxBoolCheckBox->hide();
955 mxStringEdit->hide();
956 mxNumericEdit->hide();
957 mxQuantumRangeBox->hide();
958 mxStringRangeBox->hide();
959 mxButtonOption->hide();
960 mxVectorBox->hide();
961 mxVectorTxt->hide();
962 mxOptionDescTxt->hide();
965 void SaneDlg::EstablishBoolOption()
967 bool bSuccess, bValue;
969 bSuccess = mrSane.GetOptionValue( mnCurrentOption, bValue );
970 if( bSuccess )
972 mxBoolCheckBox->set_label( mrSane.GetOptionName( mnCurrentOption ) );
973 mxBoolCheckBox->set_active( bValue );
974 mxBoolCheckBox->show();
978 void SaneDlg::EstablishStringOption()
980 bool bSuccess;
981 OString aValue;
983 bSuccess = mrSane.GetOptionValue( mnCurrentOption, aValue );
984 if( bSuccess )
986 mxOptionDescTxt->set_label( mrSane.GetOptionName( mnCurrentOption ) );
987 mxOptionDescTxt->show();
988 mxStringEdit->set_text(OStringToOUString(aValue, osl_getThreadTextEncoding()));
989 mxStringEdit->show();
993 void SaneDlg::EstablishStringRange()
995 const char** ppStrings = mrSane.GetStringConstraint( mnCurrentOption );
996 mxStringRangeBox->clear();
997 for( int i = 0; ppStrings[i] != nullptr; i++ )
998 mxStringRangeBox->append_text( OUString( ppStrings[i], strlen(ppStrings[i]), osl_getThreadTextEncoding() ) );
999 OString aValue;
1000 mrSane.GetOptionValue( mnCurrentOption, aValue );
1001 mxStringRangeBox->set_active_text(OStringToOUString(aValue, osl_getThreadTextEncoding()));
1002 mxStringRangeBox->show();
1003 mxOptionDescTxt->set_label( mrSane.GetOptionName( mnCurrentOption ) );
1004 mxOptionDescTxt->show();
1007 void SaneDlg::EstablishQuantumRange()
1009 mpRange.reset();
1010 int nValues = mrSane.GetRange( mnCurrentOption, mpRange );
1011 if( nValues == 0 )
1013 mfMin = mpRange[ 0 ];
1014 mfMax = mpRange[ 1 ];
1015 mpRange.reset();
1016 EstablishNumericOption();
1018 else if( nValues > 0 )
1020 char pBuf[ 256 ];
1021 mxQuantumRangeBox->clear();
1022 mfMin = mpRange[ 0 ];
1023 mfMax = mpRange[ nValues-1 ];
1024 for( int i = 0; i < nValues; i++ )
1026 o3tl::sprintf( pBuf, "%g", mpRange[ i ] );
1027 mxQuantumRangeBox->append_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
1029 double fValue;
1030 if( mrSane.GetOptionValue( mnCurrentOption, fValue, mnCurrentElement ) )
1032 o3tl::sprintf( pBuf, "%g", fValue );
1033 mxQuantumRangeBox->set_active_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
1035 mxQuantumRangeBox->show();
1036 OUString aText = mrSane.GetOptionName( mnCurrentOption ) + " "
1037 + mrSane.GetOptionUnitName( mnCurrentOption );
1038 mxOptionDescTxt->set_label(aText);
1039 mxOptionDescTxt->show();
1043 void SaneDlg::EstablishNumericOption()
1045 bool bSuccess;
1046 double fValue;
1048 bSuccess = mrSane.GetOptionValue( mnCurrentOption, fValue );
1049 if( ! bSuccess )
1050 return;
1052 char pBuf[256];
1053 OUString aText = mrSane.GetOptionName( mnCurrentOption ) + " "
1054 + mrSane.GetOptionUnitName( mnCurrentOption );
1055 if( mfMin != mfMax )
1057 o3tl::sprintf( pBuf, " < %g ; %g >", mfMin, mfMax );
1058 aText += OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() );
1060 mxOptionDescTxt->set_label( aText );
1061 mxOptionDescTxt->show();
1062 o3tl::sprintf( pBuf, "%g", fValue );
1063 mxNumericEdit->set_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
1064 mxNumericEdit->show();
1067 void SaneDlg::EstablishButtonOption()
1069 mxOptionDescTxt->set_label(mrSane.GetOptionName(mnCurrentOption));
1070 mxOptionDescTxt->show();
1071 mxButtonOption->show();
1074 bool ScanPreview::MouseMove(const MouseEvent& rMEvt)
1076 if( !mbIsDragging )
1077 return false;
1079 Point aMousePos = rMEvt.GetPosPixel();
1080 // move into valid area
1081 Point aLogicPos = GetLogicPos( aMousePos );
1082 aMousePos = GetPixelPos( aLogicPos );
1083 switch( meDragDirection )
1085 case TopLeft: maTopLeft = aMousePos; break;
1086 case Top: maTopLeft.setY( aMousePos.Y() ); break;
1087 case TopRight:
1088 maTopLeft.setY( aMousePos.Y() );
1089 maBottomRight.setX( aMousePos.X() );
1090 break;
1091 case Right: maBottomRight.setX( aMousePos.X() ); break;
1092 case BottomRight: maBottomRight = aMousePos; break;
1093 case Bottom: maBottomRight.setY( aMousePos.Y() ); break;
1094 case BottomLeft:
1095 maTopLeft.setX( aMousePos.X() );
1096 maBottomRight.setY( aMousePos.Y() );
1097 break;
1098 case Left: maTopLeft.setX( aMousePos.X() ); break;
1099 default: break;
1101 int nSwap;
1102 if( maTopLeft.X() > maBottomRight.X() )
1104 nSwap = maTopLeft.X();
1105 maTopLeft.setX( maBottomRight.X() );
1106 maBottomRight.setX( nSwap );
1108 if( maTopLeft.Y() > maBottomRight.Y() )
1110 nSwap = maTopLeft.Y();
1111 maTopLeft.setY( maBottomRight.Y() );
1112 maBottomRight.setY( nSwap );
1114 Invalidate();
1115 mpParentDialog->UpdateScanArea(false);
1116 return false;
1119 bool ScanPreview::MouseButtonDown( const MouseEvent& rMEvt )
1121 if (!mbIsDragging && mbDragEnable)
1123 Point aMousePixel = rMEvt.GetPosPixel();
1125 int nMiddleX = ( maBottomRight.X() - maTopLeft.X() ) / 2 - RECT_SIZE_PIX/2 + maTopLeft.X();
1126 int nMiddleY = ( maBottomRight.Y() - maTopLeft.Y() ) / 2 - RECT_SIZE_PIX/2 + maTopLeft.Y();
1127 if( aMousePixel.Y() >= maTopLeft.Y() &&
1128 aMousePixel.Y() < maTopLeft.Y() + RECT_SIZE_PIX )
1130 if( aMousePixel.X() >= maTopLeft.X() &&
1131 aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
1133 meDragDirection = TopLeft;
1134 mbIsDragging = true;
1136 else if( aMousePixel.X() >= nMiddleX &&
1137 aMousePixel.X() < nMiddleX + RECT_SIZE_PIX )
1139 meDragDirection = Top;
1140 mbIsDragging = true;
1142 else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
1143 aMousePixel.X() <= maBottomRight.X() )
1145 meDragDirection = TopRight;
1146 mbIsDragging = true;
1149 else if( aMousePixel.Y() >= nMiddleY &&
1150 aMousePixel.Y() < nMiddleY + RECT_SIZE_PIX )
1152 if( aMousePixel.X() >= maTopLeft.X() &&
1153 aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
1155 meDragDirection = Left;
1156 mbIsDragging = true;
1158 else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
1159 aMousePixel.X() <= maBottomRight.X() )
1161 meDragDirection = Right;
1162 mbIsDragging = true;
1165 else if( aMousePixel.Y() <= maBottomRight.Y() &&
1166 aMousePixel.Y() > maBottomRight.Y() - RECT_SIZE_PIX )
1168 if( aMousePixel.X() >= maTopLeft.X() &&
1169 aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
1171 meDragDirection = BottomLeft;
1172 mbIsDragging = true;
1174 else if( aMousePixel.X() >= nMiddleX &&
1175 aMousePixel.X() < nMiddleX + RECT_SIZE_PIX )
1177 meDragDirection = Bottom;
1178 mbIsDragging = true;
1180 else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
1181 aMousePixel.X() <= maBottomRight.X() )
1183 meDragDirection = BottomRight;
1184 mbIsDragging = true;
1189 if( mbIsDragging )
1190 Invalidate();
1192 return false;
1195 bool ScanPreview::MouseButtonUp(const MouseEvent&)
1197 if( mbIsDragging )
1198 mpParentDialog->UpdateScanArea(true);
1199 mbIsDragging = false;
1201 return false;
1204 void ScanPreview::DrawDrag(vcl::RenderContext& rRenderContext)
1206 if (!mbDragEnable)
1207 return;
1209 rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
1211 DrawRectangles(rRenderContext, maTopLeft, maBottomRight);
1213 rRenderContext.SetMapMode(MapMode(MapUnit::MapAppFont));
1216 Point ScanPreview::GetPixelPos( const Point& rIn) const
1218 Point aConvert(
1219 ( ( rIn.X() * PREVIEW_WIDTH ) /
1220 ( maMaxBottomRight.X() - maMinTopLeft.X() ) )
1222 ( ( rIn.Y() * PREVIEW_HEIGHT )
1223 / ( maMaxBottomRight.Y() - maMinTopLeft.Y() ) )
1226 return GetDrawingArea()->get_ref_device().LogicToPixel(aConvert, MapMode(MapUnit::MapAppFont));
1229 Point ScanPreview::GetLogicPos(const Point& rIn) const
1231 Point aConvert = GetDrawingArea()->get_ref_device().PixelToLogic(rIn, MapMode(MapUnit::MapAppFont));
1232 if( aConvert.X() < 0 )
1233 aConvert.setX( 0 );
1234 if( aConvert.X() >= PREVIEW_WIDTH )
1235 aConvert.setX( PREVIEW_WIDTH-1 );
1236 if( aConvert.Y() < 0 )
1237 aConvert.setY( 0 );
1238 if( aConvert.Y() >= PREVIEW_HEIGHT )
1239 aConvert.setY( PREVIEW_HEIGHT-1 );
1241 aConvert.setX( aConvert.X() * ( maMaxBottomRight.X() - maMinTopLeft.X() ) );
1242 aConvert.setX( aConvert.X() / ( PREVIEW_WIDTH) );
1243 aConvert.setY( aConvert.Y() * ( maMaxBottomRight.Y() - maMinTopLeft.Y() ) );
1244 aConvert.setY( aConvert.Y() / ( PREVIEW_HEIGHT) );
1245 return aConvert;
1248 void SaneDlg::UpdateScanArea(bool bSend)
1250 if (!mxPreview->IsDragEnabled())
1251 return;
1253 Point aUL, aBR;
1254 mxPreview->GetPreviewLogicRect(aUL, aBR);
1256 mxLeftField->set_value(aUL.X(), FieldUnit::NONE);
1257 mxTopField->set_value(aUL.Y(), FieldUnit::NONE);
1258 mxRightField->set_value(aBR.X(), FieldUnit::NONE);
1259 mxBottomField->set_value(aBR.Y(), FieldUnit::NONE);
1261 if (!bSend)
1262 return;
1264 if( mrSane.IsOpen() )
1266 SetAdjustedNumericalValue( "tl-x", static_cast<double>(aUL.X()) );
1267 SetAdjustedNumericalValue( "tl-y", static_cast<double>(aUL.Y()) );
1268 SetAdjustedNumericalValue( "br-x", static_cast<double>(aBR.X()) );
1269 SetAdjustedNumericalValue( "br-y", static_cast<double>(aBR.Y()) );
1273 bool SaneDlg::LoadState()
1275 int i;
1277 if( ! Sane::IsSane() )
1278 return false;
1280 const char* pEnv = getenv("HOME");
1281 OUString aFileName = (pEnv ? OUString(pEnv, strlen(pEnv), osl_getThreadTextEncoding() ) : OUString()) + "/.so_sane_state";
1282 Config aConfig( aFileName );
1283 if( ! aConfig.HasGroup( "SANE" ) )
1284 return false;
1286 aConfig.SetGroup( "SANE"_ostr );
1287 OString aString = aConfig.ReadKey( "SO_LastSaneDevice"_ostr );
1288 for( i = 0; i < Sane::CountDevices() && aString != OUStringToOString(Sane::GetName(i), osl_getThreadTextEncoding()); i++ ) ;
1289 if( i == Sane::CountDevices() )
1290 return false;
1292 mrSane.Close();
1293 mrSane.Open( aString.getStr() );
1295 DisableOption();
1296 InitFields();
1298 if( mrSane.IsOpen() )
1300 int iMax = aConfig.GetKeyCount();
1301 for (i = 0; i < iMax; ++i)
1303 aString = aConfig.GetKeyName( i );
1304 OString aValue = aConfig.ReadKey( i );
1305 int nOption = mrSane.GetOptionByName( aString.getStr() );
1306 if( nOption == -1 )
1307 continue;
1309 if (aValue.startsWith("BOOL="))
1311 aValue = aValue.copy(RTL_CONSTASCII_LENGTH("BOOL="));
1312 bool aBOOL = aValue.toInt32() != 0;
1313 mrSane.SetOptionValue( nOption, aBOOL );
1315 else if (aValue.startsWith("STRING="))
1317 aValue = aValue.copy(RTL_CONSTASCII_LENGTH("STRING="));
1318 mrSane.SetOptionValue(nOption,OStringToOUString(aValue, osl_getThreadTextEncoding()) );
1320 else if (aValue.startsWith("NUMERIC="))
1322 aValue = aValue.copy(RTL_CONSTASCII_LENGTH("NUMERIC="));
1324 sal_Int32 nIndex = 0;
1325 int n = 0;
1328 OString aSub = aValue.getToken(0, ':', nIndex);
1329 double fValue=0.0;
1330 sscanf(aSub.getStr(), "%lg", &fValue);
1331 SetAdjustedNumericalValue(aString.getStr(), fValue, n++);
1333 while ( nIndex >= 0 );
1338 DisableOption();
1339 InitFields();
1341 return true;
1344 void SaneDlg::SaveState()
1346 if( ! Sane::IsSane() )
1347 return;
1349 const char* pEnv = getenv( "HOME" );
1350 OUString aFileName;
1352 if( pEnv )
1353 aFileName = OUString::createFromAscii(pEnv) + "/.so_sane_state";
1354 else
1355 aFileName = OStringToOUString("", osl_getThreadTextEncoding()) + "/.so_sane_state";
1357 Config aConfig( aFileName );
1358 aConfig.DeleteGroup( "SANE" );
1359 aConfig.SetGroup( "SANE"_ostr );
1360 aConfig.WriteKey( "SO_LastSANEDevice"_ostr,
1361 OUStringToOString(mxDeviceBox->get_active_text(), RTL_TEXTENCODING_UTF8) );
1363 static char const* pSaveOptions[] = {
1364 "resolution",
1365 "tl-x",
1366 "tl-y",
1367 "br-x",
1368 "br-y"
1370 for(const char * pSaveOption : pSaveOptions)
1372 OString aOption = pSaveOption;
1373 int nOption = mrSane.GetOptionByName( pSaveOption );
1374 if( nOption > -1 )
1376 SANE_Value_Type nType = mrSane.GetOptionType( nOption );
1377 switch( nType )
1379 case SANE_TYPE_BOOL:
1381 bool bValue;
1382 if( mrSane.GetOptionValue( nOption, bValue ) )
1384 OString aString = "BOOL=" + OString::number(static_cast<sal_Int32>(bValue));
1385 aConfig.WriteKey(aOption, aString);
1388 break;
1389 case SANE_TYPE_STRING:
1391 OString aValue;
1392 if( mrSane.GetOptionValue( nOption, aValue ) )
1394 OString aString = "STRING=" + aValue;
1395 aConfig.WriteKey( aOption, aString );
1398 break;
1399 case SANE_TYPE_FIXED:
1400 case SANE_TYPE_INT:
1402 OStringBuffer aString("NUMERIC=");
1403 double fValue;
1404 char buf[256];
1405 int n;
1407 for( n = 0; n < mrSane.GetOptionElements( nOption ); n++ )
1409 if( ! mrSane.GetOptionValue( nOption, fValue, n ) )
1410 break;
1411 if( n > 0 )
1412 aString.append(':');
1413 o3tl::sprintf( buf, "%lg", fValue );
1414 aString.append(buf);
1416 if( n >= mrSane.GetOptionElements( nOption ) )
1417 aConfig.WriteKey( aOption, aString.makeStringAndClear() );
1419 break;
1420 default:
1421 break;
1427 bool SaneDlg::SetAdjustedNumericalValue(
1428 const char* pOption,
1429 double fValue,
1430 int nElement )
1432 if (! Sane::IsSane() || ! mrSane.IsOpen())
1433 return false;
1434 int const nOption(mrSane.GetOptionByName(pOption));
1435 if (nOption == -1)
1436 return false;
1438 if( nElement < 0 || nElement >= mrSane.GetOptionElements( nOption ) )
1439 return false;
1441 std::unique_ptr<double[]> pValues;
1442 int nValues;
1443 if( ( nValues = mrSane.GetRange( nOption, pValues ) ) < 0 )
1445 return false;
1448 SAL_INFO("extensions.scanner", "SaneDlg::SetAdjustedNumericalValue(\"" << pOption << "\", " << fValue << ") ");
1450 if( nValues )
1452 int nNearest = 0;
1453 double fNearest = 1e6;
1454 for( int i = 0; i < nValues; i++ )
1456 if( fabs( fValue - pValues[ i ] ) < fNearest )
1458 fNearest = fabs( fValue - pValues[ i ] );
1459 nNearest = i;
1462 fValue = pValues[ nNearest ];
1464 else
1466 if( fValue < pValues[0] )
1467 fValue = pValues[0];
1468 if( fValue > pValues[1] )
1469 fValue = pValues[1];
1471 mrSane.SetOptionValue( nOption, fValue, nElement );
1472 SAL_INFO("extensions.scanner", "yields " << fValue);
1475 return true;
1479 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */