#前回のまとめ
btとgdbで、一番のメインっぽい関数を見つけた。
#Main関数を見る
##Main関数のなかみ
int Desktop::Main()
{
pExecGlobals = new ExecuteGlobals();
SAL_INFO( "desktop.app", "desktop (cd100003) ::Desktop::Main" );
// Remember current context object
com::sun::star::uno::ContextLayer layer(
com::sun::star::uno::getCurrentContext() );
if ( m_aBootstrapError != BE_OK )
{
HandleBootstrapErrors( m_aBootstrapError, m_aBootstrapErrorMessage );
return EXIT_FAILURE;
}
BootstrapStatus eStatus = GetBootstrapStatus();
if (eStatus == BS_TERMINATE) {
return EXIT_SUCCESS;
}
// Detect desktop environment - need to do this as early as possible
com::sun::star::uno::setCurrentContext(
new DesktopContext( com::sun::star::uno::getCurrentContext() ) );
CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
#if HAVE_FEATURE_DESKTOP
OUString aUnknown( rCmdLineArgs.GetUnknown() );
if ( !aUnknown.isEmpty() )
{
displayCmdlineHelp( aUnknown );
return EXIT_FAILURE;
}
if ( rCmdLineArgs.IsHelp() )
{
displayCmdlineHelp( OUString() );
return EXIT_SUCCESS;
}
if ( rCmdLineArgs.IsVersion() )
{
displayVersion();
return EXIT_SUCCESS;
}
#endif
// setup configuration error handling
ConfigurationErrorHandler aConfigErrHandler;
if (!ShouldSuppressUI(rCmdLineArgs))
aConfigErrHandler.activate();
ResMgr::SetReadStringHook( ReplaceStringHookProc );
// Startup screen
SAL_INFO( "desktop.app", "desktop (lo119109) Desktop::Main { OpenSplashScreen" );
OpenSplashScreen();
SAL_INFO( "desktop.app", "desktop (lo119109) Desktop::Main } OpenSplashScreen" );
SetSplashScreenProgress(100/*10*/);
userinstall::Status inst_fin = userinstall::finalize();
if (inst_fin != userinstall::EXISTED && inst_fin != userinstall::CREATED)
{
SAL_WARN( "desktop.app", "userinstall failed");
if ( inst_fin == userinstall::ERROR_NO_SPACE )
HandleBootstrapErrors(
BE_USERINSTALL_NOTENOUGHDISKSPACE, OUString() );
else if ( inst_fin == userinstall::ERROR_CANT_WRITE )
HandleBootstrapErrors( BE_USERINSTALL_NOWRITEACCESS, OUString() );
else
HandleBootstrapErrors( BE_USERINSTALL_FAILED, OUString() );
return EXIT_FAILURE;
}
// refresh path information
utl::Bootstrap::reloadData();
SetSplashScreenProgress(90/*20*/);
Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
Reference< XRestartManager > xRestartManager( OfficeRestartManager::get(xContext) );
Reference< XDesktop2 > xDesktop;
try
{
RegisterServices(xContext);
SetSplashScreenProgress(75/*25*/);
#if HAVE_FEATURE_DESKTOP
// check user installation directory for lockfile so we can be sure
// there is no other instance using our data files from a remote host
SAL_INFO( "desktop.app", "desktop (lo119109) Desktop::Main -> Lockfile" );
m_xLockfile.reset(new Lockfile);
if ( !rCmdLineArgs.IsHeadless() && !rCmdLineArgs.IsInvisible() &&
!rCmdLineArgs.IsNoLockcheck() && !m_xLockfile->check( Lockfile_execWarning ))
{
// Lockfile exists, and user clicked 'no'
return EXIT_FAILURE;
}
SAL_INFO( "desktop.app", "desktop (lo119109) Desktop::Main <- Lockfile" );
// check if accessibility is enabled but not working and allow to quit
SAL_INFO( "desktop.app", "{ GetEnableATToolSupport" );
if( Application::GetSettings().GetMiscSettings().GetEnableATToolSupport() )
{
if( !InitAccessBridge() )
return EXIT_FAILURE;
}
SAL_INFO( "desktop.app", "} GetEnableATToolSupport" );
#endif
// terminate if requested...
if( rCmdLineArgs.IsTerminateAfterInit() )
return EXIT_SUCCESS;
// Read the common configuration items for optimization purpose
if ( !InitializeConfiguration() )
return EXIT_FAILURE;
SetSplashScreenProgress(70/*30*/);
// set static variable to disable crash reporting
osl_setErrorReporting( false );
// create title string
LanguageTag aLocale( LANGUAGE_SYSTEM);
ResMgr* pLabelResMgr = ResMgr::SearchCreateResMgr( "ofa", aLocale );
OUString aTitle = pLabelResMgr ? ResId(RID_APPTITLE, *pLabelResMgr).toString() : OUString();
delete pLabelResMgr;
#ifdef DBG_UTIL
//include buildid in non product builds
OUString aDefault("development");
aTitle += " [";
aTitle += utl::Bootstrap::getBuildIdData(aDefault);
aTitle += "]";
#endif
SetDisplayName( aTitle );
SetSplashScreenProgress(65/*35*/);
SAL_INFO( "desktop.app", "{ create SvtPathOptions and SvtLanguageOptions" );
pExecGlobals->pPathOptions.reset( new SvtPathOptions);
SetSplashScreenProgress(60/*40*/);
SAL_INFO( "desktop.app", "} create SvtPathOptions and SvtLanguageOptions" );
xDesktop = css::frame::Desktop::create( xContext );
// create service for loadin SFX (still needed in startup)
pExecGlobals->xGlobalBroadcaster = Reference < css::document::XDocumentEventListener >
( css::frame::theGlobalEventBroadcaster::get(xContext), UNO_SET_THROW );
/* ensure existence of a default window that messages can be dispatched to
This is for the benefit of testtool which uses PostUserEvent extensively
and else can deadlock while creating this window from another tread while
the main thread is not yet in the event loop.
*/
Application::GetDefaultDevice();
#if HAVE_FEATURE_EXTENSIONS
// Check if bundled or shared extensions were added /removed
// and process those extensions (has to be done before checking
// the extension dependencies!
SynchronizeExtensionRepositories();
bool bAbort = CheckExtensionDependencies();
if ( bAbort )
return EXIT_FAILURE;
if (inst_fin == userinstall::CREATED)
{
Migration::migrateSettingsIfNecessary();
}
#endif
// keep a language options instance...
pExecGlobals->pLanguageOptions.reset( new SvtLanguageOptions(true));
css::document::DocumentEvent aEvent;
aEvent.EventName = "OnStartApp";
pExecGlobals->xGlobalBroadcaster->documentEventOccured(aEvent);
SetSplashScreenProgress(50/*50*/);
// Backing Component
bool bCrashed = false;
bool bExistsRecoveryData = false;
bool bExistsSessionData = false;
SAL_INFO( "desktop.app", "{ impl_checkRecoveryState" );
impl_checkRecoveryState(bCrashed, bExistsRecoveryData, bExistsSessionData);
SAL_INFO( "desktop.app", "} impl_checkRecoveryState" );
OUString pidfileName = rCmdLineArgs.GetPidfileName();
if ( !pidfileName.isEmpty() )
{
OUString pidfileURL;
if ( osl_getFileURLFromSystemPath(pidfileName.pData, &pidfileURL.pData) == osl_File_E_None )
{
osl::File pidfile( pidfileURL );
osl::FileBase::RC rc;
osl::File::remove( pidfileURL );
if ( (rc = pidfile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ) ) == osl::File::E_None )
{
OString pid( OString::number( GETPID() ) );
sal_uInt64 written = 0;
if ( pidfile.write(pid.getStr(), pid.getLength(), written) != osl::File::E_None )
{
SAL_WARN("desktop.app", "cannot write pidfile " << pidfile.getURL());
}
pidfile.close();
}
else
{
SAL_WARN("desktop.app", "cannot open pidfile " << pidfile.getURL() << osl::FileBase::RC(rc));
}
}
else
{
SAL_WARN("desktop.app", "cannot get pidfile URL from path" << pidfileName);
}
}
if ( rCmdLineArgs.IsHeadless() )
{
// Ensure that we use not the system file dialogs as
// headless mode relies on Application::EnableHeadlessMode()
// which does only work for VCL dialogs!!
SvtMiscOptions aMiscOptions;
pExecGlobals->bUseSystemFileDialog = aMiscOptions.UseSystemFileDialog();
aMiscOptions.SetUseSystemFileDialog( false );
}
pExecGlobals->bRestartRequested = xRestartManager->isRestartRequested(
true);
if ( !pExecGlobals->bRestartRequested )
{
if ((!rCmdLineArgs.WantsToLoadDocument() && !rCmdLineArgs.IsInvisible() && !rCmdLineArgs.IsHeadless() && !rCmdLineArgs.IsQuickstart()) &&
(SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::E_SSTARTMODULE)) &&
(!bExistsRecoveryData ) &&
(!bExistsSessionData ) &&
(!Application::AnyInput( VCL_INPUT_APPEVENT ) ))
{
SAL_INFO( "desktop.app", "{ create BackingComponent" );
ShowBackingComponent(this);
SAL_INFO( "desktop.app", "} create BackingComponent" );
}
}
}
catch ( const com::sun::star::lang::WrappedTargetException& wte )
{
com::sun::star::uno::Exception te;
wte.TargetException >>= te;
FatalError( MakeStartupConfigAccessErrorMessage(wte.Message + te.Message) );
}
catch ( const com::sun::star::uno::Exception& e )
{
FatalError( MakeStartupErrorMessage(e.Message) );
}
SetSplashScreenProgress(45/*55*/);
SvtFontSubstConfig().Apply();
SvtTabAppearanceCfg aAppearanceCfg;
SvtTabAppearanceCfg::SetInitialized();
aAppearanceCfg.SetApplicationDefaults( this );
SvtAccessibilityOptions aOptions;
aOptions.SetVCLSettings();
SetSplashScreenProgress(40/*60*/);
#if ENABLE_TELEPATHY
bool bListen = rCmdLineArgs.IsInvisible();
TeleManager::init( bListen );
#endif
if ( !pExecGlobals->bRestartRequested )
{
Application::SetFilterHdl( LINK( this, Desktop, ImplInitFilterHdl ) );
bool bTerminateRequested = false;
// Preload function depends on an initialized sfx application!
SetSplashScreenProgress(25/*75*/);
// use system window dialogs
Application::SetSystemWindowMode( SYSTEMWINDOW_MODE_DIALOG );
SetSplashScreenProgress(20/*80*/);
if ( !bTerminateRequested && !rCmdLineArgs.IsInvisible() &&
!rCmdLineArgs.IsNoQuickstart() )
InitializeQuickstartMode( xContext );
SAL_INFO( "desktop.app", "desktop (cd100003) createInstance com.sun.star.frame.Desktop" );
try
{
if ( xDesktop.is() )
xDesktop->addTerminateListener( new OfficeIPCThreadController );
SetSplashScreenProgress(5/*100*/);
}
catch ( const com::sun::star::uno::Exception& e )
{
FatalError( MakeStartupErrorMessage(e.Message) );
}
// Release solar mutex just before we wait for our client to connect
int nAcquireCount = Application::ReleaseSolarMutex();
// Post user event to startup first application component window
// We have to send this OpenClients message short before execute() to
// minimize the risk that this message overtakes type detection construction!!
Application::PostUserEvent( LINK( this, Desktop, OpenClients_Impl ) );
// Post event to enable acceptors
Application::PostUserEvent( LINK( this, Desktop, EnableAcceptors_Impl) );
// The configuration error handler currently is only for startup
aConfigErrHandler.deactivate();
// Acquire solar mutex just before we enter our message loop
if ( nAcquireCount )
Application::AcquireSolarMutex( nAcquireCount );
// call Application::Execute to process messages in vcl message loop
SAL_INFO( "desktop.app", "PERFORMANCE - enter Application::Execute()" );
try
{
#if HAVE_FEATURE_JAVA
// The JavaContext contains an interaction handler which is used when
// the creation of a Java Virtual Machine fails
com::sun::star::uno::ContextLayer layer2(
new svt::JavaContext( com::sun::star::uno::getCurrentContext() ) );
#endif
// check whether the shutdown is caused by restart just before entering the Execute
pExecGlobals->bRestartRequested = pExecGlobals->bRestartRequested ||
xRestartManager->isRestartRequested(true);
if ( !pExecGlobals->bRestartRequested )
{
// if this run of the office is triggered by restart, some additional actions should be done
DoRestartActionsIfNecessary( !rCmdLineArgs.IsInvisible() && !rCmdLineArgs.IsNoQuickstart() );
Execute();
}
}
catch(const com::sun::star::document::CorruptedFilterConfigurationException& exFilterCfg)
{
OfficeIPCThread::SetDowning();
FatalError( MakeStartupErrorMessage(exFilterCfg.Message) );
}
catch(const com::sun::star::configuration::CorruptedConfigurationException& exAnyCfg)
{
OfficeIPCThread::SetDowning();
FatalError( MakeStartupErrorMessage(exAnyCfg.Message) );
}
catch( const ::com::sun::star::uno::Exception& exUNO)
{
OfficeIPCThread::SetDowning();
FatalError( exUNO.Message);
}
catch( const std::exception& exSTD)
{
OfficeIPCThread::SetDowning();
FatalError( OUString::createFromAscii( exSTD.what()));
}
catch( ...)
{
OfficeIPCThread::SetDowning();
FatalError( OUString( "Caught Unknown Exception: Aborting!"));
}
}
else
{
if (xDesktop.is())
xDesktop->terminate();
}
// CAUTION: you do not necessarily get here e.g. on the Mac.
// please put all deinitialization code into doShutdown
return doShutdown();
}
##プログレスバー
CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
ここをnext
で通過すると、
(gdb) p rCmdLineArgs
$6 = (desktop::CommandLineArgs &) @0x7ffff7b331a0:
(中略)
m_help = false, m_writer = false, m_calc = true, m_draw = false,
(後略)
--calc
のコマンドが読み取られていることがわかる。
OpenSplashScreen();
を実行すると、中身が空のウィンドウが宙に浮いたような感じででてくる。いまからここにロゴを書いていくのだろう。
SetSplashScreenProgress(10);
、SetSplashScreenProgress(20);
などを実行するたびに、画面上のプログレスバーが増えていく。
Execute();
を実行するとCalcが起動するので、この関数を見ていく。
void Application::Execute()
{
ImplSVData* pSVData = ImplGetSVData();
pSVData->maAppData.mbInAppExecute = true;
while ( !pSVData->maAppData.mbAppQuit )
Application::Yield();
pSVData->maAppData.mbInAppExecute = false;
}
この後、Application::Yield();
をどんどん探っていったが、いろいろな関数をたらい回しにされた挙句、最終的にGTKとかのウィンドウ制御のメインループにたどり着いてしまった。これ以降をgdbで探るのは無理そう…。
#全探索する
ここまでの知識を投げ捨てて、全検索に走るしかなくなってしまった。ので、全検索しまくる。
##全検索に便利なサイト
Opengrokというサイトがあり、ここでLibreOfficeのコードを簡単に検索することができる。
ソースコードのDocumentationによると、LibreOffice Calcは「sc」という名前で管理されているようなので、scフォルダ以下を探るのが良さそう。
##検索する言葉
いまやりたいのは「=ifなどと打った時に出てくるツールチップをもっと良くしたい」ということなので、とりあえず「AVERAGEIF」で検索してみる(関数一覧がどこかにあるはずなので、それを見つければ話は早いはず)。
こんなのを見つけた。
static const FunctionData saFuncTableBiff2[] =
{
{ "COUNT", "COUNT", 0, 0, 0, MX, V, { RX }, 0 },
{ "IF", "IF", 1, 1, 2, 3, R, { VO, RO }, 0 },
{ "ISNA", "ISNA", 2, 2, 1, 1, V, { VR }, 0 },
// 中略
};
これはかなり求めてるものに近いぞ、ということで、この変数を呼んでいる場所を探す。
…が、formulabase.cxx
内でしか見つからない。
void FunctionProviderImpl::initFuncs( const FunctionData* pBeg, const FunctionData* pEnd, sal_uInt8 nMaxParam,
bool bImportFilter, FilterType eFilter )
{
for( const FunctionData* pIt = pBeg; pIt != pEnd; ++pIt )
if( pIt->isSupported( bImportFilter, eFilter ) )
initFunc( *pIt, nMaxParam );
}
void FunctionProviderImpl::initFunc( const FunctionData& rFuncData, sal_uInt8 nMaxParam )
{
// create a function info object
FunctionInfoRef xFuncInfo( new FunctionInfo );
if( rFuncData.mpcOdfFuncName )
xFuncInfo->maOdfFuncName = OUString::createFromAscii( rFuncData.mpcOdfFuncName );
if( rFuncData.mpcOoxFuncName )
xFuncInfo->maOoxFuncName = OUString::createFromAscii( rFuncData.mpcOoxFuncName );
if( getFlag( rFuncData.mnFlags, FUNCFLAG_MACROCALL ) )
{
OSL_ENSURE( !xFuncInfo->maOoxFuncName.isEmpty(), "FunctionProviderImpl::initFunc - missing OOXML function name" );
OSL_ENSURE( !getFlag( rFuncData.mnFlags, FUNCFLAG_MACROCALLODF ), "FunctionProviderImpl::initFunc - unexpected flag FUNCFLAG_MACROCALLODF" );
xFuncInfo->maBiffMacroName = "_xlfn." + xFuncInfo->maOoxFuncName;
if( getFlag( rFuncData.mnFlags, FUNCFLAG_MACROCALL_FN ) )
{
xFuncInfo->maOoxFuncName = "_xlfn." + xFuncInfo->maOoxFuncName;
//! From here on maOoxFuncName contains the _xlfn. prefix!
}
}
else if( getFlag( rFuncData.mnFlags, FUNCFLAG_MACROCALLODF ) )
{
OSL_ENSURE( !xFuncInfo->maOdfFuncName.isEmpty(), "FunctionProviderImpl::initFunc - missing ODF function name" );
xFuncInfo->maBiffMacroName = "_xlfnodf." + xFuncInfo->maOdfFuncName;
}
xFuncInfo->meFuncLibType = FUNCFLAGS_TO_FUNCLIB( rFuncData.mnFlags );
xFuncInfo->mnApiOpCode = -1;
xFuncInfo->mnBiff12FuncId = rFuncData.mnBiff12FuncId;
xFuncInfo->mnBiffFuncId = rFuncData.mnBiffFuncId;
xFuncInfo->mnMinParamCount = rFuncData.mnMinParamCount;
xFuncInfo->mnMaxParamCount = (rFuncData.mnMaxParamCount == MX) ? nMaxParam : rFuncData.mnMaxParamCount;
xFuncInfo->mnRetClass = rFuncData.mnRetClass;
xFuncInfo->mpParamInfos = rFuncData.mpParamInfos;
xFuncInfo->mbParamPairs = getFlag( rFuncData.mnFlags, FUNCFLAG_PARAMPAIRS );
xFuncInfo->mbVolatile = getFlag( rFuncData.mnFlags, FUNCFLAG_VOLATILE );
xFuncInfo->mbExternal = getFlag( rFuncData.mnFlags, FUNCFLAG_EXTERNAL );
xFuncInfo->mbInternal = !xFuncInfo->mbExternal || getFlag( rFuncData.mnFlags, FUNCFLAG_INTERNAL );
bool bMacroCmd = getFlag( rFuncData.mnFlags, FUNCFLAG_MACROCMD );
xFuncInfo->mbMacroFunc = bMacroCmd || getFlag( rFuncData.mnFlags, FUNCFLAG_MACROFUNC );
xFuncInfo->mbVarParam = bMacroCmd || (rFuncData.mnMinParamCount != rFuncData.mnMaxParamCount) || getFlag( rFuncData.mnFlags, FUNCFLAG_ALWAYSVAR );
setFlag( xFuncInfo->mnBiff12FuncId, BIFF_TOK_FUNCVAR_CMD, bMacroCmd );
setFlag( xFuncInfo->mnBiffFuncId, BIFF_TOK_FUNCVAR_CMD, bMacroCmd );
// insert the function info into the member maps
maFuncs.push_back( xFuncInfo );
if( !xFuncInfo->maOoxFuncName.isEmpty() )
maOoxFuncs[ xFuncInfo->maOoxFuncName ] = xFuncInfo;
if( xFuncInfo->mnBiff12FuncId != NOID )
maBiff12Funcs[ xFuncInfo->mnBiff12FuncId ] = xFuncInfo;
if( xFuncInfo->mnBiffFuncId != NOID )
maBiffFuncs[ xFuncInfo->mnBiffFuncId ] = xFuncInfo;
if( !xFuncInfo->maBiffMacroName.isEmpty() )
maMacroFuncs[ xFuncInfo->maBiffMacroName ] = xFuncInfo;
}
// create a function info object
の文字にテンションが上がる。引数に付いている&マークは参照渡しの意味。
この後、この関数についていろいろ調べてみたが、どうもいまいち空振りっぽい感触。ブレークポイントを仕込んでも止まらず、printfをはさんでも出力されない。この関数はなんなんだ…?
##ooxの意味
ここで、なんとなくソースコードのDocumentationを読んでいて、あることに気付いた。ソースコードのパスにある「oox」の文字が見出しにある。意味は、「Support for Office Open XML, the office XML-format designed by Microsoft.」
ん、もしかして、このファイルって「*.xlsx」を読み込むときにしか使われないプログラムなんですかね…?ま、まさかね…?
恐る恐るxlsxファイルを与えて起動してみると、事前に設定しておいたブレークポイントで止まった!!
##結局
今回調べたところは、関係のない部分だった。
#次回予告
今回の教訓を活かして、次回はフォルダ名から的を絞って検索を行ってみる。
#リンク
##LibreOfficeCalcに関数候補表示機能を付けるまで