1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_desktop.hxx"
26 
27 
28 
29 
30 #include "sal/config.h"
31 
32 #include <cstddef>
33 
34 #include "com/sun/star/beans/PropertyValue.hpp"
35 #include "com/sun/star/beans/NamedValue.hpp"
36 
37 #include "com/sun/star/deployment/DependencyException.hpp"
38 #include "com/sun/star/deployment/LicenseException.hpp"
39 #include "com/sun/star/deployment/VersionException.hpp"
40 #include "com/sun/star/deployment/InstallException.hpp"
41 #include "com/sun/star/deployment/PlatformException.hpp"
42 
43 #include "com/sun/star/deployment/ui/LicenseDialog.hpp"
44 #include "com/sun/star/deployment/DeploymentException.hpp"
45 #include "com/sun/star/deployment/UpdateInformationProvider.hpp"
46 #include "com/sun/star/deployment/XPackage.hpp"
47 
48 #include "com/sun/star/task/XAbortChannel.hpp"
49 #include "com/sun/star/task/XInteractionAbort.hpp"
50 #include "com/sun/star/task/XInteractionApprove.hpp"
51 
52 #include "com/sun/star/ucb/CommandAbortedException.hpp"
53 #include "com/sun/star/ucb/CommandFailedException.hpp"
54 #include "com/sun/star/ucb/XCommandEnvironment.hpp"
55 
56 #include "com/sun/star/ui/dialogs/ExecutableDialogResults.hpp"
57 
58 #include "com/sun/star/uno/Reference.hxx"
59 #include "com/sun/star/uno/RuntimeException.hpp"
60 #include "com/sun/star/uno/Sequence.hxx"
61 #include "com/sun/star/uno/XInterface.hpp"
62 #include "com/sun/star/uno/TypeClass.hpp"
63 #include "osl/diagnose.h"
64 #include "osl/mutex.hxx"
65 #include "rtl/ref.hxx"
66 #include "rtl/ustring.h"
67 #include "rtl/ustring.hxx"
68 #include "sal/types.h"
69 #include "ucbhelper/content.hxx"
70 #include "cppuhelper/exc_hlp.hxx"
71 #include "cppuhelper/implbase3.hxx"
72 #include "comphelper/anytostring.hxx"
73 #include "vcl/msgbox.hxx"
74 #include "toolkit/helper/vclunohelper.hxx"
75 #include "comphelper/processfactory.hxx"
76 
77 #include "dp_gui.h"
78 #include "dp_gui_thread.hxx"
79 #include "dp_gui_extensioncmdqueue.hxx"
80 #include "dp_gui_dependencydialog.hxx"
81 #include "dp_gui_dialog2.hxx"
82 #include "dp_gui_shared.hxx"
83 #include "dp_gui_theextmgr.hxx"
84 #include "dp_gui_updatedialog.hxx"
85 #include "dp_gui_updateinstalldialog.hxx"
86 #include "dp_dependencies.hxx"
87 #include "dp_identifier.hxx"
88 #include "dp_version.hxx"
89 #include <dp_gui_handleversionexception.hxx>
90 
91 #include <queue>
92 #include <boost/shared_ptr.hpp>
93 
94 #if (defined(_MSC_VER) && (_MSC_VER < 1400))
95 #define _WIN32_WINNT 0x0400
96 #endif
97 
98 #ifdef WNT
99 #include "tools/prewin.h"
100 #include <objbase.h>
101 #include "tools/postwin.h"
102 #endif
103 
104 
105 using namespace ::com::sun::star;
106 using ::rtl::OUString;
107 
108 namespace {
109 
110 OUString getVersion( OUString const & sVersion )
111 {
112     return ( sVersion.getLength() == 0 ) ? OUString( RTL_CONSTASCII_USTRINGPARAM( "0" ) ) : sVersion;
113 }
114 
115 OUString getVersion( const uno::Reference< deployment::XPackage > &rPackage )
116 {
117     return getVersion( rPackage->getVersion());
118 }
119 }
120 
121 
122 namespace dp_gui {
123 
124 //==============================================================================
125 
126 class ProgressCmdEnv
127     : public ::cppu::WeakImplHelper3< ucb::XCommandEnvironment,
128                                       task::XInteractionHandler,
129                                       ucb::XProgressHandler >
130 {
131     uno::Reference< task::XInteractionHandler> m_xHandler;
132     uno::Reference< uno::XComponentContext > m_xContext;
133     uno::Reference< task::XAbortChannel> m_xAbortChannel;
134 
135     DialogHelper   *m_pDialogHelper;
136     OUString        m_sTitle;
137     bool            m_bAborted;
138     bool            m_bWarnUser;
139     sal_Int32       m_nCurrentProgress;
140 
141     void updateProgress();
142 
143     void update_( uno::Any const & Status ) throw ( uno::RuntimeException );
144 
145 public:
146     virtual ~ProgressCmdEnv();
147 
148     /** When param bAskWhenInstalling = true, then the user is asked if he
149     agrees to install this extension. In case this extension is already installed
150     then the user is also notified and asked if he wants to replace that existing
151     extension. In first case an interaction request with an InstallException
152     will be handled and in the second case a VersionException will be handled.
153     */
154 
155     ProgressCmdEnv( const uno::Reference< uno::XComponentContext > rContext,
156                     DialogHelper *pDialogHelper,
157                     const OUString &rTitle )
158         :   m_xContext( rContext ),
159             m_pDialogHelper( pDialogHelper ),
160             m_sTitle( rTitle ),
161             m_bAborted( false ),
162             m_bWarnUser( false )
163     {}
164 
165     Dialog * activeDialog() { return m_pDialogHelper ? m_pDialogHelper->getWindow() : NULL; }
166 
167     void setTitle( const OUString& rNewTitle ) { m_sTitle = rNewTitle; }
168     void startProgress();
169     void stopProgress();
170     void progressSection( const OUString &rText,
171                           const uno::Reference< task::XAbortChannel > &xAbortChannel = 0 );
172     inline bool isAborted() const { return m_bAborted; }
173     inline void setWarnUser( bool bNewVal ) { m_bWarnUser = bNewVal; }
174 
175     // XCommandEnvironment
176     virtual uno::Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler()
177         throw ( uno::RuntimeException );
178     virtual uno::Reference< ucb::XProgressHandler > SAL_CALL getProgressHandler()
179         throw ( uno::RuntimeException );
180 
181     // XInteractionHandler
182     virtual void SAL_CALL handle( uno::Reference< task::XInteractionRequest > const & xRequest )
183         throw ( uno::RuntimeException );
184 
185     // XProgressHandler
186     virtual void SAL_CALL push( uno::Any const & Status )
187         throw ( uno::RuntimeException );
188     virtual void SAL_CALL update( uno::Any const & Status )
189         throw ( uno::RuntimeException );
190     virtual void SAL_CALL pop() throw ( uno::RuntimeException );
191 };
192 
193 //------------------------------------------------------------------------------
194 struct ExtensionCmd
195 {
196     enum E_CMD_TYPE { ADD, ENABLE, DISABLE, REMOVE, CHECK_FOR_UPDATES, ACCEPT_LICENSE };
197 
198     E_CMD_TYPE  m_eCmdType;
199     bool        m_bWarnUser;
200     OUString    m_sExtensionURL;
201     OUString    m_sRepository;
202     uno::Reference< deployment::XPackage > m_xPackage;
203     std::vector< uno::Reference< deployment::XPackage > >        m_vExtensionList;
204 
205     ExtensionCmd( const E_CMD_TYPE eCommand,
206                   const OUString &rExtensionURL,
207                   const OUString &rRepository,
208                   const bool bWarnUser )
209         : m_eCmdType( eCommand ),
210           m_bWarnUser( bWarnUser ),
211           m_sExtensionURL( rExtensionURL ),
212           m_sRepository( rRepository ) {};
213     ExtensionCmd( const E_CMD_TYPE eCommand,
214                   const uno::Reference< deployment::XPackage > &rPackage )
215         : m_eCmdType( eCommand ),
216           m_bWarnUser( false ),
217           m_xPackage( rPackage ) {};
218     ExtensionCmd( const E_CMD_TYPE eCommand,
219                   const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
220         : m_eCmdType( eCommand ),
221           m_bWarnUser( false ),
222           m_vExtensionList( vExtensionList ) {};
223 };
224 
225 typedef ::boost::shared_ptr< ExtensionCmd > TExtensionCmd;
226 
227 //------------------------------------------------------------------------------
228 class ExtensionCmdQueue::Thread: public dp_gui::Thread
229 {
230 public:
231     Thread( DialogHelper *pDialogHelper,
232             TheExtensionManager *pManager,
233             const uno::Reference< uno::XComponentContext > & rContext );
234 
235     void addExtension( const OUString &rExtensionURL,
236                        const OUString &rRepository,
237                        const bool bWarnUser );
238     void removeExtension( const uno::Reference< deployment::XPackage > &rPackage );
239     void enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
240                           const bool bEnable );
241     void checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList );
242     void acceptLicense( const uno::Reference< deployment::XPackage > &rPackage );
243     void stop();
244     bool isBusy();
245 
246     static OUString searchAndReplaceAll( const OUString &rSource,
247                                          const OUString &rWhat,
248                                          const OUString &rWith );
249 private:
250     Thread( Thread & ); // not defined
251     void operator =( Thread & ); // not defined
252 
253     virtual ~Thread();
254 
255     virtual void execute();
256     virtual void SAL_CALL onTerminated();
257 
258     void _addExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv,
259                         const OUString &rPackageURL,
260                         const OUString &rRepository,
261                         const bool bWarnUser );
262     void _removeExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv,
263                            const uno::Reference< deployment::XPackage > &xPackage );
264     void _enableExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv,
265                            const uno::Reference< deployment::XPackage > &xPackage );
266     void _disableExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv,
267                             const uno::Reference< deployment::XPackage > &xPackage );
268     void _checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList );
269     void _acceptLicense( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv,
270                            const uno::Reference< deployment::XPackage > &xPackage );
271 
272     enum Input { NONE, START, STOP };
273 
274     uno::Reference< uno::XComponentContext > m_xContext;
275     std::queue< TExtensionCmd >              m_queue;
276 
277     DialogHelper *m_pDialogHelper;
278     TheExtensionManager *m_pManager;
279 
280     const OUString   m_sEnablingPackages;
281     const OUString   m_sDisablingPackages;
282     const OUString   m_sAddingPackages;
283     const OUString   m_sRemovingPackages;
284     const OUString   m_sDefaultCmd;
285     const OUString   m_sAcceptLicense;
286     osl::Condition   m_wakeup;
287     osl::Mutex       m_mutex;
288     Input            m_eInput;
289     bool             m_bTerminated;
290     bool             m_bStopped;
291     bool             m_bWorking;
292 };
293 
294 //------------------------------------------------------------------------------
295 void ProgressCmdEnv::startProgress()
296 {
297     m_nCurrentProgress = 0;
298 
299     if ( m_pDialogHelper )
300         m_pDialogHelper->showProgress( true );
301 }
302 
303 //------------------------------------------------------------------------------
304 void ProgressCmdEnv::stopProgress()
305 {
306     if ( m_pDialogHelper )
307         m_pDialogHelper->showProgress( false );
308 }
309 
310 //------------------------------------------------------------------------------
311 void ProgressCmdEnv::progressSection( const OUString &rText,
312                                       const uno::Reference< task::XAbortChannel > &xAbortChannel )
313 {
314     m_xAbortChannel = xAbortChannel;
315     if (! m_bAborted)
316     {
317         m_nCurrentProgress = 0;
318         if ( m_pDialogHelper )
319         {
320             m_pDialogHelper->updateProgress( rText, xAbortChannel );
321             m_pDialogHelper->updateProgress( 5 );
322         }
323     }
324 }
325 
326 //------------------------------------------------------------------------------
327 void ProgressCmdEnv::updateProgress()
328 {
329     if ( ! m_bAborted )
330     {
331         long nProgress = ((m_nCurrentProgress*5) % 100) + 5;
332         if ( m_pDialogHelper )
333             m_pDialogHelper->updateProgress( nProgress );
334     }
335 }
336 
337 //------------------------------------------------------------------------------
338 ProgressCmdEnv::~ProgressCmdEnv()
339 {
340     // TODO: stop all threads and wait
341 }
342 
343 
344 //------------------------------------------------------------------------------
345 // XCommandEnvironment
346 //------------------------------------------------------------------------------
347 uno::Reference< task::XInteractionHandler > ProgressCmdEnv::getInteractionHandler()
348     throw ( uno::RuntimeException )
349 {
350     return this;
351 }
352 
353 //------------------------------------------------------------------------------
354 uno::Reference< ucb::XProgressHandler > ProgressCmdEnv::getProgressHandler()
355     throw ( uno::RuntimeException )
356 {
357     return this;
358 }
359 
360 //------------------------------------------------------------------------------
361 // XInteractionHandler
362 //------------------------------------------------------------------------------
363 bool handleVersionException(
364     com::sun::star::deployment::VersionException verExc,
365     DialogHelper* pDialogHelper )
366 {
367     bool bApprove = false;
368 
369     sal_uInt32 id;
370     switch (dp_misc::compareVersions(
371         verExc.NewVersion, verExc.Deployed->getVersion() ))
372     {
373     case dp_misc::LESS:
374         id = RID_WARNINGBOX_VERSION_LESS;
375         break;
376     case dp_misc::EQUAL:
377         id = RID_WARNINGBOX_VERSION_EQUAL;
378         break;
379     default: // dp_misc::GREATER
380         id = RID_WARNINGBOX_VERSION_GREATER;
381         break;
382     }
383     OSL_ASSERT( verExc.Deployed.is() );
384     const bool bEqualNames = verExc.NewDisplayName.equals(
385         verExc.Deployed->getDisplayName());
386     {
387         vos::OGuard guard(Application::GetSolarMutex());
388         WarningBox box( pDialogHelper ? pDialogHelper->getWindow() : NULL, ResId(id, *DeploymentGuiResMgr::get()));
389         String s;
390         if (bEqualNames)
391         {
392             s = box.GetMessText();
393         }
394         else if (id == RID_WARNINGBOX_VERSION_EQUAL)
395         {
396             //hypothetical: requires two instances of an extension with the same
397             //version to have different display names. Probably the developer forgot
398             //to change the version.
399             s = String(ResId(RID_STR_WARNINGBOX_VERSION_EQUAL_DIFFERENT_NAMES, *DeploymentGuiResMgr::get()));
400         }
401         else if (id == RID_WARNINGBOX_VERSION_LESS)
402         {
403             s = String(ResId(RID_STR_WARNINGBOX_VERSION_LESS_DIFFERENT_NAMES, *DeploymentGuiResMgr::get()));
404         }
405         else if (id == RID_WARNINGBOX_VERSION_GREATER)
406         {
407             s = String(ResId(RID_STR_WARNINGBOX_VERSION_GREATER_DIFFERENT_NAMES, *DeploymentGuiResMgr::get()));
408         }
409         s.SearchAndReplaceAllAscii( "$NAME", verExc.NewDisplayName);
410         s.SearchAndReplaceAllAscii( "$OLDNAME", verExc.Deployed->getDisplayName());
411         s.SearchAndReplaceAllAscii( "$NEW", getVersion(verExc.NewVersion) );
412         s.SearchAndReplaceAllAscii( "$DEPLOYED", getVersion(verExc.Deployed) );
413         box.SetMessText(s);
414         bApprove = box.Execute() == RET_OK;
415     }
416 
417     return bApprove;
418 }
419 
420 void ProgressCmdEnv::handle( uno::Reference< task::XInteractionRequest > const & xRequest )
421     throw ( uno::RuntimeException )
422 {
423     uno::Any request( xRequest->getRequest() );
424     OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
425     dp_misc::TRACE( OUSTR("[dp_gui_cmdenv.cxx] incoming request:\n")
426         + ::comphelper::anyToString(request) + OUSTR("\n"));
427 
428     lang::WrappedTargetException wtExc;
429     deployment::DependencyException depExc;
430 	deployment::LicenseException licExc;
431     deployment::VersionException verExc;
432 	deployment::InstallException instExc;
433     deployment::PlatformException platExc;
434 
435     // selections:
436     bool approve = false;
437     bool abort = false;
438 
439     if (request >>= wtExc) {
440         // handable deployment error signalled, e.g.
441         // bundle item registration failed, notify cause only:
442         uno::Any cause;
443         deployment::DeploymentException dpExc;
444         if (wtExc.TargetException >>= dpExc)
445             cause = dpExc.Cause;
446         else {
447             ucb::CommandFailedException cfExc;
448             if (wtExc.TargetException >>= cfExc)
449                 cause = cfExc.Reason;
450             else
451                 cause = wtExc.TargetException;
452         }
453         update_( cause );
454 
455         // ignore intermediate errors of legacy packages, i.e.
456         // former pkgchk behaviour:
457         const uno::Reference< deployment::XPackage > xPackage( wtExc.Context, uno::UNO_QUERY );
458         OSL_ASSERT( xPackage.is() );
459         if ( xPackage.is() )
460         {
461             const uno::Reference< deployment::XPackageTypeInfo > xPackageType( xPackage->getPackageType() );
462             OSL_ASSERT( xPackageType.is() );
463             if (xPackageType.is())
464             {
465                 approve = ( xPackage->isBundle() &&
466                             xPackageType->getMediaType().matchAsciiL(
467                                 RTL_CONSTASCII_STRINGPARAM(
468                                     "application/"
469                                     "vnd.sun.star.legacy-package-bundle") ));
470             }
471         }
472         abort = !approve;
473     }
474     else if (request >>= depExc)
475     {
476         std::vector< rtl::OUString > deps;
477         for (sal_Int32 i = 0; i < depExc.UnsatisfiedDependencies.getLength();
478              ++i)
479         {
480             deps.push_back(
481                 dp_misc::Dependencies::getErrorText( depExc.UnsatisfiedDependencies[i]) );
482         }
483         {
484             vos::OGuard guard(Application::GetSolarMutex());
485             short n = DependencyDialog( m_pDialogHelper? m_pDialogHelper->getWindow() : NULL, deps ).Execute();
486             // Distinguish between closing the dialog and programatically
487             // canceling the dialog (headless VCL):
488             approve = n == RET_OK
489                 || (n == RET_CANCEL && !Application::IsDialogCancelEnabled());
490         }
491     }
492 	else if (request >>= licExc)
493     {
494         uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
495             deployment::ui::LicenseDialog::create(
496             m_xContext, VCLUnoHelper::GetInterface( m_pDialogHelper? m_pDialogHelper->getWindow() : NULL ),
497             licExc.ExtensionName, licExc.Text ) );
498         sal_Int16 res = xDialog->execute();
499         if ( res == ui::dialogs::ExecutableDialogResults::CANCEL )
500             abort = true;
501         else if ( res == ui::dialogs::ExecutableDialogResults::OK )
502             approve = true;
503         else
504         {
505             OSL_ASSERT(0);
506         }
507 	}
508     else if (request >>= verExc)
509     {
510         approve = handleVersionException( verExc, m_pDialogHelper );
511         abort = !approve;
512     }
513 	else if (request >>= instExc)
514 	{
515         if ( ! m_bWarnUser )
516         {
517             approve = true;
518         }
519         else
520         {
521             if ( m_pDialogHelper )
522             {
523                 vos::OGuard guard(Application::GetSolarMutex());
524 
525                 approve = m_pDialogHelper->installExtensionWarn( instExc.displayName );
526             }
527             else
528                 approve = false;
529             abort = !approve;
530         }
531 	}
532     else if (request >>= platExc)
533     {
534         vos::OGuard guard( Application::GetSolarMutex() );
535         String sMsg( ResId( RID_STR_UNSUPPORTED_PLATFORM, *DeploymentGuiResMgr::get() ) );
536         sMsg.SearchAndReplaceAllAscii( "%Name", platExc.package->getDisplayName() );
537         ErrorBox box( m_pDialogHelper? m_pDialogHelper->getWindow() : NULL, WB_OK, sMsg );
538         box.Execute();
539         approve = true;
540     }
541 
542 	if (approve == false && abort == false)
543     {
544         // forward to UUI handler:
545         if (! m_xHandler.is()) {
546             // late init:
547             uno::Sequence< uno::Any > handlerArgs( 1 );
548             handlerArgs[ 0 ] <<= beans::PropertyValue(
549                 OUSTR("Context"), -1, uno::Any( m_sTitle ),
550                 beans::PropertyState_DIRECT_VALUE );
551              m_xHandler.set( m_xContext->getServiceManager()
552                             ->createInstanceWithArgumentsAndContext(
553                                 OUSTR("com.sun.star.uui.InteractionHandler"),
554                                 handlerArgs, m_xContext ), uno::UNO_QUERY_THROW );
555         }
556         m_xHandler->handle( xRequest );
557     }
558 	else
559 	{
560         // select:
561         uno::Sequence< uno::Reference< task::XInteractionContinuation > > conts(
562             xRequest->getContinuations() );
563         uno::Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray();
564         sal_Int32 len = conts.getLength();
565         for ( sal_Int32 pos = 0; pos < len; ++pos )
566         {
567             if (approve) {
568                 uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY );
569                 if (xInteractionApprove.is()) {
570                     xInteractionApprove->select();
571                     // don't query again for ongoing continuations:
572                     approve = false;
573                 }
574             }
575             else if (abort) {
576                 uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY );
577                 if (xInteractionAbort.is()) {
578                     xInteractionAbort->select();
579                     // don't query again for ongoing continuations:
580                     abort = false;
581                 }
582             }
583         }
584 	}
585 }
586 
587 //------------------------------------------------------------------------------
588 // XProgressHandler
589 //------------------------------------------------------------------------------
590 void ProgressCmdEnv::push( uno::Any const & rStatus )
591     throw( uno::RuntimeException )
592 {
593     update_( rStatus );
594 }
595 
596 //------------------------------------------------------------------------------
597 void ProgressCmdEnv::update_( uno::Any const & rStatus )
598     throw( uno::RuntimeException )
599 {
600     OUString text;
601     if ( rStatus.hasValue() && !( rStatus >>= text) )
602     {
603         if ( rStatus.getValueTypeClass() == uno::TypeClass_EXCEPTION )
604             text = static_cast< uno::Exception const *>( rStatus.getValue() )->Message;
605         if ( text.getLength() == 0 )
606             text = ::comphelper::anyToString( rStatus ); // fallback
607 
608         const ::vos::OGuard aGuard( Application::GetSolarMutex() );
609         const ::std::auto_ptr< ErrorBox > aBox( new ErrorBox( m_pDialogHelper? m_pDialogHelper->getWindow() : NULL, WB_OK, text ) );
610         aBox->Execute();
611     }
612     ++m_nCurrentProgress;
613     updateProgress();
614 }
615 
616 //------------------------------------------------------------------------------
617 void ProgressCmdEnv::update( uno::Any const & rStatus )
618     throw( uno::RuntimeException )
619 {
620     update_( rStatus );
621 }
622 
623 //------------------------------------------------------------------------------
624 void ProgressCmdEnv::pop()
625     throw( uno::RuntimeException )
626 {
627     update_( uno::Any() ); // no message
628 }
629 
630 //------------------------------------------------------------------------------
631 ExtensionCmdQueue::Thread::Thread( DialogHelper *pDialogHelper,
632                                    TheExtensionManager *pManager,
633                                    const uno::Reference< uno::XComponentContext > & rContext ) :
634     m_xContext( rContext ),
635     m_pDialogHelper( pDialogHelper ),
636     m_pManager( pManager ),
637     m_sEnablingPackages( DialogHelper::getResourceString( RID_STR_ENABLING_PACKAGES ) ),
638     m_sDisablingPackages( DialogHelper::getResourceString( RID_STR_DISABLING_PACKAGES ) ),
639     m_sAddingPackages( DialogHelper::getResourceString( RID_STR_ADDING_PACKAGES ) ),
640     m_sRemovingPackages( DialogHelper::getResourceString( RID_STR_REMOVING_PACKAGES ) ),
641     m_sDefaultCmd( DialogHelper::getResourceString( RID_STR_ADD_PACKAGES ) ),
642     m_sAcceptLicense( DialogHelper::getResourceString( RID_STR_ACCEPT_LICENSE ) ),
643     m_eInput( NONE ),
644     m_bTerminated( false ),
645     m_bStopped( false ),
646     m_bWorking( false )
647 {
648     OSL_ASSERT( pDialogHelper );
649 }
650 
651 //------------------------------------------------------------------------------
652 void ExtensionCmdQueue::Thread::addExtension( const ::rtl::OUString &rExtensionURL,
653                                               const ::rtl::OUString &rRepository,
654                                               const bool bWarnUser )
655 {
656     ::osl::MutexGuard aGuard( m_mutex );
657 
658     //If someone called stop then we do not add the extension -> game over!
659     if ( m_bStopped )
660         return;
661 
662     if ( rExtensionURL.getLength() )
663     {
664         TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::ADD, rExtensionURL, rRepository, bWarnUser ) );
665 
666         m_queue.push( pEntry );
667         m_eInput = START;
668         m_wakeup.set();
669     }
670 }
671 
672 //------------------------------------------------------------------------------
673 void ExtensionCmdQueue::Thread::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
674 {
675     ::osl::MutexGuard aGuard( m_mutex );
676 
677     //If someone called stop then we do not remove the extension -> game over!
678     if ( m_bStopped )
679         return;
680 
681     if ( rPackage.is() )
682     {
683         TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::REMOVE, rPackage ) );
684 
685         m_queue.push( pEntry );
686         m_eInput = START;
687         m_wakeup.set();
688     }
689 }
690 
691 //------------------------------------------------------------------------------
692 void ExtensionCmdQueue::Thread::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
693 {
694     ::osl::MutexGuard aGuard( m_mutex );
695 
696     //If someone called stop then we do not remove the extension -> game over!
697     if ( m_bStopped )
698         return;
699 
700     if ( rPackage.is() )
701     {
702         TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::ACCEPT_LICENSE, rPackage ) );
703 
704         m_queue.push( pEntry );
705         m_eInput = START;
706         m_wakeup.set();
707     }
708 }
709 
710 //------------------------------------------------------------------------------
711 void ExtensionCmdQueue::Thread::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
712                                                  const bool bEnable )
713 {
714     ::osl::MutexGuard aGuard( m_mutex );
715 
716     //If someone called stop then we do not remove the extension -> game over!
717     if ( m_bStopped )
718         return;
719 
720     if ( rPackage.is() )
721     {
722         TExtensionCmd pEntry( new ExtensionCmd( bEnable ? ExtensionCmd::ENABLE :
723                                                           ExtensionCmd::DISABLE,
724                                                 rPackage ) );
725         m_queue.push( pEntry );
726         m_eInput = START;
727         m_wakeup.set();
728     }
729 }
730 
731 //------------------------------------------------------------------------------
732 void ExtensionCmdQueue::Thread::checkForUpdates(
733     const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
734 {
735     ::osl::MutexGuard aGuard( m_mutex );
736 
737     //If someone called stop then we do not update the extension -> game over!
738     if ( m_bStopped )
739         return;
740 
741     TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::CHECK_FOR_UPDATES, vExtensionList ) );
742     m_queue.push( pEntry );
743     m_eInput = START;
744     m_wakeup.set();
745 }
746 
747 //------------------------------------------------------------------------------
748 //Stopping this thread will not abort the installation of extensions.
749 void ExtensionCmdQueue::Thread::stop()
750 {
751     osl::MutexGuard aGuard( m_mutex );
752     m_bStopped = true;
753     m_eInput = STOP;
754     m_wakeup.set();
755 }
756 
757 //------------------------------------------------------------------------------
758 bool ExtensionCmdQueue::Thread::isBusy()
759 {
760     osl::MutexGuard aGuard( m_mutex );
761     return m_bWorking;
762 }
763 
764 //------------------------------------------------------------------------------
765 ExtensionCmdQueue::Thread::~Thread() {}
766 
767 //------------------------------------------------------------------------------
768 void ExtensionCmdQueue::Thread::execute()
769 {
770 #ifdef WNT
771     //Needed for use of the service "com.sun.star.system.SystemShellExecute" in
772     //DialogHelper::openWebBrowser
773     CoUninitialize();
774     HRESULT r = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
775 #endif
776     for (;;)
777     {
778         if ( m_wakeup.wait() != osl::Condition::result_ok )
779         {
780             dp_misc::TRACE( "dp_gui::ExtensionCmdQueue::Thread::run: ignored "
781                        "osl::Condition::wait failure\n" );
782         }
783         m_wakeup.reset();
784 
785         int nSize;
786         Input eInput;
787         {
788             osl::MutexGuard aGuard( m_mutex );
789             eInput = m_eInput;
790             m_eInput = NONE;
791             nSize = m_queue.size();
792             m_bWorking = false;
793         }
794 
795         // If this thread has been woken up by anything else except start, stop
796         // then input is NONE and we wait again.
797         // We only install the extension which are currently in the queue.
798         // The progressbar will be set to show the progress of the current number
799         // of extensions. If we allowed to add extensions now then the progressbar may
800         // have reached the end while we still install newly added extensions.
801         if ( ( eInput == NONE ) || ( nSize == 0 ) )
802             continue;
803         if ( eInput == STOP )
804             break;
805 
806         ::rtl::Reference< ProgressCmdEnv > currentCmdEnv( new ProgressCmdEnv( m_xContext, m_pDialogHelper, m_sDefaultCmd ) );
807 
808         // Do not lock the following part with addExtension. addExtension may be called in the main thread.
809         // If the message box "Do you want to install the extension (or similar)" is shown and then
810         // addExtension is called, which then blocks the main thread, then we deadlock.
811         bool bStartProgress = true;
812 
813         while ( !currentCmdEnv->isAborted() && --nSize >= 0 )
814         {
815             {
816                 osl::MutexGuard aGuard( m_mutex );
817                 m_bWorking = true;
818             }
819 
820             try
821             {
822                 TExtensionCmd pEntry;
823                 {
824                     ::osl::MutexGuard queueGuard( m_mutex );
825                     pEntry = m_queue.front();
826                     m_queue.pop();
827                 }
828 
829                 if ( bStartProgress && ( pEntry->m_eCmdType != ExtensionCmd::CHECK_FOR_UPDATES ) )
830                 {
831                     currentCmdEnv->startProgress();
832                     bStartProgress = false;
833                 }
834 
835                 switch ( pEntry->m_eCmdType ) {
836                 case ExtensionCmd::ADD :
837                     _addExtension( currentCmdEnv, pEntry->m_sExtensionURL, pEntry->m_sRepository, pEntry->m_bWarnUser );
838                     break;
839                 case ExtensionCmd::REMOVE :
840                     _removeExtension( currentCmdEnv, pEntry->m_xPackage );
841                     break;
842                 case ExtensionCmd::ENABLE :
843                     _enableExtension( currentCmdEnv, pEntry->m_xPackage );
844                     break;
845                 case ExtensionCmd::DISABLE :
846                     _disableExtension( currentCmdEnv, pEntry->m_xPackage );
847                     break;
848                 case ExtensionCmd::CHECK_FOR_UPDATES :
849                     _checkForUpdates( pEntry->m_vExtensionList );
850                     break;
851                 case ExtensionCmd::ACCEPT_LICENSE :
852                     _acceptLicense( currentCmdEnv, pEntry->m_xPackage );
853                     break;
854                 }
855             }
856             //catch ( deployment::DeploymentException &)
857             //{
858             //}
859             //catch ( lang::IllegalArgumentException &)
860             //{
861             //}
862             catch ( ucb::CommandAbortedException & )
863             {
864                 //This exception is thrown when the user clicks cancel on the progressbar.
865                 //Then we cancel the installation of all extensions and remove them from
866                 //the queue.
867                 {
868                     ::osl::MutexGuard queueGuard2(m_mutex);
869                     while ( --nSize >= 0 )
870                         m_queue.pop();
871                 }
872                 break;
873             }
874             catch ( ucb::CommandFailedException & )
875             {
876                 //This exception is thrown when a user clicked cancel in the messagebox which was
877                 //startet by the interaction handler. For example the user will be asked if he/she
878                 //really wants to install the extension.
879                 //These interaction are run for exectly one extension at a time. Therefore we continue
880                 //with installing the remaining extensions.
881                 continue;
882             }
883             catch ( uno::Exception & )
884             {
885                 //Todo display the user an error
886                 //see also DialogImpl::SyncPushButton::Click()
887                 uno::Any exc( ::cppu::getCaughtException() );
888                 OUString msg;
889                 deployment::DeploymentException dpExc;
890                 if ((exc >>= dpExc) &&
891                     dpExc.Cause.getValueTypeClass() == uno::TypeClass_EXCEPTION)
892                 {
893                     // notify error cause only:
894                     msg = reinterpret_cast< uno::Exception const * >( dpExc.Cause.getValue() )->Message;
895                 }
896                 if (msg.getLength() == 0) // fallback for debugging purposes
897                     msg = ::comphelper::anyToString(exc);
898 
899                 const ::vos::OGuard guard( Application::GetSolarMutex() );
900                 ::std::auto_ptr<ErrorBox> box(
901                     new ErrorBox( currentCmdEnv->activeDialog(), WB_OK, msg ) );
902                 if ( m_pDialogHelper )
903                     box->SetText( m_pDialogHelper->getWindow()->GetText() );
904                 box->Execute();
905                     //Continue with installation of the remaining extensions
906             }
907             {
908                 osl::MutexGuard aGuard( m_mutex );
909                 m_bWorking = false;
910             }
911         }
912 
913         {
914             // when leaving the while loop with break, we should set working to false, too
915 			osl::MutexGuard aGuard( m_mutex );
916             m_bWorking = false;
917         }
918 
919 		if ( !bStartProgress )
920             currentCmdEnv->stopProgress();
921     }
922     //end for
923     //enable all buttons
924 //     m_pDialog->m_bAddingExtensions = false;
925 //     m_pDialog->updateButtonStates();
926 #ifdef WNT
927     CoUninitialize();
928 #endif
929 }
930 
931 //------------------------------------------------------------------------------
932 void ExtensionCmdQueue::Thread::_addExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv,
933                                                const OUString &rPackageURL,
934                                                const OUString &rRepository,
935                                                const bool bWarnUser )
936 {
937     //check if we have a string in anyTitle. For example "unopkg gui \" caused anyTitle to be void
938     //and anyTitle.get<OUString> throws as RuntimeException.
939     uno::Any anyTitle;
940 	try
941 	{
942 		anyTitle = ::ucbhelper::Content( rPackageURL, rCmdEnv.get() ).getPropertyValue( OUSTR("Title") );
943 	}
944 	catch ( uno::Exception & )
945 	{
946 		return;
947 	}
948 
949     OUString sName;
950     if ( ! (anyTitle >>= sName) )
951     {
952         OSL_ENSURE(0, "Could not get file name for extension.");
953         return;
954     }
955 
956     rCmdEnv->setWarnUser( bWarnUser );
957     uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
958     uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
959     OUString sTitle = searchAndReplaceAll( m_sAddingPackages, OUSTR("%EXTENSION_NAME"), sName );
960     rCmdEnv->progressSection( sTitle, xAbortChannel );
961 
962     try
963     {
964 		xExtMgr->addExtension(rPackageURL, uno::Sequence<beans::NamedValue>(),
965                               rRepository, xAbortChannel, rCmdEnv.get() );
966     }
967     catch ( ucb::CommandFailedException & )
968     {
969         // When the extension is already installed we'll get a dialog asking if we want to overwrite. If we then press
970         // cancel this exception is thrown.
971     }
972     catch ( ucb::CommandAbortedException & )
973     {
974         // User clicked the cancel button
975         // TODO: handle cancel
976     }
977     rCmdEnv->setWarnUser( false );
978 }
979 
980 //------------------------------------------------------------------------------
981 void ExtensionCmdQueue::Thread::_removeExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv,
982                                                   const uno::Reference< deployment::XPackage > &xPackage )
983 {
984     uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
985     uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
986     OUString sTitle = searchAndReplaceAll( m_sRemovingPackages, OUSTR("%EXTENSION_NAME"), xPackage->getDisplayName() );
987     rCmdEnv->progressSection( sTitle, xAbortChannel );
988 
989     OUString id( dp_misc::getIdentifier( xPackage ) );
990     try
991     {
992         xExtMgr->removeExtension( id, xPackage->getName(), xPackage->getRepositoryName(), xAbortChannel, rCmdEnv.get() );
993     }
994     catch ( deployment::DeploymentException & )
995     {}
996     catch ( ucb::CommandFailedException & )
997     {}
998     catch ( ucb::CommandAbortedException & )
999     {}
1000 
1001     // Check, if there are still updates to be notified via menu bar icon
1002     uno::Sequence< uno::Sequence< rtl::OUString > > aItemList;
1003     UpdateDialog::createNotifyJob( false, aItemList );
1004 }
1005 
1006 //------------------------------------------------------------------------------
1007 void ExtensionCmdQueue::Thread::_checkForUpdates(
1008     const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
1009 {
1010     UpdateDialog* pUpdateDialog;
1011     std::vector< UpdateData > vData;
1012 
1013     const ::vos::OGuard guard( Application::GetSolarMutex() );
1014 
1015     pUpdateDialog = new UpdateDialog( m_xContext, m_pDialogHelper? m_pDialogHelper->getWindow() : NULL, vExtensionList, &vData );
1016 
1017     pUpdateDialog->notifyMenubar( true, false ); // prepare the checking, if there updates to be notified via menu bar icon
1018 
1019     if ( ( pUpdateDialog->Execute() == RET_OK ) && !vData.empty() )
1020     {
1021         // If there is at least one directly downloadable dialog then we
1022         // open the install dialog.
1023         ::std::vector< UpdateData > dataDownload;
1024         int countWebsiteDownload = 0;
1025         typedef std::vector< dp_gui::UpdateData >::const_iterator cit;
1026 
1027         for ( cit i = vData.begin(); i < vData.end(); i++ )
1028         {
1029             if ( i->sWebsiteURL.getLength() > 0 )
1030                 countWebsiteDownload ++;
1031             else
1032                 dataDownload.push_back( *i );
1033         }
1034 
1035         short nDialogResult = RET_OK;
1036         if ( !dataDownload.empty() )
1037         {
1038             nDialogResult = UpdateInstallDialog( m_pDialogHelper? m_pDialogHelper->getWindow() : NULL, dataDownload, m_xContext ).Execute();
1039             pUpdateDialog->notifyMenubar( false, true ); // Check, if there are still pending updates to be notified via menu bar icon
1040         }
1041         else
1042             pUpdateDialog->notifyMenubar( false, false ); // Check, if there are pending updates to be notified via menu bar icon
1043 
1044         //Now start the webbrowser and navigate to the websites where we get the updates
1045         if ( RET_OK == nDialogResult )
1046         {
1047             for ( cit i = vData.begin(); i < vData.end(); i++ )
1048             {
1049                 if ( m_pDialogHelper && ( i->sWebsiteURL.getLength() > 0 ) )
1050                     m_pDialogHelper->openWebBrowser( i->sWebsiteURL, m_pDialogHelper->getWindow()->GetText() );
1051             }
1052         }
1053     }
1054     else
1055         pUpdateDialog->notifyMenubar( false, false ); // check if there updates to be notified via menu bar icon
1056 
1057     delete pUpdateDialog;
1058 }
1059 
1060 //------------------------------------------------------------------------------
1061 void ExtensionCmdQueue::Thread::_enableExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv,
1062                                                   const uno::Reference< deployment::XPackage > &xPackage )
1063 {
1064     if ( !xPackage.is() )
1065         return;
1066 
1067     uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
1068     uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
1069     OUString sTitle = searchAndReplaceAll( m_sEnablingPackages, OUSTR("%EXTENSION_NAME"), xPackage->getDisplayName() );
1070     rCmdEnv->progressSection( sTitle, xAbortChannel );
1071 
1072     try
1073     {
1074         xExtMgr->enableExtension( xPackage, xAbortChannel, rCmdEnv.get() );
1075         if ( m_pDialogHelper )
1076             m_pDialogHelper->updatePackageInfo( xPackage );
1077     }
1078     catch ( ::ucb::CommandAbortedException & )
1079     {}
1080 }
1081 
1082 //------------------------------------------------------------------------------
1083 void ExtensionCmdQueue::Thread::_disableExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv,
1084                                                    const uno::Reference< deployment::XPackage > &xPackage )
1085 {
1086     if ( !xPackage.is() )
1087         return;
1088 
1089     uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
1090     uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
1091     OUString sTitle = searchAndReplaceAll( m_sDisablingPackages, OUSTR("%EXTENSION_NAME"), xPackage->getDisplayName() );
1092     rCmdEnv->progressSection( sTitle, xAbortChannel );
1093 
1094     try
1095     {
1096         xExtMgr->disableExtension( xPackage, xAbortChannel, rCmdEnv.get() );
1097         if ( m_pDialogHelper )
1098             m_pDialogHelper->updatePackageInfo( xPackage );
1099     }
1100     catch ( ::ucb::CommandAbortedException & )
1101     {}
1102 }
1103 
1104 //------------------------------------------------------------------------------
1105 void ExtensionCmdQueue::Thread::_acceptLicense( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv,
1106                                                 const uno::Reference< deployment::XPackage > &xPackage )
1107 {
1108     if ( !xPackage.is() )
1109         return;
1110 
1111     uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
1112     uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
1113     OUString sTitle = searchAndReplaceAll( m_sAcceptLicense, OUSTR("%EXTENSION_NAME"), xPackage->getDisplayName() );
1114     rCmdEnv->progressSection( sTitle, xAbortChannel );
1115 
1116     try
1117     {
1118         xExtMgr->checkPrerequisitesAndEnable( xPackage, xAbortChannel, rCmdEnv.get() );
1119         if ( m_pDialogHelper )
1120             m_pDialogHelper->updatePackageInfo( xPackage );
1121     }
1122     catch ( ::ucb::CommandAbortedException & )
1123     {}
1124 }
1125 
1126 //------------------------------------------------------------------------------
1127 void ExtensionCmdQueue::Thread::onTerminated()
1128 {
1129     ::osl::MutexGuard g(m_mutex);
1130     m_bTerminated = true;
1131 }
1132 
1133 //------------------------------------------------------------------------------
1134 OUString ExtensionCmdQueue::Thread::searchAndReplaceAll( const OUString &rSource,
1135                                                          const OUString &rWhat,
1136                                                          const OUString &rWith )
1137 {
1138     OUString aRet( rSource );
1139     sal_Int32 nLen = rWhat.getLength();
1140 
1141     if ( !nLen )
1142         return aRet;
1143 
1144     sal_Int32 nIndex = rSource.indexOf( rWhat );
1145     while ( nIndex != -1 )
1146     {
1147         aRet = aRet.replaceAt( nIndex, nLen, rWith );
1148         nIndex = aRet.indexOf( rWhat, nIndex + rWith.getLength() );
1149     }
1150     return aRet;
1151 }
1152 
1153 
1154 //------------------------------------------------------------------------------
1155 //------------------------------------------------------------------------------
1156 //------------------------------------------------------------------------------
1157 ExtensionCmdQueue::ExtensionCmdQueue( DialogHelper * pDialogHelper,
1158                                       TheExtensionManager *pManager,
1159                                       const uno::Reference< uno::XComponentContext > &rContext )
1160   : m_thread( new Thread( pDialogHelper, pManager, rContext ) )
1161 {
1162     m_thread->launch();
1163 }
1164 
1165 ExtensionCmdQueue::~ExtensionCmdQueue() {
1166     stop();
1167 }
1168 
1169 void ExtensionCmdQueue::addExtension( const ::rtl::OUString & extensionURL,
1170                                       const ::rtl::OUString & repository,
1171                                       const bool bWarnUser )
1172 {
1173     m_thread->addExtension( extensionURL, repository, bWarnUser );
1174 }
1175 
1176 void ExtensionCmdQueue::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
1177 {
1178     m_thread->removeExtension( rPackage );
1179 }
1180 
1181 void ExtensionCmdQueue::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
1182                                          const bool bEnable )
1183 {
1184     m_thread->enableExtension( rPackage, bEnable );
1185 }
1186 
1187 void ExtensionCmdQueue::checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
1188 {
1189     m_thread->checkForUpdates( vExtensionList );
1190 }
1191 
1192 void ExtensionCmdQueue::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
1193 {
1194     m_thread->acceptLicense( rPackage );
1195 }
1196 
1197 void ExtensionCmdQueue::syncRepositories( const uno::Reference< uno::XComponentContext > &xContext )
1198 {
1199     dp_misc::syncRepositories( new ProgressCmdEnv( xContext, NULL, OUSTR("Extension Manager") ) );
1200 }
1201 
1202 void ExtensionCmdQueue::stop()
1203 {
1204     m_thread->stop();
1205 }
1206 
1207 bool ExtensionCmdQueue::isBusy()
1208 {
1209     return m_thread->isBusy();
1210 }
1211 
1212 void handleInteractionRequest( const uno::Reference< uno::XComponentContext > & xContext,
1213                                const uno::Reference< task::XInteractionRequest > & xRequest )
1214 {
1215     ::rtl::Reference< ProgressCmdEnv > xCmdEnv( new ProgressCmdEnv( xContext, NULL, OUSTR("Extension Manager") ) );
1216     xCmdEnv->handle( xRequest );
1217 }
1218 
1219 } //namespace dp_gui
1220 
1221