1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
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/weld.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/event.hxx>
30 #include "sanedlg.hxx"
31 #include "grid.hxx"
32 #include <math.h>
33 #include <sal/macros.h>
34 #include <sal/log.hxx>
35 #include <rtl/strbuf.hxx>
36 #include <memory>
37 #include <strings.hrc>
38 
39 #define PREVIEW_WIDTH       113
40 #define PREVIEW_HEIGHT      160
41 
42 #define RECT_SIZE_PIX 7
43 
44 namespace {
45 
DrawRectangles(vcl::RenderContext & rRenderContext,Point const & rUL,Point const & rBR)46 void DrawRectangles(vcl::RenderContext& rRenderContext, Point const & rUL, Point const & rBR)
47 {
48     int nMiddleX, nMiddleY;
49     Point aBL, aUR;
50 
51     aUR = Point(rBR.X(), rUL.Y());
52     aBL = Point(rUL.X(), rBR.Y());
53     nMiddleX = (rBR.X() - rUL.X()) / 2 + rUL.X();
54     nMiddleY = (rBR.Y() - rUL.Y()) / 2 + rUL.Y();
55 
56     rRenderContext.DrawLine(rUL, aBL);
57     rRenderContext.DrawLine(aBL, rBR);
58     rRenderContext.DrawLine(rBR, aUR);
59     rRenderContext.DrawLine(aUR, rUL);
60     rRenderContext.DrawRect(tools::Rectangle(rUL, Size(RECT_SIZE_PIX,RECT_SIZE_PIX)));
61     rRenderContext.DrawRect(tools::Rectangle(aBL, Size(RECT_SIZE_PIX, -RECT_SIZE_PIX)));
62     rRenderContext.DrawRect(tools::Rectangle(rBR, Size(-RECT_SIZE_PIX, -RECT_SIZE_PIX)));
63     rRenderContext.DrawRect(tools::Rectangle(aUR, Size(-RECT_SIZE_PIX, RECT_SIZE_PIX )));
64     rRenderContext.DrawRect(tools::Rectangle(Point(nMiddleX - RECT_SIZE_PIX / 2, rUL.Y()), Size(RECT_SIZE_PIX, RECT_SIZE_PIX)));
65     rRenderContext.DrawRect(tools::Rectangle(Point(nMiddleX - RECT_SIZE_PIX / 2, rBR.Y()), Size(RECT_SIZE_PIX, -RECT_SIZE_PIX)));
66     rRenderContext.DrawRect(tools::Rectangle(Point(rUL.X(), nMiddleY - RECT_SIZE_PIX / 2), Size(RECT_SIZE_PIX, RECT_SIZE_PIX)));
67     rRenderContext.DrawRect(tools::Rectangle(Point(rBR.X(), nMiddleY - RECT_SIZE_PIX / 2), Size(-RECT_SIZE_PIX, RECT_SIZE_PIX)));
68 }
69 
70 }
71 
72 class ScanPreview : public weld::CustomWidgetController
73 {
74 private:
75     enum DragDirection { TopLeft, Top, TopRight, Right, BottomRight, Bottom,
76                          BottomLeft, Left };
77 
78     BitmapEx  maPreviewBitmapEx;
79     tools::Rectangle maPreviewRect;
80     Point     maLastUL, maLastBR;
81     Point     maTopLeft, maBottomRight;
82     Point     maMinTopLeft, maMaxBottomRight;
83     SaneDlg*  mpParentDialog;
84     DragDirection meDragDirection;
85     bool      mbDragEnable;
86     bool      mbDragDrawn;
87     bool      mbIsDragging;
88 
89 public:
ScanPreview()90     ScanPreview()
91         : maMaxBottomRight(PREVIEW_WIDTH,  PREVIEW_HEIGHT)
92         , mpParentDialog(nullptr)
93         , meDragDirection(TopLeft)
94         , mbDragEnable(false)
95         , mbDragDrawn(false)
96         , mbIsDragging(false)
97     {
98     }
99 
Init(SaneDlg * pParent)100     void Init(SaneDlg *pParent)
101     {
102         mpParentDialog = pParent;
103     }
104 
ResetForNewScanner()105     void ResetForNewScanner()
106     {
107         maTopLeft = Point();
108         maBottomRight = Point();
109         maMinTopLeft = Point();
110         maMaxBottomRight = Point(PREVIEW_WIDTH,  PREVIEW_HEIGHT);
111     }
112 
EnableDrag()113     void EnableDrag()
114     {
115         mbDragEnable = true;
116     }
117 
DisableDrag()118     void DisableDrag()
119     {
120         mbDragEnable = false;
121     }
122 
IsDragEnabled() const123     bool IsDragEnabled() const
124     {
125         return mbDragEnable;
126     }
127 
128     virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
129     virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
130     virtual bool MouseMove(const MouseEvent& rMEvt) override;
131     virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
132     Point GetPixelPos(const Point& rIn) const;
133     Point GetLogicPos(const Point& rIn) const;
134 
GetPreviewLogicRect(Point & rTopLeft,Point & rBottomRight) const135     void GetPreviewLogicRect(Point& rTopLeft, Point &rBottomRight) const
136     {
137         rTopLeft = GetLogicPos(maTopLeft);
138         rBottomRight = GetLogicPos(maBottomRight);
139     }
GetMaxLogicRect(Point & rTopLeft,Point & rBottomRight) const140     void GetMaxLogicRect(Point& rTopLeft, Point &rBottomRight) const
141     {
142         rTopLeft = maMinTopLeft;
143         rBottomRight = maMaxBottomRight;
144 
145     }
ChangePreviewLogicTopLeftY(tools::Long Y)146     void ChangePreviewLogicTopLeftY(tools::Long Y)
147     {
148         Point aPoint(0, Y);
149         aPoint = GetPixelPos(aPoint);
150         maTopLeft.setY( aPoint.Y() );
151     }
ChangePreviewLogicTopLeftX(tools::Long X)152     void ChangePreviewLogicTopLeftX(tools::Long X)
153     {
154         Point aPoint(X, 0);
155         aPoint = GetPixelPos(aPoint);
156         maTopLeft.setX( aPoint.X() );
157     }
ChangePreviewLogicBottomRightY(tools::Long Y)158     void ChangePreviewLogicBottomRightY(tools::Long Y)
159     {
160         Point aPoint(0, Y);
161         aPoint = GetPixelPos(aPoint);
162         maBottomRight.setY( aPoint.Y() );
163     }
ChangePreviewLogicBottomRightX(tools::Long X)164     void ChangePreviewLogicBottomRightX(tools::Long X)
165     {
166         Point aPoint(X, 0);
167         aPoint = GetPixelPos(aPoint);
168         maBottomRight.setX( aPoint.X() );
169     }
SetPreviewLogicRect(const Point & rTopLeft,const Point & rBottomRight)170     void SetPreviewLogicRect(const Point& rTopLeft, const Point &rBottomRight)
171     {
172         maTopLeft = GetPixelPos(rTopLeft);
173         maBottomRight = GetPixelPos(rBottomRight);
174         maPreviewRect = tools::Rectangle(maTopLeft,
175                                   Size(maBottomRight.X() - maTopLeft.X(),
176                                        maBottomRight.Y() - maTopLeft.Y()));
177     }
SetPreviewMaxRect(const Point & rTopLeft,const Point & rBottomRight)178     void SetPreviewMaxRect(const Point& rTopLeft, const Point &rBottomRight)
179     {
180         maMinTopLeft = rTopLeft;
181         maMaxBottomRight = rBottomRight;
182     }
183     void DrawDrag(vcl::RenderContext& rRenderContext);
184     void UpdatePreviewBounds();
SetBitmap(SvStream & rStream)185     void SetBitmap(SvStream &rStream)
186     {
187         ReadDIBBitmapEx(maPreviewBitmapEx, rStream, true);
188     }
SetDrawingArea(weld::DrawingArea * pDrawingArea)189     virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override
190     {
191         Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(PREVIEW_WIDTH, PREVIEW_HEIGHT), MapMode(MapUnit::MapAppFont)));
192         aSize.setWidth(aSize.getWidth()+1);
193         aSize.setHeight(aSize.getHeight()+1);
194         pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
195         CustomWidgetController::SetDrawingArea(pDrawingArea);
196         SetOutputSizePixel(aSize);
197     }
198 };
199 
SaneDlg(weld::Window * pParent,Sane & rSane,bool bScanEnabled)200 SaneDlg::SaneDlg(weld::Window* pParent, Sane& rSane, bool bScanEnabled)
201     : GenericDialogController(pParent, "modules/scanner/ui/sanedialog.ui", "SaneDialog")
202     , mpParent(pParent)
203     , mrSane(rSane)
204     , mbScanEnabled(bScanEnabled)
205     , mnCurrentOption(0)
206     , mnCurrentElement(0)
207     , mfMin(0.0)
208     , mfMax(0.0)
209     , doScan(false)
210     , mxCancelButton(m_xBuilder->weld_button("cancel"))
211     , mxDeviceInfoButton(m_xBuilder->weld_button("deviceInfoButton"))
212     , mxPreviewButton(m_xBuilder->weld_button("previewButton"))
213     , mxScanButton(m_xBuilder->weld_button("ok"))
214     , mxButtonOption(m_xBuilder->weld_button("optionsButton"))
215     , mxOptionTitle(m_xBuilder->weld_label("optionTitleLabel"))
216     , mxOptionDescTxt(m_xBuilder->weld_label("optionsDescLabel"))
217     , mxVectorTxt(m_xBuilder->weld_label("vectorLabel"))
218     , mxLeftField(m_xBuilder->weld_metric_spin_button("leftSpinbutton", FieldUnit::PIXEL))
219     , mxTopField(m_xBuilder->weld_metric_spin_button("topSpinbutton", FieldUnit::PIXEL))
220     , mxRightField(m_xBuilder->weld_metric_spin_button("rightSpinbutton", FieldUnit::PIXEL))
221     , mxBottomField(m_xBuilder->weld_metric_spin_button("bottomSpinbutton", FieldUnit::PIXEL))
222     , mxDeviceBox(m_xBuilder->weld_combo_box("deviceCombobox"))
223     , mxReslBox(m_xBuilder->weld_combo_box("reslCombobox"))
224     , mxAdvancedBox(m_xBuilder->weld_check_button("advancedCheckbutton"))
225     , mxVectorBox(m_xBuilder->weld_spin_button("vectorSpinbutton"))
226     , mxQuantumRangeBox(m_xBuilder->weld_combo_box("quantumRangeCombobox"))
227     , mxStringRangeBox(m_xBuilder->weld_combo_box("stringRangeCombobox"))
228     , mxBoolCheckBox(m_xBuilder->weld_check_button("boolCheckbutton"))
229     , mxStringEdit(m_xBuilder->weld_entry("stringEntry"))
230     , mxNumericEdit(m_xBuilder->weld_entry("numericEntry"))
231     , mxOptionBox(m_xBuilder->weld_tree_view("optionSvTreeListBox"))
232     , mxPreview(new ScanPreview)
233     , mxPreviewWnd(new weld::CustomWeld(*m_xBuilder, "preview", *mxPreview))
234 {
235     Size aSize(mxOptionBox->get_approximate_digit_width() * 32, mxOptionBox->get_height_rows(8));
236     mxOptionTitle->set_size_request(aSize.Width(), aSize.Height() / 2);
237     mxOptionBox->set_size_request(aSize.Width(), aSize.Height());
238     mxPreview->Init(this);
239     if( Sane::IsSane() )
240     {
241         InitDevices(); // opens first sane device
242         DisableOption();
243         InitFields();
244     }
245 
246     mxDeviceInfoButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
247     mxPreviewButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
248     mxScanButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
249     mxButtonOption->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
250     mxDeviceBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
251     mxOptionBox->connect_changed( LINK( this, SaneDlg, OptionsBoxSelectHdl ) );
252     mxCancelButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
253     mxBoolCheckBox->connect_toggled( LINK( this, SaneDlg, ToggleBtnHdl ) );
254     mxStringEdit->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
255     mxNumericEdit->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
256     mxVectorBox->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
257     mxReslBox->connect_changed( LINK( this, SaneDlg, ValueModifyHdl ) );
258     mxStringRangeBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
259     mxQuantumRangeBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
260     mxLeftField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl ) );
261     mxRightField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
262     mxTopField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
263     mxBottomField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
264     mxAdvancedBox->connect_toggled( LINK( this, SaneDlg, ToggleBtnHdl ) );
265 
266     maOldLink = mrSane.SetReloadOptionsHdl( LINK( this, SaneDlg, ReloadSaneOptionsHdl ) );
267 }
268 
~SaneDlg()269 SaneDlg::~SaneDlg()
270 {
271     mrSane.SetReloadOptionsHdl(maOldLink);
272 }
273 
274 namespace {
275 
SaneResId(const char * pID)276 OUString SaneResId(const char *pID)
277 {
278     return Translate::get(pID, Translate::Create("pcr"));
279 }
280 
281 }
282 
run()283 short SaneDlg::run()
284 {
285     if (!Sane::IsSane())
286     {
287         std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(mpParent,
288                                                        VclMessageType::Warning, VclButtonsType::Ok,
289                                                        SaneResId(STR_COULD_NOT_BE_INIT)));
290         xErrorBox->run();
291         return RET_CANCEL;
292     }
293     LoadState();
294     return GenericDialogController::run();
295 }
296 
InitDevices()297 void SaneDlg::InitDevices()
298 {
299     if( ! Sane::IsSane() )
300         return;
301 
302     if( mrSane.IsOpen() )
303         mrSane.Close();
304     mrSane.ReloadDevices();
305     mxDeviceBox->clear();
306     for (int i = 0; i < Sane::CountDevices(); ++i)
307         mxDeviceBox->append_text(Sane::GetName(i));
308     if( Sane::CountDevices() )
309     {
310         mrSane.Open(0);
311         mxDeviceBox->set_active(0);
312     }
313 }
314 
InitFields()315 void SaneDlg::InitFields()
316 {
317     if( ! Sane::IsSane() )
318         return;
319 
320     int nOption, i, nValue;
321     double fValue;
322     const char *ppSpecialOptions[] = {
323         "resolution",
324         "tl-x",
325         "tl-y",
326         "br-x",
327         "br-y",
328         "preview"
329     };
330 
331     mxPreview->EnableDrag();
332     mxReslBox->clear();
333     Point aTopLeft, aBottomRight;
334     mxPreview->GetPreviewLogicRect(aTopLeft, aBottomRight);
335     Point aMinTopLeft, aMaxBottomRight;
336     mxPreview->GetMaxLogicRect(aMinTopLeft, aMaxBottomRight);
337     mxScanButton->set_visible( mbScanEnabled );
338 
339     if( ! mrSane.IsOpen() )
340         return;
341 
342     // set Resolution
343     nOption = mrSane.GetOptionByName( "resolution" );
344     if( nOption != -1 )
345     {
346         double fRes;
347 
348         if( mrSane.GetOptionValue( nOption, fRes ) )
349         {
350             mxReslBox->set_sensitive(true);
351 
352             mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fRes)));
353             std::unique_ptr<double[]> pDouble;
354             nValue = mrSane.GetRange( nOption, pDouble );
355             if( nValue > -1 )
356             {
357                 assert(pDouble);
358                 if( nValue )
359                 {
360                     for( i=0; i<nValue; i++ )
361                     {
362                         if( i == 0 || i == nValue-1 || ! ( static_cast<int>(pDouble[i]) % 20) )
363                             mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[i])));
364                     }
365                 }
366                 else
367                 {
368                     mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[0])));
369                     // Can only select 75 and 2400 dpi in Scanner dialogue
370                     // scanner allows random setting of dpi resolution, a slider might be useful
371                     // support that
372                     // workaround: offer at least some more standard dpi resolution between
373                     // min and max value
374                     int bGot300 = 0;
375                     for (sal_uInt32 nRes = static_cast<sal_uInt32>(pDouble[0]) * 2; nRes < static_cast<sal_uInt32>(pDouble[1]); nRes = nRes * 2)
376                     {
377                         if ( !bGot300 && nRes > 300 ) {
378                             nRes = 300; bGot300 = 1;
379                         }
380                         mxReslBox->append_text(OUString::number(nRes));
381                     }
382                     mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[1])));
383                 }
384             }
385             else
386                 mxReslBox->set_sensitive( false );
387         }
388     }
389     else
390         mxReslBox->set_sensitive( false );
391 
392     // set scan area
393     for( i = 0; i < 4; i++ )
394     {
395         char const *pOptionName = nullptr;
396         weld::MetricSpinButton* pField = nullptr;
397         switch( i )
398         {
399             case 0:
400                 pOptionName = "tl-x";
401                 pField = mxLeftField.get();
402                 break;
403             case 1:
404                 pOptionName = "tl-y";
405                 pField = mxTopField.get();
406                 break;
407             case 2:
408                 pOptionName = "br-x";
409                 pField = mxRightField.get();
410                 break;
411             case 3:
412                 pOptionName = "br-y";
413                 pField = mxBottomField.get();
414         }
415         nOption = pOptionName ? mrSane.GetOptionByName( pOptionName ) : -1;
416         if( nOption != -1 )
417         {
418             if( mrSane.GetOptionValue( nOption, fValue ) )
419             {
420                 if( mrSane.GetOptionUnit( nOption ) == SANE_UNIT_MM )
421                 {
422                     pField->set_unit( FieldUnit::MM );
423                     pField->set_value( static_cast<int>(fValue), FieldUnit::MM );
424                 }
425                 else // SANE_UNIT_PIXEL
426                 {
427                     pField->set_unit( FieldUnit::PIXEL );
428                     pField->set_value( static_cast<int>(fValue), FieldUnit::PIXEL );
429                 }
430                 switch( i ) {
431                     case 0: aTopLeft.setX( static_cast<int>(fValue) );break;
432                     case 1: aTopLeft.setY( static_cast<int>(fValue) );break;
433                     case 2: aBottomRight.setX( static_cast<int>(fValue) );break;
434                     case 3: aBottomRight.setY( static_cast<int>(fValue) );break;
435                 }
436             }
437             std::unique_ptr<double[]> pDouble;
438             nValue = mrSane.GetRange( nOption, pDouble );
439             if( nValue > -1 )
440             {
441                 if( pDouble )
442                 {
443                     pField->set_min( static_cast<tools::Long>(pDouble[0]), FieldUnit::NONE );
444                     if( nValue )
445                         pField->set_max( static_cast<tools::Long>(pDouble[ nValue-1 ]), FieldUnit::NONE );
446                     else
447                         pField->set_max( static_cast<tools::Long>(pDouble[ 1 ]), FieldUnit::NONE );
448                 }
449                 switch( i ) {
450                     case 0: aMinTopLeft.setX( pField->get_min(FieldUnit::NONE) );break;
451                     case 1: aMinTopLeft.setY( pField->get_min(FieldUnit::NONE) );break;
452                     case 2: aMaxBottomRight.setX( pField->get_max(FieldUnit::NONE) );break;
453                     case 3: aMaxBottomRight.setY( pField->get_max(FieldUnit::NONE) );break;
454                 }
455             }
456             else
457             {
458                 switch( i ) {
459                     case 0: aMinTopLeft.setX( static_cast<int>(fValue) );break;
460                     case 1: aMinTopLeft.setY( static_cast<int>(fValue) );break;
461                     case 2: aMaxBottomRight.setX( static_cast<int>(fValue) );break;
462                     case 3: aMaxBottomRight.setY( static_cast<int>(fValue) );break;
463                 }
464             }
465             pField->set_sensitive(true);
466         }
467         else
468         {
469             mxPreview->DisableDrag();
470             pField->set_min( 0, FieldUnit::NONE );
471             switch( i ) {
472                 case 0:
473                     aMinTopLeft.setX( 0 );
474                     aTopLeft.setX( 0 );
475                     pField->set_max( PREVIEW_WIDTH, FieldUnit::NONE );
476                     pField->set_value( 0, FieldUnit::NONE );
477                     break;
478                 case 1:
479                     aMinTopLeft.setY( 0 );
480                     aTopLeft.setY( 0 );
481                     pField->set_max( PREVIEW_HEIGHT, FieldUnit::NONE );
482                     pField->set_value( 0, FieldUnit::NONE );
483                     break;
484                 case 2:
485                     aMaxBottomRight.setX( PREVIEW_WIDTH );
486                     aBottomRight.setX( PREVIEW_WIDTH );
487                     pField->set_max( PREVIEW_WIDTH, FieldUnit::NONE );
488                     pField->set_value( PREVIEW_WIDTH, FieldUnit::NONE );
489                     break;
490                 case 3:
491                     aMaxBottomRight.setY( PREVIEW_HEIGHT );
492                     aBottomRight.setY( PREVIEW_HEIGHT );
493                     pField->set_max( PREVIEW_HEIGHT, FieldUnit::NONE );
494                     pField->set_value( PREVIEW_HEIGHT, FieldUnit::NONE );
495                     break;
496             }
497             pField->set_sensitive(false);
498         }
499     }
500 
501     mxPreview->SetPreviewMaxRect(aMinTopLeft, aMaxBottomRight);
502     mxPreview->SetPreviewLogicRect(aTopLeft, aBottomRight);
503     mxPreview->Invalidate();
504 
505     // fill OptionBox
506     mxOptionBox->clear();
507     std::unique_ptr<weld::TreeIter> xParentEntry(mxOptionBox->make_iterator());
508     bool bGroupRejected = false;
509     for( i = 1; i < mrSane.CountOptions(); i++ )
510     {
511         OUString aOption=mrSane.GetOptionName( i );
512         bool bInsertAdvanced =
513             (mrSane.GetOptionCap( i ) & SANE_CAP_ADVANCED) == 0 ||
514             mxAdvancedBox->get_active();
515         if( mrSane.GetOptionType( i ) == SANE_TYPE_GROUP )
516         {
517             if( bInsertAdvanced )
518             {
519                 aOption = mrSane.GetOptionTitle( i );
520                 mxOptionBox->append(xParentEntry.get());
521                 mxOptionBox->set_text(*xParentEntry, aOption, 0);
522                 bGroupRejected = false;
523             }
524             else
525                 bGroupRejected = true;
526         }
527         else if( !aOption.isEmpty() &&
528                  ! ( mrSane.GetOptionCap( i ) &
529                      (
530                          SANE_CAP_HARD_SELECT |
531                          SANE_CAP_INACTIVE
532                          ) ) &&
533                  bInsertAdvanced && ! bGroupRejected )
534         {
535             bool bIsSpecial = false;
536             for( size_t n = 0; !bIsSpecial &&
537                      n < SAL_N_ELEMENTS(ppSpecialOptions); n++ )
538             {
539                 if( aOption == OUString::createFromAscii(ppSpecialOptions[n]) )
540                     bIsSpecial=true;
541             }
542             if( ! bIsSpecial )
543             {
544                 if (xParentEntry)
545                     mxOptionBox->append(xParentEntry.get(), aOption);
546                 else
547                     mxOptionBox->append_text(aOption);
548             }
549         }
550     }
551 }
552 
IMPL_LINK(SaneDlg,ClickBtnHdl,weld::Button &,rButton,void)553 IMPL_LINK( SaneDlg, ClickBtnHdl, weld::Button&, rButton, void )
554 {
555     if( mrSane.IsOpen() )
556     {
557         if( &rButton == mxDeviceInfoButton.get() )
558         {
559             OUString aString(SaneResId(STR_DEVICE_DESC));
560             aString = aString.replaceFirst( "%s", Sane::GetName( mrSane.GetDeviceNumber() ) );
561             aString = aString.replaceFirst( "%s", Sane::GetVendor( mrSane.GetDeviceNumber() ) );
562             aString = aString.replaceFirst( "%s", Sane::GetModel( mrSane.GetDeviceNumber() ) );
563             aString = aString.replaceFirst( "%s", Sane::GetType( mrSane.GetDeviceNumber() ) );
564             std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
565                                                           VclMessageType::Info, VclButtonsType::Ok,
566                                                           aString));
567             xInfoBox->run();
568         }
569         else if( &rButton == mxPreviewButton.get() )
570             AcquirePreview();
571         else if( &rButton == mxButtonOption.get() )
572         {
573 
574             SANE_Value_Type nType = mrSane.GetOptionType( mnCurrentOption );
575             switch( nType )
576             {
577                 case SANE_TYPE_BUTTON:
578                     mrSane.ActivateButtonOption( mnCurrentOption );
579                     break;
580                 case SANE_TYPE_FIXED:
581                 case SANE_TYPE_INT:
582                 {
583                     int nElements = mrSane.GetOptionElements( mnCurrentOption );
584                     std::unique_ptr<double[]> x(new double[ nElements ]);
585                     std::unique_ptr<double[]> y(new double[ nElements ]);
586                     for( int i = 0; i < nElements; i++ )
587                         x[ i ] = static_cast<double>(i);
588                     mrSane.GetOptionValue( mnCurrentOption, y.get() );
589 
590                     GridDialog aGrid(m_xDialog.get(), x.get(), y.get(), nElements);
591                     aGrid.set_title( mrSane.GetOptionName( mnCurrentOption ) );
592                     aGrid.setBoundings( 0, mfMin, nElements, mfMax );
593                     if (aGrid.run() && aGrid.getNewYValues())
594                         mrSane.SetOptionValue( mnCurrentOption, aGrid.getNewYValues() );
595                 }
596                 break;
597                 case SANE_TYPE_BOOL:
598                 case SANE_TYPE_STRING:
599                 case SANE_TYPE_GROUP:
600                     break;
601             }
602         }
603     }
604     if (&rButton == mxScanButton.get())
605     {
606         double fRes = static_cast<double>(mxReslBox->get_active_text().toUInt32());
607         SetAdjustedNumericalValue( "resolution", fRes );
608         UpdateScanArea(true);
609         SaveState();
610         m_xDialog->response(mrSane.IsOpen() ? RET_OK : RET_CANCEL);
611         doScan = mrSane.IsOpen();
612     }
613     else if( &rButton == mxCancelButton.get() )
614     {
615         mrSane.Close();
616         m_xDialog->response(RET_CANCEL);
617     }
618 }
619 
IMPL_LINK(SaneDlg,ToggleBtnHdl,weld::Toggleable &,rButton,void)620 IMPL_LINK( SaneDlg, ToggleBtnHdl, weld::Toggleable&, rButton, void )
621 {
622     if( mrSane.IsOpen() )
623     {
624         if( &rButton == mxBoolCheckBox.get() )
625         {
626             mrSane.SetOptionValue( mnCurrentOption,
627                                    mxBoolCheckBox->get_active() );
628         }
629         else if( &rButton == mxAdvancedBox.get() )
630         {
631             ReloadSaneOptionsHdl( mrSane );
632         }
633     }
634 }
635 
IMPL_LINK(SaneDlg,SelectHdl,weld::ComboBox &,rListBox,void)636 IMPL_LINK( SaneDlg, SelectHdl, weld::ComboBox&, rListBox, void )
637 {
638     if( &rListBox == mxDeviceBox.get() && Sane::IsSane() && Sane::CountDevices() )
639     {
640         int nNewNumber = mxDeviceBox->get_active();
641         int nOldNumber = mrSane.GetDeviceNumber();
642         if (nNewNumber != nOldNumber)
643         {
644             mrSane.Close();
645             mrSane.Open(nNewNumber);
646             mxPreview->ResetForNewScanner();
647             InitFields();
648         }
649     }
650     if( mrSane.IsOpen() )
651     {
652         if( &rListBox == mxQuantumRangeBox.get() )
653         {
654             double fValue = mxQuantumRangeBox->get_active_text().toDouble();
655             mrSane.SetOptionValue(mnCurrentOption, fValue, mnCurrentElement);
656         }
657         else if( &rListBox == mxStringRangeBox.get() )
658         {
659             mrSane.SetOptionValue(mnCurrentOption, mxStringRangeBox->get_active_text());
660         }
661     }
662 }
663 
IMPL_LINK_NOARG(SaneDlg,OptionsBoxSelectHdl,weld::TreeView &,void)664 IMPL_LINK_NOARG(SaneDlg, OptionsBoxSelectHdl, weld::TreeView&, void)
665 {
666     if (!Sane::IsSane())
667         return;
668 
669     OUString aOption = mxOptionBox->get_selected_text();
670     int nOption = mrSane.GetOptionByName(OUStringToOString(aOption,
671         osl_getThreadTextEncoding()).getStr());
672     if( nOption == -1 || nOption == mnCurrentOption )
673         return;
674 
675     DisableOption();
676     mnCurrentOption = nOption;
677     mxOptionTitle->set_label(mrSane.GetOptionTitle(mnCurrentOption));
678     SANE_Value_Type nType = mrSane.GetOptionType( mnCurrentOption );
679     SANE_Constraint_Type nConstraint;
680     switch( nType )
681     {
682         case SANE_TYPE_BOOL:    EstablishBoolOption();break;
683         case SANE_TYPE_STRING:
684             nConstraint = mrSane.GetOptionConstraintType( mnCurrentOption );
685             if( nConstraint == SANE_CONSTRAINT_STRING_LIST )
686                 EstablishStringRange();
687             else
688                 EstablishStringOption();
689             break;
690         case SANE_TYPE_FIXED:
691         case SANE_TYPE_INT:
692         {
693             nConstraint = mrSane.GetOptionConstraintType( mnCurrentOption );
694             int nElements = mrSane.GetOptionElements( mnCurrentOption );
695             mnCurrentElement = 0;
696             if( nConstraint == SANE_CONSTRAINT_RANGE ||
697                 nConstraint == SANE_CONSTRAINT_WORD_LIST )
698                 EstablishQuantumRange();
699             else
700             {
701                 mfMin = mfMax = 0.0;
702                 EstablishNumericOption();
703             }
704             if( nElements > 1 )
705             {
706                 if( nElements <= 10 )
707                 {
708                     mxVectorBox->set_range(1, mrSane.GetOptionElements(mnCurrentOption));
709                     mxVectorBox->set_value(1);
710                     mxVectorBox->show();
711                     mxVectorTxt->show();
712                 }
713                 else
714                 {
715                     DisableOption();
716                     // bring up dialog only on button click
717                     EstablishButtonOption();
718                 }
719             }
720         }
721         break;
722         case SANE_TYPE_BUTTON:
723             EstablishButtonOption();
724             break;
725         default: break;
726     }
727 }
728 
IMPL_LINK(SaneDlg,ModifyHdl,weld::Entry &,rEdit,void)729 IMPL_LINK(SaneDlg, ModifyHdl, weld::Entry&, rEdit, void)
730 {
731     if( !mrSane.IsOpen() )
732         return;
733 
734     if (&rEdit == mxStringEdit.get())
735     {
736         mrSane.SetOptionValue( mnCurrentOption, mxStringEdit->get_text() );
737     }
738     else if (&rEdit == mxNumericEdit.get())
739     {
740         double fValue = mxNumericEdit->get_text().toDouble();
741         if( mfMin != mfMax && ( fValue < mfMin || fValue > mfMax ) )
742         {
743             char pBuf[256];
744             if( fValue < mfMin )
745                 fValue = mfMin;
746             else if( fValue > mfMax )
747                 fValue = mfMax;
748             sprintf( pBuf, "%g", fValue );
749             mxNumericEdit->set_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
750         }
751         mrSane.SetOptionValue( mnCurrentOption, fValue, mnCurrentElement );
752     }
753     else if (&rEdit == mxVectorBox.get())
754     {
755         mnCurrentElement = mxVectorBox->get_value() - 1;
756         double fValue;
757         if( mrSane.GetOptionValue( mnCurrentOption, fValue, mnCurrentElement ))
758         {
759             char pBuf[256];
760             sprintf( pBuf, "%g", fValue );
761             OUString aValue( pBuf, strlen(pBuf), osl_getThreadTextEncoding() );
762             mxNumericEdit->set_text( aValue );
763             mxQuantumRangeBox->set_active_text( aValue );
764         }
765     }
766 }
767 
IMPL_LINK(SaneDlg,ValueModifyHdl,weld::ComboBox &,rEdit,void)768 IMPL_LINK(SaneDlg, ValueModifyHdl, weld::ComboBox&, rEdit, void)
769 {
770     if( !mrSane.IsOpen() )
771         return;
772 
773     if (&rEdit != mxReslBox.get())
774         return;
775 
776     double fRes = static_cast<double>(mxReslBox->get_active_text().toUInt32());
777     int nOption = mrSane.GetOptionByName( "resolution" );
778     if( nOption == -1 )
779         return;
780 
781     std::unique_ptr<double[]> pDouble;
782     int nValues = mrSane.GetRange( nOption, pDouble );
783     if( nValues > 0 )
784     {
785         int i;
786         for( i = 0; i < nValues; i++ )
787         {
788             if( fRes == pDouble[i] )
789                 break;
790         }
791         if( i >= nValues )
792             fRes = pDouble[0];
793     }
794     else if( nValues == 0 )
795     {
796         if( fRes < pDouble[ 0 ] )
797             fRes = pDouble[ 0 ];
798         if( fRes > pDouble[ 1 ] )
799             fRes = pDouble[ 1 ];
800     }
801     mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fRes)));
802 }
803 
IMPL_LINK(SaneDlg,MetricValueModifyHdl,weld::MetricSpinButton &,rEdit,void)804 IMPL_LINK(SaneDlg, MetricValueModifyHdl, weld::MetricSpinButton&, rEdit, void)
805 {
806     if( !mrSane.IsOpen() )
807         return;
808 
809     if (&rEdit == mxTopField.get())
810     {
811         mxPreview->ChangePreviewLogicTopLeftY(mxTopField->get_value(FieldUnit::NONE));
812         mxPreview->Invalidate();
813     }
814     else if (&rEdit == mxLeftField.get())
815     {
816         mxPreview->ChangePreviewLogicTopLeftX(mxLeftField->get_value(FieldUnit::NONE));
817         mxPreview->Invalidate();
818     }
819     else if (&rEdit == mxBottomField.get())
820     {
821         mxPreview->ChangePreviewLogicBottomRightY(mxBottomField->get_value(FieldUnit::NONE));
822         mxPreview->Invalidate();
823     }
824     else if (&rEdit == mxRightField.get())
825     {
826         mxPreview->ChangePreviewLogicBottomRightX(mxRightField->get_value(FieldUnit::NONE));
827         mxPreview->Invalidate();
828     }
829 }
830 
IMPL_LINK_NOARG(SaneDlg,ReloadSaneOptionsHdl,Sane &,void)831 IMPL_LINK_NOARG( SaneDlg, ReloadSaneOptionsHdl, Sane&, void )
832 {
833     mnCurrentOption = -1;
834     mnCurrentElement = 0;
835     DisableOption();
836     InitFields();
837     mxPreview->Invalidate();
838 }
839 
AcquirePreview()840 void SaneDlg::AcquirePreview()
841 {
842     if( ! mrSane.IsOpen() )
843         return;
844 
845     UpdateScanArea( true );
846     // set small resolution for preview
847     double fResl = static_cast<double>(mxReslBox->get_active_text().toUInt32());
848     SetAdjustedNumericalValue( "resolution", 30.0 );
849 
850     int nOption = mrSane.GetOptionByName( "preview" );
851     if( nOption == -1 )
852     {
853         OUString aString(SaneResId(STR_SLOW_PREVIEW));
854         std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
855                                                   VclMessageType::Warning, VclButtonsType::OkCancel,
856                                                   aString));
857         if (xBox->run() == RET_CANCEL)
858             return;
859     }
860     else
861         mrSane.SetOptionValue( nOption, true );
862 
863     rtl::Reference<BitmapTransporter> xTransporter(new BitmapTransporter);
864     if (!mrSane.Start(*xTransporter))
865     {
866         std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_xDialog.get(),
867                                                        VclMessageType::Warning, VclButtonsType::Ok,
868                                                        SaneResId(STR_ERROR_SCAN)));
869         xErrorBox->run();
870     }
871     else
872     {
873 #if OSL_DEBUG_LEVEL > 0
874         xTransporter->getStream().Seek( STREAM_SEEK_TO_END );
875         SAL_INFO("extensions.scanner", "Previewbitmapstream contains " << xTransporter->getStream().Tell() << "bytes");
876 #endif
877         xTransporter->getStream().Seek( STREAM_SEEK_TO_BEGIN );
878         mxPreview->SetBitmap(xTransporter->getStream());
879     }
880 
881     SetAdjustedNumericalValue( "resolution", fResl );
882     mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fResl)));
883 
884     mxPreview->UpdatePreviewBounds();
885     mxPreview->Invalidate();
886 }
887 
UpdatePreviewBounds()888 void ScanPreview::UpdatePreviewBounds()
889 {
890     if( mbDragEnable )
891     {
892         maPreviewRect = tools::Rectangle( maTopLeft,
893                                    Size( maBottomRight.X() - maTopLeft.X(),
894                                          maBottomRight.Y() - maTopLeft.Y() )
895                                    );
896     }
897     else
898     {
899         Size aBMSize( maPreviewBitmapEx.GetSizePixel() );
900         if( aBMSize.Width() > aBMSize.Height() && aBMSize.Width() )
901         {
902             int nVHeight = (maBottomRight.X() - maTopLeft.X()) * aBMSize.Height() / aBMSize.Width();
903             maPreviewRect = tools::Rectangle( Point( maTopLeft.X(), ( maTopLeft.Y() + maBottomRight.Y() )/2 - nVHeight/2 ),
904                                        Size( maBottomRight.X() - maTopLeft.X(),
905                                              nVHeight ) );
906         }
907         else if (aBMSize.Height())
908         {
909             int nVWidth = (maBottomRight.Y() - maTopLeft.Y()) * aBMSize.Width() / aBMSize.Height();
910             maPreviewRect = tools::Rectangle( Point( ( maTopLeft.X() + maBottomRight.X() )/2 - nVWidth/2, maTopLeft.Y() ),
911                                        Size( nVWidth,
912                                              maBottomRight.Y() - maTopLeft.Y() ) );
913         }
914     }
915 }
916 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)917 void ScanPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
918 {
919     rRenderContext.SetMapMode(MapMode(MapUnit::MapAppFont));
920     rRenderContext.SetFillColor(COL_WHITE);
921     rRenderContext.SetLineColor(COL_WHITE);
922     rRenderContext.DrawRect(tools::Rectangle(Point(0, 0),
923                                       Size(PREVIEW_WIDTH, PREVIEW_HEIGHT)));
924     rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
925     // check for sane values
926     rRenderContext.DrawBitmapEx(maPreviewRect.TopLeft(), maPreviewRect.GetSize(), maPreviewBitmapEx);
927 
928     mbDragDrawn = false;
929     DrawDrag(rRenderContext);
930 }
931 
DisableOption()932 void SaneDlg::DisableOption()
933 {
934     mxBoolCheckBox->hide();
935     mxStringEdit->hide();
936     mxNumericEdit->hide();
937     mxQuantumRangeBox->hide();
938     mxStringRangeBox->hide();
939     mxButtonOption->hide();
940     mxVectorBox->hide();
941     mxVectorTxt->hide();
942     mxOptionDescTxt->hide();
943 }
944 
EstablishBoolOption()945 void SaneDlg::EstablishBoolOption()
946 {
947     bool bSuccess, bValue;
948 
949     bSuccess = mrSane.GetOptionValue( mnCurrentOption, bValue );
950     if( bSuccess )
951     {
952         mxBoolCheckBox->set_label( mrSane.GetOptionName( mnCurrentOption ) );
953         mxBoolCheckBox->set_active( bValue );
954         mxBoolCheckBox->show();
955     }
956 }
957 
EstablishStringOption()958 void SaneDlg::EstablishStringOption()
959 {
960     bool bSuccess;
961     OString aValue;
962 
963     bSuccess = mrSane.GetOptionValue( mnCurrentOption, aValue );
964     if( bSuccess )
965     {
966         mxOptionDescTxt->set_label( mrSane.GetOptionName( mnCurrentOption ) );
967         mxOptionDescTxt->show();
968         mxStringEdit->set_text(OStringToOUString(aValue, osl_getThreadTextEncoding()));
969         mxStringEdit->show();
970     }
971 }
972 
EstablishStringRange()973 void SaneDlg::EstablishStringRange()
974 {
975     const char** ppStrings = mrSane.GetStringConstraint( mnCurrentOption );
976     mxStringRangeBox->clear();
977     for( int i = 0; ppStrings[i] != nullptr; i++ )
978         mxStringRangeBox->append_text( OUString( ppStrings[i], strlen(ppStrings[i]), osl_getThreadTextEncoding() ) );
979     OString aValue;
980     mrSane.GetOptionValue( mnCurrentOption, aValue );
981     mxStringRangeBox->set_active_text(OStringToOUString(aValue, osl_getThreadTextEncoding()));
982     mxStringRangeBox->show();
983     mxOptionDescTxt->set_label( mrSane.GetOptionName( mnCurrentOption ) );
984     mxOptionDescTxt->show();
985 }
986 
EstablishQuantumRange()987 void SaneDlg::EstablishQuantumRange()
988 {
989     mpRange.reset();
990     int nValues = mrSane.GetRange( mnCurrentOption, mpRange );
991     if( nValues == 0 )
992     {
993         mfMin = mpRange[ 0 ];
994         mfMax = mpRange[ 1 ];
995         mpRange.reset();
996         EstablishNumericOption();
997     }
998     else if( nValues > 0 )
999     {
1000         char pBuf[ 256 ];
1001         mxQuantumRangeBox->clear();
1002         mfMin = mpRange[ 0 ];
1003         mfMax = mpRange[ nValues-1 ];
1004         for( int i = 0; i < nValues; i++ )
1005         {
1006             sprintf( pBuf, "%g", mpRange[ i ] );
1007             mxQuantumRangeBox->append_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
1008         }
1009         double fValue;
1010         if( mrSane.GetOptionValue( mnCurrentOption, fValue, mnCurrentElement ) )
1011         {
1012             sprintf( pBuf, "%g", fValue );
1013             mxQuantumRangeBox->set_active_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
1014         }
1015         mxQuantumRangeBox->show();
1016         OUString aText( mrSane.GetOptionName( mnCurrentOption ) + " " );
1017         aText += mrSane.GetOptionUnitName( mnCurrentOption );
1018         mxOptionDescTxt->set_label(aText);
1019         mxOptionDescTxt->show();
1020     }
1021 }
1022 
EstablishNumericOption()1023 void SaneDlg::EstablishNumericOption()
1024 {
1025     bool bSuccess;
1026     double fValue;
1027 
1028     bSuccess = mrSane.GetOptionValue( mnCurrentOption, fValue );
1029     if( ! bSuccess )
1030         return;
1031 
1032     char pBuf[256];
1033     OUString aText( mrSane.GetOptionName( mnCurrentOption ) + " " );
1034     aText += mrSane.GetOptionUnitName( mnCurrentOption );
1035     if( mfMin != mfMax )
1036     {
1037         sprintf( pBuf, " < %g ; %g >", mfMin, mfMax );
1038         aText += OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() );
1039     }
1040     mxOptionDescTxt->set_label( aText );
1041     mxOptionDescTxt->show();
1042     sprintf( pBuf, "%g", fValue );
1043     mxNumericEdit->set_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
1044     mxNumericEdit->show();
1045 }
1046 
EstablishButtonOption()1047 void SaneDlg::EstablishButtonOption()
1048 {
1049     mxOptionDescTxt->set_label(mrSane.GetOptionName(mnCurrentOption));
1050     mxOptionDescTxt->show();
1051     mxButtonOption->show();
1052 }
1053 
MouseMove(const MouseEvent & rMEvt)1054 bool ScanPreview::MouseMove(const MouseEvent& rMEvt)
1055 {
1056     if( mbIsDragging )
1057     {
1058         Point aMousePos = rMEvt.GetPosPixel();
1059         // move into valid area
1060         Point aLogicPos = GetLogicPos( aMousePos );
1061         aMousePos = GetPixelPos( aLogicPos );
1062         switch( meDragDirection )
1063         {
1064             case TopLeft:       maTopLeft = aMousePos; break;
1065             case Top:           maTopLeft.setY( aMousePos.Y() ); break;
1066             case TopRight:
1067                 maTopLeft.setY( aMousePos.Y() );
1068                 maBottomRight.setX( aMousePos.X() );
1069                 break;
1070             case Right:         maBottomRight.setX( aMousePos.X() ); break;
1071             case BottomRight:   maBottomRight = aMousePos; break;
1072             case Bottom:        maBottomRight.setY( aMousePos.Y() ); break;
1073             case BottomLeft:
1074                 maTopLeft.setX( aMousePos.X() );
1075                 maBottomRight.setY( aMousePos.Y() );
1076                 break;
1077             case Left:          maTopLeft.setX( aMousePos.X() ); break;
1078             default: break;
1079         }
1080         int nSwap;
1081         if( maTopLeft.X() > maBottomRight.X() )
1082         {
1083             nSwap = maTopLeft.X();
1084             maTopLeft.setX( maBottomRight.X() );
1085             maBottomRight.setX( nSwap );
1086         }
1087         if( maTopLeft.Y() > maBottomRight.Y() )
1088         {
1089             nSwap = maTopLeft.Y();
1090             maTopLeft.setY( maBottomRight.Y() );
1091             maBottomRight.setY( nSwap );
1092         }
1093         Invalidate();
1094         mpParentDialog->UpdateScanArea(false);
1095     }
1096     return false;
1097 }
1098 
MouseButtonDown(const MouseEvent & rMEvt)1099 bool ScanPreview::MouseButtonDown( const MouseEvent& rMEvt )
1100 {
1101     if (!mbIsDragging && mbDragEnable)
1102     {
1103         Point aMousePixel = rMEvt.GetPosPixel();
1104 
1105         int nMiddleX = ( maBottomRight.X() - maTopLeft.X() ) / 2 - RECT_SIZE_PIX/2 + maTopLeft.X();
1106         int nMiddleY = ( maBottomRight.Y() - maTopLeft.Y() ) / 2 - RECT_SIZE_PIX/2 + maTopLeft.Y();
1107         if( aMousePixel.Y() >= maTopLeft.Y() &&
1108             aMousePixel.Y() < maTopLeft.Y() + RECT_SIZE_PIX )
1109         {
1110             if( aMousePixel.X() >= maTopLeft.X() &&
1111                 aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
1112             {
1113                 meDragDirection = TopLeft;
1114                 mbIsDragging = true;
1115             }
1116             else if( aMousePixel.X() >= nMiddleX &&
1117                      aMousePixel.X() < nMiddleX + RECT_SIZE_PIX )
1118             {
1119                 meDragDirection = Top;
1120                 mbIsDragging = true;
1121             }
1122             else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
1123                      aMousePixel.X() <= maBottomRight.X() )
1124             {
1125                 meDragDirection = TopRight;
1126                 mbIsDragging = true;
1127             }
1128         }
1129         else if( aMousePixel.Y() >= nMiddleY &&
1130                  aMousePixel.Y() < nMiddleY + RECT_SIZE_PIX )
1131         {
1132             if( aMousePixel.X() >= maTopLeft.X() &&
1133                 aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
1134             {
1135                 meDragDirection = Left;
1136                 mbIsDragging = true;
1137             }
1138             else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
1139                      aMousePixel.X() <= maBottomRight.X() )
1140             {
1141                 meDragDirection = Right;
1142                 mbIsDragging = true;
1143             }
1144         }
1145         else if( aMousePixel.Y() <= maBottomRight.Y() &&
1146                  aMousePixel.Y() > maBottomRight.Y() - RECT_SIZE_PIX )
1147         {
1148             if( aMousePixel.X() >= maTopLeft.X() &&
1149                 aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
1150             {
1151                 meDragDirection = BottomLeft;
1152                 mbIsDragging = true;
1153             }
1154             else if( aMousePixel.X() >= nMiddleX &&
1155                      aMousePixel.X() < nMiddleX + RECT_SIZE_PIX )
1156             {
1157                 meDragDirection = Bottom;
1158                 mbIsDragging = true;
1159             }
1160             else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
1161                      aMousePixel.X() <= maBottomRight.X() )
1162             {
1163                 meDragDirection = BottomRight;
1164                 mbIsDragging = true;
1165             }
1166         }
1167     }
1168 
1169     if( mbIsDragging )
1170         Invalidate();
1171 
1172     return false;
1173 }
1174 
MouseButtonUp(const MouseEvent &)1175 bool ScanPreview::MouseButtonUp(const MouseEvent&)
1176 {
1177     if( mbIsDragging )
1178         mpParentDialog->UpdateScanArea(true);
1179     mbIsDragging = false;
1180 
1181     return false;
1182 }
1183 
DrawDrag(vcl::RenderContext & rRenderContext)1184 void ScanPreview::DrawDrag(vcl::RenderContext& rRenderContext)
1185 {
1186     if (!mbDragEnable)
1187         return;
1188 
1189     RasterOp eROP = rRenderContext.GetRasterOp();
1190     rRenderContext.SetRasterOp(RasterOp::Invert);
1191     rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
1192 
1193     if (mbDragDrawn)
1194         DrawRectangles(rRenderContext, maLastUL, maLastBR);
1195 
1196     maLastUL = maTopLeft;
1197     maLastBR = maBottomRight;
1198     DrawRectangles(rRenderContext, maTopLeft, maBottomRight);
1199 
1200     mbDragDrawn = true;
1201     rRenderContext.SetRasterOp(eROP);
1202     rRenderContext.SetMapMode(MapMode(MapUnit::MapAppFont));
1203 }
1204 
GetPixelPos(const Point & rIn) const1205 Point ScanPreview::GetPixelPos( const Point& rIn) const
1206 {
1207     Point aConvert(
1208         ( ( rIn.X() * PREVIEW_WIDTH ) /
1209           ( maMaxBottomRight.X() - maMinTopLeft.X() ) )
1210         ,
1211         ( ( rIn.Y() * PREVIEW_HEIGHT )
1212           / ( maMaxBottomRight.Y() - maMinTopLeft.Y() ) )
1213         );
1214 
1215     return GetDrawingArea()->get_ref_device().LogicToPixel(aConvert, MapMode(MapUnit::MapAppFont));
1216 }
1217 
GetLogicPos(const Point & rIn) const1218 Point ScanPreview::GetLogicPos(const Point& rIn) const
1219 {
1220     Point aConvert = GetDrawingArea()->get_ref_device().PixelToLogic(rIn, MapMode(MapUnit::MapAppFont));
1221     if( aConvert.X() < 0 )
1222         aConvert.setX( 0 );
1223     if( aConvert.X() >= PREVIEW_WIDTH )
1224         aConvert.setX( PREVIEW_WIDTH-1 );
1225     if( aConvert.Y() < 0 )
1226         aConvert.setY( 0 );
1227     if( aConvert.Y() >= PREVIEW_HEIGHT )
1228         aConvert.setY( PREVIEW_HEIGHT-1 );
1229 
1230     aConvert.setX( aConvert.X() * ( maMaxBottomRight.X() - maMinTopLeft.X() ) );
1231     aConvert.setX( aConvert.X() / ( PREVIEW_WIDTH) );
1232     aConvert.setY( aConvert.Y() * ( maMaxBottomRight.Y() - maMinTopLeft.Y() ) );
1233     aConvert.setY( aConvert.Y() / ( PREVIEW_HEIGHT) );
1234     return aConvert;
1235 }
1236 
UpdateScanArea(bool bSend)1237 void SaneDlg::UpdateScanArea(bool bSend)
1238 {
1239     if (!mxPreview->IsDragEnabled())
1240         return;
1241 
1242     Point aUL, aBR;
1243     mxPreview->GetPreviewLogicRect(aUL, aBR);
1244 
1245     mxLeftField->set_value(aUL.X(), FieldUnit::NONE);
1246     mxTopField->set_value(aUL.Y(), FieldUnit::NONE);
1247     mxRightField->set_value(aBR.X(), FieldUnit::NONE);
1248     mxBottomField->set_value(aBR.Y(), FieldUnit::NONE);
1249 
1250     if (!bSend)
1251         return;
1252 
1253     if( mrSane.IsOpen() )
1254     {
1255         SetAdjustedNumericalValue( "tl-x", static_cast<double>(aUL.X()) );
1256         SetAdjustedNumericalValue( "tl-y", static_cast<double>(aUL.Y()) );
1257         SetAdjustedNumericalValue( "br-x", static_cast<double>(aBR.X()) );
1258         SetAdjustedNumericalValue( "br-y", static_cast<double>(aBR.Y()) );
1259     }
1260 }
1261 
LoadState()1262 bool SaneDlg::LoadState()
1263 {
1264     int i;
1265 
1266     if( ! Sane::IsSane() )
1267         return false;
1268 
1269     const char* pEnv = getenv("HOME");
1270     OUString aFileName = (pEnv ? OUString(pEnv, strlen(pEnv), osl_getThreadTextEncoding() ) : OUString()) + "/.so_sane_state";
1271     Config aConfig( aFileName );
1272     if( ! aConfig.HasGroup( "SANE" ) )
1273         return false;
1274 
1275     aConfig.SetGroup( "SANE" );
1276     OString aString = aConfig.ReadKey( "SO_LastSaneDevice" );
1277     for( i = 0; i < Sane::CountDevices() && aString != OUStringToOString(Sane::GetName(i), osl_getThreadTextEncoding()); i++ ) ;
1278     if( i == Sane::CountDevices() )
1279         return false;
1280 
1281     mrSane.Close();
1282     mrSane.Open( aString.getStr() );
1283 
1284     DisableOption();
1285     InitFields();
1286 
1287     if( mrSane.IsOpen() )
1288     {
1289         int iMax = aConfig.GetKeyCount();
1290         for (i = 0; i < iMax; ++i)
1291         {
1292             aString = aConfig.GetKeyName( i );
1293             OString aValue = aConfig.ReadKey( i );
1294             int nOption = mrSane.GetOptionByName( aString.getStr() );
1295             if( nOption == -1 )
1296                 continue;
1297 
1298             if (aValue.startsWith("BOOL="))
1299             {
1300                 aValue = aValue.copy(RTL_CONSTASCII_LENGTH("BOOL="));
1301                 bool aBOOL = aValue.toInt32() != 0;
1302                 mrSane.SetOptionValue( nOption, aBOOL );
1303             }
1304             else if (aValue.startsWith("STRING="))
1305             {
1306                 aValue = aValue.copy(RTL_CONSTASCII_LENGTH("STRING="));
1307                 mrSane.SetOptionValue(nOption,OStringToOUString(aValue, osl_getThreadTextEncoding()) );
1308             }
1309             else if (aValue.startsWith("NUMERIC="))
1310             {
1311                 aValue = aValue.copy(RTL_CONSTASCII_LENGTH("NUMERIC="));
1312 
1313                 sal_Int32 nIndex = 0;
1314                 int n = 0;
1315                 do
1316                 {
1317                     OString aSub = aValue.getToken(0, ':', nIndex);
1318                     double fValue=0.0;
1319                     sscanf(aSub.getStr(), "%lg", &fValue);
1320                     SetAdjustedNumericalValue(aString.getStr(), fValue, n++);
1321                 }
1322                 while ( nIndex >= 0 );
1323             }
1324         }
1325     }
1326 
1327     DisableOption();
1328     InitFields();
1329 
1330     return true;
1331 }
1332 
SaveState()1333 void SaneDlg::SaveState()
1334 {
1335     if( ! Sane::IsSane() )
1336         return;
1337 
1338     const char* pEnv = getenv( "HOME" );
1339     OUString aFileName;
1340 
1341     if( pEnv )
1342         aFileName = OUString::createFromAscii(pEnv) + "/.so_sane_state";
1343     else
1344         aFileName = OStringToOUString("", osl_getThreadTextEncoding()) + "/.so_sane_state";
1345 
1346     Config aConfig( aFileName );
1347     aConfig.DeleteGroup( "SANE" );
1348     aConfig.SetGroup( "SANE" );
1349     aConfig.WriteKey( "SO_LastSANEDevice",
1350         OUStringToOString(mxDeviceBox->get_active_text(), RTL_TEXTENCODING_UTF8) );
1351 
1352     static char const* pSaveOptions[] = {
1353         "resolution",
1354         "tl-x",
1355         "tl-y",
1356         "br-x",
1357         "br-y"
1358     };
1359     for(const char * pSaveOption : pSaveOptions)
1360     {
1361         OString aOption = pSaveOption;
1362         int nOption = mrSane.GetOptionByName( pSaveOption );
1363         if( nOption > -1 )
1364         {
1365             SANE_Value_Type nType = mrSane.GetOptionType( nOption );
1366             switch( nType )
1367             {
1368                 case SANE_TYPE_BOOL:
1369                 {
1370                     bool bValue;
1371                     if( mrSane.GetOptionValue( nOption, bValue ) )
1372                     {
1373                         OString aString = "BOOL=" + OString::number(static_cast<sal_Int32>(bValue));
1374                         aConfig.WriteKey(aOption, aString);
1375                     }
1376                 }
1377                 break;
1378                 case SANE_TYPE_STRING:
1379                 {
1380                     OString aValue;
1381                     if( mrSane.GetOptionValue( nOption, aValue ) )
1382                     {
1383                         OString aString = "STRING=" + aValue;
1384                         aConfig.WriteKey( aOption, aString );
1385                     }
1386                 }
1387                 break;
1388                 case SANE_TYPE_FIXED:
1389                 case SANE_TYPE_INT:
1390                 {
1391                     OStringBuffer aString("NUMERIC=");
1392                     double fValue;
1393                     char buf[256];
1394                     int n;
1395 
1396                     for( n = 0; n < mrSane.GetOptionElements( nOption ); n++ )
1397                     {
1398                         if( ! mrSane.GetOptionValue( nOption, fValue, n ) )
1399                             break;
1400                         if( n > 0 )
1401                             aString.append(':');
1402                         sprintf( buf, "%lg", fValue );
1403                         aString.append(buf);
1404                     }
1405                     if( n >= mrSane.GetOptionElements( nOption ) )
1406                         aConfig.WriteKey( aOption, aString.makeStringAndClear() );
1407                 }
1408                 break;
1409                 default:
1410                     break;
1411             }
1412         }
1413     }
1414 }
1415 
SetAdjustedNumericalValue(const char * pOption,double fValue,int nElement)1416 bool SaneDlg::SetAdjustedNumericalValue(
1417     const char* pOption,
1418     double fValue,
1419     int nElement )
1420 {
1421     if (! Sane::IsSane() || ! mrSane.IsOpen())
1422         return false;
1423     int const nOption(mrSane.GetOptionByName(pOption));
1424     if (nOption == -1)
1425         return false;
1426 
1427     if( nElement < 0 || nElement >= mrSane.GetOptionElements( nOption ) )
1428         return false;
1429 
1430     std::unique_ptr<double[]> pValues;
1431     int nValues;
1432     if( ( nValues = mrSane.GetRange( nOption, pValues ) ) < 0 )
1433     {
1434         return false;
1435     }
1436 
1437     SAL_INFO("extensions.scanner", "SaneDlg::SetAdjustedNumericalValue(\"" << pOption << "\", " << fValue << ") ");
1438 
1439     if( nValues )
1440     {
1441         int nNearest = 0;
1442         double fNearest = 1e6;
1443         for( int i = 0; i < nValues; i++ )
1444         {
1445             if( fabs( fValue - pValues[ i ] ) < fNearest )
1446             {
1447                 fNearest = fabs( fValue - pValues[ i ] );
1448                 nNearest = i;
1449             }
1450         }
1451         fValue = pValues[ nNearest ];
1452     }
1453     else
1454     {
1455         if( fValue < pValues[0] )
1456             fValue = pValues[0];
1457         if( fValue > pValues[1] )
1458             fValue = pValues[1];
1459     }
1460     mrSane.SetOptionValue( nOption, fValue, nElement );
1461     SAL_INFO("extensions.scanner", "yields " << fValue);
1462 
1463 
1464     return true;
1465 }
1466 
1467 
1468 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1469