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 <memory>
21 #include <ElementsDockingWindow.hxx>
22 
23 #include <starmath.hrc>
24 #include <strings.hrc>
25 #include <smmod.hxx>
26 #include <view.hxx>
27 #include <visitors.hxx>
28 #include <document.hxx>
29 #include <node.hxx>
30 #include "uiobject.hxx"
31 #include <strings.hxx>
32 
33 #include <svl/stritem.hxx>
34 #include <sfx2/dispatch.hxx>
35 #include <sfx2/sfxmodelfactory.hxx>
36 #include <vcl/event.hxx>
37 #include <vcl/help.hxx>
38 #include <vcl/settings.hxx>
39 #include <vcl/uitest/logger.hxx>
40 #include <vcl/uitest/eventdescription.hxx>
41 
42 SmElement::SmElement(std::unique_ptr<SmNode>&& pNode, const OUString& aText, const OUString& aHelpText) :
43     mpNode(std::move(pNode)),
44     maText(aText),
45     maHelpText(aHelpText)
46 {}
47 
48 SmElement::~SmElement()
49 {}
50 
51 const std::unique_ptr<SmNode>& SmElement::getNode()
52 {
53     return mpNode;
54 }
55 
56 SmElementSeparator::SmElementSeparator() :
57     SmElement(std::unique_ptr<SmNode>(), OUString(), OUString())
58 {}
59 
60 const std::pair<const char*, const char*> SmElementsControl::aUnaryBinaryOperatorsList[] =
61 {
62     {RID_PLUSX, RID_PLUSX_HELP}, {RID_MINUSX, RID_MINUSX_HELP},
63     {RID_PLUSMINUSX, RID_PLUSMINUSX_HELP}, {RID_MINUSPLUSX, RID_MINUSPLUSX_HELP},
64     {nullptr, nullptr},
65     {RID_XPLUSY, RID_XPLUSY_HELP}, {RID_XMINUSY, RID_XMINUSY_HELP},
66     {RID_XCDOTY, RID_XCDOTY_HELP}, {RID_XTIMESY, RID_XTIMESY_HELP},
67     {RID_XSYMTIMESY, RID_XSYMTIMESY_HELP}, {RID_XOVERY, RID_XOVERY_HELP},
68     {RID_XDIVY, RID_XDIVY_HELP}, {RID_XSYMDIVIDEY, RID_XSYMDIVIDEY_HELP},
69     {RID_XOPLUSY, RID_XOPLUSY_HELP}, {RID_XOMINUSY, RID_XOMINUSY_HELP},
70     {RID_XODOTY, RID_XODOTY_HELP}, {RID_XOTIMESY, RID_XOTIMESY_HELP},
71     {RID_XODIVIDEY, RID_XODIVIDEY_HELP}, {RID_XCIRCY, RID_XCIRCY_HELP},
72     {RID_XWIDESLASHY, RID_XWIDESLASHY_HELP}, {RID_XWIDEBSLASHY, RID_XWIDEBSLASHY_HELP},
73     {nullptr, nullptr},
74     {RID_NEGX, RID_NEGX_HELP}, {RID_XANDY, RID_XANDY_HELP}, {RID_XORY, RID_XORY_HELP},
75 };
76 
77 const std::pair<const char*, const char*> SmElementsControl::aRelationsList[] =
78 {
79     {RID_XEQY, RID_XEQY_HELP}, {RID_XNEQY, RID_XNEQY_HELP}, {RID_XLTY, RID_XLTY_HELP},
80     {RID_XLEY, RID_XLEY_HELP}, {RID_XLESLANTY, RID_XLESLANTY_HELP}, {RID_XGTY, RID_XGTY_HELP},
81     {RID_XGEY, RID_XGEY_HELP}, {RID_XGESLANTY, RID_XGESLANTY_HELP},
82     {RID_XLLY, RID_XLLY_HELP}, {RID_XGGY, RID_XGGY_HELP},
83     {nullptr, nullptr},
84     {RID_XAPPROXY, RID_XAPPROXY_HELP}, {RID_XSIMY, RID_XSIMY_HELP}, {RID_XSIMEQY, RID_XSIMEQY_HELP},
85     {RID_XEQUIVY, RID_XEQUIVY_HELP}, {RID_XPROPY, RID_XPROPY_HELP}, {RID_XPARALLELY, RID_XPARALLELY_HELP},
86     {RID_XORTHOY, RID_XORTHOY_HELP}, {RID_XDIVIDESY, RID_XDIVIDESY_HELP}, {RID_XNDIVIDESY, RID_XNDIVIDESY_HELP},
87     {RID_XTOWARDY, RID_XTOWARDY_HELP}, {RID_XTRANSLY, RID_XTRANSLY_HELP}, {RID_XTRANSRY, RID_XTRANSRY_HELP},
88     {RID_XDEFY, RID_XDEFY_HELP},
89     {nullptr, nullptr},
90     {RID_DLARROW, RID_DLARROW_HELP}, {RID_DLRARROW, RID_DLRARROW_HELP}, {RID_DRARROW, RID_DRARROW_HELP},
91     {nullptr, nullptr},
92     {RID_XPRECEDESY, RID_XPRECEDESY_HELP}, {RID_XSUCCEEDSY, RID_XSUCCEEDSY_HELP},
93     {RID_XPRECEDESEQUALY, RID_XPRECEDESEQUALY_HELP}, {RID_XSUCCEEDSEQUALY, RID_XSUCCEEDSEQUALY_HELP},
94     {RID_XPRECEDESEQUIVY, RID_XPRECEDESEQUIVY_HELP}, {RID_XSUCCEEDSEQUIVY, RID_XSUCCEEDSEQUIVY_HELP},
95     {RID_XNOTPRECEDESY, RID_XNOTPRECEDESY_HELP}, {RID_XNOTSUCCEEDSY, RID_XNOTSUCCEEDSY_HELP},
96 };
97 
98 const std::pair<const char*, const char*> SmElementsControl::aSetOperations[] =
99 {
100     {RID_XINY, RID_XINY_HELP}, {RID_XNOTINY, RID_XNOTINY_HELP}, {RID_XOWNSY, RID_XOWNSY_HELP},
101     {nullptr, nullptr},
102     {RID_XINTERSECTIONY, RID_XINTERSECTIONY_HELP}, {RID_XUNIONY, RID_XUNIONY_HELP},
103     {RID_XSETMINUSY, RID_XSETMINUSY_HELP}, {RID_XSLASHY, RID_XSLASHY_HELP},
104     {RID_XSUBSETY, RID_XSUBSETY_HELP}, {RID_XSUBSETEQY, RID_XSUBSETEQY_HELP},
105     {RID_XSUPSETY, RID_XSUPSETY_HELP}, {RID_XSUPSETEQY, RID_XSUPSETEQY_HELP},
106     {RID_XNSUBSETY, RID_XNSUBSETY_HELP}, {RID_XNSUBSETEQY, RID_XNSUBSETEQY_HELP},
107     {RID_XNSUPSETY, RID_XNSUPSETY_HELP}, {RID_XNSUPSETEQY, RID_XNSUPSETEQY_HELP},
108     {nullptr, nullptr},
109     {RID_EMPTYSET, RID_EMPTYSET_HELP}, {RID_ALEPH, RID_ALEPH_HELP}, {RID_SETN, RID_SETN_HELP},
110     {RID_SETZ, RID_SETZ_HELP}, {RID_SETQ, RID_SETQ_HELP}, {RID_SETR, RID_SETR_HELP}, {RID_SETC, RID_SETC_HELP}
111 };
112 
113 const std::pair<const char*, const char*> SmElementsControl::aFunctions[] =
114 {
115     {RID_ABSX, RID_ABSX_HELP}, {RID_FACTX, RID_FACTX_HELP}, {RID_SQRTX, RID_SQRTX_HELP},
116     {RID_NROOTXY, RID_NROOTXY_HELP}, {RID_RSUPX, RID_RSUPX_HELP}, {RID_EX, RID_EX_HELP},
117     {RID_LNX, RID_LNX_HELP}, {RID_EXPX, RID_EXPX_HELP}, {RID_LOGX, RID_LOGX_HELP},
118     {nullptr, nullptr},
119     {RID_SINX, RID_SINX_HELP}, {RID_COSX, RID_COSX_HELP}, {RID_TANX, RID_TANX_HELP}, {RID_COTX, RID_COTX_HELP},
120     {RID_SINHX, RID_SINHX_HELP}, {RID_COSHX, RID_COSHX_HELP}, {RID_TANHX, RID_TANHX_HELP},
121     {RID_COTHX, RID_COTHX_HELP},
122     {nullptr, nullptr},
123     {RID_ARCSINX, RID_ARCSINX_HELP}, {RID_ARCCOSX, RID_ARCCOSX_HELP}, {RID_ARCTANX, RID_ARCTANX_HELP},
124     {RID_ARCCOTX, RID_ARCCOTX_HELP}, {RID_ARSINHX, RID_ARSINHX_HELP}, {RID_ARCOSHX, RID_ARCOSHX_HELP},
125     {RID_ARTANHX, RID_ARTANHX_HELP}, {RID_ARCOTHX, RID_ARCOTHX_HELP}
126 };
127 
128 const std::pair<const char*, const char*> SmElementsControl::aOperators[] =
129 {
130     {RID_LIMX, RID_LIMX_HELP}, {RID_LIM_FROMX, RID_LIM_FROMX_HELP},
131     {RID_LIM_TOX, RID_LIM_TOX_HELP}, {RID_LIM_FROMTOX, RID_LIM_FROMTOX_HELP},
132     {nullptr, nullptr},
133     {RID_LIMINFX, RID_LIMINFX_HELP}, {RID_LIMINF_FROMX, RID_LIMINF_FROMX_HELP},
134     {RID_LIMINF_TOX, RID_LIMINF_TOX_HELP}, {RID_LIMINF_FROMTOX, RID_LIMINF_FROMTOX_HELP},
135     {nullptr, nullptr},
136     {RID_LIMSUPX, RID_LIMSUPX_HELP}, {RID_LIMSUP_FROMX, RID_LIMSUP_FROMX_HELP},
137     {RID_LIMSUP_TOX, RID_LIMSUP_TOX_HELP}, {RID_LIMSUP_FROMTOX, RID_LIMSUP_FROMTOX_HELP},
138     {nullptr, nullptr},
139     {RID_SUMX, RID_SUMX_HELP}, {RID_SUM_FROMX, RID_SUM_FROMX_HELP},
140     {RID_SUM_TOX, RID_SUM_TOX_HELP}, {RID_SUM_FROMTOX, RID_SUM_FROMTOX_HELP},
141     {nullptr, nullptr},
142     {RID_PRODX, RID_PRODX_HELP}, {RID_PROD_FROMX, RID_PROD_FROMX_HELP},
143     {RID_PROD_TOX, RID_PROD_TOX_HELP}, {RID_PROD_FROMTOX, RID_PROD_FROMTOX_HELP},
144     {nullptr, nullptr},
145     {RID_COPRODX, RID_COPRODX_HELP}, {RID_COPROD_FROMX, RID_COPROD_FROMX_HELP},
146     {RID_COPROD_TOX, RID_COPROD_TOX_HELP}, {RID_COPROD_FROMTOX, RID_COPROD_FROMTOX_HELP},
147     {nullptr, nullptr},
148     {RID_INTX, RID_INTX_HELP}, {RID_INT_FROMX, RID_INT_FROMX_HELP},
149     {RID_INT_TOX, RID_INT_TOX_HELP}, {RID_INT_FROMTOX, RID_INT_FROMTOX_HELP},
150     {nullptr, nullptr},
151     {RID_IINTX, RID_IINTX_HELP}, {RID_IINT_FROMX, RID_IINT_FROMX_HELP},
152     {RID_IINT_TOX, RID_IINT_TOX_HELP}, {RID_IINT_FROMTOX, RID_IINT_FROMTOX_HELP},
153     {nullptr, nullptr},
154     {RID_IIINTX, RID_IIINTX_HELP}, {RID_IIINT_FROMX, RID_IIINT_FROMX_HELP},
155     {RID_IIINT_TOX, RID_IIINT_TOX_HELP}, {RID_IIINT_FROMTOX, RID_IIINT_FROMTOX_HELP},
156     {nullptr, nullptr},
157     {RID_LINTX, RID_LINTX_HELP}, {RID_LINT_FROMX, RID_LINT_FROMX_HELP},
158     {RID_LINT_TOX, RID_LINT_TOX_HELP}, {RID_LINT_FROMTOX, RID_LINT_FROMTOX_HELP},
159     {nullptr, nullptr},
160     {RID_LLINTX, RID_LLINTX_HELP}, {RID_LLINT_FROMX, RID_LLINT_FROMX_HELP},
161     {RID_LLINT_TOX, RID_LLINT_TOX_HELP}, {RID_LLINT_FROMTOX, RID_LLINT_FROMTOX_HELP},
162     {nullptr, nullptr},
163     {RID_LLLINTX, RID_LLLINTX_HELP}, {RID_LLLINT_FROMX, RID_LLLINT_FROMX_HELP},
164     {RID_LLLINT_TOX, RID_LLLINT_TOX_HELP}, {RID_LLLINT_FROMTOX, RID_LLLINT_FROMTOX_HELP},
165 };
166 
167 const std::pair<const char*, const char*> SmElementsControl::aAttributes[] =
168 {
169     {RID_ACUTEX, RID_ACUTEX_HELP}, {RID_GRAVEX, RID_GRAVEX_HELP}, {RID_BREVEX, RID_BREVEX_HELP},
170     {RID_CIRCLEX, RID_CIRCLEX_HELP}, {RID_DOTX, RID_DOTX_HELP}, {RID_DDOTX, RID_DDOTX_HELP},
171     {RID_DDDOTX, RID_DDDOTX_HELP}, {RID_BARX, RID_BARX_HELP}, {RID_VECX, RID_VECX_HELP},
172     {RID_TILDEX, RID_TILDEX_HELP}, {RID_HATX, RID_HATX_HELP}, {RID_CHECKX, RID_CHECKX_HELP},
173     {nullptr, nullptr},
174     {RID_WIDEVECX, RID_WIDEVECX_HELP}, {RID_WIDETILDEX, RID_WIDETILDEX_HELP}, {RID_WIDEHATX, RID_WIDEHATX_HELP},
175     {RID_OVERLINEX, RID_OVERLINEX_HELP}, {RID_UNDERLINEX, RID_UNDERLINEX_HELP}, {RID_OVERSTRIKEX, RID_OVERSTRIKEX_HELP},
176     {nullptr, nullptr},
177     {RID_PHANTOMX, RID_PHANTOMX_HELP}, {RID_BOLDX, RID_BOLDX_HELP}, {RID_ITALX, RID_ITALX_HELP},
178     {RID_SIZEXY, RID_SIZEXY_HELP}, {RID_FONTXY, RID_FONTXY_HELP},
179     {nullptr, nullptr},
180     {RID_COLORX_BLACK, RID_COLORX_BLACK_HELP}, {RID_COLORX_BLUE, RID_COLORX_BLUE_HELP},
181     {RID_COLORX_GREEN, RID_COLORX_GREEN_HELP}, {RID_COLORX_RED, RID_COLORX_RED_HELP},
182     {RID_COLORX_CYAN, RID_COLORX_CYAN_HELP}, {RID_COLORX_MAGENTA, RID_COLORX_MAGENTA_HELP},
183     {RID_COLORX_YELLOW, RID_COLORX_YELLOW_HELP}, {RID_COLORX_GRAY, RID_COLORX_GRAY_HELP},
184     {RID_COLORX_LIME, RID_COLORX_LIME_HELP}, {RID_COLORX_MAROON, RID_COLORX_MAROON_HELP},
185     {RID_COLORX_NAVY, RID_COLORX_NAVY_HELP}, {RID_COLORX_OLIVE, RID_COLORX_OLIVE_HELP},
186     {RID_COLORX_PURPLE, RID_COLORX_PURPLE_HELP}, {RID_COLORX_SILVER, RID_COLORX_SILVER_HELP},
187     {RID_COLORX_TEAL, RID_COLORX_TEAL_HELP}
188 };
189 
190 const std::pair<const char*, const char*> SmElementsControl::aBrackets[] =
191 {
192     {RID_LRGROUPX, RID_LRGROUPX_HELP},
193     {nullptr, nullptr},
194     {RID_LRPARENTX, RID_LRPARENTX_HELP}, {RID_LRBRACKETX, RID_LRBRACKETX_HELP},
195     {RID_LRDBRACKETX, RID_LRDBRACKETX_HELP}, {RID_LRBRACEX, RID_LRBRACEX_HELP},
196     {RID_LRANGLEX, RID_LRANGLEX_HELP}, {RID_LMRANGLEXY, RID_LMRANGLEXY_HELP},
197     {RID_LRCEILX, RID_LRCEILX_HELP}, {RID_LRFLOORX, RID_LRFLOORX_HELP},
198     {RID_LRLINEX, RID_LRLINEX_HELP}, {RID_LRDLINEX, RID_LRDLINEX_HELP},
199     {nullptr, nullptr},
200     {RID_SLRPARENTX, RID_SLRPARENTX_HELP}, {RID_SLRBRACKETX, RID_SLRBRACKETX_HELP},
201     {RID_SLRDBRACKETX, RID_SLRDBRACKETX_HELP}, {RID_SLRBRACEX, RID_SLRBRACEX_HELP},
202     {RID_SLRANGLEX, RID_SLRANGLEX_HELP}, {RID_SLMRANGLEXY, RID_SLMRANGLEXY_HELP},
203     {RID_SLRCEILX, RID_SLRCEILX_HELP}, {RID_SLRFLOORX, RID_SLRFLOORX_HELP},
204     {RID_SLRLINEX, RID_SLRLINEX_HELP}, {RID_SLRDLINEX, RID_SLRDLINEX_HELP},
205     {RID_XEVALUATEDATY, RID_XEVALUATEDATY_HELP},
206     {nullptr, nullptr},
207     {RID_XOVERBRACEY, RID_XOVERBRACEY_HELP}, {RID_XUNDERBRACEY, RID_XUNDERBRACEY_HELP},
208 };
209 
210 const std::pair<const char*, const char*> SmElementsControl::aFormats[] =
211 {
212     {RID_RSUPX, RID_RSUPX_HELP}, {RID_RSUBX, RID_RSUBX_HELP}, {RID_LSUPX, RID_LSUPX_HELP},
213     {RID_LSUBX, RID_LSUBX_HELP}, {RID_CSUPX, RID_CSUPX_HELP}, {RID_CSUBX, RID_CSUBX_HELP},
214     {nullptr, nullptr},
215     {RID_NEWLINE, RID_NEWLINE_HELP}, {RID_SBLANK, RID_SBLANK_HELP}, {RID_BLANK, RID_BLANK_HELP},
216     {RID_NOSPACE, RID_NOSPACE_HELP},
217     {RID_ALIGNLX, RID_ALIGNLX_HELP}, {RID_ALIGNCX, RID_ALIGNCX_HELP}, {RID_ALIGNRX, RID_ALIGNRX_HELP},
218     {nullptr, nullptr},
219     {RID_BINOMXY, RID_BINOMXY_HELP}, {RID_STACK, RID_STACK_HELP},
220     {RID_MATRIX, RID_MATRIX_HELP},
221 };
222 
223 const std::pair<const char*, const char*> SmElementsControl::aOthers[] =
224 {
225     {RID_INFINITY, RID_INFINITY_HELP}, {RID_PARTIAL, RID_PARTIAL_HELP}, {RID_NABLA, RID_NABLA_HELP},
226     {RID_EXISTS, RID_EXISTS_HELP}, {RID_NOTEXISTS, RID_NOTEXISTS_HELP}, {RID_FORALL, RID_FORALL_HELP},
227     {RID_HBAR, RID_HBAR_HELP}, {RID_LAMBDABAR, RID_LAMBDABAR_HELP}, {RID_RE, RID_RE_HELP},
228     {RID_IM, RID_IM_HELP}, {RID_WP, RID_WP_HELP},
229     {nullptr, nullptr},
230     {RID_LEFTARROW, RID_LEFTARROW_HELP}, {RID_RIGHTARROW, RID_RIGHTARROW_HELP}, {RID_UPARROW, RID_UPARROW_HELP},
231     {RID_DOWNARROW, RID_DOWNARROW_HELP},
232     {nullptr, nullptr},
233     {RID_DOTSLOW, RID_DOTSLOW_HELP}, {RID_DOTSAXIS, RID_DOTSAXIS_HELP}, {RID_DOTSVERT, RID_DOTSVERT_HELP},
234     {RID_DOTSUP, RID_DOTSUP_HELP}, {RID_DOTSDOWN, RID_DOTSDOWN_HELP}
235 };
236 
237 SmElementsControl::SmElementsControl(vcl::Window *pParent)
238     : Control(pParent, WB_TABSTOP)
239     , mpDocShell(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT))
240     , mpCurrentElement(nullptr)
241     , mbVerticalMode(true)
242     , mxScroll(VclPtr<ScrollBar>::Create(this, WB_VERT))
243 {
244     set_id("element_selector");
245     SetMapMode( MapMode(MapUnit::Map100thMM) );
246     SetDrawMode( DrawModeFlags::Default );
247     SetLayoutMode( ComplexTextLayoutFlags::Default );
248     SetDigitLanguage( LANGUAGE_ENGLISH );
249 
250     maFormat.SetBaseSize(PixelToLogic(Size(0, SmPtsTo100th_mm(12))));
251 
252     mxScroll->SetScrollHdl( LINK(this, SmElementsControl, ScrollHdl) );
253     mxScroll->Show();
254 }
255 
256 SmElementsControl::~SmElementsControl()
257 {
258     disposeOnce();
259 }
260 
261 void SmElementsControl::dispose()
262 {
263     mpDocShell->DoClose();
264     mxScroll.disposeAndClear();
265     Control::dispose();
266 }
267 
268 void SmElementsControl::setVerticalMode(bool bVerticalMode)
269 {
270     mbVerticalMode = bVerticalMode;
271 }
272 
273 void SmElementsControl::LayoutOrPaintContents(vcl::RenderContext *pContext)
274 {
275     bool bOldVisibleState = mxScroll->IsVisible();
276 
277     sal_Int32 nScrollbarWidth = bOldVisibleState ? GetSettings().GetStyleSettings().GetScrollBarSize() : 0;
278 
279     sal_Int32 nControlWidth = GetOutputSizePixel().Width() - nScrollbarWidth;
280     sal_Int32 nControlHeight = GetOutputSizePixel().Height();
281 
282     sal_Int32 boxX = maMaxElementDimensions.Width()  + 10;
283     sal_Int32 boxY = maMaxElementDimensions.Height() + 10;
284 
285     sal_Int32 x = 0;
286     sal_Int32 y = -mxScroll->GetThumbPos();
287 
288     sal_Int32 perLine = 0;
289 
290     if (mbVerticalMode)
291         perLine = nControlHeight / boxY;
292     else
293         perLine = nControlWidth / boxX;
294 
295     if (perLine <= 0)
296     {
297         perLine = 1;
298     }
299 
300     if (mbVerticalMode)
301         boxY = nControlHeight / perLine;
302     else
303         boxX = nControlWidth / perLine;
304 
305     for (std::unique_ptr<SmElement> & i : maElementList)
306     {
307         SmElement* element = i.get();
308         if (element->isSeparator())
309         {
310             if (mbVerticalMode)
311             {
312                 x += boxX;
313                 y = 0;
314 
315                 tools::Rectangle aSelectionRectangle(x + 5 - 1, y + 5,
316                                               x + 5 + 1, nControlHeight - 5);
317 
318                 if (pContext)
319                     pContext->DrawRect(PixelToLogic(aSelectionRectangle));
320                 x += 10;
321             }
322             else
323             {
324                 x = 0;
325                 y += boxY;
326 
327                 tools::Rectangle aSelectionRectangle(x + 5, y + 5 - 1,
328                                               nControlWidth - 5, y + 5 + 1);
329 
330                 if (pContext)
331                     pContext->DrawRect(PixelToLogic(aSelectionRectangle));
332                 y += 10;
333             }
334         }
335         else
336         {
337             Size aSizePixel = LogicToPixel(Size(element->getNode()->GetWidth(),
338                                                 element->getNode()->GetHeight()));
339             if (mbVerticalMode)
340             {
341                 if (y + boxY > nControlHeight)
342                 {
343                     x += boxX;
344                     y = 0;
345                 }
346             }
347             else
348             {
349                 if ( x + boxX > nControlWidth)
350                 {
351                     x = 0;
352                     y += boxY;
353                 }
354             }
355 
356             if (mpCurrentElement == element && pContext)
357             {
358                 pContext->Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
359                 pContext->SetFillColor(Color(230, 230, 230));
360                 pContext->SetLineColor(Color(230, 230, 230));
361 
362                 pContext->DrawRect(PixelToLogic(tools::Rectangle(x + 2, y + 2, x + boxX - 2, y + boxY - 2)));
363                 pContext->Pop();
364             }
365 
366             Point location(x + ((boxX - aSizePixel.Width()) / 2),
367                            y + ((boxY - aSizePixel.Height()) / 2));
368 
369             if (pContext)
370                 SmDrawingVisitor(*pContext, PixelToLogic(location), element->getNode().get());
371 
372             element->mBoxLocation = Point(x,y);
373             element->mBoxSize = Size(boxX, boxY);
374 
375             if (mbVerticalMode)
376                 y += boxY;
377             else
378                 x += boxX;
379         }
380     }
381 
382     if (pContext)
383         return;
384 
385     sal_Int32 nTotalControlHeight = y + boxY + mxScroll->GetThumbPos();
386 
387     if (nTotalControlHeight > GetOutputSizePixel().Height())
388     {
389         mxScroll->SetRangeMax(nTotalControlHeight);
390         mxScroll->SetPosSizePixel(Point(nControlWidth, 0), Size(nScrollbarWidth, nControlHeight));
391         mxScroll->SetVisibleSize(nControlHeight);
392         mxScroll->Show();
393     }
394     else
395     {
396         mxScroll->SetThumbPos(0);
397         mxScroll->Hide();
398     }
399 }
400 
401 void SmElementsControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
402 {
403     rRenderContext.Push();
404     LayoutOrPaintContents(&rRenderContext);
405     rRenderContext.Pop();
406 }
407 
408 void SmElementsControl::RequestHelp(const HelpEvent& rHEvt)
409 {
410     if (rHEvt.GetMode() & (HelpEventMode::BALLOON | HelpEventMode::QUICK))
411     {
412         SmElement* pHelpElement = mpCurrentElement;
413 
414         if (!rHEvt.KeyboardActivated())
415         {
416             Point aHelpEventPos(ScreenToOutputPixel(rHEvt.GetMousePosPixel()));
417             for (std::unique_ptr<SmElement> & i : maElementList)
418             {
419                 SmElement* pElement = i.get();
420                 tools::Rectangle aRect(pElement->mBoxLocation, pElement->mBoxSize);
421                 if (aRect.IsInside(aHelpEventPos))
422                 {
423                     pHelpElement = pElement;
424                     break;
425                 }
426             }
427         }
428 
429         if (!pHelpElement)
430             return;
431 
432         tools::Rectangle aHelpRect(pHelpElement->mBoxLocation, pHelpElement->mBoxSize);
433         Point aPt = OutputToScreenPixel( aHelpRect.TopLeft() );
434         aHelpRect.SetLeft( aPt.X() );
435         aHelpRect.SetTop( aPt.Y() );
436         aPt = OutputToScreenPixel( aHelpRect.BottomRight() );
437         aHelpRect.SetRight( aPt.X() );
438         aHelpRect.SetBottom( aPt.Y() );
439 
440         // get text and display it
441         OUString aStr = pHelpElement->getHelpText();
442         if (rHEvt.GetMode() & HelpEventMode::BALLOON)
443         {
444             Help::ShowBalloon(this, aHelpRect.Center(), aHelpRect, aStr);
445         }
446         else
447             Help::ShowQuickHelp(this, aHelpRect, aStr, QuickHelpFlags::CtrlText);
448         return;
449     }
450 
451     Control::RequestHelp(rHEvt);
452 }
453 
454 void SmElementsControl::MouseMove( const MouseEvent& rMouseEvent )
455 {
456     SmElement* pPrevElement = mpCurrentElement;
457     mpCurrentElement = nullptr;
458     if (rMouseEvent.IsLeaveWindow())
459     {
460         LayoutOrPaintContents();
461         Invalidate();
462         return;
463     }
464     if (tools::Rectangle(Point(0, 0), GetOutputSizePixel()).IsInside(rMouseEvent.GetPosPixel()))
465     {
466         for (std::unique_ptr<SmElement> & i : maElementList)
467         {
468             SmElement* element = i.get();
469             tools::Rectangle rect(element->mBoxLocation, element->mBoxSize);
470             if (rect.IsInside(rMouseEvent.GetPosPixel()))
471             {
472                 if (pPrevElement != element)
473                 {
474                     mpCurrentElement = element;
475                     LayoutOrPaintContents();
476                     Invalidate();
477                     return;
478                 }
479             }
480         }
481         return;
482     }
483 
484     Control::MouseMove(rMouseEvent);
485 }
486 
487 namespace {
488 
489 void collectUIInformation(const OUString& aID)
490 {
491     EventDescription aDescription;
492     aDescription.aID = aID;
493     aDescription.aParent = "element_selector";
494     aDescription.aAction = "SELECT";
495     aDescription.aKeyWord = "ElementUIObject";
496     UITestLogger::getInstance().logEvent(aDescription);
497 }
498 
499 }
500 
501 void SmElementsControl::MouseButtonDown(const MouseEvent& rMouseEvent)
502 {
503     GrabFocus();
504 
505     if (rMouseEvent.IsLeft() && tools::Rectangle(Point(0, 0), GetOutputSizePixel()).IsInside(rMouseEvent.GetPosPixel()) && maSelectHdlLink.IsSet())
506     {
507         sal_uInt16 nElementCount = maElementList.size();
508 
509         for (sal_uInt16 n = 0; n < nElementCount; n++)
510         {
511             std::unique_ptr<SmElement> & i = maElementList[n];
512             SmElement* element = i.get();
513             tools::Rectangle rect(element->mBoxLocation, element->mBoxSize);
514             if (rect.IsInside(rMouseEvent.GetPosPixel()))
515             {
516                 maSelectHdlLink.Call(*element);
517                 collectUIInformation(OUString::number(n));
518                 return;
519             }
520         }
521     }
522     else
523     {
524         Control::MouseButtonDown (rMouseEvent);
525     }
526 }
527 
528 IMPL_LINK_NOARG( SmElementsControl, ScrollHdl, ScrollBar*, void )
529 {
530     DoScroll(mxScroll->GetDelta());
531 }
532 
533 void SmElementsControl::DoScroll(long nDelta)
534 {
535     Point aNewPoint = mxScroll->GetPosPixel();
536     tools::Rectangle aRect(Point(), GetOutputSize());
537     aRect.AdjustRight( -(mxScroll->GetSizePixel().Width()) );
538     Scroll( 0, -nDelta, aRect );
539     mxScroll->SetPosPixel(aNewPoint);
540     LayoutOrPaintContents();
541     Invalidate();
542 }
543 
544 void SmElementsControl::addElement(const OUString& aElementVisual, const OUString& aElementSource, const OUString& aHelpText)
545 {
546     auto pNode = SmParser().ParseExpression(aElementVisual);
547 
548     pNode->Prepare(maFormat, *mpDocShell, 0);
549     pNode->SetSize(Fraction(10,8));
550     pNode->Arrange(*this, maFormat);
551 
552     Size aSizePixel = LogicToPixel(Size(pNode->GetWidth(), pNode->GetHeight()), MapMode(MapUnit::Map100thMM));
553     if (aSizePixel.Width() > maMaxElementDimensions.Width()) {
554         maMaxElementDimensions.setWidth( aSizePixel.Width() );
555     }
556 
557     if (aSizePixel.Height() > maMaxElementDimensions.Height()) {
558         maMaxElementDimensions.setHeight( aSizePixel.Height() );
559     }
560 
561     maElementList.push_back(std::make_unique<SmElement>(std::move(pNode), aElementSource, aHelpText));
562 }
563 
564 void SmElementsControl::setElementSetId(const char* pSetId)
565 {
566     msCurrentSetId = pSetId;
567     maMaxElementDimensions = Size();
568     build();
569 }
570 
571 void SmElementsControl::addElements(const std::pair<const char*, const char*> aElementsArray[], sal_uInt16 aElementsArraySize)
572 {
573     for (sal_uInt16 i = 0; i < aElementsArraySize ; i++)
574     {
575         const char* pElement = aElementsArray[i].first;
576         const char* pElementHelp = aElementsArray[i].second;
577         if (!pElement) {
578             maElementList.push_back(std::make_unique<SmElementSeparator>());
579         } else {
580             OUString aElement(OUString::createFromAscii(pElement));
581             if (aElement == RID_NEWLINE)
582                 addElement(OUString(u"\u21B5"), aElement, SmResId(pElementHelp));
583             else if (aElement == RID_SBLANK)
584                 addElement("\"`\"", aElement, SmResId(pElementHelp));
585             else if (aElement == RID_BLANK)
586                 addElement("\"~\"", aElement, SmResId(pElementHelp));
587             else if (aElement == RID_PHANTOMX)
588                 addElement("\"" + SmResId(STR_HIDE) +"\"", aElement, SmResId(pElementHelp));
589             else if (aElement == RID_BOLDX)
590                 addElement("bold B", aElement, SmResId(pElementHelp));
591             else if (aElement == RID_ITALX)
592                 addElement("ital I", aElement, SmResId(pElementHelp));
593             else if (aElement == RID_SIZEXY)
594                 addElement("\"" + SmResId(STR_SIZE) + "\"", aElement, SmResId(pElementHelp));
595             else if (aElement == RID_FONTXY)
596                 addElement("\"" + SmResId(STR_FONT) + "\"", aElement, SmResId(pElementHelp));
597             else if (aElement == RID_COLORX_BLACK)
598                 addElement("color black { \"" + SmResId(STR_BLACK) + "\" }", aElement, SmResId(pElementHelp));
599             else if (aElement == RID_COLORX_BLUE)
600                 addElement("color blue { \"" + SmResId(STR_BLUE) + "\" }", aElement, SmResId(pElementHelp));
601             else if (aElement == RID_COLORX_GREEN)
602                 addElement("color green { \"" + SmResId(STR_GREEN) + "\" }", aElement, SmResId(pElementHelp));
603             else if (aElement == RID_COLORX_RED)
604                 addElement("color red { \"" + SmResId(STR_RED) + "\" }", aElement, SmResId(pElementHelp));
605             else if (aElement == RID_COLORX_CYAN)
606                 addElement("color cyan { \"" + SmResId(STR_CYAN) + "\" }", aElement, SmResId(pElementHelp));
607             else if (aElement == RID_COLORX_MAGENTA)
608                 addElement("color magenta { \"" + SmResId(STR_MAGENTA) + "\" }", aElement, SmResId(pElementHelp));
609             else if (aElement == RID_COLORX_YELLOW)
610                 addElement("color yellow { \"" + SmResId(STR_YELLOW) + "\" }", aElement, SmResId(pElementHelp));
611             else if (aElement == RID_COLORX_GRAY)
612                 addElement("color gray { \"" + SmResId(STR_GRAY) + "\" }", aElement, SmResId(pElementHelp));
613             else if (aElement == RID_COLORX_LIME)
614                 addElement("color lime { \"" + SmResId(STR_LIME) + "\" }", aElement, SmResId(pElementHelp));
615             else if (aElement == RID_COLORX_MAROON)
616                 addElement("color maroon { \"" + SmResId(STR_MAROON) + "\" }", aElement, SmResId(pElementHelp));
617             else if (aElement == RID_COLORX_NAVY)
618                 addElement("color navy { \"" + SmResId(STR_NAVY) + "\" }", aElement, SmResId(pElementHelp));
619             else if (aElement == RID_COLORX_OLIVE)
620                 addElement("color olive { \"" + SmResId(STR_OLIVE) + "\" }", aElement, SmResId(pElementHelp));
621             else if (aElement == RID_COLORX_PURPLE)
622                 addElement("color purple { \"" + SmResId(STR_PURPLE) + "\" }", aElement, SmResId(pElementHelp));
623             else if (aElement == RID_COLORX_SILVER)
624                 addElement("color silver { \"" + SmResId(STR_SILVER) + "\" }", aElement, SmResId(pElementHelp));
625             else if (aElement == RID_COLORX_TEAL)
626                 addElement("color teal { \"" + SmResId(STR_TEAL) + "\" }", aElement, SmResId(pElementHelp));
627             else if (aElement == RID_ALIGNLX)
628                 addElement("\"" + SmResId(STR_ALIGN_LEFT) + "\"", aElement, SmResId(pElementHelp));
629             else if (aElement == RID_ALIGNCX)
630                 addElement("\"" + SmResId(STR_ALIGN_CENTER) + "\"", aElement, SmResId(pElementHelp));
631             else if (aElement == RID_ALIGNRX)
632                 addElement("\"" + SmResId(STR_ALIGN_RIGHT) + "\"", aElement, SmResId(pElementHelp));
633 
634             else if (aElement == RID_SLRPARENTX)
635                 addElement("left ( binom{<?>}{<?>} right ) ", aElement, SmResId(pElementHelp));
636             else if (aElement == RID_SLRBRACKETX)
637                 addElement("left [ binom{<?>}{<?>} right ] ", aElement, SmResId(pElementHelp));
638             else if (aElement == RID_SLRDBRACKETX)
639                 addElement("left ldbracket binom{<?>}{<?>} right rdbracket ", aElement, SmResId(pElementHelp));
640             else if (aElement == RID_SLRBRACEX)
641                 addElement("left lbrace binom{<?>}{<?>} right rbrace ", aElement, SmResId(pElementHelp));
642             else if (aElement == RID_SLRANGLEX)
643                 addElement("left langle binom{<?>}{<?>} right rangle ", aElement, SmResId(pElementHelp));
644             else if (aElement == RID_SLRCEILX)
645                 addElement("left lceil binom{<?>}{<?>} right rceil ", aElement, SmResId(pElementHelp));
646             else if (aElement == RID_SLRFLOORX)
647                 addElement("left lfloor binom{<?>}{<?>} right rfloor ", aElement, SmResId(pElementHelp));
648 
649             else if (aElement == RID_SLRLINEX)
650                 addElement("left lline binom{<?>}{<?>} right rline ", aElement, SmResId(pElementHelp));
651             else if (aElement == RID_SLRDLINEX)
652                 addElement("left ldline binom{<?>}{<?>} right rdline ", aElement, SmResId(pElementHelp));
653             else if (aElement == RID_SLMRANGLEXY)
654                 addElement("left langle binom{<?>}{<?>} mline binom{<?>}{<?>} right rangle ", aElement, SmResId(pElementHelp));
655 
656             else if (aElement == RID_XOVERBRACEY)
657                 addElement("{<?><?><?>} overbrace {<?>} ", aElement, SmResId(pElementHelp));
658             else if (aElement == RID_XUNDERBRACEY)
659                 addElement("{<?><?><?>} underbrace {<?>} ", aElement, SmResId(pElementHelp));
660             else
661                 addElement(aElement, aElement, SmResId(pElementHelp));
662         }
663     }
664 }
665 
666 void SmElementsControl::build()
667 {
668     maElementList.clear();
669 
670     if (msCurrentSetId == RID_CATEGORY_UNARY_BINARY_OPERATORS)
671         addElements(aUnaryBinaryOperatorsList, SAL_N_ELEMENTS(aUnaryBinaryOperatorsList));
672     else if (msCurrentSetId == RID_CATEGORY_RELATIONS)
673         addElements(aRelationsList, SAL_N_ELEMENTS(aRelationsList));
674     else if (msCurrentSetId == RID_CATEGORY_SET_OPERATIONS)
675         addElements(aSetOperations, SAL_N_ELEMENTS(aSetOperations));
676     else if (msCurrentSetId == RID_CATEGORY_FUNCTIONS)
677         addElements(aFunctions, SAL_N_ELEMENTS(aFunctions));
678     else if (msCurrentSetId == RID_CATEGORY_OPERATORS)
679         addElements(aOperators, SAL_N_ELEMENTS(aOperators));
680     else if (msCurrentSetId == RID_CATEGORY_ATTRIBUTES)
681         addElements(aAttributes, SAL_N_ELEMENTS(aAttributes));
682     else if (msCurrentSetId ==  RID_CATEGORY_BRACKETS)
683         addElements(aBrackets, SAL_N_ELEMENTS(aBrackets));
684     else if (msCurrentSetId == RID_CATEGORY_FORMATS)
685         addElements(aFormats, SAL_N_ELEMENTS(aFormats));
686     else if (msCurrentSetId == RID_CATEGORY_OTHERS)
687         addElements(aOthers, SAL_N_ELEMENTS(aOthers));
688     else if (msCurrentSetId == RID_CATEGORY_EXAMPLES)
689     {
690         OUString aEquation = "C=%pi cdot d = 2 cdot %pi cdot r";
691         addElement(aEquation, aEquation, "");
692         aEquation = "E=mc^2";
693         addElement(aEquation, aEquation, "");
694         aEquation = "a^2 + b^2 = c^2";
695         addElement(aEquation, aEquation, "");
696         aEquation = "f ( x ) = sum from { { i = 0 } } to { infinity } { {f^{(i)}(0)} over {i!} x^i}";
697         addElement(aEquation, aEquation, "");
698         aEquation = "f ( x ) = {1} over {%sigma sqrt{2%pi} }func e^-{{(x-%mu)^2} over {2%sigma^2}}";
699         addElement(aEquation, aEquation, "");
700     }
701     LayoutOrPaintContents();
702     Invalidate();
703 }
704 
705 Size SmElementsControl::GetOptimalSize() const
706 {
707     return LogicToPixel(Size(100, 100), MapMode(MapUnit::MapAppFont));
708 }
709 
710 FactoryFunction SmElementsControl::GetUITestFactory() const
711 {
712     return ElementSelectorUIObject::create;
713 }
714 
715 const char* SmElementsDockingWindow::aCategories[] = {
716     RID_CATEGORY_UNARY_BINARY_OPERATORS,
717     RID_CATEGORY_RELATIONS,
718     RID_CATEGORY_SET_OPERATIONS,
719     RID_CATEGORY_FUNCTIONS,
720     RID_CATEGORY_OPERATORS,
721     RID_CATEGORY_ATTRIBUTES,
722     RID_CATEGORY_BRACKETS,
723     RID_CATEGORY_FORMATS,
724     RID_CATEGORY_OTHERS,
725     RID_CATEGORY_EXAMPLES
726 };
727 
728 SmElementsDockingWindow::SmElementsDockingWindow(SfxBindings* pInputBindings, SfxChildWindow* pChildWindow, vcl::Window* pParent) :
729     SfxDockingWindow(pInputBindings, pChildWindow, pParent, "DockingElements",
730         "modules/smath/ui/dockingelements.ui")
731 {
732     mpElementsControl = VclPtr<SmElementsControl>::Create(get<vcl::Window>("box"));
733     mpElementsControl->set_hexpand(true);
734     mpElementsControl->set_vexpand(true);
735     mpElementsControl->Show();
736     get(mpElementListBox, "listbox");
737 
738     mpElementsControl->SetBorderStyle( WindowBorderStyle::MONO );
739 
740     mpElementListBox->SetDropDownLineCount( SAL_N_ELEMENTS(aCategories) );
741 
742     for (const char* pCategory : aCategories)
743     {
744         mpElementListBox->InsertEntry(SmResId(pCategory));
745     }
746 
747     mpElementListBox->SetSelectHdl(LINK(this, SmElementsDockingWindow, ElementSelectedHandle));
748     mpElementListBox->SelectEntry(SmResId(RID_CATEGORY_UNARY_BINARY_OPERATORS));
749 
750     mpElementsControl->SetBackground( COL_WHITE );
751     mpElementsControl->SetTextColor( COL_BLACK );
752     mpElementsControl->setElementSetId(RID_CATEGORY_UNARY_BINARY_OPERATORS);
753     mpElementsControl->SetSelectHdl(LINK(this, SmElementsDockingWindow, SelectClickHandler));
754 }
755 
756 SmElementsDockingWindow::~SmElementsDockingWindow ()
757 {
758     disposeOnce();
759 }
760 
761 void SmElementsDockingWindow::dispose()
762 {
763     mpElementsControl.disposeAndClear();
764     mpElementListBox.clear();
765     SfxDockingWindow::dispose();
766 }
767 
768 void SmElementsDockingWindow::ToggleFloatingMode()
769 {
770     SfxDockingWindow::ToggleFloatingMode();
771 
772     if (GetFloatingWindow())
773         GetFloatingWindow()->SetMinOutputSizePixel( Size(100, 100) );
774 
775     Invalidate();
776 }
777 
778 void SmElementsDockingWindow::EndDocking( const tools::Rectangle& rReactangle, bool bFloatMode)
779 {
780     SfxDockingWindow::EndDocking(rReactangle, bFloatMode);
781     bool bVertical = ( GetAlignment() == SfxChildAlignment::TOP || GetAlignment() == SfxChildAlignment::BOTTOM );
782     mpElementsControl->setVerticalMode(bVertical);
783 }
784 
785 IMPL_LINK(SmElementsDockingWindow, SelectClickHandler, SmElement&, rElement, void)
786 {
787     SmViewShell* pViewSh = GetView();
788 
789     if (pViewSh)
790     {
791         std::unique_ptr<SfxStringItem> pInsertCommand = std::make_unique<SfxStringItem>(SID_INSERTCOMMANDTEXT, rElement.getText());
792         pViewSh->GetViewFrame()->GetDispatcher()->ExecuteList(
793             SID_INSERTCOMMANDTEXT, SfxCallMode::RECORD,
794             { pInsertCommand.get() });
795     }
796 }
797 
798 IMPL_LINK( SmElementsDockingWindow, ElementSelectedHandle, ListBox&, rList, void)
799 {
800     for (const char* pCurrentCategory : aCategories)
801     {
802         OUString aCurrentCategoryString = SmResId(pCurrentCategory);
803         if (aCurrentCategoryString == rList.GetSelectedEntry())
804         {
805             mpElementsControl->setElementSetId(pCurrentCategory);
806             return;
807         }
808     }
809 }
810 
811 SmViewShell* SmElementsDockingWindow::GetView()
812 {
813     SfxViewShell* pView = GetBindings().GetDispatcher()->GetFrame()->GetViewShell();
814     return  dynamic_cast<SmViewShell*>( pView);
815 }
816 
817 void SmElementsDockingWindow::Resize()
818 {
819     bool bVertical = ( GetAlignment() == SfxChildAlignment::TOP || GetAlignment() == SfxChildAlignment::BOTTOM );
820     mpElementsControl->setVerticalMode(bVertical);
821 
822     SfxDockingWindow::Resize();
823     Invalidate();
824 }
825 
826 SFX_IMPL_DOCKINGWINDOW_WITHID(SmElementsDockingWindowWrapper, SID_ELEMENTSDOCKINGWINDOW);
827 
828 SmElementsDockingWindowWrapper::SmElementsDockingWindowWrapper(
829                             vcl::Window *pParentWindow, sal_uInt16 nId,
830                             SfxBindings *pBindings, SfxChildWinInfo *pInfo) :
831     SfxChildWindow(pParentWindow, nId)
832 {
833     VclPtrInstance<SmElementsDockingWindow> pDialog(pBindings, this, pParentWindow);
834     SetWindow(pDialog);
835     pDialog->setDeferredProperties();
836     pDialog->SetPosSizePixel(Point(0, 0), Size(300, 0));
837     pDialog->Show();
838 
839     SetAlignment(SfxChildAlignment::LEFT);
840 
841     pDialog->Initialize( pInfo );
842 }
843 
844 SmElementsDockingWindowWrapper::~SmElementsDockingWindowWrapper()
845 {
846 }
847 
848 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
849