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 "dbmm_global.hrc"
21 #include "dbmm_module.hxx"
22 #include "docinteraction.hxx"
23 #include "macromigration.hrc"
24 #include "macromigrationdialog.hxx"
25 #include "macromigrationpages.hxx"
26 #include "migrationengine.hxx"
27 #include "migrationerror.hxx"
28 #include "migrationlog.hxx"
30 #include <com/sun/star/sdb/application/XDatabaseDocumentUI.hpp>
31 #include <com/sun/star/frame/XModel2.hpp>
32 #include <com/sun/star/frame/XStorable.hpp>
33 #include <com/sun/star/util/XCloseable.hpp>
34 #include <com/sun/star/frame/XComponentLoader.hpp>
35 #include <com/sun/star/util/XModifiable.hpp>
36 #include <com/sun/star/ucb/UniversalContentBroker.hpp>
37 #include <com/sun/star/ucb/XContent.hpp>
39 #include <comphelper/namedvaluecollection.hxx>
40 #include <cppuhelper/exc_hlp.hxx>
41 #include <cppuhelper/implbase1.hxx>
42 #include <rtl/ref.hxx>
43 #include <svl/filenotation.hxx>
44 #include <tools/diagnose_ex.h>
45 #include <ucbhelper/content.hxx>
46 #include <vcl/layout.hxx>
53 #define STATE_CLOSE_SUB_DOCS 0
54 #define STATE_BACKUP_DBDOC 1
55 #define STATE_MIGRATE 2
56 #define STATE_SUMMARY 3
58 #define PATH_DEFAULT 1
60 using ::com::sun::star::uno::Reference
;
61 using ::com::sun::star::uno::XComponentContext
;
62 using ::com::sun::star::uno::XInterface
;
63 using ::com::sun::star::uno::UNO_QUERY
;
64 using ::com::sun::star::uno::UNO_QUERY_THROW
;
65 using ::com::sun::star::uno::UNO_SET_THROW
;
66 using ::com::sun::star::uno::Exception
;
67 using ::com::sun::star::uno::RuntimeException
;
68 using ::com::sun::star::uno::Any
;
69 using ::com::sun::star::uno::makeAny
;
70 using ::com::sun::star::sdb::application::XDatabaseDocumentUI
;
71 using ::com::sun::star::sdb::XOfficeDatabaseDocument
;
72 using ::com::sun::star::frame::XModel2
;
73 using ::com::sun::star::frame::XController
;
74 using ::com::sun::star::frame::XController2
;
75 using ::com::sun::star::container::XEnumeration
;
76 using ::com::sun::star::frame::XStorable
;
77 using ::com::sun::star::uno::Sequence
;
78 using ::com::sun::star::beans::PropertyValue
;
79 using ::com::sun::star::frame::XFrame
;
80 using ::com::sun::star::awt::XWindow
;
81 using ::com::sun::star::util::XCloseable
;
82 using ::com::sun::star::util::XCloseListener
;
83 using ::com::sun::star::util::CloseVetoException
;
84 using ::com::sun::star::lang::EventObject
;
85 using ::com::sun::star::frame::XComponentLoader
;
86 using ::com::sun::star::util::XModifiable
;
87 using ::com::sun::star::ucb::UniversalContentBroker
;
88 using ::com::sun::star::ucb::XCommandEnvironment
;
89 using ::com::sun::star::ucb::XContent
;
90 using ::com::sun::star::ucb::XContentIdentifier
;
93 static void lcl_getControllers_throw(const Reference
< XModel2
>& _rxDocument
,
94 ::std::list
< Reference
< XController2
> >& _out_rControllers
)
96 _out_rControllers
.clear();
97 Reference
< XEnumeration
> xControllerEnum( _rxDocument
->getControllers(), UNO_SET_THROW
);
98 while ( xControllerEnum
->hasMoreElements() )
99 _out_rControllers
.push_back( Reference
< XController2
>( xControllerEnum
->nextElement(), UNO_QUERY_THROW
) );
102 // MacroMigrationDialog_Data
103 struct MacroMigrationDialog_Data
105 Reference
<XComponentContext
> aContext
;
106 MigrationLog aLogger
;
107 Reference
< XOfficeDatabaseDocument
> xDocument
;
108 Reference
< XModel2
> xDocumentModel
;
109 OUString sSuccessfulBackupLocation
;
110 bool bMigrationIsRunning
;
111 bool bMigrationFailure
;
112 bool bMigrationSuccess
;
114 MacroMigrationDialog_Data(
115 const Reference
<XComponentContext
>& _rContext
,
116 const Reference
< XOfficeDatabaseDocument
>& _rxDocument
)
117 :aContext( _rContext
)
119 ,xDocument( _rxDocument
)
120 ,xDocumentModel( _rxDocument
, UNO_QUERY
)
121 ,bMigrationIsRunning( false )
122 ,bMigrationFailure( false )
123 ,bMigrationSuccess( false )
128 // MacroMigrationDialog
129 MacroMigrationDialog::MacroMigrationDialog(vcl::Window
* _pParent
, const Reference
<XComponentContext
>& _rContext
,
130 const Reference
< XOfficeDatabaseDocument
>& _rxDocument
)
131 : MacroMigrationDialog_Base(_pParent
)
132 , m_pData( new MacroMigrationDialog_Data( _rContext
, _rxDocument
) )
134 OUString
sTitlePrepare( MacroMigrationResId( STR_STATE_CLOSE_SUB_DOCS
) );
135 OUString
sTitleStoreAs( MacroMigrationResId( STR_STATE_BACKUP_DBDOC
) );
136 OUString
sTitleMigrate( MacroMigrationResId( STR_STATE_MIGRATE
) );
137 OUString
sTitleSummary( MacroMigrationResId( STR_STATE_SUMMARY
) );
139 describeState( STATE_CLOSE_SUB_DOCS
, sTitlePrepare
, &PreparationPage::Create
);
140 describeState( STATE_BACKUP_DBDOC
, sTitleStoreAs
, &SaveDBDocPage::Create
);
141 describeState( STATE_MIGRATE
, sTitleMigrate
, &ProgressPage::Create
);
142 describeState( STATE_SUMMARY
, sTitleSummary
, &ResultPage::Create
);
144 declarePath( PATH_DEFAULT
, STATE_CLOSE_SUB_DOCS
, STATE_BACKUP_DBDOC
, STATE_MIGRATE
, STATE_SUMMARY
, WZS_INVALID_STATE
);
146 SetPageSizePixel( LogicToPixel( ::Size( TAB_PAGE_WIDTH
, TAB_PAGE_HEIGHT
), MAP_APPFONT
) );
147 SetRoadmapInteractive( true );
148 enableAutomaticNextButtonState();
149 defaultButton( WizardButtonFlags::NEXT
);
150 enableButtons( WizardButtonFlags::FINISH
, true );
153 OSL_PRECOND( m_pData
->xDocumentModel
.is(), "MacroMigrationDialog::MacroMigrationDialog: illegal document!" );
156 const Reference
<XComponentContext
>& MacroMigrationDialog::getComponentContext() const
158 return m_pData
->aContext
;
161 const Reference
< XOfficeDatabaseDocument
>& MacroMigrationDialog::getDocument() const
163 return m_pData
->xDocument
;
166 short MacroMigrationDialog::Execute()
168 short nResult
= MacroMigrationDialog_Base::Execute();
169 if ( !m_pData
->bMigrationFailure
&& !m_pData
->bMigrationSuccess
)
170 // migration did not even start
173 OSL_ENSURE( !m_pData
->bMigrationFailure
|| !m_pData
->bMigrationSuccess
,
174 "MacroMigrationDialog::Execute: success *and* failure at the same time?!" );
175 impl_reloadDocument_nothrow( m_pData
->bMigrationSuccess
);
180 bool MacroMigrationDialog::Close()
182 if ( m_pData
->bMigrationIsRunning
)
184 return MacroMigrationDialog_Base::Close();
187 void MacroMigrationDialog::enterState( WizardState _nState
)
189 MacroMigrationDialog_Base::enterState( _nState
);
193 case STATE_CLOSE_SUB_DOCS
:
194 enableButtons( WizardButtonFlags::FINISH
, false );
195 enableState( STATE_MIGRATE
, false );
196 enableState( STATE_SUMMARY
, false );
199 case STATE_BACKUP_DBDOC
:
200 enableState( STATE_MIGRATE
, true );
201 // Note that the state is automatically disabled if the current page
202 // (SaveDBDocPage) returns false in its canAdvance, not caring that
203 // we enabled it here.
208 // disable everything. The process we will start here cannot be cancelled, the user
209 // needs to wait 'til it's finished.
210 enableState( STATE_CLOSE_SUB_DOCS
, false );
211 enableState( STATE_BACKUP_DBDOC
, false );
212 enableState( STATE_SUMMARY
, false );
214 enableButtons( WizardButtonFlags::FINISH
| WizardButtonFlags::CANCEL
| WizardButtonFlags::PREVIOUS
| WizardButtonFlags::NEXT
, false );
216 // start the migration asynchronously
217 PostUserEvent( LINK( this, MacroMigrationDialog
, OnStartMigration
), NULL
, true );
222 // disable the previous step - we can't return to the actual migration, it already happened (or failed)
223 enableState( STATE_MIGRATE
, false );
226 // display the results
227 dynamic_cast< ResultPage
& >( *GetPage( STATE_SUMMARY
) ).displayMigrationLog(
228 m_pData
->bMigrationSuccess
, m_pData
->aLogger
.getCompleteLog() );
230 enableButtons( WizardButtonFlags::FINISH
, m_pData
->bMigrationSuccess
);
231 enableButtons( WizardButtonFlags::CANCEL
, m_pData
->bMigrationFailure
);
232 defaultButton( m_pData
->bMigrationSuccess
? WizardButtonFlags::FINISH
: WizardButtonFlags::CANCEL
);
236 OSL_FAIL( "MacroMigrationDialog::enterState: unhandled state!" );
240 bool MacroMigrationDialog::prepareLeaveCurrentState( CommitPageReason _eReason
)
242 if ( !MacroMigrationDialog_Base::prepareLeaveCurrentState( _eReason
) )
245 switch ( getCurrentState() )
247 case STATE_CLOSE_SUB_DOCS
:
248 if ( !impl_closeSubDocs_nothrow() )
251 case STATE_BACKUP_DBDOC
:
252 if ( !impl_backupDocument_nothrow() )
260 OSL_FAIL( "MacroMigrationDialog::prepareLeaveCurrentState: unhandled state!" );
266 bool MacroMigrationDialog::leaveState( WizardState _nState
)
268 return MacroMigrationDialog_Base::leaveState( _nState
);
271 MacroMigrationDialog::WizardState
MacroMigrationDialog::determineNextState( WizardState _nCurrentState
) const
273 return MacroMigrationDialog_Base::determineNextState( _nCurrentState
);
276 bool MacroMigrationDialog::onFinish()
278 return MacroMigrationDialog_Base::onFinish();
281 IMPL_LINK_NOARG( MacroMigrationDialog
, OnStartMigration
)
284 m_pData
->bMigrationIsRunning
= true;
286 // initialize migration engine and progress
287 ProgressPage
& rProgressPage( dynamic_cast< ProgressPage
& >( *GetPage( STATE_MIGRATE
) ) );
288 MigrationEngine
aEngine( m_pData
->aContext
, m_pData
->xDocument
, rProgressPage
, m_pData
->aLogger
);
289 rProgressPage
.setDocumentCounts( aEngine
.getFormCount(), aEngine
.getReportCount() );
292 m_pData
->bMigrationSuccess
= aEngine
.migrateAll();
293 m_pData
->bMigrationFailure
= !m_pData
->bMigrationSuccess
;
296 enableButtons( WizardButtonFlags::FINISH
| WizardButtonFlags::NEXT
, true );
297 enableState( STATE_SUMMARY
, true );
300 m_pData
->bMigrationIsRunning
= false;
302 if ( m_pData
->bMigrationSuccess
)
304 rProgressPage
.onFinishedSuccessfully();
307 { // if there was an error, show the summary automatically
315 void MacroMigrationDialog::impl_showCloseDocsError( bool _bShow
)
317 PreparationPage
* pPreparationPage
= dynamic_cast< PreparationPage
* >( GetPage( STATE_CLOSE_SUB_DOCS
) );
318 OSL_ENSURE( pPreparationPage
, "MacroMigrationDialog::impl_showCloseDocsError: did not find the page!" );
319 if ( pPreparationPage
)
320 pPreparationPage
->showCloseDocsError( _bShow
);
323 bool MacroMigrationDialog::impl_closeSubDocs_nothrow()
325 OSL_PRECOND( m_pData
->xDocument
.is(), "MacroMigrationDialog::impl_closeSubDocs_nothrow: no document!" );
326 if ( !m_pData
->xDocument
.is() )
329 impl_showCloseDocsError( false );
331 bool bSuccess
= true;
334 // collect all controllers of our document
335 ::std::list
< Reference
< XController2
> > aControllers
;
336 lcl_getControllers_throw( m_pData
->xDocumentModel
, aControllers
);
338 // close all sub documents of all controllers
339 for ( ::std::list
< Reference
< XController2
> >::const_iterator pos
= aControllers
.begin();
340 pos
!= aControllers
.end() && bSuccess
;
344 Reference
< XDatabaseDocumentUI
> xController( *pos
, UNO_QUERY
);
345 OSL_ENSURE( xController
.is(), "MacroMigrationDialog::impl_closeSubDocs_nothrow: unexpected: controller is missing an important interface!" );
346 // at the moment, only one implementation for a DBDoc's controller exists, which should
347 // support this interface
348 if ( !xController
.is() )
351 bSuccess
= xController
->closeSubComponents();
354 catch( const Exception
& )
356 DBG_UNHANDLED_EXCEPTION();
360 impl_showCloseDocsError( !bSuccess
);
366 bool lcl_equalURLs_nothrow(
367 const Reference
< XComponentContext
>& context
,
368 const OUString
& _lhs
, const OUString
& _rhs
)
370 // the cheap situation: the URLs are equal
377 ::ucbhelper::Content aContentLHS
= ::ucbhelper::Content( _lhs
, Reference
< XCommandEnvironment
>(), context
);
378 ::ucbhelper::Content aContentRHS
= ::ucbhelper::Content( _rhs
, Reference
< XCommandEnvironment
>(), context
);
379 Reference
< XContent
> xContentLHS( aContentLHS
.get(), UNO_SET_THROW
);
380 Reference
< XContent
> xContentRHS( aContentRHS
.get(), UNO_SET_THROW
);
381 Reference
< XContentIdentifier
> xID1( xContentLHS
->getIdentifier(), UNO_SET_THROW
);
382 Reference
< XContentIdentifier
> xID2( xContentRHS
->getIdentifier(), UNO_SET_THROW
);
384 bEqual
= UniversalContentBroker::create(context
)->compareContentIds( xID1
, xID2
) == 0;
386 catch( const Exception
& )
388 DBG_UNHANDLED_EXCEPTION();
394 bool MacroMigrationDialog::impl_backupDocument_nothrow() const
396 if ( !m_pData
->xDocumentModel
.is() )
397 // should never happen, but has been reported as assertion before
400 SaveDBDocPage
& rBackupPage
= dynamic_cast< SaveDBDocPage
& >( *GetPage( STATE_BACKUP_DBDOC
) );
401 OUString
sBackupLocation( rBackupPage
.getBackupLocation() );
406 // check that the backup location isn't the same as the document itself
407 if ( lcl_equalURLs_nothrow( m_pData
->aContext
, sBackupLocation
, m_pData
->xDocumentModel
->getURL() ) )
409 ScopedVclPtrInstance
< MessageDialog
> aErrorBox( const_cast< MacroMigrationDialog
* >( this ), MacroMigrationResId( STR_INVALID_BACKUP_LOCATION
) );
410 aErrorBox
->Execute();
411 rBackupPage
.grabLocationFocus();
415 // store to the backup location
416 const Reference
< XStorable
> xDocument( getDocument(), UNO_QUERY_THROW
);
417 xDocument
->storeToURL( sBackupLocation
, Sequence
< PropertyValue
>() );
418 m_pData
->sSuccessfulBackupLocation
= sBackupLocation
;
420 catch( const Exception
& )
422 aError
= ::cppu::getCaughtException();
424 if ( !aError
.hasValue() )
426 ::svt::OFileNotation
aFileNotation( sBackupLocation
);
427 m_pData
->aLogger
.backedUpDocument( aFileNotation
.get( ::svt::OFileNotation::N_SYSTEM
) );
431 // display the error to the user
432 InteractionHandler
aHandler( m_pData
->aContext
, m_pData
->xDocumentModel
.get() );
433 aHandler
.reportError( aError
);
435 m_pData
->aLogger
.logFailure( MigrationError(
436 ERR_DOCUMENT_BACKUP_FAILED
,
444 void MacroMigrationDialog::impl_reloadDocument_nothrow( bool _bMigrationSuccess
)
446 typedef ::std::pair
< Reference
< XFrame
>, OUString
> ViewDescriptor
;
447 ::std::list
< ViewDescriptor
> aViews
;
451 // the information which is necessary to reload the document
452 OUString
sDocumentURL ( m_pData
->xDocumentModel
->getURL() );
453 ::comphelper::NamedValueCollection
aDocumentArgs( m_pData
->xDocumentModel
->getArgs() );
454 if ( !_bMigrationSuccess
)
456 // if the migration was not successful, then reload from the backup
457 aDocumentArgs
.put( "SalvagedFile", m_pData
->sSuccessfulBackupLocation
);
458 // reset the modified flag of the document, so the controller can be suspended later
459 Reference
< XModifiable
> xModify( m_pData
->xDocument
, UNO_QUERY_THROW
);
460 xModify
->setModified( sal_False
);
461 // after this reload, don't show the migration warning, again
462 aDocumentArgs
.put( "SuppressMigrationWarning", true );
465 // remove anything from the args which might refer to the old document
466 aDocumentArgs
.remove( "Model" );
467 aDocumentArgs
.remove( "Stream" );
468 aDocumentArgs
.remove( "InputStream" );
469 aDocumentArgs
.remove( "FileName" );
470 aDocumentArgs
.remove( "URL" );
472 // collect all controllers of our document
473 ::std::list
< Reference
< XController2
> > aControllers
;
474 lcl_getControllers_throw( m_pData
->xDocumentModel
, aControllers
);
476 // close all those controllers
477 while ( !aControllers
.empty() )
479 Reference
< XController2
> xController( aControllers
.front(), UNO_SET_THROW
);
480 aControllers
.pop_front();
482 Reference
< XFrame
> xFrame( xController
->getFrame(), UNO_SET_THROW
);
483 OUString
sViewName( xController
->getViewControllerName() );
485 if ( !xController
->suspend( sal_True
) )
486 { // ouch. There shouldn't be any modal dialogs and such, so there
487 // really is no reason why suspending shouldn't work.
488 OSL_FAIL( "MacroMigrationDialog::impl_reloadDocument_nothrow: could not suspend a controller!" );
489 // ignoring this would be at the cost of a crash (potentially)
490 // so, we cannot continue here.
491 throw CloseVetoException();
494 aViews
.push_back( ViewDescriptor( xFrame
, sViewName
) );
495 xFrame
->setComponent( NULL
, NULL
);
496 xController
->dispose();
499 // Note the document is closed now - disconnecting the last controller
500 // closes it automatically.
502 Reference
< XOfficeDatabaseDocument
> xNewDocument
;
504 // re-create the views
505 while ( !aViews
.empty() )
507 ViewDescriptor
aView( aViews
.front() );
510 // load the document into this frame
511 Reference
< XComponentLoader
> xLoader( aView
.first
, UNO_QUERY_THROW
);
512 aDocumentArgs
.put( "ViewName", aView
.second
);
513 Reference
< XInterface
> xReloaded( xLoader
->loadComponentFromURL(
517 aDocumentArgs
.getPropertyValues()
520 OSL_ENSURE( xReloaded
!= m_pData
->xDocumentModel
,
521 "MacroMigrationDialog::impl_reloadDocument_nothrow: this should have been a new instance!" );
522 // this would be unexpected, but recoverable: The loader should at least have done
523 // this: really *load* the document, even if it loaded it into the old document instance
524 if ( !xNewDocument
.is() )
526 xNewDocument
.set( xReloaded
, UNO_QUERY_THROW
);
527 // for subsequent loads, into different frames, put the document into the load args
528 aDocumentArgs
.put( "Model", xNewDocument
);
530 #if OSL_DEBUG_LEVEL > 0
533 OSL_ENSURE( xNewDocument
== xReloaded
,
534 "MacroMigrationDialog::impl_reloadDocument_nothrow: unexpected: subsequent load attempt returned a wrong document!" );
539 m_pData
->xDocument
= xNewDocument
;
540 m_pData
->xDocumentModel
.set( xNewDocument
, UNO_QUERY
);
542 // finally, now that the document has been reloaded - if the migration was not successful,
543 // then it was reloaded from the backup, but the real document still is broken. So, save
544 // the document once, which will write the content loaded from the backup to the real docfile.
545 if ( !_bMigrationSuccess
)
547 Reference
< XModifiable
> xModify( m_pData
->xDocument
, UNO_QUERY_THROW
);
548 xModify
->setModified( sal_True
);
549 // this is just parnoia - in case saving the doc fails, perhaps the user is tempted to do so
550 Reference
< XStorable
> xStor( m_pData
->xDocument
, UNO_QUERY_THROW
);
554 catch( const Exception
& )
556 DBG_UNHANDLED_EXCEPTION();
559 // close all frames from aViews - the respective controllers have been closed, but
560 // reloading didn't work, so the frames are zombies now.
561 while ( !aViews
.empty() )
563 ViewDescriptor
aView( aViews
.front() );
567 Reference
< XCloseable
> xFrameClose( aView
.first
, UNO_QUERY_THROW
);
568 xFrameClose
->close( sal_True
);
570 catch( const Exception
& )
572 DBG_UNHANDLED_EXCEPTION();
579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */