Avoid potential negative array index access to cached text.
[LibreOffice.git] / toolkit / source / helper / btndlg.cxx
bloba2471c3f92cd486d9528fa32b03cf87f11bc0e80
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <vcl/toolkit/button.hxx>
21 #include <vcl/stdtext.hxx>
22 #include <helper/btndlg.hxx>
23 #include <sal/log.hxx>
24 #include <map>
25 #include <memory>
27 struct ImplBtnDlgItem
29 sal_uInt16 mnId;
30 bool mbOwnButton;
31 tools::Long mnSepSize;
32 VclPtr<PushButton> mpPushButton;
34 ImplBtnDlgItem() : mnId(0), mbOwnButton(false), mnSepSize(0) {}
37 void ButtonDialog::ImplInitButtonDialogData()
39 mnButtonSize = 0;
40 mnCurButtonId = 0;
41 mnFocusButtonId = BUTTONDIALOG_BUTTON_NOTFOUND;
42 mbFormat = true;
45 ButtonDialog::ButtonDialog( WindowType nType ) :
46 Dialog( nType )
48 ImplInitButtonDialogData();
51 ButtonDialog::~ButtonDialog()
53 disposeOnce();
56 void ButtonDialog::dispose()
58 for (auto & it : m_ItemList)
60 if ( it->mbOwnButton )
61 it->mpPushButton.disposeAndClear();
63 m_ItemList.clear();
64 Dialog::dispose();
67 VclPtr<PushButton> ButtonDialog::ImplCreatePushButton( ButtonDialogFlags nBtnFlags )
69 VclPtr<PushButton> pBtn;
70 WinBits nStyle = 0;
72 if ( nBtnFlags & ButtonDialogFlags::Default )
73 nStyle |= WB_DEFBUTTON;
74 if ( nBtnFlags & ButtonDialogFlags::Cancel )
75 pBtn = VclPtr<CancelButton>::Create( this, nStyle );
76 else if ( nBtnFlags & ButtonDialogFlags::OK )
77 pBtn = VclPtr<OKButton>::Create( this, nStyle );
78 else if ( nBtnFlags & ButtonDialogFlags::Help )
79 pBtn = VclPtr<HelpButton>::Create( this, nStyle );
80 else
81 pBtn = VclPtr<PushButton>::Create( this, nStyle );
83 if ( !(nBtnFlags & ButtonDialogFlags::Help) )
84 pBtn->SetClickHdl( LINK( this, ButtonDialog, ImplClickHdl ) );
86 return pBtn;
89 tools::Long ButtonDialog::ImplGetButtonSize()
91 if ( !mbFormat )
92 return mnButtonSize;
94 // Calculate ButtonSize
95 tools::Long nLastSepSize = 0;
96 tools::Long nSepSize = 0;
97 maCtrlSize = Size( IMPL_MINSIZE_BUTTON_WIDTH, IMPL_MINSIZE_BUTTON_HEIGHT );
99 for (const auto & it : m_ItemList)
101 nSepSize += nLastSepSize;
103 tools::Long nTxtWidth = it->mpPushButton->GetOutDev()->GetCtrlTextWidth(it->mpPushButton->GetText());
104 nTxtWidth += IMPL_EXTRA_BUTTON_WIDTH;
106 if ( nTxtWidth > maCtrlSize.Width() )
107 maCtrlSize.setWidth( nTxtWidth );
109 tools::Long nTxtHeight = it->mpPushButton->GetTextHeight();
110 nTxtHeight += IMPL_EXTRA_BUTTON_HEIGHT;
112 if ( nTxtHeight > maCtrlSize.Height() )
113 maCtrlSize.setHeight( nTxtHeight );
115 nSepSize += it->mnSepSize;
117 if ( GetStyle() & WB_HORZ )
118 nLastSepSize = IMPL_SEP_BUTTON_X;
119 else
120 nLastSepSize = IMPL_SEP_BUTTON_Y;
123 size_t const nButtonCount = m_ItemList.size();
125 if ( GetStyle() & WB_HORZ )
126 mnButtonSize = nSepSize + (nButtonCount*maCtrlSize.Width());
127 else
128 mnButtonSize = nSepSize + (nButtonCount*maCtrlSize.Height());
130 return mnButtonSize;
133 void ButtonDialog::ImplPosControls()
135 if ( !mbFormat )
136 return;
138 // Create PushButtons and determine Sizes
139 ImplGetButtonSize();
141 // determine dialog size
142 Size aDlgSize = maPageSize;
143 tools::Long nX;
144 tools::Long nY;
145 if ( GetStyle() & WB_HORZ )
147 if ( mnButtonSize+(IMPL_DIALOG_OFFSET*2) > aDlgSize.Width() )
148 aDlgSize.setWidth( mnButtonSize+(IMPL_DIALOG_OFFSET*2) );
149 if ( GetStyle() & WB_LEFT )
150 nX = IMPL_DIALOG_OFFSET;
151 else if ( GetStyle() & WB_RIGHT )
152 nX = aDlgSize.Width()-mnButtonSize-IMPL_DIALOG_OFFSET;
153 else
154 nX = (aDlgSize.Width()-mnButtonSize)/2;
156 aDlgSize.AdjustHeight(IMPL_DIALOG_OFFSET+maCtrlSize.Height() );
157 nY = aDlgSize.Height()-maCtrlSize.Height()-IMPL_DIALOG_OFFSET;
159 else
161 if ( mnButtonSize+(IMPL_DIALOG_OFFSET*2) > aDlgSize.Height() )
162 aDlgSize.setHeight( mnButtonSize+(IMPL_DIALOG_OFFSET*2) );
163 if ( GetStyle() & WB_BOTTOM )
164 nY = aDlgSize.Height()-mnButtonSize-IMPL_DIALOG_OFFSET;
165 else if ( GetStyle() & WB_VCENTER )
166 nY = (aDlgSize.Height()-mnButtonSize)/2;
167 else
168 nY = IMPL_DIALOG_OFFSET;
170 aDlgSize.AdjustWidth(IMPL_DIALOG_OFFSET+maCtrlSize.Width() );
171 nX = aDlgSize.Width()-maCtrlSize.Width()-IMPL_DIALOG_OFFSET;
174 // Arrange PushButtons
175 for (auto & it : m_ItemList)
177 if ( GetStyle() & WB_HORZ )
178 nX += it->mnSepSize;
179 else
180 nY += it->mnSepSize;
182 it->mpPushButton->SetPosSizePixel( Point( nX, nY ), maCtrlSize );
183 it->mpPushButton->Show();
185 if ( GetStyle() & WB_HORZ )
186 nX += maCtrlSize.Width()+IMPL_SEP_BUTTON_X;
187 else
188 nY += maCtrlSize.Height()+IMPL_SEP_BUTTON_Y;
191 SetOutputSizePixel(aDlgSize);
192 SetMinOutputSizePixel(aDlgSize);
194 mbFormat = false;
197 IMPL_LINK( ButtonDialog, ImplClickHdl, Button*, pBtn, void )
199 for (const auto & it : m_ItemList)
201 if ( it->mpPushButton == pBtn )
203 mnCurButtonId = it->mnId;
204 if ( IsInExecute() )
205 EndDialog( mnCurButtonId );
206 break;
211 void ButtonDialog::Resize()
215 void ButtonDialog::StateChanged( StateChangedType nType )
217 if ( nType == StateChangedType::InitShow )
219 ImplPosControls();
220 for (auto & it : m_ItemList)
222 if ( it->mpPushButton && it->mbOwnButton )
223 it->mpPushButton->SetZOrder(nullptr, ZOrderFlags::Last);
226 // Set focus on default button.
227 if ( mnFocusButtonId != BUTTONDIALOG_BUTTON_NOTFOUND )
229 for (auto & it : m_ItemList)
231 if (it->mnId == mnFocusButtonId )
233 if (it->mpPushButton->IsVisible())
234 it->mpPushButton->GrabFocus();
236 break;
242 Dialog::StateChanged( nType );
245 void ButtonDialog::AddButton( StandardButtonType eType, sal_uInt16 nId,
246 ButtonDialogFlags nBtnFlags, tools::Long nSepPixel )
248 // PageItem anlegen
249 std::unique_ptr<ImplBtnDlgItem> pItem(new ImplBtnDlgItem);
250 pItem->mnId = nId;
251 pItem->mbOwnButton = true;
252 pItem->mnSepSize = nSepPixel;
254 if ( eType == StandardButtonType::OK )
255 nBtnFlags |= ButtonDialogFlags::OK;
256 else if ( eType == StandardButtonType::Help )
257 nBtnFlags |= ButtonDialogFlags::Help;
258 else if ( (eType == StandardButtonType::Cancel) || (eType == StandardButtonType::Close) )
259 nBtnFlags |= ButtonDialogFlags::Cancel;
260 pItem->mpPushButton = ImplCreatePushButton( nBtnFlags );
262 // Standard-Buttons have the right text already
263 if ( !((eType == StandardButtonType::OK && pItem->mpPushButton->GetType() == WindowType::OKBUTTON) ||
264 (eType == StandardButtonType::Cancel && pItem->mpPushButton->GetType() == WindowType::CANCELBUTTON) ||
265 (eType == StandardButtonType::Help && pItem->mpPushButton->GetType() == WindowType::HELPBUTTON)) )
267 std::map<StandardButtonType, OUString> mapButtonTypeToID = {{StandardButtonType::Yes, "yes"},
268 {StandardButtonType::No, "no"}, {StandardButtonType::Retry, "retry"},
269 {StandardButtonType::Close, "close"}, {StandardButtonType::More, "more"},
270 {StandardButtonType::Ignore, "ignore"}, {StandardButtonType::Abort, "abort"},
271 {StandardButtonType::Less, "less"}, {StandardButtonType::Count, "count"}};
272 auto itr = mapButtonTypeToID.find(eType);
273 if (itr != mapButtonTypeToID.end())
274 pItem->mpPushButton->set_id(itr->second);
275 pItem->mpPushButton->SetText( GetStandardText( eType ) );
278 if ( nBtnFlags & ButtonDialogFlags::Focus )
279 mnFocusButtonId = nId;
281 m_ItemList.push_back(std::move(pItem));
283 mbFormat = true;
286 void ButtonDialog::RemoveButton( sal_uInt16 nId )
288 auto it = std::find_if(m_ItemList.begin(), m_ItemList.end(),
289 [&nId](const std::unique_ptr<ImplBtnDlgItem>& rItem) { return rItem->mnId == nId; });
290 if (it != m_ItemList.end())
292 (*it)->mpPushButton->Hide();
293 if ((*it)->mbOwnButton)
294 (*it)->mpPushButton.disposeAndClear();
295 else
296 (*it)->mpPushButton.clear();
297 m_ItemList.erase(it);
298 return;
301 SAL_WARN( "vcl.window", "ButtonDialog::RemoveButton(): ButtonId invalid" );
304 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */