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