/src/libreoffice/vcl/source/treelist/iconviewimpl.cxx
Line | Count | Source |
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 <vcl/svapp.hxx> |
21 | | #include <vcl/toolkit/treelistentry.hxx> |
22 | | #include <tools/debug.hxx> |
23 | | #include "iconviewimpl.hxx" |
24 | | |
25 | | IconViewImpl::IconViewImpl(IconView* pIconView, SvTreeList* pTreeList, WinBits nWinStyle) |
26 | 0 | : SvImpLBox(pIconView, pTreeList, nWinStyle) |
27 | 0 | { |
28 | 0 | } |
29 | | |
30 | | IconView& IconViewImpl::GetIconView() const |
31 | 0 | { |
32 | 0 | assert(m_pView); |
33 | 0 | return static_cast<IconView&>(*m_pView); |
34 | 0 | } |
35 | | |
36 | | Size IconViewImpl::GetEntrySize(const SvTreeListEntry& entry) const |
37 | 0 | { |
38 | 0 | return GetIconView().GetEntrySize(entry); |
39 | 0 | } |
40 | | |
41 | | void IconViewImpl::IterateVisibleEntryAreas(const IterateEntriesFunc& f, bool fromStartEntry) const |
42 | 0 | { |
43 | 0 | tools::Long x = 0, y = 0; |
44 | 0 | short column = 0; |
45 | 0 | const tools::Long rowWidth = m_pView->GetEntryWidth() * GetIconView().GetColumnCount(); |
46 | 0 | tools::Long nPrevHeight = 0; |
47 | 0 | for (auto entry = fromStartEntry ? m_pStartEntry : m_pView->FirstVisible(); entry; |
48 | 0 | entry = m_pView->NextVisible(entry)) |
49 | 0 | { |
50 | 0 | const Size s = GetEntrySize(*entry); |
51 | 0 | if (x >= rowWidth || entry->IsSeparator()) |
52 | 0 | { |
53 | 0 | column = 0; |
54 | 0 | x = 0; |
55 | 0 | y += nPrevHeight; |
56 | 0 | } |
57 | 0 | EntryAreaInfo info{ *entry, column, tools::Rectangle{ Point{ x, y }, s } }; |
58 | 0 | const auto result = f(info); |
59 | 0 | if (result == CallbackResult::Stop) |
60 | 0 | return; |
61 | 0 | ++column; |
62 | 0 | x += s.Width(); |
63 | 0 | nPrevHeight = s.Height(); |
64 | 0 | } |
65 | 0 | } |
66 | | |
67 | | tools::Long IconViewImpl::GetEntryRow(const SvTreeListEntry* entry) const |
68 | 0 | { |
69 | 0 | tools::Long nEntryRow = -1; |
70 | 0 | auto GetRow = [entry, &nEntryRow, row = -1](const EntryAreaInfo& info) mutable |
71 | 0 | { |
72 | 0 | if (info.column == 0 && !info.entry.IsSeparator()) |
73 | 0 | ++row; |
74 | 0 | if (&info.entry != entry) |
75 | 0 | return CallbackResult::Continue; |
76 | 0 | nEntryRow = row; |
77 | 0 | return CallbackResult::Stop; |
78 | 0 | }; |
79 | 0 | IterateVisibleEntryAreas(GetRow); |
80 | 0 | return nEntryRow; |
81 | 0 | } |
82 | | |
83 | | void IconViewImpl::SetStartEntry(SvTreeListEntry* entry) |
84 | 0 | { |
85 | 0 | const tools::Long max = m_aVerSBar->GetRangeMax() - m_aVerSBar->GetVisibleSize(); |
86 | 0 | tools::Long row = -1; |
87 | 0 | auto GetEntryAndRow = [&entry, &row, max, found = entry](const EntryAreaInfo& info) mutable |
88 | 0 | { |
89 | 0 | if (info.column == 0 && !info.entry.IsSeparator()) |
90 | 0 | { |
91 | 0 | found = &info.entry; |
92 | 0 | ++row; |
93 | 0 | } |
94 | 0 | if (row >= max || &info.entry == entry) |
95 | 0 | { |
96 | 0 | entry = found; |
97 | 0 | return CallbackResult::Stop; |
98 | 0 | } |
99 | 0 | return CallbackResult::Continue; |
100 | 0 | }; |
101 | 0 | IterateVisibleEntryAreas(GetEntryAndRow); |
102 | |
|
103 | 0 | m_pStartEntry = entry; |
104 | 0 | m_aVerSBar->SetThumbPos(row); |
105 | 0 | m_pView->Invalidate(GetVisibleArea()); |
106 | 0 | } |
107 | | |
108 | | void IconViewImpl::ScrollTo(SvTreeListEntry& rEntry) |
109 | 0 | { |
110 | 0 | if (!m_aVerSBar->IsVisible()) |
111 | 0 | return; |
112 | 0 | const tools::Long entryRow = GetEntryRow(&rEntry); |
113 | 0 | const tools::Long oldStartRow = m_aVerSBar->GetThumbPos(); |
114 | 0 | if (entryRow < oldStartRow) |
115 | 0 | IconViewImpl::SetStartEntry(&rEntry); |
116 | 0 | const tools::Long visibleRows = m_aVerSBar->GetVisibleSize(); |
117 | 0 | const tools::Long posRelativeToBottom = entryRow - (oldStartRow + visibleRows) + 1; |
118 | 0 | if (posRelativeToBottom > 0) |
119 | 0 | IconViewImpl::SetStartEntry(GoToNextRow(m_pStartEntry, posRelativeToBottom)); |
120 | 0 | } |
121 | | |
122 | | SvTreeListEntry* IconViewImpl::GoToPrevRow(SvTreeListEntry* pEntry, int nRows) const |
123 | 0 | { |
124 | 0 | SvTreeListEntry* pPrev = pEntry; |
125 | 0 | auto FindPrev = [this, pEntry, nRows, &pPrev, |
126 | 0 | prevs = std::vector<SvTreeListEntry*>()](const EntryAreaInfo& info) mutable |
127 | 0 | { |
128 | 0 | if (info.column == 0 && !info.entry.IsSeparator()) |
129 | 0 | prevs.push_back(&info.entry); |
130 | 0 | if (pEntry == &info.entry) |
131 | 0 | { |
132 | 0 | if (prevs.size() > 1) |
133 | 0 | { |
134 | 0 | int i = std::max(0, static_cast<int>(prevs.size()) - nRows - 1); |
135 | 0 | pPrev = prevs[i]; |
136 | 0 | for (short column = info.column; column; --column) |
137 | 0 | { |
138 | 0 | SvTreeListEntry* pNext = m_pView->NextVisible(pPrev); |
139 | 0 | if (!pNext || pNext->IsSeparator()) |
140 | 0 | break; |
141 | 0 | pPrev = pNext; |
142 | 0 | } |
143 | 0 | } |
144 | 0 | return CallbackResult::Stop; |
145 | 0 | } |
146 | 0 | return CallbackResult::Continue; |
147 | 0 | }; |
148 | 0 | IterateVisibleEntryAreas(FindPrev); |
149 | |
|
150 | 0 | return pPrev; |
151 | 0 | } |
152 | | |
153 | | SvTreeListEntry* IconViewImpl::GoToNextRow(SvTreeListEntry* pEntry, int nRows) const |
154 | 0 | { |
155 | 0 | SvTreeListEntry* pNext = pEntry; |
156 | 0 | auto FindNext = [pEntry, nRows, &pNext, column = -1](const EntryAreaInfo& info) mutable |
157 | 0 | { |
158 | 0 | if (info.column <= column && !info.entry.IsSeparator()) |
159 | 0 | { |
160 | 0 | if (info.column == 0 && --nRows < 0) |
161 | 0 | return CallbackResult::Stop; |
162 | 0 | pNext = &info.entry; |
163 | 0 | if (info.column == column && nRows == 0) |
164 | 0 | return CallbackResult::Stop; |
165 | 0 | } |
166 | 0 | else if (pEntry == &info.entry) |
167 | 0 | { |
168 | 0 | column = info.column; |
169 | 0 | } |
170 | 0 | return CallbackResult::Continue; |
171 | 0 | }; |
172 | 0 | IterateVisibleEntryAreas(FindNext); |
173 | |
|
174 | 0 | return pNext; |
175 | 0 | } |
176 | | |
177 | | void IconViewImpl::CursorUp() |
178 | 0 | { |
179 | 0 | if (!m_pStartEntry) |
180 | 0 | return; |
181 | | |
182 | 0 | SvTreeListEntry* pPrevFirstToDraw = GoToPrevRow(m_pStartEntry, 1); |
183 | |
|
184 | 0 | m_nFlags &= ~LBoxFlags::Filling; |
185 | 0 | ShowCursor( false ); |
186 | 0 | SetStartEntry(pPrevFirstToDraw); |
187 | 0 | ShowCursor( true ); |
188 | 0 | m_pView->NotifyScrolled(); |
189 | 0 | } |
190 | | |
191 | | void IconViewImpl::CursorDown() |
192 | 0 | { |
193 | 0 | if (!m_pStartEntry) |
194 | 0 | return; |
195 | | |
196 | 0 | SvTreeListEntry* pNextFirstToDraw = GoToNextRow(m_pStartEntry, 1); |
197 | |
|
198 | 0 | m_nFlags &= ~LBoxFlags::Filling; |
199 | 0 | ShowCursor( false ); |
200 | 0 | SetStartEntry(pNextFirstToDraw); |
201 | 0 | ShowCursor( true ); |
202 | 0 | m_pView->NotifyScrolled(); |
203 | 0 | } |
204 | | |
205 | | void IconViewImpl::PageDown( sal_uInt16 nDelta ) |
206 | 0 | { |
207 | 0 | if( !nDelta ) |
208 | 0 | return; |
209 | | |
210 | 0 | if (!m_pStartEntry) |
211 | 0 | return; |
212 | | |
213 | 0 | SvTreeListEntry* pNext = GoToNextRow(m_pStartEntry, nDelta); |
214 | |
|
215 | 0 | ShowCursor( false ); |
216 | |
|
217 | 0 | m_nFlags &= ~LBoxFlags::Filling; |
218 | 0 | SetStartEntry(pNext); |
219 | |
|
220 | 0 | ShowCursor( true ); |
221 | 0 | } |
222 | | |
223 | | void IconViewImpl::PageUp( sal_uInt16 nDelta ) |
224 | 0 | { |
225 | 0 | if( !nDelta ) |
226 | 0 | return; |
227 | | |
228 | 0 | if (!m_pStartEntry) |
229 | 0 | return; |
230 | | |
231 | 0 | SvTreeListEntry* pPrev = GoToPrevRow(m_pStartEntry, nDelta); |
232 | |
|
233 | 0 | m_nFlags &= ~LBoxFlags::Filling; |
234 | 0 | ShowCursor( false ); |
235 | |
|
236 | 0 | SetStartEntry(pPrev); |
237 | |
|
238 | 0 | ShowCursor( true ); |
239 | 0 | } |
240 | | |
241 | | void IconViewImpl::KeyDown( bool bPageDown ) |
242 | 0 | { |
243 | 0 | if( !m_aVerSBar->IsVisible() ) |
244 | 0 | return; |
245 | | |
246 | 0 | tools::Long nDelta; |
247 | 0 | if( bPageDown ) |
248 | 0 | nDelta = m_aVerSBar->GetPageSize(); |
249 | 0 | else |
250 | 0 | nDelta = 1; |
251 | |
|
252 | 0 | if( nDelta <= 0 ) |
253 | 0 | return; |
254 | | |
255 | 0 | m_nFlags &= ~LBoxFlags::Filling; |
256 | |
|
257 | 0 | if( bPageDown ) |
258 | 0 | PageDown( static_cast<short>(nDelta) ); |
259 | 0 | else |
260 | 0 | CursorDown(); |
261 | 0 | } |
262 | | |
263 | | void IconViewImpl::KeyUp( bool bPageUp ) |
264 | 0 | { |
265 | 0 | if( !m_aVerSBar->IsVisible() ) |
266 | 0 | return; |
267 | | |
268 | 0 | tools::Long nDelta; |
269 | 0 | if( bPageUp ) |
270 | 0 | nDelta = m_aVerSBar->GetPageSize(); |
271 | 0 | else |
272 | 0 | nDelta = 1; |
273 | |
|
274 | 0 | m_nFlags &= ~LBoxFlags::Filling; |
275 | |
|
276 | 0 | if( bPageUp ) |
277 | 0 | PageUp( static_cast<short>(nDelta) ); |
278 | 0 | else |
279 | 0 | CursorUp(); |
280 | 0 | } |
281 | | |
282 | | tools::Long IconViewImpl::GetEntryLine(const SvTreeListEntry* pEntry) const |
283 | 0 | { |
284 | 0 | if(!m_pStartEntry ) |
285 | 0 | return -1; // invisible position |
286 | | |
287 | 0 | return IconViewImpl::GetEntryPosition(pEntry).Y(); |
288 | 0 | } |
289 | | |
290 | | Point IconViewImpl::GetEntryPosition(const SvTreeListEntry* pEntry) const |
291 | 0 | { |
292 | 0 | Point result{ -m_pView->GetEntryWidth(), -m_pView->GetEntryHeight() }; // invisible |
293 | 0 | auto FindEntryPos = [pEntry, &result](const EntryAreaInfo& info) |
294 | 0 | { |
295 | 0 | if (pEntry == &info.entry) |
296 | 0 | { |
297 | 0 | result = info.area.TopLeft(); |
298 | 0 | return CallbackResult::Stop; |
299 | 0 | } |
300 | 0 | return CallbackResult::Continue; |
301 | 0 | }; |
302 | 0 | IterateVisibleEntryAreas(FindEntryPos, true); |
303 | |
|
304 | 0 | return result; |
305 | 0 | } |
306 | | |
307 | | // Returns the last entry (in respective row) if position is just past the last entry |
308 | | SvTreeListEntry* IconViewImpl::GetClickedEntry( const Point& rPoint ) const |
309 | 0 | { |
310 | 0 | DBG_ASSERT( m_pView->GetModel(), "IconViewImpl::GetClickedEntry: how can this ever happen?" ); |
311 | 0 | if ( !m_pView->GetModel() ) |
312 | 0 | return nullptr; |
313 | 0 | if( m_pView->GetEntryCount() == 0 || !m_pStartEntry || !m_pView->GetEntryHeight() || !m_pView->GetEntryWidth()) |
314 | 0 | return nullptr; |
315 | | |
316 | 0 | SvTreeListEntry* pEntry = nullptr; |
317 | 0 | auto FindEntryByPos = [&pEntry, &rPoint](const EntryAreaInfo& info) |
318 | 0 | { |
319 | 0 | if (info.area.Contains(rPoint)) |
320 | 0 | { |
321 | 0 | pEntry = &info.entry; |
322 | 0 | return CallbackResult::Stop; |
323 | 0 | } |
324 | 0 | else if (info.area.Top() > rPoint.Y()) |
325 | 0 | { |
326 | 0 | return CallbackResult::Stop; // we are already below the clicked row |
327 | 0 | } |
328 | 0 | else if (info.area.Bottom() > rPoint.Y()) |
329 | 0 | { |
330 | 0 | pEntry = &info.entry; // Same row; store the entry in case the click is past all entries |
331 | 0 | } |
332 | 0 | return CallbackResult::Continue; |
333 | 0 | }; |
334 | 0 | IterateVisibleEntryAreas(FindEntryByPos, true); |
335 | |
|
336 | 0 | return pEntry; |
337 | 0 | } |
338 | | |
339 | | bool IconViewImpl::IsEntryInView( SvTreeListEntry* pEntry ) const |
340 | 0 | { |
341 | | // parent collapsed |
342 | 0 | if( !m_pView->IsEntryVisible(pEntry) ) |
343 | 0 | return false; |
344 | | |
345 | 0 | tools::Long nY = GetEntryLine( pEntry ); |
346 | 0 | if( nY < 0 ) |
347 | 0 | return false; |
348 | | |
349 | 0 | tools::Long height = GetEntrySize(*pEntry).Height(); |
350 | 0 | if (nY + height > m_aOutputSize.Height()) |
351 | 0 | return false; |
352 | | |
353 | 0 | return true; |
354 | 0 | } |
355 | | |
356 | | void IconViewImpl::AdjustScrollBars( Size& rSize ) |
357 | 0 | { |
358 | 0 | tools::Long nEntryHeight = m_pView->GetEntryHeight(); |
359 | 0 | if( !nEntryHeight ) |
360 | 0 | return; |
361 | | |
362 | 0 | sal_uInt16 nResult = 0; |
363 | |
|
364 | 0 | Size aOSize( m_pView->Control::GetOutputSizePixel() ); |
365 | |
|
366 | 0 | const WinBits nWindowStyle = m_pView->GetStyle(); |
367 | 0 | bool bVerSBar = ( nWindowStyle & WB_VSCROLL ) != 0; |
368 | | |
369 | | // number of entries visible within the view |
370 | 0 | const tools::Long nVisibleRows = aOSize.Height() / nEntryHeight; |
371 | 0 | m_nVisibleCount = nVisibleRows * GetIconView().GetColumnCount(); |
372 | |
|
373 | 0 | tools::Long nTotalRows = 0; |
374 | 0 | tools::Long totalHeight = 0; |
375 | 0 | auto CountRowsAndHeight = [&nTotalRows, &totalHeight](const EntryAreaInfo& info) |
376 | 0 | { |
377 | 0 | totalHeight = std::max(totalHeight, info.area.Bottom()); |
378 | 0 | if (info.column == 0 && !info.entry.IsSeparator()) |
379 | 0 | ++nTotalRows; |
380 | 0 | return CallbackResult::Continue; |
381 | 0 | }; |
382 | 0 | IterateVisibleEntryAreas(CountRowsAndHeight); |
383 | | |
384 | | // do we need a vertical scrollbar? |
385 | 0 | if( bVerSBar || totalHeight > aOSize.Height()) |
386 | 0 | { |
387 | 0 | nResult = 1; |
388 | 0 | } |
389 | | |
390 | | // do we need a Horizontal scrollbar? |
391 | 0 | bool bHorSBar = (nWindowStyle & WB_HSCROLL) != 0; |
392 | 0 | if (bHorSBar || m_pView->GetEntryWidth() > aOSize.Width()) |
393 | 0 | { |
394 | 0 | nResult += 2; |
395 | 0 | m_aHorSBar->SetRange(Range(0, m_pView->GetEntryWidth())); |
396 | 0 | m_aHorSBar->SetVisibleSize(aOSize.Width()); |
397 | 0 | } |
398 | |
|
399 | 0 | PositionScrollBars( aOSize, nResult ); |
400 | | |
401 | | // adapt Range, VisibleRange etc. |
402 | | |
403 | | // refresh output size, in case we have to scroll |
404 | 0 | tools::Rectangle aRect; |
405 | 0 | aRect.SetSize( aOSize ); |
406 | 0 | m_aSelEng.SetVisibleArea( aRect ); |
407 | | |
408 | | // vertical scrollbar |
409 | 0 | if( !m_bInVScrollHdl ) |
410 | 0 | { |
411 | 0 | m_aVerSBar->SetRange(Range(0, nTotalRows)); |
412 | 0 | m_aVerSBar->SetPageSize(nVisibleRows); |
413 | 0 | m_aVerSBar->SetVisibleSize(nVisibleRows); |
414 | 0 | } |
415 | 0 | else |
416 | 0 | { |
417 | 0 | m_nFlags |= LBoxFlags::EndScrollSetVisSize; |
418 | 0 | } |
419 | |
|
420 | 0 | if( nResult & 0x0001 ) |
421 | 0 | m_aVerSBar->Show(); |
422 | 0 | else |
423 | 0 | m_aVerSBar->Hide(); |
424 | |
|
425 | 0 | if (nResult & 0x0002) |
426 | 0 | m_aHorSBar->Show(); |
427 | 0 | else |
428 | 0 | m_aHorSBar->Hide(); |
429 | |
|
430 | 0 | rSize = aOSize; |
431 | 0 | } |
432 | | |
433 | | // returns 0 if position is just past the last entry |
434 | | SvTreeListEntry* IconViewImpl::GetEntry( const Point& rPoint ) const |
435 | 0 | { |
436 | 0 | if( (m_pView->GetEntryCount() == 0) || !m_pStartEntry || |
437 | 0 | (rPoint.Y() > m_aOutputSize.Height()) |
438 | 0 | || !m_pView->GetEntryHeight() |
439 | 0 | || !m_pView->GetEntryWidth()) |
440 | 0 | return nullptr; |
441 | | |
442 | 0 | SvTreeListEntry* pEntry = nullptr; |
443 | 0 | auto FindEntryByPos = [&pEntry, &rPoint](const EntryAreaInfo& info) |
444 | 0 | { |
445 | 0 | if (info.area.Contains(rPoint)) |
446 | 0 | { |
447 | 0 | pEntry = &info.entry; |
448 | 0 | return CallbackResult::Stop; |
449 | 0 | } |
450 | 0 | else if (info.area.Top() > rPoint.Y()) |
451 | 0 | { |
452 | 0 | return CallbackResult::Stop; // we are already below the clicked row |
453 | 0 | } |
454 | 0 | return CallbackResult::Continue; |
455 | 0 | }; |
456 | 0 | IterateVisibleEntryAreas(FindEntryByPos, true); |
457 | |
|
458 | 0 | return pEntry; |
459 | 0 | } |
460 | | |
461 | | void IconViewImpl::SyncVerThumb() |
462 | 0 | { |
463 | 0 | m_aVerSBar->SetThumbPos(GetEntryRow(m_pStartEntry)); |
464 | 0 | } |
465 | | |
466 | | void IconViewImpl::UpdateAll() |
467 | 0 | { |
468 | 0 | FindMostRight(); |
469 | 0 | AdjustScrollBars(m_aOutputSize); |
470 | 0 | SyncVerThumb(); |
471 | 0 | FillView(); |
472 | 0 | ShowVerSBar(); |
473 | 0 | if( m_bSimpleTravel && m_pCursor && m_pView->HasFocus() ) |
474 | 0 | m_pView->Select( m_pCursor ); |
475 | 0 | ShowCursor( true ); |
476 | 0 | m_pView->Invalidate( GetVisibleArea() ); |
477 | 0 | } |
478 | | |
479 | | void IconViewImpl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) |
480 | 0 | { |
481 | 0 | if (!m_pView->GetVisibleCount()) |
482 | 0 | return; |
483 | | |
484 | 0 | m_nFlags |= LBoxFlags::InPaint; |
485 | |
|
486 | 0 | if (m_nFlags & LBoxFlags::Filling) |
487 | 0 | { |
488 | 0 | SvTreeListEntry* pFirst = m_pView->First(); |
489 | 0 | if (pFirst != m_pStartEntry) |
490 | 0 | { |
491 | 0 | ShowCursor(false); |
492 | 0 | m_pStartEntry = m_pView->First(); |
493 | 0 | m_aVerSBar->SetThumbPos( 0 ); |
494 | 0 | StopUserEvent(); |
495 | 0 | ShowCursor(true); |
496 | 0 | m_nCurUserEvent = Application::PostUserEvent(LINK(this, SvImpLBox, MyUserEvent), |
497 | 0 | reinterpret_cast<void*>(1)); |
498 | 0 | return; |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | 0 | if (!m_pStartEntry) |
503 | 0 | { |
504 | 0 | m_pStartEntry = m_pView->First(); |
505 | 0 | } |
506 | |
|
507 | 0 | if (!m_pCursor && !mbNoAutoCurEntry) |
508 | 0 | { |
509 | | // do not select if multiselection or explicit set |
510 | 0 | bool bNotSelect = (m_aSelEng.GetSelectionMode() == SelectionMode::Multiple ) || ((m_nStyle & WB_NOINITIALSELECTION) == WB_NOINITIALSELECTION); |
511 | 0 | SetCursor(m_pStartEntry, bNotSelect); |
512 | 0 | } |
513 | |
|
514 | 0 | auto PaintEntry = [&rIconView = GetIconView(), &rRect, |
515 | 0 | &rRenderContext](const EntryAreaInfo& info) |
516 | 0 | { |
517 | 0 | if (!info.area.GetIntersection(rRect).IsEmpty()) |
518 | 0 | { |
519 | 0 | rIconView.PaintEntry(info.entry, info.area.Left(), info.area.Top(), rRenderContext); |
520 | 0 | } |
521 | 0 | else if (info.area.Top() > rRect.Bottom()) |
522 | 0 | { |
523 | 0 | return CallbackResult::Stop; // we are already below the last visible row |
524 | 0 | } |
525 | 0 | return CallbackResult::Continue; |
526 | 0 | }; |
527 | 0 | IterateVisibleEntryAreas(PaintEntry, true); |
528 | |
|
529 | 0 | m_nFlags &= ~LBoxFlags::DeselectAll; |
530 | 0 | rRenderContext.SetClipRegion(); |
531 | 0 | m_nFlags &= ~LBoxFlags::InPaint; |
532 | 0 | } |
533 | | |
534 | | void IconViewImpl::InvalidateEntry( tools::Long nId ) const |
535 | 0 | { |
536 | 0 | if( m_nFlags & LBoxFlags::InPaint ) |
537 | 0 | return; |
538 | 0 | if (nId < 0) |
539 | 0 | return; |
540 | | |
541 | | // nId is a Y coordinate of the top of the element, coming from GetEntryLine |
542 | 0 | tools::Rectangle aRect( GetVisibleArea() ); |
543 | 0 | if (nId > aRect.Bottom()) |
544 | 0 | return; |
545 | 0 | aRect.SetTop(nId); // Invalidate everything below |
546 | 0 | m_pView->Invalidate( aRect ); |
547 | 0 | } |
548 | | |
549 | | bool IconViewImpl::KeyInput( const KeyEvent& rKEvt ) |
550 | 0 | { |
551 | 0 | const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); |
552 | |
|
553 | 0 | if( rKeyCode.IsMod2() ) |
554 | 0 | return false; // don't evaluate Alt key |
555 | | |
556 | 0 | m_nFlags &= ~LBoxFlags::Filling; |
557 | |
|
558 | 0 | if( !m_pCursor ) |
559 | 0 | m_pCursor = m_pStartEntry; |
560 | 0 | if( !m_pCursor ) |
561 | 0 | return false; |
562 | | |
563 | 0 | sal_uInt16 aCode = rKeyCode.GetCode(); |
564 | |
|
565 | 0 | bool bShift = rKeyCode.IsShift(); |
566 | 0 | bool bMod1 = rKeyCode.IsMod1(); |
567 | |
|
568 | 0 | SvTreeListEntry* pNewCursor; |
569 | |
|
570 | 0 | bool bHandled = true; |
571 | |
|
572 | 0 | switch( aCode ) |
573 | 0 | { |
574 | 0 | case KEY_LEFT: |
575 | 0 | if( !IsEntryInView( m_pCursor ) ) |
576 | 0 | MakeVisible( m_pCursor ); |
577 | |
|
578 | 0 | pNewCursor = m_pCursor; |
579 | 0 | do |
580 | 0 | { |
581 | 0 | pNewCursor = m_pView->PrevVisible(pNewCursor); |
582 | 0 | } while( pNewCursor && !IsSelectable(pNewCursor) ); |
583 | | |
584 | | // if there is no next entry, take the current one |
585 | | // this ensures that in case of _one_ entry in the list, this entry is selected when pressing |
586 | | // the cursor key |
587 | 0 | if (!pNewCursor) |
588 | 0 | pNewCursor = m_pCursor; |
589 | |
|
590 | 0 | m_aSelEng.CursorPosChanging( bShift, bMod1 ); |
591 | 0 | SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on |
592 | 0 | if( !IsEntryInView( pNewCursor ) ) |
593 | 0 | KeyUp( false ); |
594 | 0 | break; |
595 | | |
596 | 0 | case KEY_RIGHT: |
597 | 0 | if( !IsEntryInView( m_pCursor ) ) |
598 | 0 | MakeVisible( m_pCursor ); |
599 | |
|
600 | 0 | pNewCursor = m_pCursor; |
601 | 0 | do |
602 | 0 | { |
603 | 0 | pNewCursor = m_pView->NextVisible(pNewCursor); |
604 | 0 | } while( pNewCursor && !IsSelectable(pNewCursor) ); |
605 | | |
606 | | // if there is no next entry, take the current one |
607 | | // this ensures that in case of _one_ entry in the list, this entry is selected when pressing |
608 | | // the cursor key |
609 | 0 | if ( !pNewCursor && m_pCursor ) |
610 | 0 | pNewCursor = m_pCursor; |
611 | |
|
612 | 0 | if( pNewCursor ) |
613 | 0 | { |
614 | 0 | m_aSelEng.CursorPosChanging( bShift, bMod1 ); |
615 | 0 | if( IsEntryInView( pNewCursor ) ) |
616 | 0 | SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on |
617 | 0 | else |
618 | 0 | { |
619 | 0 | if( m_pCursor ) |
620 | 0 | m_pView->Select( m_pCursor, false ); |
621 | 0 | KeyDown( false ); |
622 | 0 | SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on |
623 | 0 | } |
624 | 0 | } |
625 | 0 | else |
626 | 0 | KeyDown( false ); // because scrollbar range might still |
627 | | // allow scrolling |
628 | 0 | break; |
629 | | |
630 | 0 | case KEY_UP: |
631 | 0 | { |
632 | 0 | pNewCursor = GoToPrevRow(m_pCursor, 1); |
633 | |
|
634 | 0 | if( pNewCursor ) |
635 | 0 | { |
636 | 0 | m_aSelEng.CursorPosChanging( bShift, bMod1 ); |
637 | 0 | SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on |
638 | 0 | ScrollTo(*pNewCursor); |
639 | 0 | } |
640 | 0 | break; |
641 | 0 | } |
642 | | |
643 | 0 | case KEY_DOWN: |
644 | 0 | { |
645 | 0 | pNewCursor = GoToNextRow(m_pCursor, 1); |
646 | |
|
647 | 0 | if( pNewCursor ) |
648 | 0 | { |
649 | 0 | m_aSelEng.CursorPosChanging( bShift, bMod1 ); |
650 | 0 | ScrollTo(*pNewCursor); |
651 | 0 | SetCursor(pNewCursor, bMod1); // no selection, when Ctrl is on |
652 | 0 | } |
653 | 0 | else |
654 | 0 | KeyDown( false ); // because scrollbar range might still |
655 | | // allow scrolling |
656 | 0 | break; |
657 | 0 | } |
658 | | |
659 | 0 | case KEY_PAGEUP: |
660 | 0 | if (!bMod1) |
661 | 0 | { |
662 | 0 | const sal_uInt16 nDelta = m_aVerSBar->GetPageSize(); |
663 | 0 | pNewCursor = GoToPrevRow(m_pCursor, nDelta); |
664 | |
|
665 | 0 | if (pNewCursor) |
666 | 0 | { |
667 | 0 | m_aSelEng.CursorPosChanging(bShift, bMod1); |
668 | 0 | ScrollTo(*pNewCursor); |
669 | 0 | SetCursor(pNewCursor); |
670 | 0 | } |
671 | 0 | } |
672 | 0 | else |
673 | 0 | bHandled = false; |
674 | 0 | break; |
675 | | |
676 | 0 | case KEY_PAGEDOWN: |
677 | 0 | if (!bMod1) |
678 | 0 | { |
679 | 0 | const sal_uInt16 nDelta = m_aVerSBar->GetPageSize(); |
680 | 0 | pNewCursor = GoToNextRow(m_pCursor, nDelta); |
681 | |
|
682 | 0 | if (pNewCursor) |
683 | 0 | { |
684 | 0 | m_aSelEng.CursorPosChanging(bShift, bMod1); |
685 | 0 | ScrollTo(*pNewCursor); |
686 | 0 | SetCursor(pNewCursor); |
687 | 0 | } |
688 | 0 | else |
689 | 0 | KeyDown(false); |
690 | 0 | } |
691 | 0 | else |
692 | 0 | bHandled = false; |
693 | 0 | break; |
694 | | |
695 | 0 | case KEY_RETURN: |
696 | 0 | case KEY_SPACE: |
697 | 0 | { |
698 | 0 | bHandled = !m_pView->aDoubleClickHdl.Call(m_pView); |
699 | 0 | break; |
700 | 0 | } |
701 | | |
702 | 0 | case KEY_END: |
703 | 0 | { |
704 | 0 | pNewCursor = m_pView->GetModel()->Last(); |
705 | |
|
706 | 0 | while( pNewCursor && !IsSelectable(pNewCursor) ) |
707 | 0 | { |
708 | 0 | pNewCursor = m_pView->PrevVisible(pNewCursor); |
709 | 0 | } |
710 | |
|
711 | 0 | SetStartEntry(pNewCursor); |
712 | |
|
713 | 0 | if( pNewCursor && pNewCursor != m_pCursor) |
714 | 0 | { |
715 | | // SelAllDestrAnch( false ); |
716 | 0 | m_aSelEng.CursorPosChanging( bShift, bMod1 ); |
717 | 0 | SetCursor( pNewCursor ); |
718 | 0 | } |
719 | |
|
720 | 0 | bHandled = true; |
721 | |
|
722 | 0 | break; |
723 | 0 | } |
724 | | |
725 | 0 | default: |
726 | 0 | { |
727 | 0 | bHandled = false; |
728 | 0 | break; |
729 | 0 | } |
730 | 0 | } |
731 | | |
732 | 0 | if(!bHandled) |
733 | 0 | return SvImpLBox::KeyInput( rKEvt ); |
734 | | |
735 | 0 | return true; |
736 | 0 | } |
737 | | |
738 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |