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 .
20 #include <boost/scoped_ptr.hpp>
22 #include "filepickerstate.hxx"
23 #include <osl/diagnose.h>
24 #include "controlaccess.hxx"
25 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
26 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
27 #include <com/sun/star/ui/dialogs/ListboxControlActions.hpp>
28 #include <com/sun/star/ui/dialogs/ControlActions.hpp>
29 #include "controlcommandrequest.hxx"
30 #include "controlcommandresult.hxx"
31 #include <com/sun/star/uno/Reference.hxx>
32 #include <com/sun/star/uno/XInterface.hpp>
33 #include <osl/file.hxx>
34 #include "FileOpenDlg.hxx"
36 #include "../misc/WinImplHelper.hxx"
37 //---------------------------------------------
39 //---------------------------------------------
41 using com::sun::star::uno::Any
;
42 using com::sun::star::uno::Sequence
;
43 using com::sun::star::uno::Reference
;
44 using com::sun::star::uno::XInterface
;
46 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds
;
47 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds
;
48 using namespace ::com::sun::star::ui::dialogs::ListboxControlActions
;
50 //---------------------------------------------
52 //---------------------------------------------
54 const sal_Int32 MAX_LABEL
= 256;
55 const sal_Int16 LISTBOX_LABEL_OFFSET
= 100;
57 //---------------------------------------------
59 //---------------------------------------------
61 CFilePickerState::~CFilePickerState( )
65 //---------------------------------------------
67 //---------------------------------------------
69 CNonExecuteFilePickerState::CNonExecuteFilePickerState( ) :
70 m_FirstControlCommand( NULL
)
74 //---------------------------------------------
76 //---------------------------------------------
78 CNonExecuteFilePickerState::~CNonExecuteFilePickerState( )
83 //---------------------------------------------
85 //---------------------------------------------
87 void SAL_CALL
CNonExecuteFilePickerState::setValue( sal_Int16 aControlId
, sal_Int16 aControlAction
, const Any
& aValue
)
89 CValueControlCommand
* value_command
= new CValueControlCommand(
90 aControlId
, aControlAction
, aValue
);
92 addControlCommand( value_command
);
95 //---------------------------------------------
97 //---------------------------------------------
99 Any SAL_CALL
CNonExecuteFilePickerState::getValue( sal_Int16 aControlId
, sal_Int16 aControlAction
)
101 CValueControlCommandRequest
value_request( aControlId
, aControlAction
);
104 if (m_FirstControlCommand
)
106 // pass the request along the command-chain
107 boost::scoped_ptr
< CControlCommandResult
> result( m_FirstControlCommand
->handleRequest( &value_request
) );
109 OSL_ENSURE( result
.get(), "invalid getValue request" );
113 // #101753 must remove assertion else running into deadlock
114 // because getValue may be called asynchronously from main thread
115 // with locked SOLAR_MUTEX but we also need SOLAR_MUTEX in
116 // WinFileOpenDialog::onInitDone ... but we cannot dismiss the
117 // assertion dialog because at this point the FileOpen Dialog
118 // has aleady the focus but is not yet visible :-(
119 // The real cure is to remove the VCL/SOLAR_MUTEX dependency
120 // cause by the use of our resource manager and not being able to
121 // generate native windows resources
122 //OSL_ENSURE( result->hasResult( ), "invalid getValue request" );
124 if ( result
->hasResult( ) )
126 CValueCommandResult
* value_result
= dynamic_cast< CValueCommandResult
* >( result
.get( ) );
127 OSL_ENSURE( value_result
, "should have be a CValueCommandResult" );
129 aAny
= value_result
->getValue( );
130 OSL_ENSURE( aAny
.hasValue( ), "empty any" );
138 //---------------------------------------------
140 //---------------------------------------------
142 void SAL_CALL
CNonExecuteFilePickerState::enableControl( sal_Int16 aControlId
, sal_Bool bEnable
)
144 CEnableControlCommand
* enable_command
= new CEnableControlCommand(
145 aControlId
, bEnable
);
147 addControlCommand( enable_command
);
150 //---------------------------------------------
152 //---------------------------------------------
154 void SAL_CALL
CNonExecuteFilePickerState::setLabel( sal_Int16 aControlId
, const OUString
& aLabel
)
156 CLabelControlCommand
* label_command
= new CLabelControlCommand(
157 aControlId
, aLabel
);
159 addControlCommand( label_command
);
162 //---------------------------------------------
164 //---------------------------------------------
166 OUString SAL_CALL
CNonExecuteFilePickerState::getLabel( sal_Int16 aControlId
)
168 CControlCommandRequest
label_request( aControlId
);
170 // pass the request along the command-chain
171 boost::scoped_ptr
< CControlCommandResult
> result( m_FirstControlCommand
->handleRequest( &label_request
) );
173 OSL_ENSURE( result
->hasResult( ), "invalid getValue request" );
177 if ( result
->hasResult( ) )
179 CLabelCommandResult
* label_result
= dynamic_cast< CLabelCommandResult
* >( result
.get( ) );
180 OSL_ENSURE( label_result
, "should have be a CLabelCommandResult" );
182 aLabel
= label_result
->getLabel( );
189 When typing file names with drive letter but without '\'
190 in the "File name" box of the FileOpen dialog the FileOpen
191 dialog makes strange paths out of them e.g. "d:.\test.sxw".
192 Such file names will not be accepted by sal so we fix these
193 somehow broken paths here. */
194 OUString
MatchFixBrokenPath(const OUString
& path
)
196 OSL_ASSERT(path
.getLength() >= 4);
198 if (path
[1] == ':' && path
[2] == '.' && path
[3] == '\\')
201 return OUString(path
.getStr(), 2) + path
.copy(3, path
.getLength() - 3);
206 //-----------------------------------------------------------------------------------------
208 //-----------------------------------------------------------------------------------------
209 static OUString
trimTrailingSpaces(const OUString
& rString
)
211 OUString
aResult(rString
);
213 sal_Int32 nIndex
= rString
.lastIndexOf(' ');
214 if (nIndex
== rString
.getLength()-1)
216 while (nIndex
>= 0 && rString
[nIndex
] == ' ')
219 aResult
= rString
.copy(0,nIndex
+1);
221 aResult
= OUString();
226 Sequence
< OUString
> SAL_CALL
CNonExecuteFilePickerState::getFiles( CFileOpenDialog
* aFileOpenDialog
)
228 OSL_PRECOND( aFileOpenDialog
, "invalid parameter" );
230 Sequence
< OUString
> aFilePathList
;
231 OUString aFilePathURL
;
233 ::osl::FileBase::RC rc
;
235 aFilePath
= aFileOpenDialog
->getFullFileName( );
237 if ( aFilePath
.getLength( ) )
239 // tokenize the returned string and copy the
240 // sub-strings separately into a sequence
241 const sal_Unicode
* pTemp
= aFilePath
.getStr();
242 const sal_Unicode
* pStrEnd
= pTemp
+ aFilePath
.getLength();
245 while (pTemp
< pStrEnd
)
247 // detect the length of the next sub string
248 lSubStr
= rtl_ustr_getLength(pTemp
);
250 aFilePathList
.realloc(aFilePathList
.getLength() + 1);
252 aFilePathList
[aFilePathList
.getLength() - 1] =
253 MatchFixBrokenPath(OUString(pTemp
, lSubStr
));
255 pTemp
+= (lSubStr
+ 1);
258 // change all entries to file URLs
259 sal_Int32 lenFileList
= aFilePathList
.getLength( );
260 OSL_ASSERT( lenFileList
>= 1 );
262 for ( sal_Int32 i
= 0; i
< lenFileList
; i
++ )
264 aFilePath
= trimTrailingSpaces(aFilePathList
[i
]);
265 rc
= ::osl::FileBase::getFileURLFromSystemPath(
266 aFilePath
, aFilePathURL
);
268 // we do return all or nothing, that means
269 // in case of failures we destroy the sequence
270 // and return an empty sequence
271 if ( rc
!= ::osl::FileBase::E_None
)
273 aFilePathList
.realloc( 0 );
277 aFilePathList
[i
] = aFilePathURL
;
281 return aFilePathList
;
284 //---------------------------------------------
286 //---------------------------------------------
288 OUString SAL_CALL
CNonExecuteFilePickerState::getDisplayDirectory( CFileOpenDialog
* aFileOpenDialog
)
290 OSL_PRECOND( aFileOpenDialog
, "invalid parameter" );
295 displayDir
= aFileOpenDialog
->getLastDisplayDirectory( );
297 if ( displayDir
.getLength( ) )
298 ::osl::FileBase::getFileURLFromSystemPath( displayDir
, pathURL
);
303 //---------------------------------------------
305 //---------------------------------------------
307 void SAL_CALL
CNonExecuteFilePickerState::reset( )
309 CControlCommand
* nextCommand
;
310 CControlCommand
* currentCommand
= m_FirstControlCommand
;
312 while( currentCommand
)
314 nextCommand
= currentCommand
->getNextCommand( );
315 delete currentCommand
;
316 currentCommand
= nextCommand
;
319 m_FirstControlCommand
= NULL
;
322 //---------------------------------------------
324 //---------------------------------------------
326 CControlCommand
* SAL_CALL
CNonExecuteFilePickerState::getControlCommand( ) const
328 return m_FirstControlCommand
;
331 //---------------------------------------------
332 // we append new control commands to the end
333 // of the list so that we are sure the commands
334 // will be executed as the client issued it
335 //---------------------------------------------
337 void SAL_CALL
CNonExecuteFilePickerState::addControlCommand( CControlCommand
* aControlCommand
)
339 OSL_ASSERT( aControlCommand
);
341 if ( NULL
== m_FirstControlCommand
)
343 m_FirstControlCommand
= aControlCommand
;
347 CControlCommand
* pNextControlCommand
= m_FirstControlCommand
;
349 while ( pNextControlCommand
->getNextCommand( ) != NULL
)
350 pNextControlCommand
= pNextControlCommand
->getNextCommand( );
352 pNextControlCommand
->setNextCommand( aControlCommand
);
356 //#######################################################################
358 //---------------------------------------------
360 //---------------------------------------------
362 CExecuteFilePickerState::CExecuteFilePickerState( HWND hwndDlg
) :
367 //---------------------------------------------
369 //---------------------------------------------
371 void SAL_CALL
CExecuteFilePickerState::setValue( sal_Int16 aControlId
, sal_Int16 aControlAction
, const Any
& aValue
)
373 // we do not support SET_HELP_URL/GET_HELP_URL
374 if ( com::sun::star::ui::dialogs::ControlActions::SET_HELP_URL
== aControlAction
)
377 HWND hwndCtrl
= GetHwndDlgItem( aControlId
);
379 // the filter listbox can be manipulated via this
380 // method the caller should use XFilterManager
381 if ( !hwndCtrl
|| (aControlId
== LISTBOX_FILTER
) )
383 OSL_FAIL( "invalid control id" );
387 CTRL_CLASS aCtrlClass
= GetCtrlClass( hwndCtrl
);
388 if ( UNKNOWN
== aCtrlClass
)
390 OSL_FAIL( "unsupported control class" );
394 CTRL_SETVALUE_FUNCTION_T lpfnSetValue
=
395 GetCtrlSetValueFunction( aCtrlClass
, aControlAction
);
399 OSL_FAIL( "unsupported control action" );
403 // the function that we call should throw an IllegalArgumentException if
404 // the given value is invalid or empty, that's why we provide a Reference
405 // to an XInterface and a argument position
406 lpfnSetValue( hwndCtrl
, aValue
, Reference
< XInterface
>( ), 3 );
409 //---------------------------------------------
411 //---------------------------------------------
413 Any SAL_CALL
CExecuteFilePickerState::getValue( sal_Int16 aControlId
, sal_Int16 aControlAction
)
415 // we do not support SET_HELP_URL/GET_HELP_URL
416 if ( com::sun::star::ui::dialogs::ControlActions::GET_HELP_URL
== aControlAction
)
419 HWND hwndCtrl
= GetHwndDlgItem( aControlId
);
421 // the filter listbox can be manipulated via this
422 // method the caller should use XFilterManager
423 if ( !hwndCtrl
|| (aControlId
== LISTBOX_FILTER
) )
425 OSL_FAIL( "invalid control id" );
429 CTRL_CLASS aCtrlClass
= GetCtrlClass( hwndCtrl
);
430 if ( UNKNOWN
== aCtrlClass
)
432 OSL_FAIL( "unsupported control class" );
436 CTRL_GETVALUE_FUNCTION_T lpfnGetValue
=
437 GetCtrlGetValueFunction( aCtrlClass
, aControlAction
);
441 OSL_FAIL( "unsupported control action" );
445 return lpfnGetValue( hwndCtrl
);
448 //---------------------------------------------
450 //---------------------------------------------
452 void SAL_CALL
CExecuteFilePickerState::enableControl( sal_Int16 aControlId
, sal_Bool bEnable
)
454 HWND hwndCtrl
= GetHwndDlgItem( aControlId
);
456 OSL_ENSURE( IsWindow( hwndCtrl
), "invalid element id");
458 EnableWindow( hwndCtrl
, bEnable
);
461 //---------------------------------------------
463 //---------------------------------------------
465 void SAL_CALL
CExecuteFilePickerState::setLabel( sal_Int16 aControlId
, const OUString
& aLabel
)
467 HWND hwndCtrl
= GetHwndDlgItem( aControlId
);
469 OSL_ENSURE( IsWindow( hwndCtrl
), "invalid element id");
471 if ( IsListboxControl( hwndCtrl
) )
472 hwndCtrl
= GetListboxLabelItem( aControlId
);
474 OUString aWinLabel
= SOfficeToWindowsLabel( aLabel
);
476 // somewhat risky because we don't know if OUString
477 // has a terminating '\0'
478 SetWindowText( hwndCtrl
, reinterpret_cast<LPCTSTR
>(aWinLabel
.getStr( )) );
481 //---------------------------------------------
483 //---------------------------------------------
485 OUString SAL_CALL
CExecuteFilePickerState::getLabel( sal_Int16 aControlId
)
487 HWND hwndCtrl
= GetHwndDlgItem( aControlId
);
489 OSL_ENSURE( IsWindow( hwndCtrl
), "invalid element id");
491 if ( IsListboxControl( hwndCtrl
) )
492 hwndCtrl
= GetListboxLabelItem( aControlId
);
494 sal_Unicode aLabel
[MAX_LABEL
];
495 int nRet
= GetWindowText( hwndCtrl
, reinterpret_cast<LPTSTR
>(aLabel
), MAX_LABEL
);
500 ctrlLabel
= WindowsToSOfficeLabel( aLabel
);
506 //---------------------------------------------
508 //---------------------------------------------
510 Sequence
< OUString
> SAL_CALL
CExecuteFilePickerState::getFiles( CFileOpenDialog
* aFileOpenDialog
)
512 OSL_POSTCOND( aFileOpenDialog
, "invalid parameter" );
514 Sequence
< OUString
> aFilePathList
;
515 OUString aFilePathURL
;
517 ::osl::FileBase::RC rc
;
519 // in execution mode getFullFileName doesn't
520 // return anything, so we must use another way
522 // returns the currently selected file(s)
523 // including path information
524 aFilePath
= aFileOpenDialog
->getCurrentFilePath( );
526 // if multiple files are selected or the user
527 // typed anything that doesn't seem to be a valid
528 // file name getFileURLFromSystemPath fails
529 // and we return an empty file list
530 rc
= ::osl::FileBase::getFileURLFromSystemPath(
531 aFilePath
, aFilePathURL
);
533 if ( ::osl::FileBase::E_None
== rc
)
535 aFilePathList
.realloc( 1 );
536 aFilePathList
[0] = aFilePathURL
;
539 return aFilePathList
;
542 //---------------------------------------------
544 //---------------------------------------------
546 OUString SAL_CALL
CExecuteFilePickerState::getDisplayDirectory( CFileOpenDialog
* aFileOpenDialog
)
548 OSL_POSTCOND( aFileOpenDialog
, "invalid parameter" );
553 displayDir
= aFileOpenDialog
->getCurrentFolderPath( );
555 if ( displayDir
.getLength( ) )
556 ::osl::FileBase::getFileURLFromSystemPath( displayDir
, pathURL
);
561 //---------------------------------------------
563 //---------------------------------------------
565 void SAL_CALL
CExecuteFilePickerState::initFilePickerControls( CControlCommand
* firstControlCommand
)
567 CControlCommand
* aControlCommand
= firstControlCommand
;
569 while ( aControlCommand
)
571 aControlCommand
->exec( this );
572 aControlCommand
= aControlCommand
->getNextCommand( );
576 //---------------------------------------------
578 //---------------------------------------------
580 void SAL_CALL
CExecuteFilePickerState::cacheControlState( HWND hwndControl
, CFilePickerState
* aNonExecFilePickerState
)
582 OSL_ENSURE( hwndControl
&& aNonExecFilePickerState
, "invalid parameters" );
584 CTRL_CLASS aCtrlClass
= GetCtrlClass( hwndControl
);
586 sal_Int16 aControlAction
;
587 CTRL_GETVALUE_FUNCTION_T lpfnGetValue
;
589 if ( CHECKBOX
== aCtrlClass
)
593 lpfnGetValue
= GetCtrlGetValueFunction( aCtrlClass
, aControlAction
);
595 OSL_ASSERT( lpfnGetValue
);
597 aNonExecFilePickerState
->setValue(
598 sal::static_int_cast
< sal_Int16
>( GetDlgCtrlID( hwndControl
) ),
600 lpfnGetValue( hwndControl
) );
602 aNonExecFilePickerState
->setLabel(
603 sal::static_int_cast
< sal_Int16
>( GetDlgCtrlID( hwndControl
) ),
605 sal::static_int_cast
< sal_Int16
>(
606 GetDlgCtrlID( hwndControl
) ) ) );
608 else if ( LISTBOX
== aCtrlClass
)
610 // for listboxes we save only the
611 // last selected item and the last
612 // selected item index
614 aControlAction
= GET_SELECTED_ITEM
;
616 lpfnGetValue
= GetCtrlGetValueFunction( aCtrlClass
, aControlAction
);
618 aNonExecFilePickerState
->setValue(
619 sal::static_int_cast
< sal_Int16
>( GetDlgCtrlID( hwndControl
) ),
621 lpfnGetValue( hwndControl
) );
623 aControlAction
= ::com::sun::star::ui::dialogs::ControlActions::GET_SELECTED_ITEM_INDEX
;
625 lpfnGetValue
= GetCtrlGetValueFunction( aCtrlClass
, aControlAction
);
627 aNonExecFilePickerState
->setValue(
628 sal::static_int_cast
< sal_Int16
>( GetDlgCtrlID( hwndControl
) ),
630 lpfnGetValue( hwndControl
) );
634 //---------------------------------------------
636 //---------------------------------------------
638 void SAL_CALL
CExecuteFilePickerState::setHwnd( HWND hwndDlg
)
643 //---------------------------------------------
645 //---------------------------------------------
647 inline sal_Bool SAL_CALL
CExecuteFilePickerState::IsListboxControl( HWND hwndControl
) const
649 OSL_PRECOND( IsWindow( hwndControl
), "invalid parameter" );
651 CTRL_CLASS aCtrlClass
= GetCtrlClass( hwndControl
);
652 return ( LISTBOX
== aCtrlClass
);
655 //---------------------------------------------
656 // because listboxes (comboboxes) and their labels
657 // are separated we have to translate the listbox
658 // id to their corresponding label id
659 // the convention is that the label id of a listbox
660 // is the id of the listbox + 100
661 //---------------------------------------------
663 inline sal_Int16 SAL_CALL
CExecuteFilePickerState::ListboxIdToListboxLabelId( sal_Int16 aListboxId
) const
665 return ( aListboxId
+ LISTBOX_LABEL_OFFSET
);
668 //---------------------------------------------
670 //---------------------------------------------
672 inline HWND SAL_CALL
CExecuteFilePickerState::GetListboxLabelItem( sal_Int16 aControlId
) const
674 sal_Int16 aLabelId
= ListboxIdToListboxLabelId( aControlId
);
675 HWND hwndCtrl
= GetHwndDlgItem( aLabelId
);
677 OSL_ASSERT( IsWindow( hwndCtrl
) );
682 //---------------------------------------------
684 //---------------------------------------------
686 HWND SAL_CALL
CExecuteFilePickerState::GetHwndDlgItem( sal_Int16 aControlId
, sal_Bool bIncludeStdCtrls
) const
688 OSL_ENSURE( IsWindow( m_hwndDlg
), "no valid parent window set before" );
690 HWND hwndCtrl
= GetDlgItem( m_hwndDlg
, aControlId
);
692 // maybe it's a control of the dialog itself for instance
693 // the ok and cancel button
694 if ( !hwndCtrl
&& bIncludeStdCtrls
)
696 hwndCtrl
= GetDlgItem(
697 GetParent( m_hwndDlg
),
698 CommonFilePickerCtrlIdToWinFileOpenCtrlId( aControlId
) );
704 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */