Line data Source code
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 <sfx2/thumbnailviewitem.hxx>
21 :
22 : #include <sfx2/thumbnailview.hxx>
23 : #include "thumbnailviewacc.hxx"
24 :
25 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
26 : #include <basegfx/range/b2drectangle.hxx>
27 : #include <basegfx/vector/b2dsize.hxx>
28 : #include <basegfx/polygon/b2dpolygon.hxx>
29 : #include <drawinglayer/attribute/fillgraphicattribute.hxx>
30 : #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
31 : #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
32 : #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
33 : #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
34 : #include <drawinglayer/primitive2d/textprimitive2d.hxx>
35 : #include <drawinglayer/processor2d/baseprocessor2d.hxx>
36 : #include <vcl/button.hxx>
37 : #include <vcl/graph.hxx>
38 : #include <vcl/svapp.hxx>
39 : #include <vcl/texteng.hxx>
40 : #include <svtools/optionsdrawinglayer.hxx>
41 :
42 : using namespace basegfx;
43 : using namespace basegfx::tools;
44 : using namespace ::com::sun::star;
45 : using namespace drawinglayer::attribute;
46 : using namespace drawinglayer::primitive2d;
47 :
48 0 : class ResizableMultiLineEdit : public VclMultiLineEdit
49 : {
50 : private:
51 : ThumbnailViewItem* mpItem;
52 : bool mbIsInGrabFocus;
53 :
54 : public:
55 : ResizableMultiLineEdit (vcl::Window* pParent, ThumbnailViewItem* pItem);
56 :
57 0 : void SetInGrabFocus(bool bInGrabFocus) { mbIsInGrabFocus = bInGrabFocus; }
58 :
59 : virtual bool PreNotify(NotifyEvent& rNEvt) SAL_OVERRIDE;
60 : virtual void Modify() SAL_OVERRIDE;
61 : };
62 :
63 0 : ResizableMultiLineEdit::ResizableMultiLineEdit (vcl::Window* pParent, ThumbnailViewItem* pItem) :
64 : VclMultiLineEdit (pParent, WB_CENTER | WB_BORDER),
65 : mpItem(pItem),
66 0 : mbIsInGrabFocus(false)
67 : {
68 0 : }
69 :
70 0 : bool ResizableMultiLineEdit::PreNotify(NotifyEvent& rNEvt)
71 : {
72 0 : bool nDone = false;
73 0 : if( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
74 : {
75 0 : const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
76 0 : vcl::KeyCode aCode = rKEvt.GetKeyCode();
77 0 : switch (aCode.GetCode())
78 : {
79 : case KEY_RETURN:
80 0 : mpItem->setTitle( GetText() );
81 : //fall-through
82 : case KEY_ESCAPE:
83 0 : mpItem->setEditTitle(false);
84 0 : nDone = true;
85 0 : break;
86 : default:
87 0 : break;
88 : }
89 : }
90 0 : else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS && !mbIsInGrabFocus )
91 : {
92 0 : mpItem->setTitle( GetText() );
93 0 : mpItem->setEditTitle(false, false);
94 : }
95 0 : return nDone || VclMultiLineEdit::PreNotify(rNEvt);
96 : }
97 :
98 0 : void ResizableMultiLineEdit::Modify()
99 : {
100 0 : VclMultiLineEdit::Modify();
101 0 : mpItem->updateTitleEditSize();
102 0 : }
103 :
104 0 : ThumbnailViewItem::ThumbnailViewItem(ThumbnailView &rView, sal_uInt16 nId)
105 : : mrParent(rView)
106 : , mnId(nId)
107 : , mbVisible(true)
108 : , mbSelected(false)
109 : , mbHover(false)
110 : , mpxAcc(NULL)
111 : , mbEditTitle(false)
112 : , mpTitleED(NULL)
113 0 : , maTextEditMaxArea()
114 : {
115 0 : mpTitleED = VclPtr<ResizableMultiLineEdit>::Create(&rView, this);
116 0 : }
117 :
118 0 : ThumbnailViewItem::~ThumbnailViewItem()
119 : {
120 0 : mpTitleED.disposeAndClear();
121 0 : if( mpxAcc )
122 : {
123 0 : static_cast< ThumbnailViewItemAcc* >( mpxAcc->get() )->ParentDestroyed();
124 0 : delete mpxAcc;
125 : }
126 0 : }
127 :
128 0 : void ThumbnailViewItem::show (bool bVisible)
129 : {
130 0 : mbVisible = bVisible;
131 0 : if (!mbVisible)
132 0 : mpTitleED->Show(false);
133 0 : }
134 :
135 0 : void ThumbnailViewItem::setSelection (bool state)
136 : {
137 0 : mbSelected = state;
138 0 : }
139 :
140 0 : void ThumbnailViewItem::setHighlight (bool state)
141 : {
142 0 : mbHover = state;
143 0 : }
144 :
145 0 : Rectangle ThumbnailViewItem::updateHighlight(bool bVisible, const Point& rPoint)
146 : {
147 0 : bool bNeedsPaint = false;
148 :
149 0 : if (bVisible && getDrawArea().IsInside(rPoint))
150 : {
151 0 : if (!isHighlighted())
152 0 : bNeedsPaint = true;
153 0 : setHighlight(true);
154 : }
155 : else
156 : {
157 0 : if (isHighlighted())
158 0 : bNeedsPaint = true;
159 0 : setHighlight(false);
160 : }
161 :
162 0 : if (bNeedsPaint)
163 0 : return getDrawArea();
164 :
165 0 : return Rectangle();
166 : }
167 :
168 0 : OUString ThumbnailViewItem::getHelpText() const
169 : {
170 0 : return maTitle;
171 : }
172 :
173 0 : void ThumbnailViewItem::setEditTitle (bool edit, bool bChangeFocus)
174 : {
175 0 : mbEditTitle = edit;
176 0 : mpTitleED->Show(edit);
177 0 : if (edit)
178 : {
179 0 : mpTitleED->SetText(maTitle);
180 0 : updateTitleEditSize();
181 0 : static_cast<ResizableMultiLineEdit*>(mpTitleED.get())->SetInGrabFocus(true);
182 0 : mpTitleED->GrabFocus();
183 0 : static_cast<ResizableMultiLineEdit*>(mpTitleED.get())->SetInGrabFocus(false);
184 : }
185 0 : else if (bChangeFocus)
186 : {
187 0 : mrParent.GrabFocus();
188 : }
189 0 : }
190 :
191 0 : Rectangle ThumbnailViewItem::getTextArea() const
192 : {
193 0 : Rectangle aTextArea(maTextEditMaxArea);
194 :
195 0 : TextEngine aTextEngine;
196 0 : aTextEngine.SetMaxTextWidth(maDrawArea.getWidth());
197 0 : aTextEngine.SetText(maTitle);
198 :
199 0 : long nTxtHeight = aTextEngine.GetTextHeight() + 6;
200 0 : if (nTxtHeight < aTextArea.GetHeight())
201 0 : aTextArea.SetSize(Size(aTextArea.GetWidth(), nTxtHeight));
202 :
203 0 : return aTextArea;
204 : }
205 :
206 0 : void ThumbnailViewItem::updateTitleEditSize()
207 : {
208 0 : Rectangle aTextArea = getTextArea();
209 0 : Point aPos = aTextArea.TopLeft();
210 0 : Size aSize = aTextArea.GetSize();
211 0 : mpTitleED->SetPosSizePixel(aPos, aSize);
212 0 : }
213 :
214 0 : void ThumbnailViewItem::setTitle (const OUString& rTitle)
215 : {
216 0 : if (mrParent.renameItem(this, rTitle))
217 0 : maTitle = rTitle;
218 0 : }
219 :
220 0 : uno::Reference< accessibility::XAccessible > ThumbnailViewItem::GetAccessible( bool bIsTransientChildrenDisabled )
221 : {
222 0 : if( !mpxAcc )
223 0 : mpxAcc = new uno::Reference< accessibility::XAccessible >( new ThumbnailViewItemAcc( this, bIsTransientChildrenDisabled ) );
224 :
225 0 : return *mpxAcc;
226 : }
227 :
228 0 : void ThumbnailViewItem::setDrawArea (const Rectangle &area)
229 : {
230 0 : maDrawArea = area;
231 0 : }
232 :
233 0 : void ThumbnailViewItem::calculateItemsPosition (const long nThumbnailHeight, const long,
234 : const long nPadding, sal_uInt32 nMaxTextLength,
235 : const ThumbnailItemAttributes *pAttrs)
236 : {
237 0 : drawinglayer::primitive2d::TextLayouterDevice aTextDev;
238 : aTextDev.setFontAttribute(pAttrs->aFontAttr,
239 : pAttrs->aFontSize.getX(), pAttrs->aFontSize.getY(),
240 0 : com::sun::star::lang::Locale() );
241 :
242 0 : Size aRectSize = maDrawArea.GetSize();
243 0 : Size aImageSize = maPreview1.GetSizePixel();
244 :
245 : // Calculate thumbnail position
246 0 : Point aPos = maDrawArea.TopLeft();
247 0 : aPos.X() = maDrawArea.getX() + (aRectSize.Width()-aImageSize.Width())/2;
248 0 : aPos.Y() = maDrawArea.getY() + nPadding + (nThumbnailHeight-aImageSize.Height())/2;
249 0 : maPrev1Pos = aPos;
250 :
251 : // Calculate text position
252 0 : aPos.Y() = maDrawArea.getY() + nThumbnailHeight + nPadding * 2;
253 0 : aPos.X() = maDrawArea.Left() + (aRectSize.Width() - aTextDev.getTextWidth(maTitle,0,nMaxTextLength))/2;
254 0 : maTextPos = aPos;
255 :
256 : // Calculate the text edit max area
257 0 : aPos = Point(maDrawArea.getX() + nPadding, maTextPos.getY());
258 0 : Size aEditSize(maDrawArea.GetWidth() - nPadding * 2,
259 0 : maDrawArea.Bottom() - maTextPos.Y());
260 0 : maTextEditMaxArea = Rectangle( aPos, aEditSize );
261 0 : }
262 :
263 0 : void ThumbnailViewItem::setSelectClickHdl (const Link<> &link)
264 : {
265 0 : maClickHdl = link;
266 0 : }
267 :
268 0 : void ThumbnailViewItem::Paint (drawinglayer::processor2d::BaseProcessor2D *pProcessor,
269 : const ThumbnailItemAttributes *pAttrs)
270 : {
271 0 : BColor aFillColor = pAttrs->aFillColor;
272 0 : drawinglayer::primitive2d::Primitive2DSequence aSeq(4);
273 0 : double fTransparence = 0.0;
274 :
275 : // Draw background
276 0 : if (mbSelected || mbHover)
277 0 : aFillColor = pAttrs->aHighlightColor;
278 :
279 0 : if (mbHover)
280 : {
281 0 : const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer;
282 0 : fTransparence = aSvtOptionsDrawinglayer.GetTransparentSelectionPercent() * 0.01;
283 : }
284 :
285 0 : sal_uInt32 nPrimitive = 0;
286 0 : aSeq[nPrimitive++] = drawinglayer::primitive2d::Primitive2DReference( new PolyPolygonSelectionPrimitive2D(
287 : B2DPolyPolygon(Polygon(maDrawArea, THUMBNAILVIEW_ITEM_CORNER, THUMBNAILVIEW_ITEM_CORNER).getB2DPolygon()),
288 : aFillColor,
289 : fTransparence,
290 : 0.0,
291 0 : true));
292 :
293 : // Draw thumbnail
294 0 : Point aPos = maPrev1Pos;
295 0 : Size aImageSize = maPreview1.GetSizePixel();
296 :
297 0 : aSeq[nPrimitive++] = drawinglayer::primitive2d::Primitive2DReference( new FillGraphicPrimitive2D(
298 0 : createTranslateB2DHomMatrix(aPos.X(),aPos.Y()),
299 : FillGraphicAttribute(Graphic(maPreview1),
300 : B2DRange(
301 : B2DPoint(0,0),
302 0 : B2DPoint(aImageSize.Width(),aImageSize.Height())),
303 : false)
304 0 : ));
305 :
306 : // draw thumbnail borders
307 0 : float fWidth = aImageSize.Width();
308 0 : float fHeight = aImageSize.Height();
309 0 : float fPosX = maPrev1Pos.getX();
310 0 : float fPosY = maPrev1Pos.getY();
311 :
312 0 : B2DPolygon aBounds;
313 0 : aBounds.append(B2DPoint(fPosX,fPosY));
314 0 : aBounds.append(B2DPoint(fPosX+fWidth,fPosY));
315 0 : aBounds.append(B2DPoint(fPosX+fWidth,fPosY+fHeight));
316 0 : aBounds.append(B2DPoint(fPosX,fPosY+fHeight));
317 0 : aBounds.setClosed(true);
318 :
319 0 : aSeq[nPrimitive++] = drawinglayer::primitive2d::Primitive2DReference(createBorderLine(aBounds));
320 :
321 : // Draw text below thumbnail
322 0 : aPos = maTextPos;
323 0 : addTextPrimitives( maTitle, pAttrs, aPos, aSeq );
324 :
325 0 : pProcessor->process(aSeq);
326 0 : }
327 :
328 0 : void ThumbnailViewItem::addTextPrimitives (const OUString& rText, const ThumbnailItemAttributes *pAttrs, Point aPos, drawinglayer::primitive2d::Primitive2DSequence& rSeq)
329 : {
330 0 : drawinglayer::primitive2d::TextLayouterDevice aTextDev;
331 :
332 0 : aPos.setY(aPos.getY() + aTextDev.getTextHeight());
333 :
334 0 : OUString aText (rText);
335 :
336 0 : TextEngine aTextEngine;
337 0 : aTextEngine.SetMaxTextWidth(maDrawArea.getWidth());
338 0 : aTextEngine.SetText(rText);
339 :
340 0 : sal_Int32 nPrimitives = rSeq.getLength();
341 0 : rSeq.realloc(nPrimitives + aTextEngine.GetLineCount(0));
342 :
343 : // Create the text primitives
344 0 : sal_uInt16 nLineStart = 0;
345 0 : for (sal_uInt16 i=0; i < aTextEngine.GetLineCount(0); ++i)
346 : {
347 0 : sal_uInt16 nLineLength = aTextEngine.GetLineLen(0, i);
348 0 : double nLineWidth = aTextDev.getTextWidth (aText, nLineStart, nLineLength);
349 :
350 0 : bool bTooLong = (aPos.getY() + aTextEngine.GetCharHeight()) > maDrawArea.Bottom();
351 0 : if (bTooLong && (nLineLength + nLineStart) < rText.getLength())
352 : {
353 : // Add the '...' to the last line to show, even though it may require to shorten the line
354 0 : double nDotsWidth = aTextDev.getTextWidth(OUString("..."),0,3);
355 :
356 0 : sal_uInt16 nLength = nLineLength - 1;
357 0 : while ( nDotsWidth + aTextDev.getTextWidth(aText, nLineStart, nLength) > maDrawArea.getWidth() && nLength > 0)
358 : {
359 0 : --nLength;
360 : }
361 :
362 0 : aText = aText.copy(0, nLineStart+nLength);
363 0 : aText += "...";
364 0 : nLineLength = nLength + 3;
365 : }
366 :
367 0 : double nLineX = maDrawArea.Left() + (maDrawArea.getWidth() - nLineWidth) / 2.0;
368 :
369 : basegfx::B2DHomMatrix aTextMatrix( createScaleTranslateB2DHomMatrix(
370 : pAttrs->aFontSize.getX(), pAttrs->aFontSize.getY(),
371 0 : nLineX, double( aPos.Y() ) ) );
372 :
373 0 : rSeq[nPrimitives++] = drawinglayer::primitive2d::Primitive2DReference(
374 : new TextSimplePortionPrimitive2D(aTextMatrix,
375 : aText,nLineStart,nLineLength,
376 : std::vector< double >( ),
377 : pAttrs->aFontAttr,
378 : com::sun::star::lang::Locale(),
379 0 : Color(COL_BLACK).getBColor() ) );
380 0 : nLineStart += nLineLength;
381 0 : aPos.setY(aPos.getY() + aTextEngine.GetCharHeight());
382 :
383 0 : if (bTooLong)
384 0 : break;
385 0 : }
386 0 : }
387 :
388 : drawinglayer::primitive2d::PolygonHairlinePrimitive2D*
389 0 : ThumbnailViewItem::createBorderLine (const basegfx::B2DPolygon& rPolygon)
390 : {
391 0 : return new PolygonHairlinePrimitive2D(rPolygon, Color(186,186,186).getBColor());
392 648 : }
393 :
394 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
395 :
396 :
|