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 :
10 : // FIX fdo#38246 https://bugs.libreoffice.org/show_bug.cgi?id=38246
11 : // Design proposal: https://wiki.documentfoundation.org/Design/Whiteboards/Comments_Ruler_Control
12 : // TODO Alpha blend border when it doesn't fit in window
13 :
14 : #include "swruler.hxx"
15 :
16 : #include "viewsh.hxx"
17 : #include "edtwin.hxx"
18 : #include "PostItMgr.hxx"
19 : #include "viewopt.hxx"
20 : #include <view.hxx>
21 : #include "cmdid.h"
22 : #include <sfx2/request.hxx>
23 : #include <vcl/svapp.hxx>
24 : #include <vcl/window.hxx>
25 : #include <vcl/settings.hxx>
26 : #include "misc.hrc"
27 :
28 : #define CONTROL_BORDER_WIDTH 1
29 :
30 : #define CONTROL_LEFT_OFFSET 6
31 : #define CONTROL_RIGHT_OFFSET 3
32 : #define CONTROL_TOP_OFFSET 4
33 :
34 : #define CONTROL_TRIANGLE_WIDTH 4
35 : #define CONTROL_TRIANGLE_PAD 3
36 :
37 : namespace {
38 :
39 : /**
40 : * Draw a little horizontal arrow tip on VirtualDevice.
41 : * \param nX left coordinate of arrow
42 : * \param nY top coordinate of arrow
43 : * \param Color arrow color
44 : * \param bPointRight if arrow should point to right. Otherwise, it will point left.
45 : */
46 195 : void ImplDrawArrow(vcl::RenderContext& rRenderContext, long nX, long nY, const Color& rColor, bool bPointRight)
47 : {
48 195 : rRenderContext.SetLineColor();
49 195 : rRenderContext.SetFillColor(rColor);
50 195 : if (bPointRight)
51 : {
52 0 : rRenderContext.DrawRect(Rectangle(nX + 0, nY + 0, nX + 0, nY + 6) );
53 0 : rRenderContext.DrawRect(Rectangle(nX + 1, nY + 1, nX + 1, nY + 5) );
54 0 : rRenderContext.DrawRect(Rectangle(nX + 2, nY + 2, nX + 2, nY + 4) );
55 0 : rRenderContext.DrawRect(Rectangle(nX + 3, nY + 3, nX + 3, nY + 3) );
56 : }
57 : else
58 : {
59 195 : rRenderContext.DrawRect(Rectangle(nX + 0, nY + 3, nX + 0, nY + 3));
60 195 : rRenderContext.DrawRect(Rectangle(nX + 1, nY + 2, nX + 1, nY + 4));
61 195 : rRenderContext.DrawRect(Rectangle(nX + 2, nY + 1, nX + 2, nY + 5));
62 195 : rRenderContext.DrawRect(Rectangle(nX + 3, nY + 0, nX + 3, nY + 6));
63 : }
64 195 : }
65 :
66 : }
67 :
68 : // Constructor
69 2761 : SwCommentRuler::SwCommentRuler( SwViewShell* pViewSh, vcl::Window* pParent, SwEditWin* pWin, SvxRulerSupportFlags nRulerFlags, SfxBindings& rBindings, WinBits nWinStyle)
70 : : SvxRuler(pParent, pWin, nRulerFlags, rBindings, nWinStyle | WB_HSCROLL)
71 : , mpViewShell(pViewSh)
72 : , mpSwWin(pWin)
73 : , mbIsHighlighted(false)
74 : , mnFadeRate(0)
75 2761 : , maVirDev( VclPtr<VirtualDevice>::Create(*this) )
76 : {
77 : // Set fading timeout: 5 x 40ms = 200ms
78 2761 : maFadeTimer.SetTimeout(40);
79 2761 : maFadeTimer.SetTimeoutHdl( LINK( this, SwCommentRuler, FadeHandler ) );
80 2761 : }
81 :
82 : // Destructor
83 8271 : SwCommentRuler::~SwCommentRuler()
84 : {
85 2757 : disposeOnce();
86 5514 : }
87 :
88 2757 : void SwCommentRuler::dispose()
89 : {
90 2757 : mpSwWin.clear();
91 2757 : SvxRuler::dispose();
92 2757 : }
93 :
94 10575 : void SwCommentRuler::Paint(vcl::RenderContext& rRenderContext, const Rectangle& rRect)
95 : {
96 10575 : SvxRuler::Paint(rRenderContext, rRect);
97 :
98 : // Don't draw if there is not any note
99 10575 : if (mpViewShell->GetPostItMgr() && mpViewShell->GetPostItMgr()->HasNotes())
100 195 : DrawCommentControl(rRenderContext);
101 10575 : }
102 :
103 195 : void SwCommentRuler::DrawCommentControl(vcl::RenderContext& rRenderContext)
104 : {
105 195 : const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
106 195 : bool bIsCollapsed = ! mpViewShell->GetPostItMgr()->ShowNotes();
107 :
108 195 : Rectangle aControlRect = GetCommentControlRegion();
109 195 : maVirDev->SetOutputSizePixel(aControlRect.GetSize());
110 :
111 : // Paint comment control background
112 : // TODO Check if these are best colors to be used
113 195 : Color aBgColor = GetFadedColor( rStyleSettings.GetDarkShadowColor(), rStyleSettings.GetWorkspaceColor() );
114 195 : maVirDev->SetFillColor( aBgColor );
115 :
116 195 : if ( mbIsHighlighted || !bIsCollapsed )
117 : {
118 : // Draw borders
119 195 : maVirDev->SetLineColor( rStyleSettings.GetShadowColor() );
120 : }
121 : else
122 : {
123 : // No borders
124 0 : maVirDev->SetLineColor();
125 : }
126 :
127 195 : maVirDev->DrawRect( Rectangle( Point(), aControlRect.GetSize() ) );
128 :
129 : // Label and arrow tip
130 195 : OUString aLabel( SW_RESSTR ( STR_COMMENTS_LABEL ) );
131 : // Get label and arrow coordinates
132 195 : Point aLabelPos;
133 195 : Point aArrowPos;
134 : bool bArrowToRight;
135 : // TODO Discover why it should be 0 instead of CONTROL_BORDER_WIDTH + CONTROL_TOP_OFFSET
136 195 : aLabelPos.Y() = 0;
137 195 : aArrowPos.Y() = CONTROL_BORDER_WIDTH + CONTROL_TOP_OFFSET;
138 195 : if ( !AllSettings::GetLayoutRTL() )
139 : {
140 : // LTR
141 195 : if ( bIsCollapsed )
142 : {
143 : // It should draw something like | > Comments |
144 0 : aLabelPos.X() = CONTROL_LEFT_OFFSET + CONTROL_TRIANGLE_WIDTH + CONTROL_TRIANGLE_PAD;
145 0 : aArrowPos.X() = CONTROL_LEFT_OFFSET;
146 : }
147 : else
148 : {
149 : // It should draw something like | Comments < |
150 195 : aLabelPos.X() = CONTROL_LEFT_OFFSET;
151 195 : aArrowPos.X() = aControlRect.GetSize().Width() - 1 - CONTROL_RIGHT_OFFSET - CONTROL_BORDER_WIDTH - CONTROL_TRIANGLE_WIDTH;
152 : }
153 195 : bArrowToRight = bIsCollapsed;
154 : }
155 : else
156 : {
157 : // RTL
158 0 : long nLabelWidth = GetTextWidth( aLabel );
159 0 : if ( bIsCollapsed )
160 : {
161 : // It should draw something like | Comments < |
162 0 : aArrowPos.X() = aControlRect.GetSize().Width() - 1 - CONTROL_RIGHT_OFFSET - CONTROL_BORDER_WIDTH - CONTROL_TRIANGLE_WIDTH;
163 0 : aLabelPos.X() = aArrowPos.X() - CONTROL_TRIANGLE_PAD - nLabelWidth;
164 : }
165 : else
166 : {
167 : // It should draw something like | > Comments |
168 0 : aLabelPos.X() = aControlRect.GetSize().Width() - 1 - CONTROL_RIGHT_OFFSET - CONTROL_BORDER_WIDTH - nLabelWidth;
169 0 : aArrowPos.X() = CONTROL_LEFT_OFFSET;
170 : }
171 0 : bArrowToRight = !bIsCollapsed;
172 : }
173 :
174 : // Draw label
175 195 : Color aTextColor = GetFadedColor( rStyleSettings.GetButtonTextColor(), rStyleSettings.GetDarkShadowColor() );
176 195 : maVirDev->SetTextColor( aTextColor );
177 : // FIXME Expected font size?
178 195 : maVirDev->DrawText( aLabelPos, aLabel );
179 :
180 : // Draw arrow
181 : // FIXME consistence of button colors. http://opengrok.libreoffice.org/xref/core/vcl/source/control/button.cxx#785
182 195 : Color aArrowColor = GetFadedColor(Color(COL_BLACK), rStyleSettings.GetShadowColor());
183 195 : ImplDrawArrow(*maVirDev.get(), aArrowPos.X(), aArrowPos.Y(), aArrowColor, bArrowToRight);
184 :
185 : // Blit comment control
186 195 : rRenderContext.DrawOutDev(aControlRect.TopLeft(), aControlRect.GetSize(), Point(), aControlRect.GetSize(), *maVirDev.get());
187 195 : }
188 :
189 : // Just accept double-click outside comment control
190 0 : void SwCommentRuler::Command( const CommandEvent& rCEvt )
191 : {
192 0 : Point aMousePos = rCEvt.GetMousePosPixel();
193 : // Ignore command request if it is inside Comment Control
194 0 : if ( !mpViewShell->GetPostItMgr()
195 0 : || !mpViewShell->GetPostItMgr()->HasNotes()
196 0 : || !GetCommentControlRegion().IsInside( aMousePos ) )
197 0 : SvxRuler::Command( rCEvt );
198 0 : }
199 :
200 0 : void SwCommentRuler::MouseMove(const MouseEvent& rMEvt)
201 : {
202 0 : SvxRuler::MouseMove(rMEvt);
203 0 : if ( ! mpViewShell->GetPostItMgr() || ! mpViewShell->GetPostItMgr()->HasNotes() )
204 0 : return;
205 :
206 0 : Point aMousePos = rMEvt.GetPosPixel();
207 0 : bool bWasHighlighted = mbIsHighlighted;
208 0 : mbIsHighlighted = GetCommentControlRegion().IsInside( aMousePos );
209 0 : if ( mbIsHighlighted != bWasHighlighted )
210 : {
211 : // Set proper help text
212 0 : if ( mbIsHighlighted )
213 : {
214 : // Mouse over comment control
215 0 : UpdateCommentHelpText();
216 : }
217 : else
218 : {
219 : // Mouse out of comment control
220 : // FIXME Should remember previous tooltip text?
221 0 : SetQuickHelpText( OUString() );
222 : }
223 : // Do start fading
224 0 : maFadeTimer.Start();
225 : }
226 : }
227 :
228 0 : void SwCommentRuler::MouseButtonDown( const MouseEvent& rMEvt )
229 : {
230 0 : Point aMousePos = rMEvt.GetPosPixel();
231 0 : if ( !rMEvt.IsLeft() || IsTracking() || !GetCommentControlRegion().IsInside( aMousePos ) )
232 : {
233 0 : SvxRuler::MouseButtonDown(rMEvt);
234 0 : return;
235 : }
236 :
237 : // Toggle notes visibility
238 0 : SwView &rView = mpSwWin->GetView();
239 0 : SfxRequest aRequest( rView.GetViewFrame(), FN_VIEW_NOTES );
240 0 : rView.ExecViewOptions( aRequest );
241 :
242 : // It is inside comment control, so update help text
243 0 : UpdateCommentHelpText();
244 :
245 0 : Invalidate();
246 : }
247 :
248 27860 : void SwCommentRuler::Update()
249 : {
250 27860 : Rectangle aPreviousControlRect = GetCommentControlRegion();
251 27860 : SvxRuler::Update();
252 27860 : if (aPreviousControlRect != GetCommentControlRegion())
253 292 : Invalidate();
254 27860 : }
255 :
256 0 : void SwCommentRuler::UpdateCommentHelpText()
257 : {
258 : int nTooltipResId;
259 0 : if ( mpViewShell->GetPostItMgr()->ShowNotes() )
260 0 : nTooltipResId = STR_HIDE_COMMENTS;
261 : else
262 0 : nTooltipResId = STR_SHOW_COMMENTS;
263 0 : SetQuickHelpText( OUString( SW_RESSTR( nTooltipResId ) ) );
264 0 : }
265 :
266 : // TODO Make Ruler return its central rectangle instead of margins.
267 55915 : Rectangle SwCommentRuler::GetCommentControlRegion()
268 : {
269 55915 : long nLeft = 0;
270 55915 : SwPostItMgr *pPostItMgr = mpViewShell->GetPostItMgr();
271 :
272 : //rhbz#1006850 When the SwPostItMgr ctor is called from SwView::SwView it
273 : //triggers an update of the uiview, but the result of the ctor hasn't been
274 : //set into the mpViewShell yet, so GetPostItMgr is temporarily still NULL
275 55915 : if (!pPostItMgr)
276 0 : return Rectangle();
277 :
278 55915 : unsigned long nSidebarWidth = pPostItMgr->GetSidebarWidth(true);
279 : //FIXME When the page width is larger then screen, the ruler is misplaced by one pixel
280 55915 : if (GetTextRTL())
281 0 : nLeft = GetPageOffset() - nSidebarWidth + GetBorderOffset();
282 : else
283 55915 : nLeft = GetWinOffset() + GetPageOffset() + mpSwWin->LogicToPixel(Size(GetPageWidth(), 0)).Width();
284 55915 : long nTop = 0 + 4; // Ruler::ImplDraw uses RULER_OFF (value: 3px) as offset, and Ruler::ImplFormat adds one extra pixel
285 : // Somehow pPostItMgr->GetSidebarBorderWidth() returns border width already doubled
286 55915 : long nRight = nLeft + nSidebarWidth + pPostItMgr->GetSidebarBorderWidth(true);
287 55915 : long nBottom = nTop + GetRulerVirHeight() - 3;
288 :
289 55915 : Rectangle aRect(nLeft, nTop, nRight, nBottom);
290 55915 : return aRect;
291 : }
292 :
293 585 : Color SwCommentRuler::GetFadedColor(const Color &rHighColor, const Color &rLowColor)
294 : {
295 585 : if (!maFadeTimer.IsActive())
296 585 : return mbIsHighlighted ? rHighColor : rLowColor;
297 :
298 0 : Color aColor = rHighColor;
299 0 : aColor.Merge(rLowColor, mnFadeRate * 255 / 100.0f);
300 0 : return aColor;
301 : }
302 :
303 0 : IMPL_LINK_NOARG_TYPED(SwCommentRuler, FadeHandler, Timer *, void)
304 : {
305 0 : const int nStep = 25;
306 0 : if ( mbIsHighlighted && mnFadeRate < 100 )
307 0 : mnFadeRate += nStep;
308 0 : else if ( !mbIsHighlighted && mnFadeRate > 0 )
309 0 : mnFadeRate -= nStep;
310 : else
311 0 : return;
312 :
313 0 : Invalidate();
314 :
315 0 : if ( mnFadeRate != 0 && mnFadeRate != 100)
316 0 : maFadeTimer.Start();
317 177 : }
318 :
319 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|