blob: ffdc140b0410e60ca1254e7739c7f072423b604e [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_fpicker.hxx"
//------------------------------------------------------------------------
// includes
//------------------------------------------------------------------------
#include "filepickerstate.hxx"
#include <osl/diagnose.h>
#include "controlaccess.hxx"
#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
#include <com/sun/star/ui/dialogs/ListBoxControlActions.hpp>
#include <com/sun/star/ui/dialogs/ControlActions.hpp>
#include "controlcommandrequest.hxx"
#include "controlcommandresult.hxx"
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/XInterface.hpp>
#include <osl/file.hxx>
#include "FileOpenDlg.hxx"
#include <memory>
#include "..\misc\WinImplHelper.hxx"
//---------------------------------------------
//
//---------------------------------------------
using rtl::OUString;
using com::sun::star::uno::Any;
using com::sun::star::uno::Sequence;
using com::sun::star::uno::Reference;
using com::sun::star::uno::XInterface;
using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
using namespace ::com::sun::star::ui::dialogs::ListboxControlActions;
//---------------------------------------------
//
//---------------------------------------------
const sal_Int32 MAX_LABEL = 256;
const sal_Int16 LISTBOX_LABEL_OFFSET = 100;
//---------------------------------------------
// declaration
//---------------------------------------------
CFilePickerState::~CFilePickerState( )
{
}
//---------------------------------------------
//
//---------------------------------------------
CNonExecuteFilePickerState::CNonExecuteFilePickerState( ) :
m_FirstControlCommand( NULL )
{
}
//---------------------------------------------
//
//---------------------------------------------
CNonExecuteFilePickerState::~CNonExecuteFilePickerState( )
{
reset( );
}
//---------------------------------------------
//
//---------------------------------------------
void SAL_CALL CNonExecuteFilePickerState::setValue( sal_Int16 aControlId, sal_Int16 aControlAction, const Any& aValue )
{
CValueControlCommand* value_command = new CValueControlCommand(
aControlId, aControlAction, aValue );
addControlCommand( value_command );
}
//---------------------------------------------
//
//---------------------------------------------
Any SAL_CALL CNonExecuteFilePickerState::getValue( sal_Int16 aControlId, sal_Int16 aControlAction )
{
CValueControlCommandRequest value_request( aControlId, aControlAction );
Any aAny;
if (m_FirstControlCommand)
{
// pass the request along the command-chain
std::auto_ptr< CControlCommandResult > result( m_FirstControlCommand->handleRequest( &value_request ) );
OSL_ENSURE( result.get(), "invalid getValue request" );
if ( result.get() )
{
// #101753 must remove assertion else running into deadlock
// because getValue may be called asynchronously from main thread
// with locked SOLAR_MUTEX but we also need SOLAR_MUTEX in
// WinFileOpenDialog::onInitDone ... but we cannot dismiss the
// assertion dialog because at this point the FileOpen Dialog
// has aleady the focus but is not yet visible :-(
// The real cure is to remove the VCL/SOLAR_MUTEX dependency
// cause by the use of our resource manager and not being able to
// generate native windows resources
//OSL_ENSURE( result->hasResult( ), "invalid getValue request" );
if ( result->hasResult( ) )
{
CValueCommandResult* value_result = dynamic_cast< CValueCommandResult* >( result.get( ) );
OSL_ENSURE( value_result, "should have be a CValueCommandResult" );
aAny = value_result->getValue( );
OSL_ENSURE( aAny.hasValue( ), "empty any" );
}
}
}
return aAny;
}
//---------------------------------------------
//
//---------------------------------------------
void SAL_CALL CNonExecuteFilePickerState::enableControl( sal_Int16 aControlId, sal_Bool bEnable )
{
CEnableControlCommand* enable_command = new CEnableControlCommand(
aControlId, bEnable );
addControlCommand( enable_command );
}
//---------------------------------------------
//
//---------------------------------------------
void SAL_CALL CNonExecuteFilePickerState::setLabel( sal_Int16 aControlId, const ::rtl::OUString& aLabel )
{
CLabelControlCommand* label_command = new CLabelControlCommand(
aControlId, aLabel );
addControlCommand( label_command );
}
//---------------------------------------------
//
//---------------------------------------------
OUString SAL_CALL CNonExecuteFilePickerState::getLabel( sal_Int16 aControlId )
{
CControlCommandRequest label_request( aControlId );
// pass the request along the command-chain
std::auto_ptr< CControlCommandResult > result( m_FirstControlCommand->handleRequest( &label_request ) );
OSL_ENSURE( result->hasResult( ), "invalid getValue request" );
OUString aLabel;
if ( result->hasResult( ) )
{
CLabelCommandResult* label_result = dynamic_cast< CLabelCommandResult* >( result.get( ) );
OSL_ENSURE( label_result, "should have be a CLabelCommandResult" );
aLabel = label_result->getLabel( );
}
return aLabel;
}
/* #i26224#
When typing file names with drive letter but without '\'
in the "File name" box of the FileOpen dialog the FileOpen
dialog makes strange paths out of them e.g. "d:.\test.sxw".
Such file names will not be accepted by sal so we fix these
somehow broken paths here. */
OUString MatchFixBrokenPath(const OUString& path)
{
OSL_ASSERT(path.getLength() >= 4);
if (path[1] == ':' && path[2] == '.' && path[3] == '\\')
{
// skip the '.'
return (path.copy(0,2) + path.copy(3));
}
return path;
}
//-----------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------
static ::rtl::OUString trimTrailingSpaces(const ::rtl::OUString& rString)
{
rtl::OUString aResult(rString);
sal_Int32 nIndex = rString.lastIndexOf(' ');
if (nIndex == rString.getLength()-1)
{
while (nIndex >= 0 && rString[nIndex] == ' ')
nIndex--;
if (nIndex >= 0)
aResult = rString.copy(0,nIndex+1);
else
aResult = ::rtl::OUString();
}
return aResult;
}
Sequence< OUString > SAL_CALL CNonExecuteFilePickerState::getFiles( CFileOpenDialog* aFileOpenDialog )
{
OSL_PRECOND( aFileOpenDialog, "invalid parameter" );
Sequence< OUString > aFilePathList;
OUString aFilePathURL;
OUString aFilePath;
::osl::FileBase::RC rc;
aFilePath = aFileOpenDialog->getFullFileName( );
if ( aFilePath.getLength( ) )
{
// tokenize the returned string and copy the
// sub-strings separately into a sequence
const sal_Unicode* pTemp = aFilePath.getStr();
const sal_Unicode* pStrEnd = pTemp + aFilePath.getLength();
sal_uInt32 lSubStr;
while (pTemp < pStrEnd)
{
// detect the length of the next sub string
lSubStr = rtl_ustr_getLength(pTemp);
aFilePathList.realloc(aFilePathList.getLength() + 1);
aFilePathList[aFilePathList.getLength() - 1] =
MatchFixBrokenPath(OUString(pTemp, lSubStr));
pTemp += (lSubStr + 1);
}
// change all entries to file URLs
sal_Int32 lenFileList = aFilePathList.getLength( );
OSL_ASSERT( lenFileList >= 1 );
for ( sal_Int32 i = 0; i < lenFileList; i++ )
{
aFilePath = trimTrailingSpaces(aFilePathList[i]);
rc = ::osl::FileBase::getFileURLFromSystemPath(
aFilePath, aFilePathURL );
// we do return all or nothing, that means
// in case of failures we destroy the sequence
// and return an empty sequence
if ( rc != ::osl::FileBase::E_None )
{
aFilePathList.realloc( 0 );
break;
}
aFilePathList[i] = aFilePathURL;
}
}
return aFilePathList;
}
//---------------------------------------------
//
//---------------------------------------------
OUString SAL_CALL CNonExecuteFilePickerState::getDisplayDirectory( CFileOpenDialog* aFileOpenDialog )
{
OSL_PRECOND( aFileOpenDialog, "invalid parameter" );
OUString pathURL;
OUString displayDir;
displayDir = aFileOpenDialog->getLastDisplayDirectory( );
if ( displayDir.getLength( ) )
::osl::FileBase::getFileURLFromSystemPath( displayDir, pathURL );
return pathURL;
}
//---------------------------------------------
//
//---------------------------------------------
void SAL_CALL CNonExecuteFilePickerState::reset( )
{
CControlCommand* nextCommand;
CControlCommand* currentCommand = m_FirstControlCommand;
while( currentCommand )
{
nextCommand = currentCommand->getNextCommand( );
delete currentCommand;
currentCommand = nextCommand;
}
m_FirstControlCommand = NULL;
}
//---------------------------------------------
//
//---------------------------------------------
CControlCommand* SAL_CALL CNonExecuteFilePickerState::getControlCommand( ) const
{
return m_FirstControlCommand;
}
//---------------------------------------------
// we append new control commands to the end
// of the list so that we are sure the commands
// will be executed as the client issued it
//---------------------------------------------
void SAL_CALL CNonExecuteFilePickerState::addControlCommand( CControlCommand* aControlCommand )
{
OSL_ASSERT( aControlCommand );
if ( NULL == m_FirstControlCommand )
{
m_FirstControlCommand = aControlCommand;
}
else
{
CControlCommand* pNextControlCommand = m_FirstControlCommand;
while ( pNextControlCommand->getNextCommand( ) != NULL )
pNextControlCommand = pNextControlCommand->getNextCommand( );
pNextControlCommand->setNextCommand( aControlCommand );
}
}
//#######################################################################
//---------------------------------------------
//
//---------------------------------------------
CExecuteFilePickerState::CExecuteFilePickerState( HWND hwndDlg ) :
m_hwndDlg( hwndDlg )
{
}
//---------------------------------------------
//
//---------------------------------------------
void SAL_CALL CExecuteFilePickerState::setValue( sal_Int16 aControlId, sal_Int16 aControlAction, const Any& aValue )
{
// we do not support SET_HELP_URL/GET_HELP_URL
if ( com::sun::star::ui::dialogs::ControlActions::SET_HELP_URL == aControlAction )
return;
HWND hwndCtrl = GetHwndDlgItem( aControlId );
// the filter listbox can be manipulated via this
// method the caller should use XFilterManager
if ( !hwndCtrl || (aControlId == LISTBOX_FILTER) )
{
OSL_ENSURE( sal_False, "invalid control id" );
return;
}
CTRL_CLASS aCtrlClass = GetCtrlClass( hwndCtrl );
if ( UNKNOWN == aCtrlClass )
{
OSL_ENSURE( sal_False, "unsupported control class" );
return;
}
CTRL_SETVALUE_FUNCTION_T lpfnSetValue =
GetCtrlSetValueFunction( aCtrlClass, aControlAction );
if ( !lpfnSetValue )
{
OSL_ENSURE( sal_False, "unsupported control action" );
return;
}
// the function that we call should throw an IllegalArgumentException if
// the given value is invalid or empty, that's why we provide a Reference
// to an XInterface and a argument position
lpfnSetValue( hwndCtrl, aValue, Reference< XInterface >( ), 3 );
}
//---------------------------------------------
//
//---------------------------------------------
Any SAL_CALL CExecuteFilePickerState::getValue( sal_Int16 aControlId, sal_Int16 aControlAction )
{
// we do not support SET_HELP_URL/GET_HELP_URL
if ( com::sun::star::ui::dialogs::ControlActions::GET_HELP_URL == aControlAction )
return Any( );
HWND hwndCtrl = GetHwndDlgItem( aControlId );
// the filter listbox can be manipulated via this
// method the caller should use XFilterManager
if ( !hwndCtrl || (aControlId == LISTBOX_FILTER) )
{
OSL_ENSURE( sal_False, "invalid control id" );
return Any( );
}
CTRL_CLASS aCtrlClass = GetCtrlClass( hwndCtrl );
if ( UNKNOWN == aCtrlClass )
{
OSL_ENSURE( sal_False, "unsupported control class" );
return Any( );
}
CTRL_GETVALUE_FUNCTION_T lpfnGetValue =
GetCtrlGetValueFunction( aCtrlClass, aControlAction );
if ( !lpfnGetValue )
{
OSL_ENSURE( sal_False, "unsupported control action" );
return Any( );
}
return lpfnGetValue( hwndCtrl );
}
//---------------------------------------------
//
//---------------------------------------------
void SAL_CALL CExecuteFilePickerState::enableControl( sal_Int16 aControlId, sal_Bool bEnable )
{
HWND hwndCtrl = GetHwndDlgItem( aControlId );
OSL_ENSURE( IsWindow( hwndCtrl ), "invalid element id");
EnableWindow( hwndCtrl, bEnable );
}
//---------------------------------------------
//
//---------------------------------------------
void SAL_CALL CExecuteFilePickerState::setLabel( sal_Int16 aControlId, const OUString& aLabel )
{
HWND hwndCtrl = GetHwndDlgItem( aControlId );
OSL_ENSURE( IsWindow( hwndCtrl ), "invalid element id");
if ( IsListboxControl( hwndCtrl ) )
hwndCtrl = GetListboxLabelItem( aControlId );
OUString aWinLabel = SOfficeToWindowsLabel( aLabel );
// somewhat risky because we don't know if OUString
// has a terminating '\0'
SetWindowText( hwndCtrl, reinterpret_cast<LPCTSTR>(aWinLabel.getStr( )) );
}
//---------------------------------------------
//
//---------------------------------------------
OUString SAL_CALL CExecuteFilePickerState::getLabel( sal_Int16 aControlId )
{
HWND hwndCtrl = GetHwndDlgItem( aControlId );
OSL_ENSURE( IsWindow( hwndCtrl ), "invalid element id");
if ( IsListboxControl( hwndCtrl ) )
hwndCtrl = GetListboxLabelItem( aControlId );
sal_Unicode aLabel[MAX_LABEL];
int nRet = GetWindowText( hwndCtrl, reinterpret_cast<LPTSTR>(aLabel), MAX_LABEL );
OUString ctrlLabel;
if ( nRet )
{
ctrlLabel = OUString( aLabel, rtl_ustr_getLength( aLabel ) );
ctrlLabel = WindowsToSOfficeLabel( aLabel );
}
return ctrlLabel;
}
//---------------------------------------------
//
//---------------------------------------------
Sequence< OUString > SAL_CALL CExecuteFilePickerState::getFiles( CFileOpenDialog* aFileOpenDialog )
{
OSL_POSTCOND( aFileOpenDialog, "invalid parameter" );
Sequence< OUString > aFilePathList;
OUString aFilePathURL;
OUString aFilePath;
::osl::FileBase::RC rc;
// in execution mode getFullFileName doesn't
// return anything, so we must use another way
// returns the currently selected file(s)
// including path information
aFilePath = aFileOpenDialog->getCurrentFilePath( );
// if multiple files are selected or the user
// typed anything that doesn't seem to be a valid
// file name getFileURLFromSystemPath fails
// and we return an empty file list
rc = ::osl::FileBase::getFileURLFromSystemPath(
aFilePath, aFilePathURL );
if ( ::osl::FileBase::E_None == rc )
{
aFilePathList.realloc( 1 );
aFilePathList[0] = aFilePathURL;
}
return aFilePathList;
}
//---------------------------------------------
//
//---------------------------------------------
OUString SAL_CALL CExecuteFilePickerState::getDisplayDirectory( CFileOpenDialog* aFileOpenDialog )
{
OSL_POSTCOND( aFileOpenDialog, "invalid parameter" );
OUString pathURL;
OUString displayDir;
displayDir = aFileOpenDialog->getCurrentFolderPath( );
if ( displayDir.getLength( ) )
::osl::FileBase::getFileURLFromSystemPath( displayDir, pathURL );
return pathURL;
}
//---------------------------------------------
//
//---------------------------------------------
void SAL_CALL CExecuteFilePickerState::initFilePickerControls( CControlCommand* firstControlCommand )
{
CControlCommand* aControlCommand = firstControlCommand;
while ( aControlCommand )
{
aControlCommand->exec( this );
aControlCommand = aControlCommand->getNextCommand( );
}
}
//---------------------------------------------
//
//---------------------------------------------
void SAL_CALL CExecuteFilePickerState::cacheControlState( HWND hwndControl, CFilePickerState* aNonExecFilePickerState )
{
OSL_ENSURE( hwndControl && aNonExecFilePickerState, "invalid parameters" );
CTRL_CLASS aCtrlClass = GetCtrlClass( hwndControl );
sal_Int16 aControlAction;
CTRL_GETVALUE_FUNCTION_T lpfnGetValue;
if ( CHECKBOX == aCtrlClass )
{
aControlAction = 0;
lpfnGetValue = GetCtrlGetValueFunction( aCtrlClass, aControlAction );
OSL_ASSERT( lpfnGetValue );
aNonExecFilePickerState->setValue(
sal::static_int_cast< sal_Int16 >( GetDlgCtrlID( hwndControl ) ),
aControlAction,
lpfnGetValue( hwndControl ) );
aNonExecFilePickerState->setLabel(
sal::static_int_cast< sal_Int16 >( GetDlgCtrlID( hwndControl ) ),
getLabel(
sal::static_int_cast< sal_Int16 >(
GetDlgCtrlID( hwndControl ) ) ) );
}
else if ( LISTBOX == aCtrlClass )
{
// for listboxes we save only the
// last selected item and the last
// selected item index
aControlAction = GET_SELECTED_ITEM;
lpfnGetValue = GetCtrlGetValueFunction( aCtrlClass, aControlAction );
aNonExecFilePickerState->setValue(
sal::static_int_cast< sal_Int16 >( GetDlgCtrlID( hwndControl ) ),
aControlAction,
lpfnGetValue( hwndControl ) );
aControlAction = ::com::sun::star::ui::dialogs::ControlActions::GET_SELECTED_ITEM_INDEX;
lpfnGetValue = GetCtrlGetValueFunction( aCtrlClass, aControlAction );
aNonExecFilePickerState->setValue(
sal::static_int_cast< sal_Int16 >( GetDlgCtrlID( hwndControl ) ),
aControlAction,
lpfnGetValue( hwndControl ) );
}
}
//---------------------------------------------
//
//---------------------------------------------
void SAL_CALL CExecuteFilePickerState::setHwnd( HWND hwndDlg )
{
m_hwndDlg = hwndDlg;
}
//---------------------------------------------
//
//---------------------------------------------
inline sal_Bool SAL_CALL CExecuteFilePickerState::IsListboxControl( HWND hwndControl ) const
{
OSL_PRECOND( IsWindow( hwndControl ), "invalid parameter" );
CTRL_CLASS aCtrlClass = GetCtrlClass( hwndControl );
return ( LISTBOX == aCtrlClass );
}
//---------------------------------------------
// because listboxes (comboboxes) and their labels
// are seperated we have to translate the listbox
// id to their corresponding label id
// the convention is that the label id of a listbox
// is the id of the listbox + 100
//---------------------------------------------
inline sal_Int16 SAL_CALL CExecuteFilePickerState::ListboxIdToListboxLabelId( sal_Int16 aListboxId ) const
{
return ( aListboxId + LISTBOX_LABEL_OFFSET );
}
//---------------------------------------------
//
//---------------------------------------------
inline HWND SAL_CALL CExecuteFilePickerState::GetListboxLabelItem( sal_Int16 aControlId ) const
{
sal_Int16 aLabelId = ListboxIdToListboxLabelId( aControlId );
HWND hwndCtrl = GetHwndDlgItem( aLabelId );
OSL_ASSERT( IsWindow( hwndCtrl ) );
return hwndCtrl;
}
//---------------------------------------------
//
//---------------------------------------------
HWND SAL_CALL CExecuteFilePickerState::GetHwndDlgItem( sal_Int16 aControlId, sal_Bool bIncludeStdCtrls ) const
{
OSL_ENSURE( IsWindow( m_hwndDlg ), "no valid parent window set before" );
HWND hwndCtrl = GetDlgItem( m_hwndDlg, aControlId );
// maybe it's a control of the dialog itself for instance
// the ok and cancel button
if ( !hwndCtrl && bIncludeStdCtrls )
{
hwndCtrl = GetDlgItem(
GetParent( m_hwndDlg ),
CommonFilePickerCtrlIdToWinFileOpenCtrlId( aControlId ) );
}
return hwndCtrl;
}