1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "sal/config.h"
24 #include <math.h> // for M_LN10 and M_E
33 ResId
SaneResId( sal_uInt32
);
35 /***********************************************************************
39 ***********************************************************************/
41 // ---------------------------------------------------------------------
43 GridWindow::GridWindow(double* pXValues
, double* pYValues
, int nValues
, Window
* pParent
, sal_Bool bCutValues
)
44 : ModalDialog( pParent
, SaneResId( GRID_DIALOG
) ),
45 m_aGridArea( 50, 15, 100, 100 ),
46 m_pXValues( pXValues
),
47 m_pOrigYValues( pYValues
),
49 m_pNewYValues( NULL
),
50 m_bCutValues( bCutValues
),
52 m_nDragIndex( 0xffffffff ),
53 m_aMarkerBitmap( Bitmap( SaneResId( GRID_DIALOG_HANDLE_BMP
) ), Color( 255, 255, 255 ) ),
54 m_aOKButton( this, SaneResId( GRID_DIALOG_OK_BTN
) ),
55 m_aCancelButton( this, SaneResId( GRID_DIALOG_CANCEL_BTN
) ),
56 m_aResetTypeBox( this, SaneResId( GRID_DIALOG_TYPE_BOX
) ),
57 m_aResetButton( this, SaneResId( GRID_DIALOG_RESET_BTN
) )
59 sal_uInt16 nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_LINEAR_ASCENDING
) ) );
60 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_LINEAR_ASCENDING
);
62 nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_LINEAR_DESCENDING
) ) );
63 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_LINEAR_DESCENDING
);
65 nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_RESET
) ) );
66 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_RESET
);
68 nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_EXPONENTIAL
) ) );
69 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_EXPONENTIAL
);
71 m_aResetTypeBox
.SelectEntryPos( 0 );
73 m_aResetButton
.SetClickHdl( LINK( this, GridWindow
, ClickButtonHdl
) );
75 SetMapMode( MapMode( MAP_PIXEL
) );
76 Size aSize
= GetOutputSizePixel();
77 Size aBtnSize
= m_aOKButton
.GetOutputSizePixel();
78 m_aGridArea
.setWidth( aSize
.Width() - aBtnSize
.Width() - 80 );
79 m_aGridArea
.setHeight( aSize
.Height() - 40 );
81 if( m_pOrigYValues
&& m_nValues
)
83 m_pNewYValues
= new double[ m_nValues
];
84 memcpy( m_pNewYValues
, m_pOrigYValues
, sizeof( double ) * m_nValues
);
87 setBoundings( 0, 0, 1023, 1023 );
90 // create left and right marker as first and last entry
91 m_BmOffX
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Width() >> 1);
92 m_BmOffY
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Height() >> 1);
93 m_aHandles
.push_back(impHandle(transform(findMinX(), findMinY()), m_BmOffX
, m_BmOffY
));
94 m_aHandles
.push_back(impHandle(transform(findMaxX(), findMaxY()), m_BmOffX
, m_BmOffY
));
99 // ---------------------------------------------------------------------
101 GridWindow::~GridWindow()
104 delete [] m_pNewYValues
;
107 // ---------------------------------------------------------------------
109 double GridWindow::findMinX()
113 double fMin
= m_pXValues
[0];
114 for( int i
= 1; i
< m_nValues
; i
++ )
115 if( m_pXValues
[ i
] < fMin
)
116 fMin
= m_pXValues
[ i
];
120 // ---------------------------------------------------------------------
122 double GridWindow::findMinY()
124 if( ! m_pNewYValues
)
126 double fMin
= m_pNewYValues
[0];
127 for( int i
= 1; i
< m_nValues
; i
++ )
128 if( m_pNewYValues
[ i
] < fMin
)
129 fMin
= m_pNewYValues
[ i
];
133 // ---------------------------------------------------------------------
135 double GridWindow::findMaxX()
139 double fMax
= m_pXValues
[0];
140 for( int i
= 1; i
< m_nValues
; i
++ )
141 if( m_pXValues
[ i
] > fMax
)
142 fMax
= m_pXValues
[ i
];
146 // ---------------------------------------------------------------------
148 double GridWindow::findMaxY()
150 if( ! m_pNewYValues
)
152 double fMax
= m_pNewYValues
[0];
153 for( int i
= 1; i
< m_nValues
; i
++ )
154 if( m_pNewYValues
[ i
] > fMax
)
155 fMax
= m_pNewYValues
[ i
];
159 // ---------------------------------------------------------------------
161 void GridWindow::computeExtremes()
163 if( m_nValues
&& m_pXValues
&& m_pOrigYValues
)
165 m_fMaxX
= m_fMinX
= m_pXValues
[0];
166 m_fMaxY
= m_fMinY
= m_pOrigYValues
[0];
167 for( int i
= 1; i
< m_nValues
; i
++ )
169 if( m_pXValues
[ i
] > m_fMaxX
)
170 m_fMaxX
= m_pXValues
[ i
];
171 else if( m_pXValues
[ i
] < m_fMinX
)
172 m_fMinX
= m_pXValues
[ i
];
173 if( m_pOrigYValues
[ i
] > m_fMaxY
)
174 m_fMaxY
= m_pOrigYValues
[ i
];
175 else if( m_pOrigYValues
[ i
] < m_fMinY
)
176 m_fMinY
= m_pOrigYValues
[ i
];
178 setBoundings( m_fMinX
, m_fMinY
, m_fMaxX
, m_fMaxY
);
182 // ---------------------------------------------------------------------
184 Point
GridWindow::transform( double x
, double y
)
188 aRet
.X() = (long)( ( x
- m_fMinX
) *
189 (double)m_aGridArea
.GetWidth() / ( m_fMaxX
- m_fMinX
)
190 + m_aGridArea
.Left() );
192 m_aGridArea
.Bottom() -
194 (double)m_aGridArea
.GetHeight() / ( m_fMaxY
- m_fMinY
) );
198 // ---------------------------------------------------------------------
200 void GridWindow::transform( const Point
& rOriginal
, double& x
, double& y
)
202 x
= ( rOriginal
.X() - m_aGridArea
.Left() ) * (m_fMaxX
- m_fMinX
) / (double)m_aGridArea
.GetWidth() + m_fMinX
;
203 y
= ( m_aGridArea
.Bottom() - rOriginal
.Y() ) * (m_fMaxY
- m_fMinY
) / (double)m_aGridArea
.GetHeight() + m_fMinY
;
206 // ---------------------------------------------------------------------
208 void GridWindow::drawLine( double x1
, double y1
, double x2
, double y2
)
210 DrawLine( transform( x1
, y1
), transform( x2
, y2
) );
213 // ---------------------------------------------------------------------
215 void GridWindow::computeChunk( double fMin
, double fMax
, double& fChunkOut
, double& fMinChunkOut
)
217 // get a nice chunk size like 10, 100, 25 or such
218 fChunkOut
= ( fMax
- fMin
) / 6.0;
219 int logchunk
= (int)std::log10( fChunkOut
);
220 int nChunk
= (int)( fChunkOut
/ std::exp( (double)(logchunk
-1) * M_LN10
) );
223 else if( nChunk
>= 35 )
225 else if ( nChunk
> 20 )
227 else if ( nChunk
>= 13 )
229 else if( nChunk
> 5 )
233 fChunkOut
= (double) nChunk
* exp( (double)(logchunk
-1) * M_LN10
);
234 // compute whole chunks fitting into fMin
235 nChunk
= (int)( fMin
/ fChunkOut
);
236 fMinChunkOut
= (double)nChunk
* fChunkOut
;
237 while( fMinChunkOut
< fMin
)
238 fMinChunkOut
+= fChunkOut
;
241 // ---------------------------------------------------------------------
243 void GridWindow::computeNew()
245 if(2L == m_aHandles
.size())
247 // special case: only left and right markers
249 double xright
, yright
;
250 transform(m_aHandles
[0L].maPos
, xleft
, yleft
);
251 transform(m_aHandles
[1L].maPos
, xright
, yright
);
252 double factor
= (yright
-yleft
)/(xright
-xleft
);
253 for( int i
= 0; i
< m_nValues
; i
++ )
255 m_pNewYValues
[ i
] = yleft
+ ( m_pXValues
[ i
] - xleft
)*factor
;
261 std::sort(m_aHandles
.begin(), m_aHandles
.end());
262 const int nSorted
= m_aHandles
.size();
266 double* nodex
= new double[ nSorted
];
267 double* nodey
= new double[ nSorted
];
269 for( i
= 0L; i
< nSorted
; i
++ )
270 transform( m_aHandles
[i
].maPos
, nodex
[ i
], nodey
[ i
] );
272 for( i
= 0; i
< m_nValues
; i
++ )
274 double x
= m_pXValues
[ i
];
275 m_pNewYValues
[ i
] = interpolate( x
, nodex
, nodey
, nSorted
);
278 if( m_pNewYValues
[ i
] > m_fMaxY
)
279 m_pNewYValues
[ i
] = m_fMaxY
;
280 else if( m_pNewYValues
[ i
] < m_fMinY
)
281 m_pNewYValues
[ i
] = m_fMinY
;
290 // ---------------------------------------------------------------------
292 double GridWindow::interpolate(
298 // compute Lagrange interpolation
300 for( int i
= 0; i
< nNodes
; i
++ )
302 double sum
= pNodeY
[ i
];
303 for( int n
= 0; n
< nNodes
; n
++ )
307 sum
*= x
- pNodeX
[ n
];
308 sum
/= pNodeX
[ i
] - pNodeX
[ n
];
316 // ---------------------------------------------------------------------
318 void GridWindow::setBoundings( double fMinX
, double fMinY
, double fMaxX
, double fMaxY
)
325 computeChunk( m_fMinX
, m_fMaxX
, m_fChunkX
, m_fMinChunkX
);
326 computeChunk( m_fMinY
, m_fMaxY
, m_fChunkY
, m_fMinChunkY
);
329 // ---------------------------------------------------------------------
331 void GridWindow::drawGrid()
334 SetLineColor( Color( COL_BLACK
) );
335 // draw vertical lines
336 for( double fX
= m_fMinChunkX
; fX
< m_fMaxX
; fX
+= m_fChunkX
)
338 drawLine( fX
, m_fMinY
, fX
, m_fMaxY
);
340 Point aPt
= transform( fX
, m_fMinY
);
341 std::sprintf( pBuf
, "%g", fX
);
342 String
aMark( pBuf
, osl_getThreadTextEncoding() );
343 Size
aTextSize( GetTextWidth( aMark
), GetTextHeight() );
344 aPt
.X() -= aTextSize
.Width()/2;
345 aPt
.Y() += aTextSize
.Height()/2;
346 DrawText( aPt
, aMark
);
348 // draw horizontal lines
349 for( double fY
= m_fMinChunkY
; fY
< m_fMaxY
; fY
+= m_fChunkY
)
351 drawLine( m_fMinX
, fY
, m_fMaxX
, fY
);
353 Point aPt
= transform( m_fMinX
, fY
);
354 std::sprintf( pBuf
, "%g", fY
);
355 String
aMark( pBuf
, osl_getThreadTextEncoding() );
356 Size
aTextSize( GetTextWidth( aMark
), GetTextHeight() );
357 aPt
.X() -= aTextSize
.Width() + 2;
358 aPt
.Y() -= aTextSize
.Height()/2;
359 DrawText( aPt
, aMark
);
363 drawLine( m_fMinX
, m_fMinY
, m_fMaxX
, m_fMinY
);
364 drawLine( m_fMinX
, m_fMaxY
, m_fMaxX
, m_fMaxY
);
365 drawLine( m_fMinX
, m_fMinY
, m_fMinX
, m_fMaxY
);
366 drawLine( m_fMaxX
, m_fMinY
, m_fMaxX
, m_fMaxY
);
369 // ---------------------------------------------------------------------
371 void GridWindow::drawOriginal()
373 if( m_nValues
&& m_pXValues
&& m_pOrigYValues
)
375 SetLineColor( Color( COL_RED
) );
376 for( int i
= 0; i
< m_nValues
-1; i
++ )
378 drawLine( m_pXValues
[ i
], m_pOrigYValues
[ i
],
379 m_pXValues
[ i
+1 ], m_pOrigYValues
[ i
+1 ] );
384 // ---------------------------------------------------------------------
386 void GridWindow::drawNew()
388 if( m_nValues
&& m_pXValues
&& m_pNewYValues
)
390 SetClipRegion(Region(m_aGridArea
));
391 SetLineColor( Color( COL_YELLOW
) );
392 for( int i
= 0; i
< m_nValues
-1; i
++ )
394 drawLine( m_pXValues
[ i
], m_pNewYValues
[ i
],
395 m_pXValues
[ i
+1 ], m_pNewYValues
[ i
+1 ] );
401 // ---------------------------------------------------------------------
403 void GridWindow::drawHandles()
405 for(sal_uInt32
i(0L); i
< m_aHandles
.size(); i
++)
407 m_aHandles
[i
].draw(*this, m_aMarkerBitmap
);
411 // ---------------------------------------------------------------------
413 void GridWindow::Paint( const Rectangle
& rRect
)
415 ModalDialog::Paint( rRect
);
422 // ---------------------------------------------------------------------
424 void GridWindow::MouseMove( const MouseEvent
& rEvt
)
426 if( rEvt
.GetButtons() == MOUSE_LEFT
&& m_nDragIndex
!= 0xffffffff )
428 Point
aPoint( rEvt
.GetPosPixel() );
430 if( m_nDragIndex
== 0L || m_nDragIndex
== m_aHandles
.size() - 1L)
432 aPoint
.X() = m_aHandles
[m_nDragIndex
].maPos
.X();
436 if(aPoint
.X() < m_aGridArea
.Left())
437 aPoint
.X() = m_aGridArea
.Left();
438 else if(aPoint
.X() > m_aGridArea
.Right())
439 aPoint
.X() = m_aGridArea
.Right();
442 if( aPoint
.Y() < m_aGridArea
.Top() )
443 aPoint
.Y() = m_aGridArea
.Top();
444 else if( aPoint
.Y() > m_aGridArea
.Bottom() )
445 aPoint
.Y() = m_aGridArea
.Bottom();
447 if( aPoint
!= m_aHandles
[m_nDragIndex
].maPos
)
449 m_aHandles
[m_nDragIndex
].maPos
= aPoint
;
450 Invalidate( m_aGridArea
);
454 ModalDialog::MouseMove( rEvt
);
457 // ---------------------------------------------------------------------
459 void GridWindow::MouseButtonUp( const MouseEvent
& rEvt
)
461 if( rEvt
.GetButtons() == MOUSE_LEFT
)
463 if( m_nDragIndex
!= 0xffffffff )
465 m_nDragIndex
= 0xffffffff;
467 Invalidate( m_aGridArea
);
468 Paint( m_aGridArea
);
472 ModalDialog::MouseButtonUp( rEvt
);
475 // ---------------------------------------------------------------------
477 void GridWindow::MouseButtonDown( const MouseEvent
& rEvt
)
479 Point
aPoint( rEvt
.GetPosPixel() );
480 sal_uInt32 nMarkerIndex
= 0xffffffff;
482 for(sal_uInt32
a(0L); nMarkerIndex
== 0xffffffff && a
< m_aHandles
.size(); a
++)
484 if(m_aHandles
[a
].isHit(*this, aPoint
))
490 if( rEvt
.GetButtons() == MOUSE_LEFT
)
492 // user wants to drag a button
493 if( nMarkerIndex
!= 0xffffffff )
495 m_nDragIndex
= nMarkerIndex
;
498 else if( rEvt
.GetButtons() == MOUSE_RIGHT
)
500 // user wants to add/delete a button
501 if( nMarkerIndex
!= 0xffffffff )
503 if( nMarkerIndex
!= 0L && nMarkerIndex
!= m_aHandles
.size() - 1L)
505 // delete marker under mouse
506 if( m_nDragIndex
== nMarkerIndex
)
507 m_nDragIndex
= 0xffffffff;
509 m_aHandles
.erase(m_aHandles
.begin() + nMarkerIndex
);
514 m_BmOffX
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Width() >> 1);
515 m_BmOffY
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Height() >> 1);
516 m_aHandles
.push_back(impHandle(aPoint
, m_BmOffX
, m_BmOffY
));
520 Invalidate( m_aGridArea
);
521 Paint( m_aGridArea
);
524 ModalDialog::MouseButtonDown( rEvt
);
527 // ---------------------------------------------------------------------
529 IMPL_LINK( GridWindow
, ClickButtonHdl
, Button
*, pButton
)
531 if( pButton
== &m_aResetButton
)
533 int nType
= (int)(sal_IntPtr
)m_aResetTypeBox
.GetEntryData( m_aResetTypeBox
.GetSelectEntryPos() );
536 case RESET_TYPE_LINEAR_ASCENDING
:
538 for( int i
= 0; i
< m_nValues
; i
++ )
540 m_pNewYValues
[ i
] = m_fMinY
+ (m_fMaxY
-m_fMinY
)/(m_fMaxX
-m_fMinX
)*(m_pXValues
[i
]-m_fMinX
);
544 case RESET_TYPE_LINEAR_DESCENDING
:
546 for( int i
= 0; i
< m_nValues
; i
++ )
548 m_pNewYValues
[ i
] = m_fMaxY
- (m_fMaxY
-m_fMinY
)/(m_fMaxX
-m_fMinX
)*(m_pXValues
[i
]-m_fMinX
);
552 case RESET_TYPE_RESET
:
554 if( m_pOrigYValues
&& m_pNewYValues
&& m_nValues
)
555 memcpy( m_pNewYValues
, m_pOrigYValues
, m_nValues
*sizeof(double) );
558 case RESET_TYPE_EXPONENTIAL
:
560 for( int i
= 0; i
< m_nValues
; i
++ )
562 m_pNewYValues
[ i
] = m_fMinY
+ (m_fMaxY
-m_fMinY
)*(std::exp((m_pXValues
[i
]-m_fMinX
)/(m_fMaxX
-m_fMinX
))-1.0)/(M_E
-1.0);
571 for(sal_uInt32
i(0L); i
< m_aHandles
.size(); i
++)
573 // find nearest xvalue
575 transform( m_aHandles
[i
].maPos
, x
, y
);
577 double delta
= std::fabs( x
-m_pXValues
[0] );
578 for( int n
= 1; n
< m_nValues
; n
++ )
580 if( delta
> std::fabs( x
- m_pXValues
[ n
] ) )
582 delta
= std::fabs( x
- m_pXValues
[ n
] );
587 m_aHandles
[i
].maPos
= transform( m_fMinX
, m_pNewYValues
[ nIndex
] );
588 else if( m_aHandles
.size() - 1L == i
)
589 m_aHandles
[i
].maPos
= transform( m_fMaxX
, m_pNewYValues
[ nIndex
] );
591 m_aHandles
[i
].maPos
= transform( m_pXValues
[ nIndex
], m_pNewYValues
[ nIndex
] );
594 Invalidate( m_aGridArea
);
600 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */