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 // __________ Imports __________
25 
26 import com.sun.star.uno.UnoRuntime;
27 
28 import java.lang.String;
29 import java.awt.Component;
30 import javax.swing.JCheckBox;
31 import javax.swing.JLabel;
32 import java.util.Vector;
33 
34 // __________ Implementation __________
35 
36 /**
37  * reacts for status events we listen for
38  * We listen for status events to update our UI.
39  * To know which event must be used for which UI control
40  * we use a special class to do that. Otherwhise we have
41  * to guess it ...
42  *
43  * Further we are frame action listener too.
44  * So we can update our status listener connections and
45  * internal holded dispatch object automaticly.
46  *
47  * Another reason for such extra class for listening:
48  * Most listener callbacks are asynchronoues [oneay] requests.
49  * And it's not allowed to call back synchronously there.
50  * So we must start threads for updating something internaly.
51  *
52  * @author     Andreas Schlüns
53  * @created    15.07.2002 12:36
54  */
55 class StatusListener implements com.sun.star.frame.XStatusListener,
56                                 com.sun.star.frame.XFrameActionListener,
57                                 IShutdownListener,
58                                 IOnewayLink
59 {
60     //_____________________
61 
62     /**
63      * @member m_rControl           reference to the UI control, which should be updated
64      * @member m_sTrueText          this text will be shown at the used UI control as description for an enabled status
65      * @member m_sFalseText         this text will be shown at the used UI control as description for an disabled status
66      * @member m_xDispatch          if we listen for status events, we must hold the dispatch object alive!
67      * @member m_xFrame             reference to the frame, which can provide new dispatch objects if it's neccessary to update it
68      * @member m_aURL               and of course we must be registered for a special URL
69      * @member m_bIsActionListener  indicates if we are currently registered as a listener for frame action events or not
70      * @member m_bIsStatusListener  indicates if we are currently registered as a listener for status events or not
71      * @member m_bDead              there exist more then one way to finish an object of this class - we must know it sometimes
72      */
73     private Component                    m_rControl         ;
74     private String                       m_sTrueText        ;
75     private String                       m_sFalseText       ;
76     private com.sun.star.frame.XDispatch m_xDispatch        ;
77     private com.sun.star.frame.XFrame    m_xFrame           ;
78     private com.sun.star.util.URL        m_aURL             ;
79     private boolean                      m_bIsActionListener;
80     private boolean                      m_bIsStatusListener;
81     private boolean                      m_bDead            ;
82 
83     //_____________________
84 
85     /**
86      * ctor
87      * It initialize an instance of this class only.
88      * We sett all neccessary informations on our internal member - that's it
89      */
StatusListener( Component rControl , String sTrueText , String sFalseText , com.sun.star.frame.XFrame xFrame , String sURL )90     StatusListener( /*IN*/ Component                   rControl   ,
91                     /*IN*/ String                      sTrueText  ,
92                     /*IN*/ String                      sFalseText ,
93                     /*IN*/ com.sun.star.frame.XFrame   xFrame     ,
94                     /*IN*/ String                      sURL       )
95     {
96         m_rControl          = rControl   ;
97         m_sTrueText         = sTrueText  ;
98         m_sFalseText        = sFalseText ;
99         m_xFrame            = xFrame     ;
100         m_bIsStatusListener = false      ;
101         m_bIsActionListener = false      ;
102         m_bDead             = false      ;
103         // to be perform - we parse the given URL  one times only
104         // and use it till we die ...
105         m_aURL = FunctionHelper.parseURL(sURL);
106     }
107 
108     //_____________________
109 
110     /**
111      * start working as frame action listener realy.
112      * In case we get such frame action, it indicates that we should
113      * update our internal saved dispatch object on which we listen
114      * for status events. So we can do it automaticly. The outside code
115      * mustn't check such things. We can work with one frame,
116      * till it die. It doesn't matter if he will be used for different
117      * load/save or any other requests. We will be up to date everytime.
118      */
startListening()119     public void startListening()
120     {
121         com.sun.star.frame.XFrame xFrame = null;
122         synchronized(this)
123         {
124             if (m_bDead)
125                 return;
126             if (m_xFrame==null)
127                 return;
128             if (m_bIsActionListener==true)
129                 return;
130             xFrame = m_xFrame;
131         }
132         xFrame.addFrameActionListener(this);
133         synchronized(this)
134         {
135             m_bIsActionListener=true;
136         }
137     }
138 
139     //_____________________
140 
141     /**
142      * In case we got an oneway listener callback - we had to use the office
143      * asynchronous then. This method is the callback from the started thread
144      * (started inside the original oneway method). We found all parameters of
145      * the original request packed inside a vector. Here we unpack it and
146      * call the right internal helper method, which implements the right
147      * funtionality.
148      *
149      * @seealso frameAction()
150      * @seealso statusChanged()
151      *
152      * @param nRequest
153      *          indicates, which was the original request (identifies the
154      *          original called method)
155      *
156      * @param lParams
157      *          the vector with all packed parameters of the original request
158      */
execOneway( int nRequest, Vector lParams )159     public void execOneway(/*IN*/  int nRequest,/*IN*/  Vector lParams )
160     {
161         synchronized(this)
162         {
163             if (m_bDead)
164                 return;
165         }
166         // was it frameAction()?
167         if (nRequest==OnewayExecutor.REQUEST_FRAMEACTION)
168         {
169             com.sun.star.frame.FrameActionEvent[] lOutAction   = new com.sun.star.frame.FrameActionEvent[1];
170             Vector[]                              lInParams    = new Vector[1];
171                                                   lInParams[0] = lParams;
172 
173             OnewayExecutor.codeFrameAction( OnewayExecutor.DECODE_PARAMS ,
174                                             lInParams                    ,
175                                             lOutAction                   );
176             impl_frameAction(lOutAction[0]);
177         }
178     }
179 
180     //_____________________
181 
182     /**
183      * This is the callback method for such frame action events, we listen for.
184      * Because it's a oneway method we start a thread as reaction. This thread call
185      * us back and we can do neccessary things there.
186      * But we shouldn't start such action - if it's not realy neccessary.
187      * So we check before, if we are intereested on this event realy.
188      *
189      * @see impl_frameAction()
190      *
191      * @param aEvent
192      *          describes the action, which triggered this event
193      */
frameAction( com.sun.star.frame.FrameActionEvent aEvent)194     public /*ONEWAY*/ void frameAction(/*IN*/ com.sun.star.frame.FrameActionEvent aEvent)
195     {
196         synchronized(this)
197         {
198             if (m_bDead)
199                 return;
200         }
201         boolean bHandle = false;
202         switch(aEvent.Action.getValue())
203         {
204             case com.sun.star.frame.FrameAction.COMPONENT_ATTACHED_value   : bHandle=true; break;
205             case com.sun.star.frame.FrameAction.COMPONENT_DETACHING_value  : bHandle=true; break;
206             case com.sun.star.frame.FrameAction.COMPONENT_REATTACHED_value : bHandle=true; break;
207             case com.sun.star.frame.FrameAction.CONTEXT_CHANGED_value      : bHandle=true; break;
208         }
209 
210         if (! bHandle)
211             return;
212 
213         Vector[]                              lOutParams   = new Vector[1];
214         com.sun.star.frame.FrameActionEvent[] lInAction    = new com.sun.star.frame.FrameActionEvent[1];
215                                               lInAction[0] = aEvent;
216 
217         OnewayExecutor.codeFrameAction( OnewayExecutor.ENCODE_PARAMS ,
218                                         lOutParams                   ,
219                                         lInAction                    );
220         OnewayExecutor aExecutor = new OnewayExecutor( (IOnewayLink)this                  ,
221                                                        OnewayExecutor.REQUEST_FRAMEACTION ,
222                                                        lOutParams[0]                      );
223         aExecutor.start();
224     }
225 
226     //_____________________
227 
228     /**
229      * This is the callback method for the status we listen for an wish to show it
230      * on our UI control. Of yourse it's a oneway method ... but we doesn't call back
231      * to the office synchronously here. We update our UI only. So we don't leave this
232      * java process. In such case it's not neccessary to use threads to decouple it.
233      * Do it here and now ...
234      *
235      * @param aEvent
236      *          describes the status, we can use to update our UI control
237      */
statusChanged( com.sun.star.frame.FeatureStateEvent aEvent)238     public /*ONEWAY*/ void statusChanged(/*IN*/ com.sun.star.frame.FeatureStateEvent aEvent)
239     {
240         synchronized(this)
241         {
242             if (m_bDead)
243                 return;
244 
245             // enable/dsiable th control.
246             // Means: If the feature isn't available currently - we can't show an status realy here.
247             // Then we should colorize it gray ...
248             m_rControl.setEnabled(aEvent.IsEnabled);
249 
250             // Only if status is enabled we can look for his value!
251             if (aEvent.IsEnabled)
252             {
253                 // look for the right type ofthe UI control
254                 // Following actions depend on it.
255 
256                 //.............................................................
257                 // it's a check box
258                 if (m_rControl instanceof JCheckBox)
259                 {
260                     JCheckBox aBox = (JCheckBox)m_rControl;
261 
262                     // State must be a boolean value too. Otherwhise must
263                     // ignore this event.
264                     if ( ! (aEvent.State instanceof Boolean ) )
265                         return;
266 
267                     boolean bState = ((Boolean)(aEvent.State)).booleanValue();
268                     aBox.setSelected(bState);
269                     if (bState)
270                         aBox.setText(m_sTrueText);
271                     else
272                         aBox.setText(m_sFalseText);
273                 }
274                 else
275                 //.............................................................
276                 // it's a label
277                 if (m_rControl instanceof JLabel)
278                 {
279                     JLabel aLabel = (JLabel)m_rControl;
280 
281                     // Detect type of state value
282                     // and set it on internal well known UI control
283                     // But do it only, if value realy change.
284                     if(aEvent.State instanceof String)
285                     {
286                         String sState = (String)aEvent.State;
287                         aLabel.setText(sState);
288                     }
289                     else
290                     if(aEvent.State instanceof Boolean)
291                     {
292                         boolean bState = ((Boolean)aEvent.State).booleanValue();
293                         if (bState)
294                             aLabel.setText(m_sTrueText);
295                         else
296                             aLabel.setText(m_sFalseText);
297                     }
298                     else
299                     if(aEvent.State instanceof Float)
300                     {
301                         String sState = ((Float)aEvent.State).toString();
302                         aLabel.setText(sState);
303                     }
304                 }
305             }
306         }
307     }
308 
309     //_____________________
310 
311     /**
312      * Internal call back for frame action events, triggered by the used
313      * OnewayExecutor thread we started in frameAction().
314      * We use it to update internal saved dispatch object and the corresponding
315      * listener connection for status events.
316      *
317      * @param aEvent
318      *          describes the action
319      */
impl_frameAction( com.sun.star.frame.FrameActionEvent aEvent)320     public void impl_frameAction(/*IN*/ com.sun.star.frame.FrameActionEvent aEvent)
321     {
322         synchronized(this)
323         {
324             if (m_bDead)
325                 return;
326         }
327         // Don't look for ignoring actions - it was done already inside original frameAction() call!
328         // deregistration as status listener will be done here everytime - but registration only, if neccessary!
329         boolean bRegister = false;
330         switch(aEvent.Action.getValue())
331         {
332             case com.sun.star.frame.FrameAction.COMPONENT_ATTACHED_value   : bRegister=true ; break;
333             case com.sun.star.frame.FrameAction.COMPONENT_DETACHING_value  : bRegister=false; break;
334             case com.sun.star.frame.FrameAction.COMPONENT_REATTACHED_value : bRegister=true ; break;
335             case com.sun.star.frame.FrameAction.CONTEXT_CHANGED_value      : bRegister=true ; break;
336         }
337 
338         boolean                      bIsStatusListener = false;
339         com.sun.star.frame.XFrame    xFrame            = null ;
340         com.sun.star.frame.XDispatch xDispatch         = null ;
341         com.sun.star.util.URL        aURL              = null ;
342         synchronized(this)
343         {
344             bIsStatusListener   = m_bIsStatusListener;
345             m_bIsStatusListener = false;
346 
347             xDispatch   = m_xDispatch;
348             m_xDispatch = null;
349 
350             aURL        = m_aURL;
351             xFrame      = m_xFrame;
352         }
353 
354         if (bIsStatusListener)
355             xDispatch.removeStatusListener(this,aURL);
356         xDispatch = null;
357 
358         if (! bRegister)
359             return;
360 
361         com.sun.star.frame.XDispatchProvider xProvider = (com.sun.star.frame.XDispatchProvider)UnoRuntime.queryInterface(
362             com.sun.star.frame.XDispatchProvider.class,
363             xFrame);
364 
365         if (xProvider==null)
366             return;
367 
368         xDispatch = xProvider.queryDispatch(aURL,"",0);
369 
370         if (xDispatch==null)
371             return;
372 
373         xDispatch.addStatusListener(this,aURL);
374         synchronized(this)
375         {
376             m_xDispatch         = xDispatch;
377             m_bIsStatusListener = true;
378         }
379     }
380 
381     // ____________________
382 
383     /**
384      * callback for disposing events
385      * Our dispatch or frame object inform us about his following dead ...
386      * So we must forget his reference. But it's not neccessary to
387      * remove listener connections here. Because the broadcaster
388      * forget us automaticly. The only thing we have to do: release
389      * his reference and let him die!
390      *
391      * @param aEvent
392      *          describes the source which fire this event
393      *          Must be our internal saved dispatch or frame. Otherwhise
394      *          somewhere know us without a registration ...
395      */
disposing( com.sun.star.lang.EventObject aEvent)396     public /*ONEWAY*/ void disposing(/*IN*/ com.sun.star.lang.EventObject aEvent)
397     {
398         synchronized(this)
399         {
400             if (m_bDead)
401                 return;
402             if (m_xFrame!=null && UnoRuntime.areSame(aEvent.Source,m_xFrame))
403             {
404                 m_bIsActionListener = false;
405                 m_xFrame            = null ;
406             }
407             else
408             if (m_xDispatch!=null && UnoRuntime.areSame(aEvent.Source,m_xDispatch))
409             {
410                 m_bIsStatusListener = false;
411                 m_xDispatch         = null ;
412                 m_aURL              = null ;
413             }
414         }
415         shutdown();
416     }
417 
418     // ____________________
419 
420     /**
421      * If this java application shutdown - we must cancel all current existing
422      * listener connections. Otherwhise the office will run into some
423      * DisposedExceptions if it tries to use these forgotten listener references.
424      * And of course it can die doing that.
425      * We are registered at a central object to be informed if the VM will exit.
426      * So we can react.
427      */
shutdown()428     public void shutdown()
429     {
430         boolean                      bIsActionListener = false;
431         boolean                      bIsStatusListener = false;
432         com.sun.star.frame.XFrame    xFrame            = null ;
433         com.sun.star.frame.XDispatch xDispatch         = null ;
434         com.sun.star.util.URL        aURL              = null ;
435         synchronized(this)
436         {
437             // don't react a second time here!
438             if (m_bDead)
439                 return;
440             m_bDead = true;
441 
442             bIsActionListener   = m_bIsActionListener;
443             m_bIsActionListener = false;
444 
445             bIsStatusListener   = m_bIsStatusListener;
446             m_bIsStatusListener = false;
447 
448             xFrame              = m_xFrame;
449             m_xFrame            = null;
450 
451             xDispatch           = m_xDispatch;
452             m_xDispatch         = null;
453 
454             aURL                = m_aURL;
455             m_aURL              = null;
456         }
457 
458         if (bIsStatusListener)
459             xDispatch.removeStatusListener(this,aURL);
460         xDispatch = null ;
461         aURL      = null ;
462 
463         if (bIsActionListener)
464             xFrame.removeFrameActionListener(this);
465         xFrame = null ;
466     }
467 }
468