1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
27 #include <vcl/textrectinfo.hxx>
28 #include <vcl/metric.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/wrkwin.hxx>
31 #include <vcl/fixed.hxx>
32 #include <vcl/vclmedit.hxx>
33 #include <vcl/msgbox.hxx>
34 #include <vcl/button.hxx>
35 #include <vcl/mnemonic.hxx>
36 #include <vcl/settings.hxx>
38 static void ImplInitMsgBoxImageList()
40 ImplSVData
* pSVData
= ImplGetSVData();
41 if ( !pSVData
->maWinData
.mpMsgBoxImgList
)
43 ResMgr
* pResMgr
= ImplGetResMgr();
44 pSVData
->maWinData
.mpMsgBoxImgList
= new ImageList();
47 Color
aNonAlphaMask( 0xC0, 0xC0, 0xC0 );
48 pSVData
->maWinData
.mpMsgBoxImgList
->InsertFromHorizontalBitmap
49 ( ResId( SV_RESID_BITMAP_MSGBOX
, *pResMgr
), 4, &aNonAlphaMask
);
54 void MessBox::ImplInitButtons()
56 WinBits nStyle
= GetStyle();
57 ButtonDialogFlags nOKFlags
= ButtonDialogFlags::OK
;
58 ButtonDialogFlags nCancelFlags
= ButtonDialogFlags::Cancel
;
59 ButtonDialogFlags nRetryFlags
= ButtonDialogFlags::NONE
;
60 ButtonDialogFlags nYesFlags
= ButtonDialogFlags::NONE
;
61 ButtonDialogFlags nNoFlags
= ButtonDialogFlags::NONE
;
63 if ( nStyle
& WB_OK_CANCEL
)
65 if ( nStyle
& WB_DEF_CANCEL
)
66 nCancelFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
68 nOKFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
70 AddButton( StandardButtonType::OK
, RET_OK
, nOKFlags
);
71 AddButton( StandardButtonType::Cancel
, RET_CANCEL
, nCancelFlags
);
73 else if ( nStyle
& WB_YES_NO
)
75 if ( nStyle
& WB_DEF_YES
)
76 nYesFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
78 nNoFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
79 nNoFlags
|= ButtonDialogFlags::Cancel
;
81 AddButton( StandardButtonType::Yes
, RET_YES
, nYesFlags
);
82 AddButton( StandardButtonType::No
, RET_NO
, nNoFlags
);
84 else if ( nStyle
& WB_YES_NO_CANCEL
)
86 if ( nStyle
& WB_DEF_YES
)
87 nYesFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
88 else if ( nStyle
& WB_DEF_NO
)
89 nNoFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
91 nCancelFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
93 AddButton( StandardButtonType::Yes
, RET_YES
, nYesFlags
);
94 AddButton( StandardButtonType::No
, RET_NO
, nNoFlags
);
95 AddButton( StandardButtonType::Cancel
, RET_CANCEL
, nCancelFlags
);
97 else if ( nStyle
& WB_RETRY_CANCEL
)
99 if ( nStyle
& WB_DEF_CANCEL
)
100 nCancelFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
102 nRetryFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
104 AddButton( StandardButtonType::Retry
, RET_RETRY
, nRetryFlags
);
105 AddButton( StandardButtonType::Cancel
, RET_CANCEL
, nCancelFlags
);
107 else if ( nStyle
& WB_ABORT_RETRY_IGNORE
)
109 ButtonDialogFlags nAbortFlags
= ButtonDialogFlags::NONE
;
110 ButtonDialogFlags nIgnoreFlags
= ButtonDialogFlags::NONE
;
112 if ( nStyle
& WB_DEF_CANCEL
)
113 nAbortFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
114 else if ( nStyle
& WB_DEF_RETRY
)
115 nRetryFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
116 else if ( nStyle
& WB_DEF_IGNORE
)
117 nIgnoreFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
119 AddButton( StandardButtonType::Abort
, RET_CANCEL
, nAbortFlags
);
120 AddButton( StandardButtonType::Retry
, RET_RETRY
, nRetryFlags
);
121 AddButton( StandardButtonType::Ignore
, RET_IGNORE
, nIgnoreFlags
);
123 else if ( nStyle
& WB_OK
)
125 nOKFlags
|= ButtonDialogFlags::Default
| ButtonDialogFlags::Focus
;
127 AddButton( StandardButtonType::OK
, RET_OK
, nOKFlags
);
131 MessBox::MessBox( vcl::Window
* pParent
, WinBits nStyle
,
132 const OUString
& rTitle
, const OUString
& rMessage
) :
133 ButtonDialog( WINDOW_MESSBOX
),
136 maMessText( rMessage
)
138 ImplLOKNotifier(pParent
);
139 ImplInit( pParent
, nStyle
| WB_MOVEABLE
| WB_HORZ
| WB_CENTER
);
142 if ( !rTitle
.isEmpty() )
151 void MessBox::dispose()
153 mpVCLMultiLineEdit
.disposeAndClear();
154 mpFixedImage
.disposeAndClear();
155 mpCheckBox
.disposeAndClear();
156 ButtonDialog::dispose();
159 void MessBox::ImplPosControls()
161 if ( !GetHelpId().isEmpty() )
165 AddButton( StandardButtonType::Help
, RET_HELP
, ButtonDialogFlags::Help
, 3 );
173 RemoveButton( RET_HELP
);
178 TextRectInfo aTextInfo
;
179 Rectangle
aRect( 0, 0, 30000, 30000 );
180 Rectangle aFormatRect
;
181 Point
aTextPos( IMPL_DIALOG_OFFSET
, IMPL_DIALOG_OFFSET
+IMPL_MSGBOX_OFFSET_EXTRA_Y
);
186 long nButtonSize
= ImplGetButtonSize();
187 long nMaxWidth
= GetDesktopRectPixel().GetWidth()-8;
190 WinBits nWinStyle
= WB_LEFT
| WB_NOLABEL
;
191 DrawTextFlags nTextStyle
= DrawTextFlags::MultiLine
| DrawTextFlags::Top
| DrawTextFlags::Left
;
193 mpVCLMultiLineEdit
.disposeAndClear();
194 mpFixedImage
.disposeAndClear();
197 mbCheck
= mpCheckBox
->IsChecked();
198 mpCheckBox
.disposeAndClear();
201 // Clean up message text with tabs
202 OUString
aMessText(maMessText
.replaceAll("\t", " "));
204 //If window too small, we make dialog box be wider
205 if ( mpWindowImpl
->mbFrame
)
207 nMaxWidth
= 630 * GetDPIScaleFactor();
209 else if ( nMaxWidth
< 120 )
212 nMaxWidth
-= mpWindowImpl
->mnLeftBorder
+mpWindowImpl
->mnRightBorder
+4;
214 // MessagBox should be at least as wide as to see the title
215 // Extra-Width for Close button, because Close button is set after this call
216 nTitleWidth
= CalcTitleWidth();
217 nTitleWidth
+= mpWindowImpl
->mnTopBorder
;
219 nMaxWidth
-= (IMPL_DIALOG_OFFSET
*2)+(IMPL_MSGBOX_OFFSET_EXTRA_X
*2);
221 // for an image, get its size, create a suitable control and position it
222 aImageSize
= maImage
.GetSizePixel();
223 if ( aImageSize
.Width() )
225 aImageSize
.Width() += 4;
226 aImageSize
.Height() += 4;
227 aTextPos
.X() += aImageSize
.Width()+IMPL_SEP_MSGBOX_IMAGE
;
228 mpFixedImage
= VclPtr
<FixedImage
>::Create( this );
229 mpFixedImage
->SetPosSizePixel( Point( IMPL_DIALOG_OFFSET
-2+IMPL_MSGBOX_OFFSET_EXTRA_X
,
230 IMPL_DIALOG_OFFSET
-2+IMPL_MSGBOX_OFFSET_EXTRA_Y
),
232 mpFixedImage
->SetImage( maImage
);
233 mpFixedImage
->Show();
234 nMaxWidth
-= aImageSize
.Width()+IMPL_SEP_MSGBOX_IMAGE
;
237 aTextPos
.X() += IMPL_MSGBOX_OFFSET_EXTRA_X
;
239 // Determine maximum line length without wordbreak
240 aFormatRect
= GetTextRect( aRect
, aMessText
, nTextStyle
, &aTextInfo
);
241 nMaxLineWidth
= aFormatRect
.GetWidth();
242 nTextStyle
|= DrawTextFlags::WordBreak
;
244 // Determine the width for text formatting
245 if ( nMaxLineWidth
> 450 )
247 else if ( nMaxLineWidth
> 300 )
248 nWidth
= nMaxLineWidth
+5;
252 nWidth
*= GetDPIScaleFactor();
254 if ( nButtonSize
> nWidth
)
255 nWidth
= nButtonSize
-(aTextPos
.X()-IMPL_DIALOG_OFFSET
);
256 if ( nWidth
> nMaxWidth
)
259 aRect
.Right() = nWidth
;
260 aFormatRect
= GetTextRect( aRect
, aMessText
, nTextStyle
, &aTextInfo
);
261 if ( aTextInfo
.GetMaxLineWidth() > nWidth
)
263 nWidth
= aTextInfo
.GetMaxLineWidth()+8;
264 aRect
.Right() = nWidth
;
265 aFormatRect
= GetTextRect( aRect
, aMessText
, nTextStyle
, &aTextInfo
);
268 // get Style for VCLMultiLineEdit
269 aMEditSize
.Width() = aTextInfo
.GetMaxLineWidth()+1;
270 aMEditSize
.Height() = aFormatRect
.GetHeight();
271 aPageSize
.Width() = aImageSize
.Width();
272 if ( aMEditSize
.Height() < aImageSize
.Height() )
274 nWinStyle
|= WB_VCENTER
;
275 aPageSize
.Height() = aImageSize
.Height();
276 aMEditSize
.Height() = aImageSize
.Height();
281 aPageSize
.Height() = aMEditSize
.Height();
283 if ( aImageSize
.Width() )
284 aPageSize
.Width() += IMPL_SEP_MSGBOX_IMAGE
;
285 aPageSize
.Width() += (IMPL_DIALOG_OFFSET
*2)+(IMPL_MSGBOX_OFFSET_EXTRA_X
*2);
286 aPageSize
.Width() += aMEditSize
.Width()+1;
287 aPageSize
.Height() += (IMPL_DIALOG_OFFSET
*2)+(IMPL_MSGBOX_OFFSET_EXTRA_Y
*2);
289 if ( aPageSize
.Width() < IMPL_MINSIZE_MSGBOX_WIDTH
)
290 aPageSize
.Width() = IMPL_MINSIZE_MSGBOX_WIDTH
;
291 if ( aPageSize
.Width() < nTitleWidth
)
292 aPageSize
.Width() = nTitleWidth
;
294 if ( !maCheckBoxText
.isEmpty() )
296 Size
aMinCheckboxSize ( aMEditSize
);
297 if ( aPageSize
.Width() < IMPL_MINSIZE_MSGBOX_WIDTH
+80 )
299 aPageSize
.Width() = IMPL_MINSIZE_MSGBOX_WIDTH
+80;
300 aMinCheckboxSize
.Width() += 80;
303 // #104492# auto mnemonics for CJK strings may increase the length, so measure the
304 // checkbox length including a temporary mnemonic, the correct auto mnemonic will be
305 // generated later in the dialog (see init_show)
307 OUString
aMnemonicString( maCheckBoxText
);
308 if( GetSettings().GetStyleSettings().GetAutoMnemonic() )
310 if( aMnemonicString
== GetNonMnemonicString( maCheckBoxText
) )
312 // no mnemonic found -> create one
313 MnemonicGenerator aMnemonicGenerator
;
314 aMnemonicString
= aMnemonicGenerator
.CreateMnemonic( aMnemonicString
);
318 mpCheckBox
= VclPtr
<CheckBox
>::Create( this );
319 mpCheckBox
->Check( mbCheck
);
320 mpCheckBox
->SetText( aMnemonicString
);
321 mpCheckBox
->SetStyle( mpCheckBox
->GetStyle() | WB_WORDBREAK
);
322 mpCheckBox
->SetHelpId( GetHelpId() ); // DR: Check box and dialog have same HID
324 // align checkbox with message text
325 Size aSize
= mpCheckBox
->CalcMinimumSize( aMinCheckboxSize
.Width() );
327 // now set the original non-mnemonic string
328 mpCheckBox
->SetText( maCheckBoxText
);
330 Point
aPos( aTextPos
);
331 aPos
.Y() += aMEditSize
.Height() + (IMPL_DIALOG_OFFSET
)+(IMPL_MSGBOX_OFFSET_EXTRA_Y
*2);
333 // increase messagebox
334 aPageSize
.Height() += aSize
.Height() + (IMPL_DIALOG_OFFSET
*2)+(IMPL_MSGBOX_OFFSET_EXTRA_Y
*2);
336 mpCheckBox
->SetPosSizePixel( aPos
, aSize
);
340 mpVCLMultiLineEdit
= VclPtr
<VclMultiLineEdit
>::Create( this, nWinStyle
);
341 mpVCLMultiLineEdit
->SetText( aMessText
);
342 mpVCLMultiLineEdit
->SetPosSizePixel( aTextPos
, aMEditSize
);
343 mpVCLMultiLineEdit
->Show();
344 mpVCLMultiLineEdit
->SetPaintTransparent(true);
345 mpVCLMultiLineEdit
->EnableCursor(false);
346 SetPageSizePixel( aPageSize
);
349 void MessBox::StateChanged( StateChangedType nType
)
351 if ( nType
== StateChangedType::InitShow
)
355 ButtonDialog::StateChanged( nType
);
358 bool MessBox::GetCheckBoxState() const
360 return mpCheckBox
? mpCheckBox
->IsChecked() : mbCheck
;
363 void MessBox::SetCheckBoxState( bool bCheck
)
365 if( mpCheckBox
) mpCheckBox
->Check( bCheck
);
369 Size
MessBox::GetOptimalSize() const
371 // FIXME: base me on the font size ?
372 return Size( 250, 100 );
375 InfoBox::InfoBox( vcl::Window
* pParent
, const OUString
& rMessage
) :
376 MessBox( pParent
, WB_OK
| WB_DEF_OK
, OUString(), rMessage
)
378 // Default Text is the display title from the application
379 if ( GetText().isEmpty() )
380 SetText( Application::GetDisplayName() );
382 SetImage( InfoBox::GetStandardImage() );
385 InfoBox::InfoBox( vcl::Window
* pParent
, WinBits nStyle
, const OUString
& rMessage
) :
386 MessBox( pParent
, nStyle
, OUString(), rMessage
)
388 // Default Text is the display title from the application
389 if ( GetText().isEmpty() )
390 SetText( Application::GetDisplayName() );
392 SetImage( InfoBox::GetStandardImage() );
395 Image
InfoBox::GetStandardImage()
397 ImplInitMsgBoxImageList();
398 return ImplGetSVData()->maWinData
.mpMsgBoxImgList
->GetImage( 4 );
401 WarningBox::WarningBox( vcl::Window
* pParent
, WinBits nStyle
,
402 const OUString
& rMessage
) :
403 MessBox( pParent
, nStyle
, OUString(), rMessage
)
405 // Default Text is the display title from the application
406 if ( GetText().isEmpty() )
407 SetText( Application::GetDisplayName() );
409 SetImage( WarningBox::GetStandardImage() );
412 void WarningBox::SetDefaultCheckBoxText()
414 ResMgr
* pResMgr
= ImplGetResMgr();
416 maCheckBoxText
= ResId(SV_STDTEXT_DONTWARNAGAIN
, *pResMgr
).toString();
419 Image
WarningBox::GetStandardImage()
421 ImplInitMsgBoxImageList();
422 return ImplGetSVData()->maWinData
.mpMsgBoxImgList
->GetImage( 3 );
425 ErrorBox::ErrorBox( vcl::Window
* pParent
, WinBits nStyle
,
426 const OUString
& rMessage
) :
427 MessBox( pParent
, nStyle
, OUString(), rMessage
)
429 // Default Text is the display title from the application
430 if ( GetText().isEmpty() )
431 SetText( Application::GetDisplayName() );
433 SetImage( ErrorBox::GetStandardImage() );
436 Image
ErrorBox::GetStandardImage()
440 ImplInitMsgBoxImageList();
442 catch (const css::uno::Exception
&)
444 // During early bootstrap we can have no initialized
445 // ucb and hence no ability to get this image, so nop.
448 return ImplGetSVData()->maWinData
.mpMsgBoxImgList
->GetImage( 1 );
451 QueryBox::QueryBox( vcl::Window
* pParent
, WinBits nStyle
, const OUString
& rMessage
) :
452 MessBox( pParent
, nStyle
, OUString(), rMessage
)
454 // Default Text is the display title from the application
455 if ( GetText().isEmpty() )
456 SetText( Application::GetDisplayName() );
458 SetImage( QueryBox::GetStandardImage() );
461 void QueryBox::SetDefaultCheckBoxText()
463 ResMgr
* pResMgr
= ImplGetResMgr();
465 maCheckBoxText
= ResId(SV_STDTEXT_DONTASKAGAIN
, *pResMgr
).toString();
468 Image
QueryBox::GetStandardImage()
470 ImplInitMsgBoxImageList();
471 return ImplGetSVData()->maWinData
.mpMsgBoxImgList
->GetImage( 2 );
474 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */