1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_extensions.hxx"
32 #include <math.h> // for M_LN10 and M_E
34 #define _USE_MATH_DEFINES
36 #undef _USE_MATH_DEFINES
43 ResId
SaneResId( sal_uInt32
);
45 /***********************************************************************
49 ***********************************************************************/
51 // ---------------------------------------------------------------------
53 GridWindow::GridWindow(double* pXValues
, double* pYValues
, int nValues
, Window
* pParent
, BOOL bCutValues
)
54 : ModalDialog( pParent
, SaneResId( GRID_DIALOG
) ),
55 m_aGridArea( 50, 15, 100, 100 ),
56 m_pXValues( pXValues
),
57 m_pOrigYValues( pYValues
),
59 m_pNewYValues( NULL
),
60 m_bCutValues( bCutValues
),
62 m_nDragIndex( 0xffffffff ),
63 m_aMarkerBitmap( Bitmap( SaneResId( GRID_DIALOG_HANDLE_BMP
) ), Color( 255, 255, 255 ) ),
64 m_aOKButton( this, SaneResId( GRID_DIALOG_OK_BTN
) ),
65 m_aCancelButton( this, SaneResId( GRID_DIALOG_CANCEL_BTN
) ),
66 m_aResetTypeBox( this, SaneResId( GRID_DIALOG_TYPE_BOX
) ),
67 m_aResetButton( this, SaneResId( GRID_DIALOG_RESET_BTN
) )
69 USHORT nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_LINEAR_ASCENDING
) ) );
70 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_LINEAR_ASCENDING
);
72 nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_LINEAR_DESCENDING
) ) );
73 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_LINEAR_DESCENDING
);
75 nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_RESET
) ) );
76 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_RESET
);
78 nPos
= m_aResetTypeBox
.InsertEntry( String( SaneResId( RESET_TYPE_EXPONENTIAL
) ) );
79 m_aResetTypeBox
.SetEntryData( nPos
, (void *)RESET_TYPE_EXPONENTIAL
);
81 m_aResetTypeBox
.SelectEntryPos( 0 );
83 m_aResetButton
.SetClickHdl( LINK( this, GridWindow
, ClickButtonHdl
) );
85 SetMapMode( MapMode( MAP_PIXEL
) );
86 Size aSize
= GetOutputSizePixel();
87 Size aBtnSize
= m_aOKButton
.GetOutputSizePixel();
88 m_aGridArea
.setWidth( aSize
.Width() - aBtnSize
.Width() - 80 );
89 m_aGridArea
.setHeight( aSize
.Height() - 40 );
91 if( m_pOrigYValues
&& m_nValues
)
93 m_pNewYValues
= new double[ m_nValues
];
94 memcpy( m_pNewYValues
, m_pOrigYValues
, sizeof( double ) * m_nValues
);
97 setBoundings( 0, 0, 1023, 1023 );
100 // create left and right marker as first and last entry
101 m_BmOffX
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Width() >> 1);
102 m_BmOffY
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Height() >> 1);
103 m_aHandles
.push_back(impHandle(transform(findMinX(), findMinY()), m_BmOffX
, m_BmOffY
));
104 m_aHandles
.push_back(impHandle(transform(findMaxX(), findMaxY()), m_BmOffX
, m_BmOffY
));
109 // ---------------------------------------------------------------------
111 GridWindow::~GridWindow()
114 delete [] m_pNewYValues
;
117 // ---------------------------------------------------------------------
119 double GridWindow::findMinX()
123 double fMin
= m_pXValues
[0];
124 for( int i
= 1; i
< m_nValues
; i
++ )
125 if( m_pXValues
[ i
] < fMin
)
126 fMin
= m_pXValues
[ i
];
130 // ---------------------------------------------------------------------
132 double GridWindow::findMinY()
134 if( ! m_pNewYValues
)
136 double fMin
= m_pNewYValues
[0];
137 for( int i
= 1; i
< m_nValues
; i
++ )
138 if( m_pNewYValues
[ i
] < fMin
)
139 fMin
= m_pNewYValues
[ i
];
143 // ---------------------------------------------------------------------
145 double GridWindow::findMaxX()
149 double fMax
= m_pXValues
[0];
150 for( int i
= 1; i
< m_nValues
; i
++ )
151 if( m_pXValues
[ i
] > fMax
)
152 fMax
= m_pXValues
[ i
];
156 // ---------------------------------------------------------------------
158 double GridWindow::findMaxY()
160 if( ! m_pNewYValues
)
162 double fMax
= m_pNewYValues
[0];
163 for( int i
= 1; i
< m_nValues
; i
++ )
164 if( m_pNewYValues
[ i
] > fMax
)
165 fMax
= m_pNewYValues
[ i
];
169 // ---------------------------------------------------------------------
171 void GridWindow::computeExtremes()
173 if( m_nValues
&& m_pXValues
&& m_pOrigYValues
)
175 m_fMaxX
= m_fMinX
= m_pXValues
[0];
176 m_fMaxY
= m_fMinY
= m_pOrigYValues
[0];
177 for( int i
= 1; i
< m_nValues
; i
++ )
179 if( m_pXValues
[ i
] > m_fMaxX
)
180 m_fMaxX
= m_pXValues
[ i
];
181 else if( m_pXValues
[ i
] < m_fMinX
)
182 m_fMinX
= m_pXValues
[ i
];
183 if( m_pOrigYValues
[ i
] > m_fMaxY
)
184 m_fMaxY
= m_pOrigYValues
[ i
];
185 else if( m_pOrigYValues
[ i
] < m_fMinY
)
186 m_fMinY
= m_pOrigYValues
[ i
];
188 setBoundings( m_fMinX
, m_fMinY
, m_fMaxX
, m_fMaxY
);
192 // ---------------------------------------------------------------------
194 Point
GridWindow::transform( double x
, double y
)
198 aRet
.X() = (long)( ( x
- m_fMinX
) *
199 (double)m_aGridArea
.GetWidth() / ( m_fMaxX
- m_fMinX
)
200 + m_aGridArea
.Left() );
202 m_aGridArea
.Bottom() -
204 (double)m_aGridArea
.GetHeight() / ( m_fMaxY
- m_fMinY
) );
208 // ---------------------------------------------------------------------
210 void GridWindow::transform( const Point
& rOriginal
, double& x
, double& y
)
212 x
= ( rOriginal
.X() - m_aGridArea
.Left() ) * (m_fMaxX
- m_fMinX
) / (double)m_aGridArea
.GetWidth() + m_fMinX
;
213 y
= ( m_aGridArea
.Bottom() - rOriginal
.Y() ) * (m_fMaxY
- m_fMinY
) / (double)m_aGridArea
.GetHeight() + m_fMinY
;
216 // ---------------------------------------------------------------------
218 void GridWindow::drawLine( double x1
, double y1
, double x2
, double y2
)
220 DrawLine( transform( x1
, y1
), transform( x2
, y2
) );
223 // ---------------------------------------------------------------------
225 void GridWindow::computeChunk( double fMin
, double fMax
, double& fChunkOut
, double& fMinChunkOut
)
227 // get a nice chunk size like 10, 100, 25 or such
228 fChunkOut
= ( fMax
- fMin
) / 6.0;
229 int logchunk
= (int)std::log10( fChunkOut
);
230 int nChunk
= (int)( fChunkOut
/ std::exp( (double)(logchunk
-1) * M_LN10
) );
233 else if( nChunk
>= 35 )
235 else if ( nChunk
> 20 )
237 else if ( nChunk
>= 13 )
239 else if( nChunk
> 5 )
243 fChunkOut
= (double) nChunk
* exp( (double)(logchunk
-1) * M_LN10
);
244 // compute whole chunks fitting into fMin
245 nChunk
= (int)( fMin
/ fChunkOut
);
246 fMinChunkOut
= (double)nChunk
* fChunkOut
;
247 while( fMinChunkOut
< fMin
)
248 fMinChunkOut
+= fChunkOut
;
251 // ---------------------------------------------------------------------
253 void GridWindow::computeNew()
255 if(2L == m_aHandles
.size())
257 // special case: only left and right markers
259 double xright
, yright
;
260 transform(m_aHandles
[0L].maPos
, xleft
, yleft
);
261 transform(m_aHandles
[1L].maPos
, xright
, yright
);
262 double factor
= (yright
-yleft
)/(xright
-xleft
);
263 for( int i
= 0; i
< m_nValues
; i
++ )
265 m_pNewYValues
[ i
] = yleft
+ ( m_pXValues
[ i
] - xleft
)*factor
;
271 std::sort(m_aHandles
.begin(), m_aHandles
.end());
272 const int nSorted
= m_aHandles
.size();
276 double* nodex
= new double[ nSorted
];
277 double* nodey
= new double[ nSorted
];
279 for( i
= 0L; i
< nSorted
; i
++ )
280 transform( m_aHandles
[i
].maPos
, nodex
[ i
], nodey
[ i
] );
282 for( i
= 0; i
< m_nValues
; i
++ )
284 double x
= m_pXValues
[ i
];
285 m_pNewYValues
[ i
] = interpolate( x
, nodex
, nodey
, nSorted
);
288 if( m_pNewYValues
[ i
] > m_fMaxY
)
289 m_pNewYValues
[ i
] = m_fMaxY
;
290 else if( m_pNewYValues
[ i
] < m_fMinY
)
291 m_pNewYValues
[ i
] = m_fMinY
;
300 // ---------------------------------------------------------------------
302 double GridWindow::interpolate(
308 // compute Lagrange interpolation
310 for( int i
= 0; i
< nNodes
; i
++ )
312 double sum
= pNodeY
[ i
];
313 for( int n
= 0; n
< nNodes
; n
++ )
317 sum
*= x
- pNodeX
[ n
];
318 sum
/= pNodeX
[ i
] - pNodeX
[ n
];
326 // ---------------------------------------------------------------------
328 void GridWindow::setBoundings( double fMinX
, double fMinY
, double fMaxX
, double fMaxY
)
335 computeChunk( m_fMinX
, m_fMaxX
, m_fChunkX
, m_fMinChunkX
);
336 computeChunk( m_fMinY
, m_fMaxY
, m_fChunkY
, m_fMinChunkY
);
339 // ---------------------------------------------------------------------
341 void GridWindow::drawGrid()
344 SetLineColor( Color( COL_BLACK
) );
345 // draw vertical lines
346 for( double fX
= m_fMinChunkX
; fX
< m_fMaxX
; fX
+= m_fChunkX
)
348 drawLine( fX
, m_fMinY
, fX
, m_fMaxY
);
350 Point aPt
= transform( fX
, m_fMinY
);
351 std::sprintf( pBuf
, "%g", fX
);
352 String
aMark( pBuf
, gsl_getSystemTextEncoding() );
353 Size
aTextSize( GetTextWidth( aMark
), GetTextHeight() );
354 aPt
.X() -= aTextSize
.Width()/2;
355 aPt
.Y() += aTextSize
.Height()/2;
356 DrawText( aPt
, aMark
);
358 // draw horizontal lines
359 for( double fY
= m_fMinChunkY
; fY
< m_fMaxY
; fY
+= m_fChunkY
)
361 drawLine( m_fMinX
, fY
, m_fMaxX
, fY
);
363 Point aPt
= transform( m_fMinX
, fY
);
364 std::sprintf( pBuf
, "%g", fY
);
365 String
aMark( pBuf
, gsl_getSystemTextEncoding() );
366 Size
aTextSize( GetTextWidth( aMark
), GetTextHeight() );
367 aPt
.X() -= aTextSize
.Width() + 2;
368 aPt
.Y() -= aTextSize
.Height()/2;
369 DrawText( aPt
, aMark
);
373 drawLine( m_fMinX
, m_fMinY
, m_fMaxX
, m_fMinY
);
374 drawLine( m_fMinX
, m_fMaxY
, m_fMaxX
, m_fMaxY
);
375 drawLine( m_fMinX
, m_fMinY
, m_fMinX
, m_fMaxY
);
376 drawLine( m_fMaxX
, m_fMinY
, m_fMaxX
, m_fMaxY
);
379 // ---------------------------------------------------------------------
381 void GridWindow::drawOriginal()
383 if( m_nValues
&& m_pXValues
&& m_pOrigYValues
)
385 SetLineColor( Color( COL_RED
) );
386 for( int i
= 0; i
< m_nValues
-1; i
++ )
388 drawLine( m_pXValues
[ i
], m_pOrigYValues
[ i
],
389 m_pXValues
[ i
+1 ], m_pOrigYValues
[ i
+1 ] );
394 // ---------------------------------------------------------------------
396 void GridWindow::drawNew()
398 if( m_nValues
&& m_pXValues
&& m_pNewYValues
)
400 SetClipRegion( m_aGridArea
);
401 SetLineColor( Color( COL_YELLOW
) );
402 for( int i
= 0; i
< m_nValues
-1; i
++ )
404 drawLine( m_pXValues
[ i
], m_pNewYValues
[ i
],
405 m_pXValues
[ i
+1 ], m_pNewYValues
[ i
+1 ] );
411 // ---------------------------------------------------------------------
413 void GridWindow::drawHandles()
415 for(sal_uInt32
i(0L); i
< m_aHandles
.size(); i
++)
417 m_aHandles
[i
].draw(*this, m_aMarkerBitmap
);
421 // ---------------------------------------------------------------------
423 void GridWindow::Paint( const Rectangle
& rRect
)
425 ModalDialog::Paint( rRect
);
432 // ---------------------------------------------------------------------
434 void GridWindow::MouseMove( const MouseEvent
& rEvt
)
436 if( rEvt
.GetButtons() == MOUSE_LEFT
&& m_nDragIndex
!= 0xffffffff )
438 Point
aPoint( rEvt
.GetPosPixel() );
440 if( m_nDragIndex
== 0L || m_nDragIndex
== m_aHandles
.size() - 1L)
442 aPoint
.X() = m_aHandles
[m_nDragIndex
].maPos
.X();
446 if(aPoint
.X() < m_aGridArea
.Left())
447 aPoint
.X() = m_aGridArea
.Left();
448 else if(aPoint
.X() > m_aGridArea
.Right())
449 aPoint
.X() = m_aGridArea
.Right();
452 if( aPoint
.Y() < m_aGridArea
.Top() )
453 aPoint
.Y() = m_aGridArea
.Top();
454 else if( aPoint
.Y() > m_aGridArea
.Bottom() )
455 aPoint
.Y() = m_aGridArea
.Bottom();
457 if( aPoint
!= m_aHandles
[m_nDragIndex
].maPos
)
459 m_aHandles
[m_nDragIndex
].maPos
= aPoint
;
460 Invalidate( m_aGridArea
);
464 ModalDialog::MouseMove( rEvt
);
467 // ---------------------------------------------------------------------
469 void GridWindow::MouseButtonUp( const MouseEvent
& rEvt
)
471 if( rEvt
.GetButtons() == MOUSE_LEFT
)
473 if( m_nDragIndex
!= 0xffffffff )
475 m_nDragIndex
= 0xffffffff;
477 Invalidate( m_aGridArea
);
478 Paint( m_aGridArea
);
482 ModalDialog::MouseButtonUp( rEvt
);
485 // ---------------------------------------------------------------------
487 void GridWindow::MouseButtonDown( const MouseEvent
& rEvt
)
489 Point
aPoint( rEvt
.GetPosPixel() );
490 sal_uInt32 nMarkerIndex
= 0xffffffff;
492 for(sal_uInt32
a(0L); nMarkerIndex
== 0xffffffff && a
< m_aHandles
.size(); a
++)
494 if(m_aHandles
[a
].isHit(*this, aPoint
))
500 if( rEvt
.GetButtons() == MOUSE_LEFT
)
502 // user wants to drag a button
503 if( nMarkerIndex
!= 0xffffffff )
505 m_nDragIndex
= nMarkerIndex
;
508 else if( rEvt
.GetButtons() == MOUSE_RIGHT
)
510 // user wants to add/delete a button
511 if( nMarkerIndex
!= 0xffffffff )
513 if( nMarkerIndex
!= 0L && nMarkerIndex
!= m_aHandles
.size() - 1L)
515 // delete marker under mouse
516 if( m_nDragIndex
== nMarkerIndex
)
517 m_nDragIndex
= 0xffffffff;
519 m_aHandles
.erase(m_aHandles
.begin() + nMarkerIndex
);
524 m_BmOffX
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Width() >> 1);
525 m_BmOffY
= sal_uInt16(m_aMarkerBitmap
.GetSizePixel().Height() >> 1);
526 m_aHandles
.push_back(impHandle(aPoint
, m_BmOffX
, m_BmOffY
));
530 Invalidate( m_aGridArea
);
531 Paint( m_aGridArea
);
534 ModalDialog::MouseButtonDown( rEvt
);
537 // ---------------------------------------------------------------------
539 IMPL_LINK( GridWindow
, ClickButtonHdl
, Button
*, pButton
)
541 if( pButton
== &m_aResetButton
)
543 int nType
= (int)(sal_IntPtr
)m_aResetTypeBox
.GetEntryData( m_aResetTypeBox
.GetSelectEntryPos() );
546 case RESET_TYPE_LINEAR_ASCENDING
:
548 for( int i
= 0; i
< m_nValues
; i
++ )
550 m_pNewYValues
[ i
] = m_fMinY
+ (m_fMaxY
-m_fMinY
)/(m_fMaxX
-m_fMinX
)*(m_pXValues
[i
]-m_fMinX
);
554 case RESET_TYPE_LINEAR_DESCENDING
:
556 for( int i
= 0; i
< m_nValues
; i
++ )
558 m_pNewYValues
[ i
] = m_fMaxY
- (m_fMaxY
-m_fMinY
)/(m_fMaxX
-m_fMinX
)*(m_pXValues
[i
]-m_fMinX
);
562 case RESET_TYPE_RESET
:
564 if( m_pOrigYValues
&& m_pNewYValues
&& m_nValues
)
565 memcpy( m_pNewYValues
, m_pOrigYValues
, m_nValues
*sizeof(double) );
568 case RESET_TYPE_EXPONENTIAL
:
570 for( int i
= 0; i
< m_nValues
; i
++ )
572 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);
581 for(sal_uInt32
i(0L); i
< m_aHandles
.size(); i
++)
583 // find nearest xvalue
585 transform( m_aHandles
[i
].maPos
, x
, y
);
587 double delta
= std::fabs( x
-m_pXValues
[0] );
588 for( int n
= 1; n
< m_nValues
; n
++ )
590 if( delta
> std::fabs( x
- m_pXValues
[ n
] ) )
592 delta
= std::fabs( x
- m_pXValues
[ n
] );
597 m_aHandles
[i
].maPos
= transform( m_fMinX
, m_pNewYValues
[ nIndex
] );
598 else if( m_aHandles
.size() - 1L == i
)
599 m_aHandles
[i
].maPos
= transform( m_fMaxX
, m_pNewYValues
[ nIndex
] );
601 m_aHandles
[i
].maPos
= transform( m_pXValues
[ nIndex
], m_pNewYValues
[ nIndex
] );
604 Invalidate( m_aGridArea
);