LibreOffice Module sw (master)  1
swruler.cxx
Go to the documentation of this file.
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 <svx/svxids.hrc>
24 #include <vcl/commandevent.hxx>
25 #include <vcl/event.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/window.hxx>
28 #include <vcl/settings.hxx>
29 #include <strings.hrc>
30 #include <comphelper/lok.hxx>
31 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
32 #include <boost/property_tree/json_parser.hpp>
33 
34 #define CONTROL_BORDER_WIDTH 1
35 #define CONTROL_LEFT_OFFSET 6
36 #define CONTROL_RIGHT_OFFSET 3
37 #define CONTROL_TOP_OFFSET 4
38 
39 #define CONTROL_TRIANGLE_WIDTH 4
40 #define CONTROL_TRIANGLE_PAD 3
41 
42 namespace {
43 
51 void ImplDrawArrow(vcl::RenderContext& rRenderContext, long nX, long nY, const Color& rColor, bool bPointRight)
52 {
53  rRenderContext.SetLineColor();
54  rRenderContext.SetFillColor(rColor);
55  if (bPointRight)
56  {
57  rRenderContext.DrawRect(tools::Rectangle(nX + 0, nY + 0, nX + 0, nY + 6) );
58  rRenderContext.DrawRect(tools::Rectangle(nX + 1, nY + 1, nX + 1, nY + 5) );
59  rRenderContext.DrawRect(tools::Rectangle(nX + 2, nY + 2, nX + 2, nY + 4) );
60  rRenderContext.DrawRect(tools::Rectangle(nX + 3, nY + 3, nX + 3, nY + 3) );
61  }
62  else
63  {
64  rRenderContext.DrawRect(tools::Rectangle(nX + 0, nY + 3, nX + 0, nY + 3));
65  rRenderContext.DrawRect(tools::Rectangle(nX + 1, nY + 2, nX + 1, nY + 4));
66  rRenderContext.DrawRect(tools::Rectangle(nX + 2, nY + 1, nX + 2, nY + 5));
67  rRenderContext.DrawRect(tools::Rectangle(nX + 3, nY + 0, nX + 3, nY + 6));
68  }
69 }
70 
71 }
72 
73 // Constructor
74 SwCommentRuler::SwCommentRuler( SwViewShell* pViewSh, vcl::Window* pParent, SwEditWin* pWin, SvxRulerSupportFlags nRulerFlags, SfxBindings& rBindings, WinBits nWinStyle)
75 : SvxRuler(pParent, pWin, nRulerFlags, rBindings, nWinStyle | WB_HSCROLL)
76 , mpViewShell(pViewSh)
77 , mpSwWin(pWin)
78 , mbIsHighlighted(false)
79 , mnFadeRate(0)
80 , maVirDev( VclPtr<VirtualDevice>::Create(*this) )
81 {
82  // Set fading timeout: 5 x 40ms = 200ms
84  maFadeTimer.SetInvokeHandler( LINK( this, SwCommentRuler, FadeHandler ) );
85  maFadeTimer.SetDebugName( "sw::SwCommentRuler maFadeTimer" );
86 }
87 
88 // Destructor
90 {
91  disposeOnce();
92 }
93 
95 {
96  mpSwWin.clear();
98 }
99 
100 void SwCommentRuler::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
101 {
103  return; // no need to waste time on startup
104 
105  SvxRuler::Paint(rRenderContext, rRect);
106 
107  // Don't draw if there is not any note
109  DrawCommentControl(rRenderContext);
110 }
111 
113 {
114  const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
115  bool bIsCollapsed = ! mpViewShell->GetPostItMgr()->ShowNotes();
116 
117  tools::Rectangle aControlRect = GetCommentControlRegion();
118  maVirDev->SetOutputSizePixel(aControlRect.GetSize());
119 
120  // Paint comment control background
121  // TODO Check if these are best colors to be used
122  Color aBgColor = GetFadedColor( rStyleSettings.GetDialogColor(), rStyleSettings.GetWorkspaceColor() );
123  maVirDev->SetFillColor( aBgColor );
124 
125  if ( mbIsHighlighted || !bIsCollapsed )
126  {
127  // Draw borders
128  maVirDev->SetLineColor( rStyleSettings.GetShadowColor() );
129  }
130  else
131  {
132  // No borders
133  maVirDev->SetLineColor();
134  }
135 
136  maVirDev->DrawRect( tools::Rectangle( Point(), aControlRect.GetSize() ) );
137 
138  // Label and arrow tip
139  OUString aLabel( SwResId ( STR_COMMENTS_LABEL ) );
140  // Get label and arrow coordinates
141  Point aLabelPos;
142  Point aArrowPos;
143  bool bArrowToRight;
144  // TODO Discover why it should be 0 instead of CONTROL_BORDER_WIDTH + CONTROL_TOP_OFFSET
145  aLabelPos.setY( 0 );
147  if ( !AllSettings::GetLayoutRTL() )
148  {
149  // LTR
150  if ( bIsCollapsed )
151  {
152  // It should draw something like | > Comments |
154  aArrowPos.setX( CONTROL_LEFT_OFFSET );
155  }
156  else
157  {
158  // It should draw something like | Comments < |
159  aLabelPos.setX( CONTROL_LEFT_OFFSET );
160  aArrowPos.setX( aControlRect.GetSize().Width() - 1 - CONTROL_RIGHT_OFFSET - CONTROL_BORDER_WIDTH - CONTROL_TRIANGLE_WIDTH );
161  }
162  bArrowToRight = bIsCollapsed;
163  }
164  else
165  {
166  // RTL
167  long nLabelWidth = GetTextWidth( aLabel );
168  if ( bIsCollapsed )
169  {
170  // It should draw something like | Comments < |
171  aArrowPos.setX( aControlRect.GetSize().Width() - 1 - CONTROL_RIGHT_OFFSET - CONTROL_BORDER_WIDTH - CONTROL_TRIANGLE_WIDTH );
172  aLabelPos.setX( aArrowPos.X() - CONTROL_TRIANGLE_PAD - nLabelWidth );
173  }
174  else
175  {
176  // It should draw something like | > Comments |
177  aLabelPos.setX( aControlRect.GetSize().Width() - 1 - CONTROL_RIGHT_OFFSET - CONTROL_BORDER_WIDTH - nLabelWidth );
178  aArrowPos.setX( CONTROL_LEFT_OFFSET );
179  }
180  bArrowToRight = !bIsCollapsed;
181  }
182 
183  // Draw label
184  Color aTextColor = GetFadedColor( rStyleSettings.GetButtonTextColor(), rStyleSettings.GetDarkShadowColor() );
185  maVirDev->SetTextColor( aTextColor );
186  // FIXME Expected font size?
187  maVirDev->DrawText( aLabelPos, aLabel );
188 
189  // Draw arrow
190  // FIXME consistence of button colors. https://opengrok.libreoffice.org/xref/core/vcl/source/control/button.cxx#785
191  ImplDrawArrow(*maVirDev, aArrowPos.X(), aArrowPos.Y(), aTextColor, bArrowToRight);
192 
193  // Blit comment control
194  rRenderContext.DrawOutDev(aControlRect.TopLeft(), aControlRect.GetSize(), Point(), aControlRect.GetSize(), *maVirDev);
195 }
196 
197 // Just accept double-click outside comment control
199 {
200  Point aMousePos = rCEvt.GetMousePosPixel();
201  // Ignore command request if it is inside Comment Control
202  if ( !mpViewShell->GetPostItMgr()
204  || !GetCommentControlRegion().IsInside( aMousePos ) )
205  SvxRuler::Command( rCEvt );
206 }
207 
209 {
210  SvxRuler::MouseMove(rMEvt);
212  return;
213 
214  Point aMousePos = rMEvt.GetPosPixel();
215  bool bWasHighlighted = mbIsHighlighted;
217  if ( mbIsHighlighted != bWasHighlighted )
218  {
219  // Set proper help text
220  if ( mbIsHighlighted )
221  {
222  // Mouse over comment control
224  }
225  else
226  {
227  // Mouse out of comment control
228  // FIXME Should remember previous tooltip text?
229  SetQuickHelpText( OUString() );
230  }
231  // Do start fading
232  maFadeTimer.Start();
233  }
234 }
235 
237 {
238  Point aMousePos = rMEvt.GetPosPixel();
239  if ( !rMEvt.IsLeft() || IsTracking() || !GetCommentControlRegion().IsInside( aMousePos ) )
240  {
241  SvxRuler::MouseButtonDown(rMEvt);
242  return;
243  }
244 
245  // Toggle notes visibility
246  SwView &rView = mpSwWin->GetView();
247  SfxRequest aRequest( rView.GetViewFrame(), SID_TOGGLE_NOTES );
248  rView.ExecViewOptions( aRequest );
249 
250  // It is inside comment control, so update help text
252 
253  Invalidate();
254 }
255 
257 {
258  boost::property_tree::ptree jsonNotif;
259 
260  jsonNotif.put("margin1", convertTwipToMm100(GetMargin1()));
261  jsonNotif.put("margin2", convertTwipToMm100(GetMargin2()));
262  jsonNotif.put("leftOffset", convertTwipToMm100(GetNullOffset()));
263  jsonNotif.put("pageOffset", convertTwipToMm100(GetPageOffset()));
264  jsonNotif.put("pageWidth", convertTwipToMm100(GetPageWidth()));
265 
266  RulerUnitData aUnitData = GetCurrentRulerUnit();
267  jsonNotif.put("unit", aUnitData.aUnitStr);
268 
269  std::stringstream aStream;
270  boost::property_tree::write_json(aStream, jsonNotif);
271  return aStream.str();
272 }
273 
275 {
277  return;
278 
279  const std::string test = CreateJsonNotification();
280  mpViewShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_RULER_UPDATE, test.c_str());
281 }
282 
284 {
285  tools::Rectangle aPreviousControlRect = GetCommentControlRegion();
287  if (aPreviousControlRect != GetCommentControlRegion())
288  Invalidate();
289  NotifyKit();
290 }
291 
293 {
294  const char* pTooltipResId;
295  if ( mpViewShell->GetPostItMgr()->ShowNotes() )
296  pTooltipResId = STR_HIDE_COMMENTS;
297  else
298  pTooltipResId = STR_SHOW_COMMENTS;
299  SetQuickHelpText(SwResId(pTooltipResId));
300 }
301 
302 // TODO Make Ruler return its central rectangle instead of margins.
304 {
305  long nLeft = 0;
306  SwPostItMgr *pPostItMgr = mpViewShell->GetPostItMgr();
307 
308  //rhbz#1006850 When the SwPostItMgr ctor is called from SwView::SwView it
309  //triggers an update of the uiview, but the result of the ctor hasn't been
310  //set into the mpViewShell yet, so GetPostItMgr is temporarily still NULL
311  if (!pPostItMgr)
312  return tools::Rectangle();
313 
314  unsigned long nSidebarWidth = pPostItMgr->GetSidebarWidth(true);
315  //FIXME When the page width is larger then screen, the ruler is misplaced by one pixel
316  if (GetTextRTL())
317  nLeft = GetPageOffset() - nSidebarWidth + GetBorderOffset();
318  else
319  nLeft = GetWinOffset() + GetPageOffset() + mpSwWin->LogicToPixel(Size(GetPageWidth(), 0)).Width();
320  long nTop = 0 + 4; // Ruler::ImplDraw uses RULER_OFF (value: 3px) as offset, and Ruler::ImplFormat adds one extra pixel
321  // Somehow pPostItMgr->GetSidebarBorderWidth() returns border width already doubled
322  long nRight = nLeft + nSidebarWidth + pPostItMgr->GetSidebarBorderWidth(true);
323  long nBottom = nTop + GetRulerVirHeight() - 3;
324 
325  tools::Rectangle aRect(nLeft, nTop, nRight, nBottom);
326  return aRect;
327 }
328 
329 Color SwCommentRuler::GetFadedColor(const Color &rHighColor, const Color &rLowColor)
330 {
331  if (!maFadeTimer.IsActive())
332  return mbIsHighlighted ? rHighColor : rLowColor;
333 
334  Color aColor = rHighColor;
335  aColor.Merge(rLowColor, mnFadeRate * 255 / 100.0f);
336  return aColor;
337 }
338 
339 IMPL_LINK_NOARG(SwCommentRuler, FadeHandler, Timer *, void)
340 {
341  const int nStep = 25;
342  if ( mbIsHighlighted && mnFadeRate < 100 )
343  mnFadeRate += nStep;
344  else if ( !mbIsHighlighted && mnFadeRate > 0 )
345  mnFadeRate -= nStep;
346  else
347  return;
348 
349  Invalidate();
350 
351  if ( mnFadeRate != 0 && mnFadeRate != 100)
352  maFadeTimer.Start();
353 }
354 
355 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
SfxViewFrame * GetViewFrame() const
long Width() const
const Color & GetShadowColor() const
SvxRulerSupportFlags
sal_Char const aUnitStr[8]
const Color & GetDialogColor() const
std::string CreateJsonNotification()
Definition: swruler.cxx:256
void UpdateCommentHelpText()
Update the tooltip text.
Definition: swruler.cxx:292
void Merge(const Color &rMergeColor, sal_uInt8 cTransparency)
const SwView & GetView() const
Definition: edtwin.hxx:242
#define CONTROL_LEFT_OFFSET
Definition: swruler.cxx:35
const StyleSettings & GetStyleSettings() const
virtual void Update()
const Color & GetWorkspaceColor() const
Timer maFadeTimer
Definition: swruler.hxx:51
SfxViewShell * GetSfxViewShell() const
Definition: viewsh.hxx:444
#define CONTROL_RIGHT_OFFSET
Definition: swruler.cxx:36
unsigned long GetSidebarBorderWidth(bool bPx=false) const
Definition: PostItMgr.cxx:2098
virtual ~SwCommentRuler() override
Definition: swruler.cxx:89
virtual void Command(const CommandEvent &rCEvt) override
SwViewShell * mpViewShell
Definition: swruler.hxx:48
ScopedVclPtr< VirtualDevice > maVirDev
Definition: swruler.hxx:53
bool IsActive() const
virtual void dispose() override
sal_Int64 WinBits
IMPL_LINK_NOARG(SwCommentRuler, FadeHandler, Timer *, void)
Definition: swruler.cxx:339
void setX(long nX)
virtual void MouseMove(const MouseEvent &rMEvt) override
WinBits const WB_HSCROLL
void libreOfficeKitViewCallback(int nType, const char *pPayload) const override
void ExecViewOptions(SfxRequest &)
Definition: view0.cxx:356
void setY(long nY)
void DrawRect(const tools::Rectangle &rRect)
virtual void MouseButtonDown(const MouseEvent &rMEvt) override
Callback function to handle a mouse button down event.
Definition: swruler.cxx:236
const Color & GetDarkShadowColor() const
void SetLineColor()
void clear()
Window class for the Writer edit area, this is the one handling mouse and keyboard events and doing t...
Definition: edtwin.hxx:58
void SetFillColor()
#define CONTROL_TRIANGLE_PAD
Definition: swruler.cxx:40
static bool GetLayoutRTL()
void NotifyKit()
Definition: swruler.cxx:274
virtual void Start() override
bool IsInside(const Point &rPOINT) const
const AllSettings & GetSettings() const
OUString SwResId(const char *pId)
Definition: swmodule.cxx:191
virtual void Paint(vcl::RenderContext &rRenderContext, const tools::Rectangle &rRect) override
Paint the ruler.
Definition: swruler.cxx:100
#define CONTROL_TOP_OFFSET
Definition: swruler.cxx:37
void SetTimeout(sal_uInt64 nTimeoutMs)
virtual void dispose() override
Definition: swruler.cxx:94
long X() const
Size GetSize() const
const Point & GetMousePosPixel() const
Point LogicToPixel(const Point &rLogicPt) const
Color GetFadedColor(const Color &rHighColor, const Color &rLowColor)
Get the proper color between two options, according to current status.
Definition: swruler.cxx:329
long GetPageWidth() const
#define CONTROL_TRIANGLE_WIDTH
Definition: swruler.cxx:39
const Color & GetButtonTextColor() const
virtual void MouseMove(const MouseEvent &rMEvt) override
Callback function to handle a mouse move event.
Definition: swruler.cxx:208
void Create(SwFormatVertOrient &rItem, SvStream &rStrm, sal_uInt16 nVersionAbusedAsSize)
Definition: legacyitem.cxx:34
bool IsLeft() const
virtual void Update() override
Update the view.
Definition: swruler.cxx:283
void SetInvokeHandler(const Link< Timer *, void > &rLink)
bool HasNotes() const
Definition: PostItMgr.cxx:2067
const Point & GetPosPixel() const
const SwPostItMgr * GetPostItMgr() const
Definition: viewsh.hxx:559
virtual void Command(const CommandEvent &rCEvt) override
Callback function to handle a context menu call (mouse right button click).
Definition: swruler.cxx:198
SwCommentRuler(SwViewShell *pViewSh, vcl::Window *pParent, SwEditWin *pWin, SvxRulerSupportFlags nRulerFlags, SfxBindings &rBindings, WinBits nWinStyle)
Definition: swruler.cxx:74
An horizontal ruler with a control for comment panel visibility for Writer.
Definition: swruler.hxx:27
unsigned long GetSidebarWidth(bool bPx=false) const
Definition: PostItMgr.cxx:2072
constexpr sal_Int64 convertTwipToMm100(sal_Int64 n)
#define CONTROL_BORDER_WIDTH
Definition: swruler.cxx:34
void(* f)(TrueTypeTable *)
tools::Rectangle GetCommentControlRegion()
Get the rectangle area that should be used to draw the comment control.
Definition: swruler.cxx:303
bool ShowNotes() const
Definition: PostItMgr.cxx:2061
void DrawCommentControl(vcl::RenderContext &rRenderContext)
Paint the comment control on VirtualDevice.
Definition: swruler.cxx:112
VclPtr< SwEditWin > mpSwWin
Definition: swruler.hxx:49
SAL_DLLPRIVATE void DrawOutDev(const Point &, const Size &, const Point &, const Size &, const Printer &)=delete
Definition: view.hxx:146
long Y() const
bool mbIsHighlighted
Definition: swruler.hxx:50
void SetDebugName(const sal_Char *pDebugName)