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 <svtools/brwbox.hxx>
21 #include <svtools/brwhead.hxx>
22 #include <o3tl/numeric.hxx>
23 #include "datwin.hxx"
24 #include <tools/debug.hxx>
25 #include <tools/fract.hxx>
26 #include <sal/log.hxx>
27 
28 #include <algorithm>
29 #include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
30 #include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
31 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
32 #include <tools/multisel.hxx>
33 #include "brwimpl.hxx"
34 
35 
36 #define SCROLL_FLAGS (ScrollFlags::Clip | ScrollFlags::NoChildren)
37 
38 using namespace com::sun::star::accessibility::AccessibleEventId;
39 using namespace com::sun::star::accessibility::AccessibleTableModelChangeType;
40 using com::sun::star::accessibility::AccessibleTableModelChange;
41 using namespace ::com::sun::star::uno;
42 using namespace svt;
43 
44 namespace
45 {
disposeAndClearHeaderCell(::svt::BrowseBoxImpl::THeaderCellMap & _rHeaderCell)46     void disposeAndClearHeaderCell(::svt::BrowseBoxImpl::THeaderCellMap& _rHeaderCell)
47     {
48         ::std::for_each(
49                         _rHeaderCell.begin(),
50                         _rHeaderCell.end(),
51                         ::svt::BrowseBoxImpl::THeaderCellMapFunctorDispose()
52                             );
53         _rHeaderCell.clear();
54     }
55 }
56 
ConstructImpl(BrowserMode nMode)57 void BrowseBox::ConstructImpl( BrowserMode nMode )
58 {
59     SAL_INFO("svtools", "BrowseBox:ConstructImpl " << this );
60     bMultiSelection = false;
61     pColSel = nullptr;
62     pVScroll = nullptr;
63     pDataWin = VclPtr<BrowserDataWin>::Create( this ).get();
64     m_pImpl.reset( new ::svt::BrowseBoxImpl() );
65 
66     InitSettings_Impl( this );
67     InitSettings_Impl( pDataWin );
68 
69     bBootstrapped = false;
70     nDataRowHeight = 0;
71     nTitleLines = 1;
72     nFirstCol = 0;
73     nTopRow = 0;
74     nCurRow = BROWSER_ENDOFSELECTION;
75     nCurColId = 0;
76     bResizing = false;
77     bSelect = false;
78     bSelecting = false;
79     bScrolling = false;
80     bSelectionIsVisible = false;
81     bNotToggleSel = false;
82     bRowDividerDrag = false;
83     bHit = false;
84     mbInteractiveRowHeight = false;
85     bHideSelect = false;
86     bHideCursor = TRISTATE_FALSE;
87     nRowCount = 0;
88     m_bFocusOnlyCursor = true;
89     m_aCursorColor = COL_TRANSPARENT;
90     m_nCurrentMode = BrowserMode::NONE;
91     nControlAreaWidth = USHRT_MAX;
92     uRow.nSel = BROWSER_ENDOFSELECTION;
93 
94     aHScroll->SetLineSize(1);
95     aHScroll->SetScrollHdl( LINK( this, BrowseBox, ScrollHdl ) );
96     pDataWin->Show();
97 
98     SetMode( nMode );
99     bSelectionIsVisible = bKeepHighlight;
100     bHasFocus = HasChildPathFocus();
101     pDataWin->nCursorHidden =
102                 ( bHasFocus ? 0 : 1 ) + ( GetUpdateMode() ? 0 : 1 );
103 }
104 
BrowseBox(vcl::Window * pParent,WinBits nBits,BrowserMode nMode)105 BrowseBox::BrowseBox( vcl::Window* pParent, WinBits nBits, BrowserMode nMode )
106     :Control( pParent, nBits | WB_3DLOOK )
107     ,DragSourceHelper( this )
108     ,DropTargetHelper( this )
109     ,aHScroll( VclPtr<ScrollBar>::Create(this, WB_HSCROLL) )
110     ,aStatusBar( VclPtr<StatusBar>::Create(this) )
111 {
112     ConstructImpl( nMode );
113 }
114 
~BrowseBox()115 BrowseBox::~BrowseBox()
116 {
117     disposeOnce();
118 }
119 
dispose()120 void BrowseBox::dispose()
121 {
122     SAL_INFO("svtools", "BrowseBox:dispose " << this );
123 
124     if ( m_pImpl->m_pAccessible )
125     {
126         disposeAndClearHeaderCell(m_pImpl->m_aColHeaderCellMap);
127         disposeAndClearHeaderCell(m_pImpl->m_aRowHeaderCellMap);
128         m_pImpl->m_pAccessible->dispose();
129     }
130 
131     Hide();
132     pDataWin->pHeaderBar.disposeAndClear();
133     pDataWin->pCornerWin.disposeAndClear();
134     pDataWin.disposeAndClear();
135     pVScroll.disposeAndClear();
136     aHScroll.disposeAndClear();
137     aStatusBar.disposeAndClear();
138 
139     // free columns-space
140     mvCols.clear();
141     pColSel.reset();
142     if ( bMultiSelection )
143         delete uRow.pSel;
144     DragSourceHelper::dispose();
145     DropTargetHelper::dispose();
146     Control::dispose();
147 }
148 
149 
GetCursorHideCount() const150 short BrowseBox::GetCursorHideCount() const
151 {
152     return pDataWin->nCursorHidden;
153 }
154 
155 
DoShowCursor(const char *)156 void BrowseBox::DoShowCursor( const char * )
157 {
158     if (!pDataWin)
159         return;
160     short nHiddenCount = --pDataWin->nCursorHidden;
161     if (PaintCursorIfHiddenOnce())
162     {
163         if (1 == nHiddenCount)
164             DrawCursor();
165     }
166     else
167     {
168         if (0 == nHiddenCount)
169             DrawCursor();
170     }
171 }
172 
173 
DoHideCursor(const char *)174 void BrowseBox::DoHideCursor( const char * )
175 {
176     short nHiddenCount = ++pDataWin->nCursorHidden;
177     if (PaintCursorIfHiddenOnce())
178     {
179         if (2 == nHiddenCount)
180             DrawCursor();
181     }
182     else
183     {
184         if (1 == nHiddenCount)
185             DrawCursor();
186     }
187 }
188 
189 
SetRealRowCount(const OUString & rRealRowCount)190 void BrowseBox::SetRealRowCount( const OUString &rRealRowCount )
191 {
192     pDataWin->aRealRowCount = rRealRowCount;
193 }
194 
195 
SetFont(const vcl::Font & rNewFont)196 void BrowseBox::SetFont( const vcl::Font& rNewFont )
197 {
198     pDataWin->SetFont( rNewFont );
199     ImpGetDataRowHeight();
200 }
201 
GetFont() const202 const vcl::Font& BrowseBox::GetFont() const
203 {
204     return pDataWin->GetFont();
205 }
206 
GetDefaultColumnWidth(const OUString & _rText) const207 sal_uLong BrowseBox::GetDefaultColumnWidth( const OUString& _rText ) const
208 {
209     return pDataWin->GetTextWidth( _rText ) + pDataWin->GetTextWidth(OUString('0')) * 4;
210 }
211 
212 
InsertHandleColumn(sal_uLong nWidth)213 void BrowseBox::InsertHandleColumn( sal_uLong nWidth )
214 {
215 
216 #if OSL_DEBUG_LEVEL > 0
217     OSL_ENSURE( ColCount() == 0 || mvCols[0]->GetId() != HandleColumnId , "BrowseBox::InsertHandleColumn: there is already a handle column" );
218     {
219         for (auto const & col : mvCols)
220             OSL_ENSURE( col->GetId() != HandleColumnId, "BrowseBox::InsertHandleColumn: there is a non-Handle column with handle ID" );
221     }
222 #endif
223 
224     mvCols.insert( mvCols.begin(), std::unique_ptr<BrowserColumn>(new BrowserColumn( 0, OUString(), nWidth, GetZoom() )) );
225     FreezeColumn( 0 );
226 
227     // adjust headerbar
228     if ( pDataWin->pHeaderBar )
229     {
230         pDataWin->pHeaderBar->SetPosSizePixel(
231                     Point(nWidth, 0),
232                     Size( GetOutputSizePixel().Width() - nWidth, GetTitleHeight() )
233                     );
234     }
235 
236     ColumnInserted( 0 );
237 }
238 
239 
InsertDataColumn(sal_uInt16 nItemId,const OUString & rText,long nWidth,HeaderBarItemBits nBits,sal_uInt16 nPos)240 void BrowseBox::InsertDataColumn( sal_uInt16 nItemId, const OUString& rText,
241         long nWidth, HeaderBarItemBits nBits, sal_uInt16 nPos )
242 {
243 
244     OSL_ENSURE( nItemId != HandleColumnId, "BrowseBox::InsertDataColumn: nItemId is HandleColumnId" );
245     OSL_ENSURE( nItemId != BROWSER_INVALIDID, "BrowseBox::InsertDataColumn: nItemId is reserved value BROWSER_INVALIDID" );
246 
247 #if OSL_DEBUG_LEVEL > 0
248     {
249         for (auto const& col : mvCols)
250             OSL_ENSURE( col->GetId() != nItemId, "BrowseBox::InsertDataColumn: duplicate column Id" );
251     }
252 #endif
253 
254     if ( nPos < mvCols.size() )
255     {
256         mvCols.emplace( mvCols.begin() + nPos, new BrowserColumn( nItemId, rText, nWidth, GetZoom() ) );
257     }
258     else
259     {
260         mvCols.emplace_back( new BrowserColumn( nItemId, rText, nWidth, GetZoom() ) );
261     }
262     if ( nCurColId == 0 )
263         nCurColId = nItemId;
264 
265     if ( pDataWin->pHeaderBar )
266     {
267         // Handle column not in the header bar
268         sal_uInt16 nHeaderPos = nPos;
269         if (nHeaderPos != HEADERBAR_APPEND && GetColumnId(0) == HandleColumnId )
270             nHeaderPos--;
271         pDataWin->pHeaderBar->InsertItem(
272                 nItemId, rText, nWidth, nBits, nHeaderPos );
273     }
274     ColumnInserted( nPos );
275 }
276 
ToggleSelectedColumn()277 sal_uInt16 BrowseBox::ToggleSelectedColumn()
278 {
279     sal_uInt16 nSelectedColId = BROWSER_INVALIDID;
280     if ( pColSel && pColSel->GetSelectCount() )
281     {
282         DoHideCursor( "ToggleSelectedColumn" );
283         ToggleSelection();
284         long nSelected = pColSel->FirstSelected();
285         if (nSelected != static_cast<long>(SFX_ENDOFSELECTION))
286             nSelectedColId = mvCols[nSelected]->GetId();
287         pColSel->SelectAll(false);
288     }
289     return nSelectedColId;
290 }
291 
SetToggledSelectedColumn(sal_uInt16 _nSelectedColumnId)292 void BrowseBox::SetToggledSelectedColumn(sal_uInt16 _nSelectedColumnId)
293 {
294     if ( pColSel && _nSelectedColumnId != BROWSER_INVALIDID )
295     {
296         pColSel->Select( GetColumnPos( _nSelectedColumnId ) );
297         ToggleSelection();
298         SAL_INFO("svtools", "BrowseBox::SetToggledSelectedColumn " << this );
299         DoShowCursor( "SetToggledSelectedColumn" );
300     }
301 }
302 
FreezeColumn(sal_uInt16 nItemId)303 void BrowseBox::FreezeColumn( sal_uInt16 nItemId )
304 {
305     // get the position in the current array
306     size_t nItemPos = GetColumnPos( nItemId );
307     if ( nItemPos >= mvCols.size() )
308         // not available!
309         return;
310 
311     // doesn't the state change?
312     if ( mvCols[ nItemPos ]->IsFrozen() )
313         return;
314 
315     // remark the column selection
316     sal_uInt16 nSelectedColId = ToggleSelectedColumn();
317 
318     // to be moved?
319     if ( nItemPos != 0 && !mvCols[ nItemPos-1 ]->IsFrozen() )
320     {
321         // move to the right of the last frozen column
322         sal_uInt16 nFirstScrollable = FrozenColCount();
323         std::unique_ptr<BrowserColumn> pColumn = std::move(mvCols[ nItemPos ]);
324         mvCols.erase( mvCols.begin() + nItemPos );
325         nItemPos = nFirstScrollable;
326         mvCols.insert( mvCols.begin() + nItemPos, std::move(pColumn) );
327     }
328 
329     // adjust the number of the first scrollable and visible column
330     if ( nFirstCol <= nItemPos )
331         nFirstCol = nItemPos + 1;
332 
333     // toggle the freeze-state of the column
334     mvCols[ nItemPos ]->Freeze();
335 
336     // align the scrollbar-range
337     UpdateScrollbars();
338 
339     // repaint
340     Control::Invalidate();
341     pDataWin->Invalidate();
342 
343     // remember the column selection
344     SetToggledSelectedColumn(nSelectedColId);
345 }
346 
347 
SetColumnPos(sal_uInt16 nColumnId,sal_uInt16 nPos)348 void BrowseBox::SetColumnPos( sal_uInt16 nColumnId, sal_uInt16 nPos )
349 {
350     // never set pos of the handle column
351     if ( nColumnId == HandleColumnId )
352         return;
353 
354     // get the position in the current array
355     sal_uInt16 nOldPos = GetColumnPos( nColumnId );
356     if ( nOldPos >= mvCols.size() )
357         // not available!
358         return;
359 
360     // does the state change?
361     if (nOldPos == nPos)
362         return;
363 
364     // remark the column selection
365     sal_uInt16 nSelectedColId = ToggleSelectedColumn();
366 
367     // determine old column area
368     Size aDataWinSize( pDataWin->GetSizePixel() );
369     if ( pDataWin->pHeaderBar )
370         aDataWinSize.AdjustHeight(pDataWin->pHeaderBar->GetSizePixel().Height() );
371 
372     tools::Rectangle aFromRect( GetFieldRect( nColumnId) );
373     aFromRect.AdjustRight(2*MIN_COLUMNWIDTH );
374 
375     sal_uInt16 nNextPos = nOldPos + 1;
376     if ( nOldPos > nPos )
377         nNextPos = nOldPos - 1;
378 
379     BrowserColumn *pNextCol = mvCols[ nNextPos ].get();
380     tools::Rectangle aNextRect(GetFieldRect( pNextCol->GetId() ));
381 
382     // move column internally
383     {
384         std::unique_ptr<BrowserColumn> pTemp = std::move(mvCols[nOldPos]);
385         mvCols.erase( mvCols.begin() + nOldPos );
386         mvCols.insert( mvCols.begin() + nPos, std::move(pTemp) );
387     }
388 
389     // determine new column area
390     tools::Rectangle aToRect( GetFieldRect( nColumnId ) );
391     aToRect.AdjustRight(2*MIN_COLUMNWIDTH );
392 
393     // do scroll, let redraw
394     if( pDataWin->GetBackground().IsScrollable() )
395     {
396         long nScroll = -aFromRect.GetWidth();
397         tools::Rectangle aScrollArea;
398         if ( nOldPos > nPos )
399         {
400             long nFrozenWidth = GetFrozenWidth();
401             if ( aToRect.Left() < nFrozenWidth )
402                 aToRect.SetLeft( nFrozenWidth );
403             aScrollArea = tools::Rectangle(Point(aToRect.Left(),0),
404                                     Point(aNextRect.Right(),aDataWinSize.Height()));
405             nScroll *= -1; // reverse direction
406         }
407         else
408             aScrollArea = tools::Rectangle(Point(aNextRect.Left(),0),
409                                     Point(aToRect.Right(),aDataWinSize.Height()));
410 
411         pDataWin->Scroll( nScroll, 0, aScrollArea );
412         aToRect.SetTop( 0 );
413         aToRect.SetBottom( aScrollArea.Bottom() );
414         Invalidate( aToRect );
415     }
416     else
417         pDataWin->Window::Invalidate( InvalidateFlags::NoChildren );
418 
419     // adjust header bar positions
420     if ( pDataWin->pHeaderBar )
421     {
422         sal_uInt16 nNewPos = nPos;
423         if ( GetColumnId(0) == HandleColumnId )
424             --nNewPos;
425         pDataWin->pHeaderBar->MoveItem(nColumnId,nNewPos);
426     }
427     // remember the column selection
428     SetToggledSelectedColumn(nSelectedColId);
429 
430     if ( !isAccessibleAlive() )
431         return;
432 
433     commitTableEvent(
434         TABLE_MODEL_CHANGED,
435         makeAny( AccessibleTableModelChange(
436                     DELETE,
437                     0,
438                     GetRowCount(),
439                     nOldPos,
440                     nOldPos
441                 )
442         ),
443         Any()
444     );
445 
446     commitTableEvent(
447         TABLE_MODEL_CHANGED,
448         makeAny( AccessibleTableModelChange(
449                     INSERT,
450                     0,
451                     GetRowCount(),
452                     nPos,
453                     nPos
454                 )
455         ),
456         Any()
457     );
458 
459 }
460 
461 
SetColumnTitle(sal_uInt16 nItemId,const OUString & rTitle)462 void BrowseBox::SetColumnTitle( sal_uInt16 nItemId, const OUString& rTitle )
463 {
464 
465     // never set title of the handle-column
466     if ( nItemId == HandleColumnId )
467         return;
468 
469     // get the position in the current array
470     sal_uInt16 nItemPos = GetColumnPos( nItemId );
471     if ( nItemPos >= mvCols.size() )
472         // not available!
473         return;
474 
475     // does the state change?
476     BrowserColumn *pCol = mvCols[ nItemPos ].get();
477     if ( pCol->Title() == rTitle )
478         return;
479 
480     OUString sOld(pCol->Title());
481 
482     pCol->Title() = rTitle;
483 
484     // adjust headerbar column
485     if ( pDataWin->pHeaderBar )
486         pDataWin->pHeaderBar->SetItemText( nItemId, rTitle );
487     else
488     {
489         // redraw visible columns
490         if ( GetUpdateMode() && ( pCol->IsFrozen() || nItemPos > nFirstCol ) )
491             Invalidate( tools::Rectangle( Point(0,0),
492                 Size( GetOutputSizePixel().Width(), GetTitleHeight() ) ) );
493     }
494 
495     if ( isAccessibleAlive() )
496     {
497         commitTableEvent(   TABLE_COLUMN_DESCRIPTION_CHANGED,
498             makeAny( rTitle ),
499             makeAny( sOld )
500         );
501     }
502 }
503 
504 
SetColumnWidth(sal_uInt16 nItemId,sal_uLong nWidth)505 void BrowseBox::SetColumnWidth( sal_uInt16 nItemId, sal_uLong nWidth )
506 {
507 
508     // get the position in the current array
509     size_t nItemPos = GetColumnPos( nItemId );
510     if ( nItemPos >= mvCols.size() )
511         return;
512 
513     // does the state change?
514     if ( !(nWidth >= LONG_MAX || mvCols[ nItemPos ]->Width() != nWidth) )
515         return;
516 
517     long nOldWidth = mvCols[ nItemPos ]->Width();
518 
519     // adjust last column, if necessary
520     if ( IsVisible() && nItemPos == mvCols.size() - 1 )
521     {
522         long nMaxWidth = pDataWin->GetSizePixel().Width();
523         nMaxWidth -= pDataWin->bAutoSizeLastCol
524                 ? GetFieldRect(nItemId).Left()
525                 : GetFrozenWidth();
526         if ( pDataWin->bAutoSizeLastCol || nWidth > static_cast<sal_uLong>(nMaxWidth) )
527         {
528             nWidth = nMaxWidth > 16 ? nMaxWidth : nOldWidth;
529         }
530     }
531 
532     // OV
533     // In AutoSizeLastColumn(), we call SetColumnWidth with nWidth==0xffff.
534     // Thus, check here, if the width has actually changed.
535     if( static_cast<sal_uLong>(nOldWidth) == nWidth )
536         return;
537 
538     // do we want to display the change immediately?
539     bool bUpdate = GetUpdateMode() &&
540                    ( mvCols[ nItemPos ]->IsFrozen() || nItemPos >= nFirstCol );
541 
542     if ( bUpdate )
543     {
544         // Selection hidden
545         DoHideCursor( "SetColumnWidth" );
546         ToggleSelection();
547         //!pDataWin->Update();
548         //!Control::Update();
549     }
550 
551     // set width
552     mvCols[ nItemPos ]->SetWidth(nWidth, GetZoom());
553 
554     // scroll and invalidate
555     if ( bUpdate )
556     {
557         // get X-Pos of the column changed
558         long nX = 0;
559         for ( size_t nCol = 0; nCol < nItemPos; ++nCol )
560         {
561             BrowserColumn *pCol = mvCols[ nCol ].get();
562             if ( pCol->IsFrozen() || nCol >= nFirstCol )
563                 nX += pCol->Width();
564         }
565 
566         // actually scroll+invalidate
567         pDataWin->SetClipRegion();
568         bool bSelVis = bSelectionIsVisible;
569         bSelectionIsVisible = false;
570         if( GetBackground().IsScrollable() )
571         {
572 
573             tools::Rectangle aScrRect( nX + std::min( static_cast<sal_uLong>(nOldWidth), nWidth ), 0,
574                                 GetSizePixel().Width() , // the header is longer than the datawin
575                                 pDataWin->GetPosPixel().Y() - 1 );
576             Control::Scroll( nWidth-nOldWidth, 0, aScrRect, SCROLL_FLAGS );
577             aScrRect.SetBottom( pDataWin->GetSizePixel().Height() );
578             pDataWin->Scroll( nWidth-nOldWidth, 0, aScrRect, SCROLL_FLAGS );
579             tools::Rectangle aInvRect( nX, 0, nX + std::max( nWidth, static_cast<sal_uLong>(nOldWidth) ), USHRT_MAX );
580             Control::Invalidate( aInvRect, InvalidateFlags::NoChildren );
581             pDataWin->Invalidate( aInvRect );
582         }
583         else
584         {
585             Control::Invalidate( InvalidateFlags::NoChildren );
586             pDataWin->Window::Invalidate( InvalidateFlags::NoChildren );
587         }
588 
589 
590         //!pDataWin->Update();
591         //!Control::Update();
592         bSelectionIsVisible = bSelVis;
593         ToggleSelection();
594         DoShowCursor( "SetColumnWidth" );
595     }
596     UpdateScrollbars();
597 
598     // adjust headerbar column
599     if ( pDataWin->pHeaderBar )
600         pDataWin->pHeaderBar->SetItemSize(
601                 nItemId ? nItemId : USHRT_MAX - 1, nWidth );
602 
603     // adjust last column
604     if ( nItemPos != mvCols.size() - 1 )
605         AutoSizeLastColumn();
606 }
607 
608 
AutoSizeLastColumn()609 void BrowseBox::AutoSizeLastColumn()
610 {
611     if ( pDataWin->bAutoSizeLastCol &&
612          pDataWin->GetUpdateMode() )
613     {
614         sal_uInt16 nId = GetColumnId( static_cast<sal_uInt16>(mvCols.size()) - 1 );
615         SetColumnWidth( nId, LONG_MAX );
616         ColumnResized( nId );
617     }
618 }
619 
620 
RemoveColumn(sal_uInt16 nItemId)621 void BrowseBox::RemoveColumn( sal_uInt16 nItemId )
622 {
623 
624     // get column position
625     sal_uInt16 nPos = GetColumnPos(nItemId);
626     if ( nPos >= ColCount() )
627         // not available
628         return;
629 
630     // correct column selection
631     if ( pColSel )
632         pColSel->Remove( nPos );
633 
634     // correct column cursor
635     if ( nCurColId == nItemId )
636         nCurColId = 0;
637 
638     // delete column
639     mvCols.erase( mvCols.begin() + nPos );
640     if ( nFirstCol >= nPos && nFirstCol > FrozenColCount() )
641     {
642         OSL_ENSURE(nFirstCol > 0,"FirstCol must be greater zero!");
643         --nFirstCol;
644     }
645 
646     // handlecolumn not in headerbar
647     if (nItemId)
648     {
649         if ( pDataWin->pHeaderBar )
650             pDataWin->pHeaderBar->RemoveItem( nItemId );
651     }
652     else
653     {
654         // adjust headerbar
655         if ( pDataWin->pHeaderBar )
656         {
657             pDataWin->pHeaderBar->SetPosSizePixel(
658                         Point(0, 0),
659                         Size( GetOutputSizePixel().Width(), GetTitleHeight() )
660                         );
661         }
662     }
663 
664     // correct vertical scrollbar
665     UpdateScrollbars();
666 
667     // trigger repaint, if necessary
668     if ( GetUpdateMode() )
669     {
670         pDataWin->Invalidate();
671         Control::Invalidate();
672         if ( pDataWin->bAutoSizeLastCol && nPos ==ColCount() )
673             SetColumnWidth( GetColumnId( nPos - 1 ), LONG_MAX );
674     }
675 
676     if ( !isAccessibleAlive() )
677         return;
678 
679     commitTableEvent(
680         TABLE_MODEL_CHANGED,
681         makeAny( AccessibleTableModelChange(    DELETE,
682                                                 0,
683                                                 GetRowCount(),
684                                                 nPos,
685                                                 nPos
686                                            )
687         ),
688         Any()
689     );
690 
691     commitHeaderBarEvent(
692         CHILD,
693         Any(),
694         makeAny( CreateAccessibleColumnHeader( nPos ) ),
695         true
696     );
697 }
698 
699 
RemoveColumns()700 void BrowseBox::RemoveColumns()
701 {
702     size_t nOldCount = mvCols.size();
703 
704     // remove all columns
705     mvCols.clear();
706 
707     // correct column selection
708     if ( pColSel )
709     {
710         pColSel->SelectAll(false);
711         pColSel->SetTotalRange( Range( 0, 0 ) );
712     }
713 
714     // correct column cursor
715     nCurColId = 0;
716     nFirstCol = 0;
717 
718     if ( pDataWin->pHeaderBar )
719         pDataWin->pHeaderBar->Clear( );
720 
721     // correct vertical scrollbar
722     UpdateScrollbars();
723 
724     // trigger repaint if necessary
725     if ( GetUpdateMode() )
726     {
727         pDataWin->Invalidate();
728         Control::Invalidate();
729     }
730 
731     if ( !isAccessibleAlive() )
732         return;
733 
734     if ( mvCols.size() == nOldCount )
735         return;
736 
737     // all columns should be removed, so we remove the column header bar and append it again
738     // to avoid to notify every column remove
739     commitBrowseBoxEvent(
740         CHILD,
741         Any(),
742         makeAny(m_pImpl->getAccessibleHeaderBar(vcl::BBTYPE_COLUMNHEADERBAR))
743     );
744 
745     // and now append it again
746     commitBrowseBoxEvent(
747         CHILD,
748         makeAny(m_pImpl->getAccessibleHeaderBar(vcl::BBTYPE_COLUMNHEADERBAR)),
749         Any()
750     );
751 
752     // notify a table model change
753     commitTableEvent(
754         TABLE_MODEL_CHANGED,
755         makeAny ( AccessibleTableModelChange( DELETE,
756                         0,
757                         GetRowCount(),
758                         0,
759                         nOldCount
760                     )
761                 ),
762         Any()
763     );
764 }
765 
766 
GetColumnTitle(sal_uInt16 nId) const767 OUString BrowseBox::GetColumnTitle( sal_uInt16 nId ) const
768 {
769 
770     sal_uInt16 nItemPos = GetColumnPos( nId );
771     if ( nItemPos >= mvCols.size() )
772         return OUString();
773     return mvCols[ nItemPos ]->Title();
774 }
775 
776 
GetRowCount() const777 long BrowseBox::GetRowCount() const
778 {
779     return nRowCount;
780 }
781 
782 
ColCount() const783 sal_uInt16 BrowseBox::ColCount() const
784 {
785 
786     return static_cast<sal_uInt16>(mvCols.size());
787 }
788 
789 
ImpGetDataRowHeight() const790 long BrowseBox::ImpGetDataRowHeight() const
791 {
792 
793     BrowseBox *pThis = const_cast<BrowseBox*>(this);
794     pThis->nDataRowHeight = pThis->CalcReverseZoom(pDataWin->GetTextHeight() + 2);
795     pThis->Resize();
796     pDataWin->Invalidate();
797     return nDataRowHeight;
798 }
799 
800 
SetDataRowHeight(long nPixel)801 void BrowseBox::SetDataRowHeight( long nPixel )
802 {
803 
804     nDataRowHeight = CalcReverseZoom(nPixel);
805     Resize();
806     pDataWin->Invalidate();
807 }
808 
809 
SetTitleLines(sal_uInt16 nLines)810 void BrowseBox::SetTitleLines( sal_uInt16 nLines )
811 {
812 
813     nTitleLines = nLines;
814 }
815 
816 
ScrollColumns(long nCols)817 long BrowseBox::ScrollColumns( long nCols )
818 {
819 
820     if ( nFirstCol + nCols < 0 ||
821          nFirstCol + nCols >= static_cast<long>(mvCols.size()) )
822         return 0;
823 
824     // implicitly hides cursor while scrolling
825     StartScroll();
826     bScrolling = true;
827     bool bScrollable = pDataWin->GetBackground().IsScrollable();
828     bool bInvalidateView = false;
829 
830     // scrolling one column to the right?
831     if ( nCols == 1 )
832     {
833         // update internal value and scrollbar
834         ++nFirstCol;
835         aHScroll->SetThumbPos( nFirstCol - FrozenColCount() );
836 
837         if ( !bScrollable )
838         {
839             bInvalidateView = true;
840         }
841         else
842         {
843             long nDelta = mvCols[ nFirstCol-1 ]->Width();
844             long nFrozenWidth = GetFrozenWidth();
845 
846             tools::Rectangle aScrollRect(  Point( nFrozenWidth + nDelta, 0 ),
847                                     Size ( GetOutputSizePixel().Width() - nFrozenWidth - nDelta,
848                                            GetTitleHeight() - 1
849                                          ) );
850 
851             // scroll the header bar area (if there is no dedicated HeaderBar control)
852             if ( !pDataWin->pHeaderBar && nTitleLines )
853             {
854                 // actually scroll
855                 Scroll( -nDelta, 0, aScrollRect, SCROLL_FLAGS );
856 
857                 // invalidate the area of the column which was scrolled out to the left hand side
858                 tools::Rectangle aInvalidateRect( aScrollRect );
859                 aInvalidateRect.SetLeft( nFrozenWidth );
860                 aInvalidateRect.SetRight( nFrozenWidth + nDelta - 1 );
861                 Invalidate( aInvalidateRect );
862             }
863 
864             // scroll the data-area
865             aScrollRect.SetBottom( pDataWin->GetOutputSizePixel().Height() );
866 
867             // actually scroll
868             pDataWin->Scroll( -nDelta, 0, aScrollRect, SCROLL_FLAGS );
869 
870             // invalidate the area of the column which was scrolled out to the left hand side
871             aScrollRect.SetLeft( nFrozenWidth );
872             aScrollRect.SetRight( nFrozenWidth + nDelta - 1 );
873             pDataWin->Invalidate( aScrollRect );
874         }
875     }
876 
877     // scrolling one column to the left?
878     else if ( nCols == -1 )
879     {
880         --nFirstCol;
881         aHScroll->SetThumbPos( nFirstCol - FrozenColCount() );
882 
883         if ( !bScrollable )
884         {
885             bInvalidateView = true;
886         }
887         else
888         {
889             long nDelta = mvCols[ nFirstCol ]->Width();
890             long nFrozenWidth = GetFrozenWidth();
891 
892             tools::Rectangle aScrollRect(  Point(  nFrozenWidth, 0 ),
893                                     Size (  GetOutputSizePixel().Width() - nFrozenWidth,
894                                             GetTitleHeight() - 1
895                                          ) );
896 
897             // scroll the header bar area (if there is no dedicated HeaderBar control)
898             if ( !pDataWin->pHeaderBar && nTitleLines )
899             {
900                 Scroll( nDelta, 0, aScrollRect, SCROLL_FLAGS );
901             }
902 
903             // scroll the data-area
904             aScrollRect.SetBottom( pDataWin->GetOutputSizePixel().Height() );
905             pDataWin->Scroll( nDelta, 0, aScrollRect, SCROLL_FLAGS );
906         }
907     }
908     else
909     {
910         if ( GetUpdateMode() )
911         {
912             Invalidate( tools::Rectangle(
913                 Point( GetFrozenWidth(), 0 ),
914                 Size( GetOutputSizePixel().Width(), GetTitleHeight() ) ) );
915             pDataWin->Invalidate( tools::Rectangle(
916                 Point( GetFrozenWidth(), 0 ),
917                 pDataWin->GetSizePixel() ) );
918         }
919 
920         nFirstCol = nFirstCol + static_cast<sal_uInt16>(nCols);
921         aHScroll->SetThumbPos( nFirstCol - FrozenColCount() );
922     }
923 
924     // adjust external headerbar, if necessary
925     if ( pDataWin->pHeaderBar )
926     {
927         long nWidth = 0;
928         for ( size_t nCol = 0;
929               nCol < mvCols.size() && nCol < nFirstCol;
930               ++nCol )
931         {
932             // not the handle column
933             if ( mvCols[ nCol ]->GetId() )
934                 nWidth += mvCols[ nCol ]->Width();
935         }
936 
937         pDataWin->pHeaderBar->SetOffset( nWidth );
938     }
939 
940     if( bInvalidateView )
941     {
942         Control::Invalidate( InvalidateFlags::NoChildren );
943         pDataWin->Window::Invalidate( InvalidateFlags::NoChildren );
944     }
945 
946     // implicitly show cursor after scrolling
947     if ( nCols )
948     {
949         pDataWin->Update();
950         Update();
951     }
952     bScrolling = false;
953     EndScroll();
954 
955     return nCols;
956 }
957 
958 
ScrollRows(long nRows)959 long BrowseBox::ScrollRows( long nRows )
960 {
961     // compute new top row
962     long nTmpMin = std::min( static_cast<long>(nTopRow + nRows), static_cast<long>(nRowCount - 1) );
963 
964     long nNewTopRow = std::max<long>( nTmpMin, 0 );
965 
966     if ( nNewTopRow == nTopRow )
967         return 0;
968 
969     sal_uInt16 nVisibleRows =
970         static_cast<sal_uInt16>(pDataWin->GetOutputSizePixel().Height() / GetDataRowHeight() + 1);
971 
972     VisibleRowsChanged(nNewTopRow, nVisibleRows);
973 
974     // compute new top row again (nTopRow might have changed!)
975     nTmpMin = std::min( static_cast<long>(nTopRow + nRows), static_cast<long>(nRowCount - 1) );
976 
977     nNewTopRow = std::max<long>( nTmpMin, 0 );
978 
979     StartScroll();
980 
981     // scroll area on screen and/or repaint
982     long nDeltaY = GetDataRowHeight() * ( nNewTopRow - nTopRow );
983     long nOldTopRow = nTopRow;
984     nTopRow = nNewTopRow;
985 
986     if ( GetUpdateMode() )
987     {
988         pVScroll->SetRange( Range( 0L, nRowCount ) );
989         pVScroll->SetThumbPos( nTopRow );
990 
991         if( pDataWin->GetBackground().IsScrollable() &&
992             std::abs( nDeltaY ) > 0 &&
993             std::abs( nDeltaY ) < pDataWin->GetSizePixel().Height() )
994         {
995             pDataWin->Scroll( 0, static_cast<short>(-nDeltaY), SCROLL_FLAGS );
996         }
997         else
998             pDataWin->Invalidate();
999 
1000         if ( nTopRow - nOldTopRow )
1001             pDataWin->Update();
1002     }
1003 
1004     EndScroll();
1005 
1006     return nTopRow - nOldTopRow;
1007 }
1008 
1009 
RowModified(long nRow,sal_uInt16 nColId)1010 void BrowseBox::RowModified( long nRow, sal_uInt16 nColId )
1011 {
1012 
1013     if ( !GetUpdateMode() )
1014         return;
1015 
1016     tools::Rectangle aRect;
1017     if ( nColId == BROWSER_INVALIDID )
1018         // invalidate the whole row
1019         aRect = tools::Rectangle( Point( 0, (nRow-nTopRow) * GetDataRowHeight() ),
1020                     Size( pDataWin->GetSizePixel().Width(), GetDataRowHeight() ) );
1021     else
1022     {
1023         // invalidate the specific field
1024         aRect = GetFieldRectPixel( nRow, nColId, false );
1025     }
1026     pDataWin->Invalidate( aRect );
1027 }
1028 
1029 
Clear()1030 void BrowseBox::Clear()
1031 {
1032 
1033     // adjust the total number of rows
1034     DoHideCursor( "Clear" );
1035     long nOldRowCount = nRowCount;
1036     nRowCount = 0;
1037     if(bMultiSelection)
1038     {
1039         assert(uRow.pSel);
1040         uRow.pSel->Reset();
1041     }
1042     else
1043         uRow.nSel = BROWSER_ENDOFSELECTION;
1044     nCurRow = BROWSER_ENDOFSELECTION;
1045     nTopRow = 0;
1046     nCurColId = 0;
1047 
1048     // nFirstCol may not be reset, else the scrolling code will become confused.
1049     // nFirstCol may only be changed when adding or deleting columns
1050     // nFirstCol = 0; -> wrong!
1051     aHScroll->SetThumbPos( 0 );
1052     pVScroll->SetThumbPos( 0 );
1053 
1054     Invalidate();
1055     UpdateScrollbars();
1056     SetNoSelection();
1057     DoShowCursor( "Clear" );
1058     CursorMoved();
1059 
1060     if ( !isAccessibleAlive() )
1061         return;
1062 
1063     // all rows should be removed, so we remove the row header bar and append it again
1064     // to avoid to notify every row remove
1065     if ( nOldRowCount == nRowCount )
1066         return;
1067 
1068     commitBrowseBoxEvent(
1069         CHILD,
1070         Any(),
1071         makeAny( m_pImpl->getAccessibleHeaderBar( vcl::BBTYPE_ROWHEADERBAR ) )
1072     );
1073 
1074     // and now append it again
1075     commitBrowseBoxEvent(
1076         CHILD,
1077         makeAny( m_pImpl->getAccessibleHeaderBar( vcl::BBTYPE_ROWHEADERBAR ) ),
1078         Any()
1079     );
1080 
1081     // notify a table model change
1082     commitTableEvent(
1083         TABLE_MODEL_CHANGED,
1084         makeAny( AccessibleTableModelChange( DELETE,
1085             0,
1086             nOldRowCount,
1087             0,
1088             GetColumnCount())
1089         ),
1090         Any()
1091     );
1092 }
1093 
RowInserted(long nRow,long nNumRows,bool bDoPaint,bool bKeepSelection)1094 void BrowseBox::RowInserted( long nRow, long nNumRows, bool bDoPaint, bool bKeepSelection )
1095 {
1096 
1097     if (nRow < 0)
1098         nRow = 0;
1099     else if (nRow > nRowCount) // maximal = nRowCount
1100         nRow = nRowCount;
1101 
1102     if ( nNumRows <= 0 )
1103         return;
1104 
1105     // adjust total row count
1106     bool bLastRow = nRow >= nRowCount;
1107     nRowCount += nNumRows;
1108 
1109     DoHideCursor( "RowInserted" );
1110 
1111     // must we paint the new rows?
1112     long nOldCurRow = nCurRow;
1113     Size aSz = pDataWin->GetOutputSizePixel();
1114     if ( bDoPaint && nRow >= nTopRow &&
1115          nRow <= nTopRow + aSz.Height() / GetDataRowHeight() )
1116     {
1117         long nY = (nRow-nTopRow) * GetDataRowHeight();
1118         if ( !bLastRow )
1119         {
1120             // scroll down the rows behind the new row
1121             pDataWin->SetClipRegion();
1122             if( pDataWin->GetBackground().IsScrollable() )
1123             {
1124                 pDataWin->Scroll( 0, GetDataRowHeight() * nNumRows,
1125                                 tools::Rectangle( Point( 0, nY ),
1126                                         Size( aSz.Width(), aSz.Height() - nY ) ),
1127                                 SCROLL_FLAGS );
1128             }
1129             else
1130                 pDataWin->Window::Invalidate( InvalidateFlags::NoChildren );
1131         }
1132         else
1133             // scroll would cause a repaint, so we must explicitly invalidate
1134             pDataWin->Invalidate( tools::Rectangle( Point( 0, nY ),
1135                          Size( aSz.Width(), nNumRows * GetDataRowHeight() ) ) );
1136     }
1137 
1138     // correct top row if necessary
1139     if ( nRow < nTopRow )
1140         nTopRow += nNumRows;
1141 
1142     // adjust the selection
1143     if ( bMultiSelection )
1144         uRow.pSel->Insert( nRow, nNumRows );
1145     else if ( uRow.nSel != BROWSER_ENDOFSELECTION && nRow <= uRow.nSel )
1146         uRow.nSel += nNumRows;
1147 
1148     // adjust the cursor
1149     if ( nCurRow == BROWSER_ENDOFSELECTION )
1150         GoToRow( 0, false, bKeepSelection );
1151     else if ( nRow <= nCurRow )
1152     {
1153         nCurRow += nNumRows;
1154         GoToRow( nCurRow, false, bKeepSelection );
1155     }
1156 
1157     // adjust the vertical scrollbar
1158     if ( bDoPaint )
1159     {
1160         UpdateScrollbars();
1161         AutoSizeLastColumn();
1162     }
1163 
1164     DoShowCursor( "RowInserted" );
1165     // notify accessible that rows were inserted
1166     if ( isAccessibleAlive() )
1167     {
1168         commitTableEvent(
1169             TABLE_MODEL_CHANGED,
1170             makeAny( AccessibleTableModelChange(
1171                         INSERT,
1172                         nRow,
1173                         nRow + nNumRows,
1174                         0,
1175                         GetColumnCount()
1176                     )
1177             ),
1178             Any()
1179         );
1180 
1181         for (long i = nRow+1 ; i <= nRowCount ; ++i)
1182         {
1183             commitHeaderBarEvent(
1184                 CHILD,
1185                 makeAny( CreateAccessibleRowHeader( i ) ),
1186                 Any(),
1187                 false
1188             );
1189         }
1190     }
1191 
1192     if ( nCurRow != nOldCurRow )
1193         CursorMoved();
1194 
1195     DBG_ASSERT(nRowCount > 0,"BrowseBox: nRowCount <= 0");
1196     DBG_ASSERT(nCurRow >= 0,"BrowseBox: nCurRow < 0");
1197     DBG_ASSERT(nCurRow < nRowCount,"nCurRow >= nRowCount");
1198 }
1199 
1200 
RowRemoved(long nRow,long nNumRows,bool bDoPaint)1201 void BrowseBox::RowRemoved( long nRow, long nNumRows, bool bDoPaint )
1202 {
1203 
1204     if ( nRow < 0 )
1205         nRow = 0;
1206     else if ( nRow >= nRowCount )
1207         nRow = nRowCount - 1;
1208 
1209     if ( nNumRows <= 0 )
1210         return;
1211 
1212     if ( nRowCount <= 0 )
1213         return;
1214 
1215     if ( bDoPaint )
1216     {
1217         // hide cursor and selection
1218         SAL_INFO("svtools", "BrowseBox::HideCursor " << this );
1219         ToggleSelection();
1220         DoHideCursor( "RowRemoved" );
1221     }
1222 
1223     // adjust total row count
1224     nRowCount -= nNumRows;
1225     if (nRowCount < 0) nRowCount = 0;
1226     long nOldCurRow = nCurRow;
1227 
1228     // adjust the selection
1229     if ( bMultiSelection )
1230         // uRow.pSel->Remove( nRow, nNumRows );
1231         for ( long i = 0; i < nNumRows; i++ )
1232             uRow.pSel->Remove( nRow );
1233     else if ( nRow < uRow.nSel && uRow.nSel >= nNumRows )
1234         uRow.nSel -= nNumRows;
1235     else if ( nRow <= uRow.nSel )
1236         uRow.nSel = BROWSER_ENDOFSELECTION;
1237 
1238     // adjust the cursor
1239     if ( nRowCount == 0 )   // don't compare nRowCount with nNumRows as nNumRows already was subtracted from nRowCount
1240         nCurRow = BROWSER_ENDOFSELECTION;
1241     else if ( nRow < nCurRow )
1242     {
1243         nCurRow -= std::min( nCurRow - nRow, nNumRows );
1244         // with the above nCurRow points a) to the first row after the removed block or b) to the same line
1245         // as before, but moved up nNumRows
1246         // case a) needs an additional correction if the last n lines were deleted, as 'the first row after the
1247         // removed block' is an invalid position then
1248         // FS - 09/28/99 - 68429
1249         if (nCurRow == nRowCount)
1250             --nCurRow;
1251     }
1252     else if( nRow == nCurRow && nCurRow == nRowCount )
1253         nCurRow = nRowCount-1;
1254 
1255     // is the deleted row visible?
1256     Size aSz = pDataWin->GetOutputSizePixel();
1257     if ( nRow >= nTopRow &&
1258          nRow <= nTopRow + aSz.Height() / GetDataRowHeight() )
1259     {
1260         if ( bDoPaint )
1261         {
1262             // scroll up the rows behind the deleted row
1263             // if there are Rows behind
1264             if (nRow < nRowCount)
1265             {
1266                 long nY = (nRow-nTopRow) * GetDataRowHeight();
1267                 pDataWin->SetClipRegion();
1268                 if( pDataWin->GetBackground().IsScrollable() )
1269                 {
1270                     pDataWin->Scroll( 0, - static_cast<short>(GetDataRowHeight()) * nNumRows,
1271                         tools::Rectangle( Point( 0, nY ), Size( aSz.Width(),
1272                             aSz.Height() - nY + nNumRows*GetDataRowHeight() ) ),
1273                             SCROLL_FLAGS );
1274                 }
1275                 else
1276                     pDataWin->Window::Invalidate( InvalidateFlags::NoChildren );
1277             }
1278             else
1279             {
1280                 // Repaint the Rect of the deleted row
1281                 tools::Rectangle aRect(
1282                         Point( 0, (nRow-nTopRow)*GetDataRowHeight() ),
1283                         Size( pDataWin->GetSizePixel().Width(),
1284                               nNumRows * GetDataRowHeight() ) );
1285                 pDataWin->Invalidate( aRect );
1286             }
1287         }
1288     }
1289     // is the deleted row above of the visible area?
1290     else if ( nRow < nTopRow )
1291         nTopRow = nTopRow >= nNumRows ? nTopRow-nNumRows : 0;
1292 
1293     if ( bDoPaint )
1294     {
1295         // reshow cursor and selection
1296         ToggleSelection();
1297         SAL_INFO("svtools", "BrowseBox::ShowCursor " << this );
1298         DoShowCursor( "RowRemoved" );
1299 
1300         // adjust the vertical scrollbar
1301         UpdateScrollbars();
1302         AutoSizeLastColumn();
1303     }
1304 
1305     if ( isAccessibleAlive() )
1306     {
1307         if ( nRowCount == 0 )
1308         {
1309             // all columns should be removed, so we remove the column header bar and append it again
1310             // to avoid to notify every column remove
1311             commitBrowseBoxEvent(
1312                 CHILD,
1313                 Any(),
1314                 makeAny( m_pImpl->getAccessibleHeaderBar( vcl::BBTYPE_ROWHEADERBAR ) )
1315             );
1316 
1317             // and now append it again
1318             commitBrowseBoxEvent(
1319                 CHILD,
1320                 makeAny(m_pImpl->getAccessibleHeaderBar(vcl::BBTYPE_ROWHEADERBAR)),
1321                 Any()
1322             );
1323             commitBrowseBoxEvent(
1324                 CHILD,
1325                 Any(),
1326                 makeAny( m_pImpl->getAccessibleTable() )
1327             );
1328 
1329             // and now append it again
1330             commitBrowseBoxEvent(
1331                 CHILD,
1332                 makeAny( m_pImpl->getAccessibleTable() ),
1333                 Any()
1334             );
1335         }
1336         else
1337         {
1338             commitTableEvent(
1339                 TABLE_MODEL_CHANGED,
1340                 makeAny( AccessibleTableModelChange(
1341                             DELETE,
1342                             nRow,
1343                             nRow + nNumRows,
1344                             0,
1345                             GetColumnCount()
1346                             )
1347                 ),
1348                 Any()
1349             );
1350 
1351             for (long i = nRow+1 ; i <= (nRow+nNumRows) ; ++i)
1352             {
1353                 commitHeaderBarEvent(
1354                     CHILD,
1355                     Any(),
1356                     makeAny( CreateAccessibleRowHeader( i ) ),
1357                     false
1358                 );
1359             }
1360         }
1361     }
1362 
1363     if ( nOldCurRow != nCurRow )
1364         CursorMoved();
1365 
1366     DBG_ASSERT(nRowCount >= 0,"BrowseBox: nRowCount < 0");
1367     DBG_ASSERT(nCurRow >= 0 || nRowCount == 0,"BrowseBox: nCurRow < 0 && nRowCount != 0");
1368     DBG_ASSERT(nCurRow < nRowCount,"nCurRow >= nRowCount");
1369 }
1370 
1371 
GoToRow(long nRow)1372 bool BrowseBox::GoToRow( long nRow)
1373 {
1374     return GoToRow(nRow, false);
1375 }
1376 
1377 
GoToRow(long nRow,bool bRowColMove,bool bKeepSelection)1378 bool BrowseBox::GoToRow( long nRow, bool bRowColMove, bool bKeepSelection )
1379 {
1380 
1381     long nOldCurRow = nCurRow;
1382 
1383     // nothing to do?
1384     if ( nRow == nCurRow && ( bMultiSelection || uRow.nSel == nRow ) )
1385         return true;
1386 
1387     // out of range?
1388     if ( nRow < 0 || nRow >= nRowCount )
1389         return false;
1390 
1391     // not allowed?
1392     if ( !bRowColMove && !IsCursorMoveAllowed( nRow, nCurColId ) )
1393         return false;
1394 
1395     // compute the last visible row
1396     Size aSz( pDataWin->GetSizePixel() );
1397     sal_uInt16 nVisibleRows = sal_uInt16( aSz.Height() / GetDataRowHeight() - 1 );
1398     long nLastRow = nTopRow + nVisibleRows;
1399 
1400     // suspend Updates
1401     pDataWin->EnterUpdateLock();
1402 
1403     // remove old highlight, if necessary
1404     if ( !bMultiSelection && !bKeepSelection )
1405         ToggleSelection();
1406     DoHideCursor( "GoToRow" );
1407 
1408     // must we scroll?
1409     bool bWasVisible = bSelectionIsVisible;
1410     if (! bMultiSelection)
1411     {
1412         if( !bKeepSelection )
1413             bSelectionIsVisible = false;
1414     }
1415     if ( nRow < nTopRow )
1416         ScrollRows( nRow - nTopRow );
1417     else if ( nRow > nLastRow )
1418         ScrollRows( nRow - nLastRow );
1419     bSelectionIsVisible = bWasVisible;
1420 
1421     // adjust cursor (selection) and thumb
1422     if ( GetUpdateMode() )
1423         pVScroll->SetThumbPos( nTopRow );
1424 
1425     // relative positioning (because nCurRow might have changed in the meantime)!
1426     if (nCurRow != BROWSER_ENDOFSELECTION )
1427         nCurRow = nCurRow + (nRow - nOldCurRow);
1428 
1429     // make sure that the current position is valid
1430     if (nCurRow == BROWSER_ENDOFSELECTION && nRowCount > 0)
1431         nCurRow = 0;
1432     else if ( nCurRow >= nRowCount )
1433         nCurRow = nRowCount - 1;
1434     aSelRange = Range( nCurRow, nCurRow );
1435 
1436     // display new highlight if necessary
1437     if ( !bMultiSelection && !bKeepSelection )
1438         uRow.nSel = nRow;
1439 
1440     // resume Updates
1441     pDataWin->LeaveUpdateLock();
1442 
1443     // Cursor+Highlight
1444     if ( !bMultiSelection && !bKeepSelection)
1445         ToggleSelection();
1446     DoShowCursor( "GoToRow" );
1447     if ( !bRowColMove  && nOldCurRow != nCurRow )
1448         CursorMoved();
1449 
1450     if ( !bMultiSelection && !bKeepSelection )
1451     {
1452         if ( !bSelecting )
1453             Select();
1454         else
1455             bSelect = true;
1456     }
1457     return true;
1458 }
1459 
1460 
GoToColumnId(sal_uInt16 nColId)1461 bool BrowseBox::GoToColumnId( sal_uInt16 nColId)
1462 {
1463     return GoToColumnId(nColId, true);
1464 }
1465 
1466 
GoToColumnId(sal_uInt16 nColId,bool bMakeVisible,bool bRowColMove)1467 bool BrowseBox::GoToColumnId( sal_uInt16 nColId, bool bMakeVisible, bool bRowColMove)
1468 {
1469     if (!bColumnCursor)
1470         return false;
1471 
1472     // allowed?
1473     if (!bRowColMove && !IsCursorMoveAllowed( nCurRow, nColId ) )
1474         return false;
1475 
1476     if ( nColId != nCurColId || (bMakeVisible && !IsFieldVisible(nCurRow, nColId, true)))
1477     {
1478         sal_uInt16 nNewPos = GetColumnPos(nColId);
1479         BrowserColumn* pColumn = (nNewPos < mvCols.size()) ? mvCols[ nNewPos ].get() : nullptr;
1480         DBG_ASSERT( pColumn, "no column object - invalid id?" );
1481         if ( !pColumn )
1482             return false;
1483 
1484         DoHideCursor( "GoToColumnId" );
1485         nCurColId = nColId;
1486 
1487         bool bScrolled = false;
1488 
1489         sal_uInt16 nFirstPos = nFirstCol;
1490         sal_uInt16 nWidth = static_cast<sal_uInt16>(pColumn->Width());
1491         sal_uInt16 nLastPos = GetColumnAtXPosPixel(
1492                             pDataWin->GetSizePixel().Width()-nWidth );
1493         sal_uInt16 nFrozen = FrozenColCount();
1494         if ( bMakeVisible && nLastPos &&
1495              nNewPos >= nFrozen && ( nNewPos < nFirstPos || nNewPos > nLastPos ) )
1496         {
1497             if ( nNewPos < nFirstPos )
1498                 ScrollColumns( nNewPos-nFirstPos );
1499             else if ( nNewPos > nLastPos )
1500                 ScrollColumns( nNewPos-nLastPos );
1501             bScrolled = true;
1502         }
1503 
1504         DoShowCursor( "GoToColumnId" );
1505         if (!bRowColMove)
1506         {
1507             //try to move to nCurRow, nColId
1508             CursorMoveAttempt aAttempt(nCurRow, nColId, bScrolled);
1509             //Detect if we are already in a call to BrowseBox::GoToColumnId
1510             //but the attempt is impossible and we are simply recursing
1511             //into BrowseBox::GoToColumnId with the same impossible to
1512             //fulfill conditions
1513             if (m_aGotoStack.empty() || aAttempt != m_aGotoStack.top())
1514             {
1515                 m_aGotoStack.push(aAttempt);
1516                 CursorMoved();
1517                 m_aGotoStack.pop();
1518             }
1519         }
1520         return true;
1521     }
1522     return true;
1523 }
1524 
1525 
GoToRowColumnId(long nRow,sal_uInt16 nColId)1526 bool BrowseBox::GoToRowColumnId( long nRow, sal_uInt16 nColId )
1527 {
1528 
1529     // out of range?
1530     if ( nRow < 0 || nRow >= nRowCount )
1531         return false;
1532 
1533     if (!bColumnCursor)
1534         return false;
1535 
1536     // nothing to do ?
1537     if ( nRow == nCurRow && ( bMultiSelection || uRow.nSel == nRow ) &&
1538          nColId == nCurColId && IsFieldVisible(nCurRow, nColId, true))
1539         return true;
1540 
1541     // allowed?
1542     if (!IsCursorMoveAllowed(nRow, nColId))
1543         return false;
1544 
1545     DoHideCursor( "GoToRowColumnId" );
1546     bool bMoved = GoToRow(nRow, true) && GoToColumnId(nColId, true, true);
1547     DoShowCursor( "GoToRowColumnId" );
1548 
1549     if (bMoved)
1550         CursorMoved();
1551 
1552     return bMoved;
1553 }
1554 
1555 
SetNoSelection()1556 void BrowseBox::SetNoSelection()
1557 {
1558 
1559     // is there no selection
1560     if ( ( !pColSel || !pColSel->GetSelectCount() ) &&
1561          ( ( !bMultiSelection && uRow.nSel == BROWSER_ENDOFSELECTION ) ||
1562            ( bMultiSelection && !uRow.pSel->GetSelectCount() ) ) )
1563         // nothing to do
1564         return;
1565 
1566     SAL_INFO("svtools", "BrowseBox::HideCursor " << this );
1567     ToggleSelection();
1568 
1569     // unselect all
1570     if ( bMultiSelection )
1571         uRow.pSel->SelectAll(false);
1572     else
1573         uRow.nSel = BROWSER_ENDOFSELECTION;
1574     if ( pColSel )
1575         pColSel->SelectAll(false);
1576     if ( !bSelecting )
1577         Select();
1578     else
1579         bSelect = true;
1580 
1581     // restore screen
1582     SAL_INFO("svtools", "BrowseBox::ShowCursor " << this );
1583 
1584     if ( isAccessibleAlive() )
1585     {
1586         commitTableEvent(
1587             SELECTION_CHANGED,
1588             Any(),
1589             Any()
1590         );
1591     }
1592 }
1593 
1594 
SelectAll()1595 void BrowseBox::SelectAll()
1596 {
1597 
1598     if ( !bMultiSelection )
1599         return;
1600 
1601     SAL_INFO("svtools", "BrowseBox::HideCursor " << this );
1602     ToggleSelection();
1603 
1604     // select all rows
1605     if ( pColSel )
1606         pColSel->SelectAll(false);
1607     uRow.pSel->SelectAll();
1608 
1609     // don't highlight handle column
1610     BrowserColumn *pFirstCol = mvCols[ 0 ].get();
1611     long nOfsX = pFirstCol->GetId() ? 0 : pFirstCol->Width();
1612 
1613     // highlight the row selection
1614     if ( !bHideSelect )
1615     {
1616         tools::Rectangle aHighlightRect;
1617         sal_uInt16 nVisibleRows =
1618             static_cast<sal_uInt16>(pDataWin->GetOutputSizePixel().Height() / GetDataRowHeight() + 1);
1619         for ( long nRow = std::max<long>( nTopRow, uRow.pSel->FirstSelected() );
1620               nRow != BROWSER_ENDOFSELECTION && nRow < nTopRow + nVisibleRows;
1621               nRow = uRow.pSel->NextSelected() )
1622             aHighlightRect.Union( tools::Rectangle(
1623                 Point( nOfsX, (nRow-nTopRow)*GetDataRowHeight() ),
1624                 Size( pDataWin->GetSizePixel().Width(), GetDataRowHeight() ) ) );
1625         pDataWin->Invalidate( aHighlightRect );
1626     }
1627 
1628     if ( !bSelecting )
1629         Select();
1630     else
1631         bSelect = true;
1632 
1633     // restore screen
1634     SAL_INFO("svtools", "BrowseBox::ShowCursor " << this );
1635 
1636     if ( !isAccessibleAlive() )
1637         return;
1638 
1639     commitTableEvent(
1640         SELECTION_CHANGED,
1641         Any(),
1642         Any()
1643     );
1644     commitHeaderBarEvent(
1645         SELECTION_CHANGED,
1646         Any(),
1647         Any(),
1648         true
1649     ); // column header event
1650 
1651     commitHeaderBarEvent(
1652         SELECTION_CHANGED,
1653         Any(),
1654         Any(),
1655         false
1656     ); // row header event
1657 }
1658 
1659 
SelectRow(long nRow,bool _bSelect,bool bExpand)1660 void BrowseBox::SelectRow( long nRow, bool _bSelect, bool bExpand )
1661 {
1662 
1663     if ( !bMultiSelection )
1664     {
1665         // deselecting is impossible, selecting via cursor
1666         if ( _bSelect )
1667             GoToRow(nRow, false);
1668         return;
1669     }
1670 
1671     SAL_INFO("svtools", "BrowseBox::HideCursor " << this );
1672 
1673     // remove old selection?
1674     if ( !bExpand || !bMultiSelection )
1675     {
1676         ToggleSelection();
1677         if ( bMultiSelection )
1678             uRow.pSel->SelectAll(false);
1679         else
1680             uRow.nSel = BROWSER_ENDOFSELECTION;
1681         if ( pColSel )
1682             pColSel->SelectAll(false);
1683     }
1684 
1685     // set new selection
1686     if  (   !bHideSelect
1687         &&  (   (   bMultiSelection
1688                 &&  uRow.pSel->GetTotalRange().Max() >= nRow
1689                 &&  uRow.pSel->Select( nRow, _bSelect )
1690                 )
1691             ||  (   !bMultiSelection
1692                 &&  ( uRow.nSel = nRow ) != BROWSER_ENDOFSELECTION )
1693                 )
1694             )
1695     {
1696         // don't highlight handle column
1697         BrowserColumn *pFirstCol = mvCols[ 0 ].get();
1698         long nOfsX = pFirstCol->GetId() ? 0 : pFirstCol->Width();
1699 
1700         // highlight only newly selected part
1701         tools::Rectangle aRect(
1702             Point( nOfsX, (nRow-nTopRow)*GetDataRowHeight() ),
1703             Size( pDataWin->GetSizePixel().Width(), GetDataRowHeight() ) );
1704         pDataWin->Invalidate( aRect );
1705     }
1706 
1707     if ( !bSelecting )
1708         Select();
1709     else
1710         bSelect = true;
1711 
1712     // restore screen
1713     SAL_INFO("svtools", "BrowseBox::ShowCursor " << this );
1714 
1715     if ( !isAccessibleAlive() )
1716         return;
1717 
1718     commitTableEvent(
1719         SELECTION_CHANGED,
1720         Any(),
1721         Any()
1722     );
1723     commitHeaderBarEvent(
1724         SELECTION_CHANGED,
1725         Any(),
1726         Any(),
1727         false
1728     ); // row header event
1729 }
1730 
1731 
GetSelectRowCount() const1732 long BrowseBox::GetSelectRowCount() const
1733 {
1734 
1735     return bMultiSelection ? uRow.pSel->GetSelectCount() :
1736            uRow.nSel == BROWSER_ENDOFSELECTION ? 0 : 1;
1737 }
1738 
1739 
SelectColumnPos(sal_uInt16 nNewColPos,bool _bSelect,bool bMakeVisible)1740 void BrowseBox::SelectColumnPos( sal_uInt16 nNewColPos, bool _bSelect, bool bMakeVisible )
1741 {
1742 
1743     if ( !bColumnCursor || nNewColPos == BROWSER_INVALIDID )
1744         return;
1745 
1746     if ( !bMultiSelection )
1747     {
1748         if ( _bSelect )
1749             GoToColumnId( mvCols[ nNewColPos ]->GetId(), bMakeVisible );
1750         return;
1751     }
1752     else
1753     {
1754         if ( !GoToColumnId( mvCols[ nNewColPos ]->GetId(), bMakeVisible ) )
1755             return;
1756     }
1757 
1758     SAL_INFO("svtools", "BrowseBox::HideCursor " << this );
1759     ToggleSelection();
1760     if ( bMultiSelection )
1761         uRow.pSel->SelectAll(false);
1762     else
1763         uRow.nSel = BROWSER_ENDOFSELECTION;
1764     pColSel->SelectAll(false);
1765 
1766     if ( pColSel->Select( nNewColPos, _bSelect ) )
1767     {
1768         // GoToColumnId( mvCols->GetObject(nNewColPos)->GetId(), bMakeVisible );
1769 
1770         // only highlight painted areas
1771         pDataWin->Update();
1772         tools::Rectangle aFieldRectPix( GetFieldRectPixel( nCurRow, nCurColId, false ) );
1773         tools::Rectangle aRect(
1774             Point( aFieldRectPix.Left() - MIN_COLUMNWIDTH, 0 ),
1775             Size( mvCols[ nNewColPos ]->Width(),
1776                   pDataWin->GetOutputSizePixel().Height() ) );
1777         pDataWin->Invalidate( aRect );
1778         if ( !bSelecting )
1779             Select();
1780         else
1781             bSelect = true;
1782 
1783         if ( isAccessibleAlive() )
1784         {
1785             commitTableEvent(
1786                 SELECTION_CHANGED,
1787                 Any(),
1788                 Any()
1789             );
1790             commitHeaderBarEvent(
1791                 SELECTION_CHANGED,
1792                 Any(),
1793                 Any(),
1794                 true
1795             ); // column header event
1796         }
1797     }
1798 
1799     // restore screen
1800     SAL_INFO("svtools", "BrowseBox::ShowCursor " << this );
1801 }
1802 
1803 
GetSelectColumnCount() const1804 sal_uInt16 BrowseBox::GetSelectColumnCount() const
1805 {
1806 
1807     // while bAutoSelect (==!pColSel), 1 if any rows (yes rows!) else none
1808     return pColSel ? static_cast<sal_uInt16>(pColSel->GetSelectCount()) :
1809            nCurRow >= 0 ? 1 : 0;
1810 }
1811 
1812 
FirstSelectedColumn() const1813 long BrowseBox::FirstSelectedColumn( ) const
1814 {
1815     return pColSel ? pColSel->FirstSelected() : BROWSER_ENDOFSELECTION;
1816 }
1817 
1818 
FirstSelectedRow()1819 long BrowseBox::FirstSelectedRow()
1820 {
1821 
1822     return bMultiSelection ? uRow.pSel->FirstSelected() : uRow.nSel;
1823 }
1824 
1825 
NextSelectedRow()1826 long BrowseBox::NextSelectedRow()
1827 {
1828 
1829     return bMultiSelection ? uRow.pSel->NextSelected() : BROWSER_ENDOFSELECTION;
1830 }
1831 
1832 
LastSelectedRow()1833 long BrowseBox::LastSelectedRow()
1834 {
1835 
1836     return bMultiSelection ? uRow.pSel->LastSelected() : uRow.nSel;
1837 }
1838 
1839 
IsRowSelected(long nRow) const1840 bool BrowseBox::IsRowSelected( long nRow ) const
1841 {
1842 
1843     return bMultiSelection ? uRow.pSel->IsSelected(nRow) : nRow == uRow.nSel;
1844 }
1845 
1846 
IsColumnSelected(sal_uInt16 nColumnId) const1847 bool BrowseBox::IsColumnSelected( sal_uInt16 nColumnId ) const
1848 {
1849 
1850     return pColSel ? pColSel->IsSelected( GetColumnPos(nColumnId) ) :
1851                      nCurColId == nColumnId;
1852 }
1853 
1854 
MakeFieldVisible(long nRow,sal_uInt16 nColId)1855 void BrowseBox::MakeFieldVisible
1856 (
1857     long    nRow,       // line number of the field (starting with 0)
1858     sal_uInt16  nColId     // column ID of the field
1859 )
1860 
1861 /*  [Description]
1862 
1863     Makes visible the field described in 'nRow' and 'nColId' by scrolling
1864     accordingly.
1865 
1866 */
1867 
1868 {
1869     if (!pDataWin)
1870         return;
1871 
1872     Size aTestSize = pDataWin->GetSizePixel();
1873 
1874     if ( !bBootstrapped ||
1875          ( aTestSize.Width() == 0 && aTestSize.Height() == 0 ) )
1876         return;
1877 
1878     // is it visible already?
1879     bool bVisible = IsFieldVisible( nRow, nColId, true/*bComplete*/ );
1880     if ( bVisible )
1881         return;
1882 
1883     // calculate column position, field rectangle and painting area
1884     sal_uInt16 nColPos = GetColumnPos( nColId );
1885     tools::Rectangle aFieldRect = GetFieldRectPixel( nRow, nColId, false );
1886     tools::Rectangle aDataRect( Point(0, 0), pDataWin->GetSizePixel() );
1887 
1888     // positioned outside on the left?
1889     if ( nColPos >= FrozenColCount() && nColPos < nFirstCol )
1890         // => scroll to the right
1891         ScrollColumns( nColPos - nFirstCol );
1892 
1893     // while outside on the right
1894     while ( aDataRect.Right() < aFieldRect.Right() )
1895     {
1896         // => scroll to the left
1897         if ( ScrollColumns( 1 ) != 1 )
1898             // no more need to scroll
1899             break;
1900         aFieldRect = GetFieldRectPixel( nRow, nColId, false );
1901     }
1902 
1903     // positioned outside above?
1904     if ( nRow < nTopRow )
1905         // scroll further to the bottom
1906         ScrollRows( nRow - nTopRow );
1907 
1908     // positioned outside below?
1909     long nBottomRow = nTopRow + GetVisibleRows();
1910     // decrement nBottomRow to make it the number of the last visible line
1911     // (count starts with 0!).
1912     // Example: BrowseBox contains exactly one entry. nBottomRow := 0 + 1 - 1
1913     if( nBottomRow )
1914         nBottomRow--;
1915 
1916     if ( nRow > nBottomRow )
1917         // scroll further to the top
1918         ScrollRows( nRow - nBottomRow );
1919 }
1920 
1921 
IsFieldVisible(long nRow,sal_uInt16 nColumnId,bool bCompletely) const1922 bool BrowseBox::IsFieldVisible( long nRow, sal_uInt16 nColumnId,
1923                                 bool bCompletely ) const
1924 {
1925 
1926     // hidden by frozen column?
1927     sal_uInt16 nColPos = GetColumnPos( nColumnId );
1928     if ( nColPos >= FrozenColCount() && nColPos < nFirstCol )
1929         return false;
1930 
1931     tools::Rectangle aRect( ImplFieldRectPixel( nRow, nColumnId ) );
1932     if ( aRect.IsEmpty() )
1933         return false;
1934 
1935     // get the visible area
1936     tools::Rectangle aOutRect( Point(0, 0), pDataWin->GetOutputSizePixel() );
1937 
1938     if ( bCompletely )
1939         // test if the field is completely visible
1940         return aOutRect.IsInside( aRect );
1941     else
1942         // test if the field is partly of completely visible
1943         return !aOutRect.Intersection( aRect ).IsEmpty();
1944 }
1945 
1946 
GetFieldRectPixel(long nRow,sal_uInt16 nColumnId,bool bRelToBrowser) const1947 tools::Rectangle BrowseBox::GetFieldRectPixel( long nRow, sal_uInt16 nColumnId,
1948                                         bool bRelToBrowser) const
1949 {
1950 
1951     // get the rectangle relative to DataWin
1952     tools::Rectangle aRect( ImplFieldRectPixel( nRow, nColumnId ) );
1953     if ( aRect.IsEmpty() )
1954         return aRect;
1955 
1956     // adjust relative to BrowseBox's output area
1957     Point aTopLeft( aRect.TopLeft() );
1958     if ( bRelToBrowser )
1959     {
1960         aTopLeft = pDataWin->OutputToScreenPixel( aTopLeft );
1961         aTopLeft = ScreenToOutputPixel( aTopLeft );
1962     }
1963 
1964     return tools::Rectangle( aTopLeft, aRect.GetSize() );
1965 }
1966 
1967 
GetRowRectPixel(long nRow) const1968 tools::Rectangle BrowseBox::GetRowRectPixel( long nRow  ) const
1969 {
1970 
1971     // get the rectangle relative to DataWin
1972     tools::Rectangle aRect;
1973     if ( nTopRow > nRow )
1974         // row is above visible area
1975         return aRect;
1976     aRect = tools::Rectangle(
1977         Point( 0, GetDataRowHeight() * (nRow-nTopRow) ),
1978         Size( pDataWin->GetOutputSizePixel().Width(), GetDataRowHeight() ) );
1979     if ( aRect.TopLeft().Y() > pDataWin->GetOutputSizePixel().Height() )
1980         // row is below visible area
1981         return aRect;
1982 
1983     // adjust relative to BrowseBox's output area
1984     Point aTopLeft( aRect.TopLeft() );
1985     aTopLeft = pDataWin->OutputToScreenPixel( aTopLeft );
1986     aTopLeft = ScreenToOutputPixel( aTopLeft );
1987 
1988     return tools::Rectangle( aTopLeft, aRect.GetSize() );
1989 }
1990 
1991 
ImplFieldRectPixel(long nRow,sal_uInt16 nColumnId) const1992 tools::Rectangle BrowseBox::ImplFieldRectPixel( long nRow, sal_uInt16 nColumnId ) const
1993 {
1994 
1995     // compute the X-coordinate relative to DataWin by accumulation
1996     long nColX = 0;
1997     sal_uInt16 nFrozenCols = FrozenColCount();
1998     size_t nCol;
1999     for ( nCol = 0;
2000           nCol < mvCols.size() && mvCols[ nCol ]->GetId() != nColumnId;
2001           ++nCol )
2002         if ( mvCols[ nCol ]->IsFrozen() || nCol >= nFirstCol )
2003             nColX += mvCols[ nCol ]->Width();
2004 
2005     if ( nCol >= mvCols.size() || ( nCol >= nFrozenCols && nCol < nFirstCol ) )
2006         return tools::Rectangle();
2007 
2008     // compute the Y-coordinate relative to DataWin
2009     long nRowY = GetDataRowHeight();
2010     if ( nRow != BROWSER_ENDOFSELECTION ) // #105497# OJ
2011         nRowY = ( nRow - nTopRow ) * GetDataRowHeight();
2012 
2013     // assemble the Rectangle relative to DataWin
2014     return tools::Rectangle(
2015         Point( nColX + MIN_COLUMNWIDTH, nRowY ),
2016         Size( (mvCols[nCol]->Width() == LONG_MAX
2017                ? LONG_MAX - (nColX + MIN_COLUMNWIDTH) : mvCols[ nCol ]->Width() - 2*MIN_COLUMNWIDTH),
2018               GetDataRowHeight() - 1 ) );
2019 }
2020 
2021 
GetRowAtYPosPixel(long nY,bool bRelToBrowser) const2022 long BrowseBox::GetRowAtYPosPixel( long nY, bool bRelToBrowser ) const
2023 {
2024 
2025     // compute the Y-coordinate
2026     if ( bRelToBrowser )
2027     {
2028         Point aDataTopLeft = pDataWin->OutputToScreenPixel( Point(0, 0) );
2029         Point aTopLeft = OutputToScreenPixel( Point(0, 0) );
2030         nY -= aDataTopLeft.Y() - aTopLeft.Y();
2031     }
2032 
2033     // no row there (e.g. in the header)
2034     if ( nY < 0 || nY >= pDataWin->GetOutputSizePixel().Height() )
2035         return -1;
2036 
2037     return nY / GetDataRowHeight() + nTopRow;
2038 }
2039 
2040 
GetFieldRect(sal_uInt16 nColumnId) const2041 tools::Rectangle BrowseBox::GetFieldRect( sal_uInt16 nColumnId ) const
2042 {
2043 
2044     return GetFieldRectPixel( nCurRow, nColumnId );
2045 }
2046 
2047 
GetColumnAtXPosPixel(long nX) const2048 sal_uInt16 BrowseBox::GetColumnAtXPosPixel( long nX ) const
2049 {
2050 
2051     // accumulate the widths of the visible columns
2052     long nColX = 0;
2053     for ( size_t nCol = 0; nCol < mvCols.size(); ++nCol )
2054     {
2055         BrowserColumn *pCol = mvCols[ nCol ].get();
2056         if ( pCol->IsFrozen() || nCol >= nFirstCol )
2057             nColX += pCol->Width();
2058 
2059         if ( nColX > nX )
2060             return nCol;
2061     }
2062 
2063     return BROWSER_INVALIDID;
2064 }
2065 
ReserveControlArea(sal_uInt16 nWidth)2066 bool BrowseBox::ReserveControlArea(sal_uInt16 nWidth)
2067 {
2068     if (nWidth != nControlAreaWidth)
2069     {
2070         OSL_ENSURE(nWidth,"Control area of 0 is not allowed, Use USHRT_MAX instead!");
2071         nControlAreaWidth = nWidth;
2072         UpdateScrollbars();
2073         return true;
2074     }
2075     return false;
2076 }
2077 
GetControlArea() const2078 tools::Rectangle BrowseBox::GetControlArea() const
2079 {
2080     auto nHeight = aHScroll->GetSizePixel().Height();
2081     return tools::Rectangle(
2082         Point( 0, GetOutputSizePixel().Height() - nHeight ),
2083         Size( GetOutputSizePixel().Width() - aHScroll->GetSizePixel().Width(),
2084              nHeight ) );
2085 }
2086 
SetMode(BrowserMode nMode)2087 void BrowseBox::SetMode( BrowserMode nMode )
2088 {
2089 
2090     pDataWin->bAutoHScroll = BrowserMode::AUTO_HSCROLL == ( nMode & BrowserMode::AUTO_HSCROLL );
2091     pDataWin->bAutoVScroll = BrowserMode::AUTO_VSCROLL == ( nMode & BrowserMode::AUTO_VSCROLL );
2092     pDataWin->bNoHScroll   = BrowserMode::NO_HSCROLL   == ( nMode & BrowserMode::NO_HSCROLL );
2093     pDataWin->bNoVScroll   = BrowserMode::NO_VSCROLL   == ( nMode & BrowserMode::NO_VSCROLL );
2094 
2095     DBG_ASSERT( !( pDataWin->bAutoHScroll && pDataWin->bNoHScroll ),
2096         "BrowseBox::SetMode: AutoHScroll *and* NoHScroll?" );
2097     DBG_ASSERT( !( pDataWin->bAutoVScroll && pDataWin->bNoVScroll ),
2098         "BrowseBox::SetMode: AutoVScroll *and* NoVScroll?" );
2099     if ( pDataWin->bAutoHScroll )
2100         pDataWin->bNoHScroll = false;
2101     if ( pDataWin->bAutoVScroll )
2102         pDataWin->bNoVScroll = false;
2103 
2104     if ( pDataWin->bNoHScroll )
2105         aHScroll->Hide();
2106 
2107     nControlAreaWidth = USHRT_MAX;
2108 
2109     long nOldRowSel = bMultiSelection ? uRow.pSel->FirstSelected() : uRow.nSel;
2110     MultiSelection *pOldRowSel = bMultiSelection ? uRow.pSel : nullptr;
2111 
2112     pVScroll.disposeAndClear();
2113 
2114     bMultiSelection = bool( nMode & BrowserMode::MULTISELECTION );
2115     bColumnCursor = bool( nMode & BrowserMode::COLUMNSELECTION );
2116     bKeepHighlight = bool( nMode & BrowserMode::KEEPHIGHLIGHT );
2117 
2118     bHideSelect = ((nMode & BrowserMode::HIDESELECT) == BrowserMode::HIDESELECT);
2119     // default: do not hide the cursor at all (untaken scrolling and such)
2120     bHideCursor = TRISTATE_FALSE;
2121 
2122     if ( BrowserMode::HIDECURSOR == ( nMode & BrowserMode::HIDECURSOR ) )
2123     {
2124         bHideCursor = TRISTATE_TRUE;
2125     }
2126 
2127     m_bFocusOnlyCursor = ((nMode & BrowserMode::CURSOR_WO_FOCUS) == BrowserMode::NONE);
2128 
2129     bHLines = ( nMode & BrowserMode::HLINES ) == BrowserMode::HLINES;
2130     bVLines = ( nMode & BrowserMode::VLINES ) == BrowserMode::VLINES;
2131 
2132     WinBits nVScrollWinBits =
2133         WB_VSCROLL | ( ( nMode & BrowserMode::THUMBDRAGGING ) ? WB_DRAG : 0 );
2134     pVScroll = ( nMode & BrowserMode::TRACKING_TIPS ) == BrowserMode::TRACKING_TIPS
2135                 ? VclPtr<BrowserScrollBar>::Create( this, nVScrollWinBits, pDataWin.get() )
2136                 : VclPtr<ScrollBar>::Create( this, nVScrollWinBits );
2137     pVScroll->SetLineSize( 1 );
2138     pVScroll->SetPageSize(1);
2139     pVScroll->SetScrollHdl( LINK( this, BrowseBox, ScrollHdl ) );
2140 
2141     pDataWin->bAutoSizeLastCol =
2142             BrowserMode::AUTOSIZE_LASTCOL == ( nMode & BrowserMode::AUTOSIZE_LASTCOL );
2143 
2144     // create a headerbar. what happens, if a headerbar has to be created and
2145     // there already are columns?
2146     if ( BrowserMode::HEADERBAR_NEW == ( nMode & BrowserMode::HEADERBAR_NEW ) )
2147     {
2148         if (!pDataWin->pHeaderBar)
2149             pDataWin->pHeaderBar = CreateHeaderBar( this );
2150     }
2151     else
2152     {
2153         pDataWin->pHeaderBar.disposeAndClear();
2154     }
2155 
2156     if ( bColumnCursor )
2157     {
2158         if (!pColSel)
2159             pColSel.reset(new MultiSelection);
2160         pColSel->SetTotalRange( Range( 0, mvCols.size()-1 ) );
2161     }
2162     else
2163     {
2164         pColSel.reset();
2165     }
2166 
2167     if ( bMultiSelection )
2168     {
2169         if ( pOldRowSel )
2170             uRow.pSel = pOldRowSel;
2171         else
2172             uRow.pSel = new MultiSelection;
2173     }
2174     else
2175     {
2176         uRow.nSel = nOldRowSel;
2177         delete pOldRowSel;
2178     }
2179 
2180     if ( bBootstrapped )
2181     {
2182         StateChanged( StateChangedType::InitShow );
2183         if ( bMultiSelection && !pOldRowSel &&
2184              nOldRowSel != BROWSER_ENDOFSELECTION )
2185             uRow.pSel->Select( nOldRowSel );
2186     }
2187 
2188     if ( pDataWin )
2189         pDataWin->Invalidate();
2190 
2191     // no cursor on handle column
2192     if ( nCurColId == HandleColumnId )
2193         nCurColId = GetColumnId( 1 );
2194 
2195     m_nCurrentMode = nMode;
2196 }
2197 
2198 
VisibleRowsChanged(long,sal_uInt16)2199 void BrowseBox::VisibleRowsChanged( long, sal_uInt16 )
2200 {
2201 
2202     // old behavior: automatically correct NumRows:
2203     if ( nRowCount < GetRowCount() )
2204     {
2205         RowInserted(nRowCount,GetRowCount() - nRowCount, false);
2206     }
2207     else if ( nRowCount > GetRowCount() )
2208     {
2209         RowRemoved(nRowCount-(nRowCount - GetRowCount()),nRowCount - GetRowCount(), false);
2210     }
2211 }
2212 
2213 
IsCursorMoveAllowed(long,sal_uInt16) const2214 bool BrowseBox::IsCursorMoveAllowed( long, sal_uInt16 ) const
2215 
2216 /*  [Description]
2217 
2218     This virtual method is always called before the cursor is moved directly.
2219     By means of 'return false', we avoid doing this if e.g. a record
2220     contradicts any rules.
2221 
2222     This method is not called, if the cursor movement results from removing or
2223     deleting a row/column (thus, in cases where only a "cursor correction" happens).
2224 
2225     The base implementation currently always returns true.
2226 */
2227 
2228 {
2229     return true;
2230 }
2231 
2232 
GetDataRowHeight() const2233 long BrowseBox::GetDataRowHeight() const
2234 {
2235     return CalcZoom(nDataRowHeight ? nDataRowHeight : ImpGetDataRowHeight());
2236 }
2237 
2238 
CreateHeaderBar(BrowseBox * pParent)2239 VclPtr<BrowserHeader> BrowseBox::CreateHeaderBar( BrowseBox* pParent )
2240 {
2241     VclPtr<BrowserHeader> pNewBar = VclPtr<BrowserHeader>::Create( pParent );
2242     pNewBar->SetStartDragHdl( LINK( this, BrowseBox, StartDragHdl ) );
2243     return pNewBar;
2244 }
2245 
SetHeaderBar(BrowserHeader * pHeaderBar)2246 void BrowseBox::SetHeaderBar( BrowserHeader* pHeaderBar )
2247 {
2248     pDataWin->pHeaderBar.disposeAndClear();
2249     pDataWin->pHeaderBar = pHeaderBar;
2250     pDataWin->pHeaderBar->SetStartDragHdl( LINK( this, BrowseBox, StartDragHdl ) );
2251 }
2252 
GetTitleHeight() const2253 long BrowseBox::GetTitleHeight() const
2254 {
2255     long nHeight;
2256     // ask the header bar for the text height (if possible), as the header bar's font is adjusted with
2257     // our (and the header's) zoom factor
2258     HeaderBar* pHeaderBar = pDataWin->pHeaderBar;
2259     if ( pHeaderBar )
2260         nHeight = pHeaderBar->GetTextHeight();
2261     else
2262         nHeight = GetTextHeight();
2263 
2264     return nTitleLines ? nTitleLines * nHeight + 4 : 0;
2265 }
2266 
CalcReverseZoom(long nVal)2267 long BrowseBox::CalcReverseZoom(long nVal)
2268 {
2269     if (IsZoom())
2270     {
2271         const Fraction& rZoom = GetZoom();
2272         double n = static_cast<double>(nVal);
2273         n *= static_cast<double>(rZoom.GetDenominator());
2274         if (!rZoom.GetNumerator())
2275             throw o3tl::divide_by_zero();
2276         n /= static_cast<double>(rZoom.GetNumerator());
2277         nVal = n>0 ? static_cast<long>(n + 0.5) : -static_cast<long>(-n + 0.5);
2278     }
2279 
2280     return nVal;
2281 }
2282 
CursorMoved()2283 void BrowseBox::CursorMoved()
2284 {
2285     // before implementing more here, please adjust the EditBrowseBox
2286 
2287     if ( isAccessibleAlive() && HasFocus() )
2288         commitTableEvent(
2289             ACTIVE_DESCENDANT_CHANGED,
2290             makeAny( CreateAccessibleCell( GetCurRow(),GetColumnPos( GetCurColumnId() ) ) ),
2291             Any()
2292         );
2293 }
2294 
LoseFocus()2295 void BrowseBox::LoseFocus()
2296 {
2297     SAL_INFO("svtools", "BrowseBox::LoseFocus " << this );
2298 
2299     if ( bHasFocus )
2300     {
2301         SAL_INFO("svtools", "BrowseBox::HideCursor " << this );
2302         DoHideCursor( "LoseFocus" );
2303 
2304         if ( !bKeepHighlight )
2305         {
2306             ToggleSelection();
2307             bSelectionIsVisible = false;
2308         }
2309 
2310         bHasFocus = false;
2311     }
2312     Control::LoseFocus();
2313 }
2314 
2315 
GetFocus()2316 void BrowseBox::GetFocus()
2317 {
2318     SAL_INFO("svtools", "BrowseBox::GetFocus " << this );
2319 
2320     if ( !bHasFocus )
2321     {
2322         if ( !bSelectionIsVisible )
2323         {
2324             bSelectionIsVisible = true;
2325             if ( bBootstrapped )
2326                 ToggleSelection();
2327         }
2328 
2329         bHasFocus = true;
2330         DoShowCursor( "GetFocus" );
2331     }
2332     Control::GetFocus();
2333 }
2334 
2335 
GetVisibleRows() const2336 sal_uInt16 BrowseBox::GetVisibleRows() const
2337 {
2338     return static_cast<sal_uInt16>((pDataWin->GetOutputSizePixel().Height() - 1 )/ GetDataRowHeight() + 1);
2339 }
2340 
GetDataWindow() const2341 vcl::Window& BrowseBox::GetDataWindow() const
2342 {
2343     return *pDataWin;
2344 }
2345 
2346 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2347