xref: /trunk/main/sfx2/source/appl/impldde.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sfx2.hxx"
30 
31 #if defined(WNT)
32 #include <tools/svwin.h>
33 #endif
34 
35 #include "impldde.hxx"
36 
37 #include <vcl/svapp.hxx>
38 #include <vcl/fixed.hxx>
39 #include <vcl/edit.hxx>
40 #include <vcl/button.hxx>
41 #include <vcl/msgbox.hxx>
42 #include <sot/exchange.hxx>
43 #include <rtl/ustring.hxx>
44 
45 #include "dde.hrc"
46 #include <sfx2/lnkbase.hxx>
47 #include <sfx2/linkmgr.hxx>
48 #include "sfx2/sfxresid.hxx"
49 
50 #include <com/sun/star/uno/Any.hxx>
51 #include <com/sun/star/uno/Sequence.hxx>
52 
53 #include <svl/svdde.hxx>
54 #include <sot/formats.hxx>
55 
56 #define DDELINK_COLD        0
57 #define DDELINK_HOT         1
58 
59 #define DDELINK_ERROR_APP   1
60 #define DDELINK_ERROR_DATA  2
61 #define DDELINK_ERROR_LINK  3
62 
63 using namespace ::com::sun::star::uno;
64 
65 namespace sfx2
66 {
67 
68 class SvDDELinkEditDialog : public ModalDialog
69 {
70     FixedText aFtDdeApp;
71     Edit aEdDdeApp;
72     FixedText aFtDdeTopic;
73     Edit aEdDdeTopic;
74     FixedText aFtDdeItem;
75     Edit aEdDdeItem;
76     FixedLine aGroupDdeChg;
77     OKButton aOKButton1;
78     CancelButton aCancelButton1;
79 
80     DECL_STATIC_LINK( SvDDELinkEditDialog, EditHdl_Impl, Edit* );
81 public:
82     SvDDELinkEditDialog( Window* pParent, SvBaseLink* );
83     String GetCmd() const;
84 };
85 
86 SvDDELinkEditDialog::SvDDELinkEditDialog( Window* pParent, SvBaseLink* pLink )
87     : ModalDialog( pParent, SfxResId( MD_DDE_LINKEDIT ) ),
88     aFtDdeApp( this, SfxResId( FT_DDE_APP ) ),
89     aEdDdeApp( this, SfxResId( ED_DDE_APP ) ),
90     aFtDdeTopic( this, SfxResId( FT_DDE_TOPIC ) ),
91     aEdDdeTopic( this, SfxResId( ED_DDE_TOPIC ) ),
92     aFtDdeItem( this, SfxResId( FT_DDE_ITEM ) ),
93     aEdDdeItem( this, SfxResId( ED_DDE_ITEM ) ),
94     aGroupDdeChg( this, SfxResId( GROUP_DDE_CHG ) ),
95     aOKButton1( this, SfxResId( 1 ) ),
96     aCancelButton1( this, SfxResId( 1 ) )
97 {
98     FreeResource();
99 
100     String sServer, sTopic, sItem;
101     pLink->GetLinkManager()->GetDisplayNames( pLink, &sServer, &sTopic, &sItem );
102 
103     aEdDdeApp.SetText( sServer );
104     aEdDdeTopic.SetText( sTopic );
105     aEdDdeItem.SetText( sItem );
106 
107     aEdDdeApp.SetModifyHdl( STATIC_LINK( this, SvDDELinkEditDialog, EditHdl_Impl));
108     aEdDdeTopic.SetModifyHdl( STATIC_LINK( this, SvDDELinkEditDialog, EditHdl_Impl));
109     aEdDdeItem.SetModifyHdl( STATIC_LINK( this, SvDDELinkEditDialog, EditHdl_Impl));
110 
111     aOKButton1.Enable( sServer.Len() && sTopic.Len() && sItem.Len() );
112 }
113 
114 String SvDDELinkEditDialog::GetCmd() const
115 {
116     String sCmd( aEdDdeApp.GetText() ), sRet;
117     ::sfx2::MakeLnkName( sRet, &sCmd, aEdDdeTopic.GetText(), aEdDdeItem.GetText() );
118     return sRet;
119 }
120 
121 IMPL_STATIC_LINK( SvDDELinkEditDialog, EditHdl_Impl, Edit *, pEdit )
122 {
123     (void)pEdit; // unused variable
124     pThis->aOKButton1.Enable( pThis->aEdDdeApp.GetText().Len() &&
125                               pThis->aEdDdeTopic.GetText().Len() &&
126                               pThis->aEdDdeItem.GetText().Len() );
127     return 0;
128 }
129 
130 /*  */
131 
132 
133 SvDDEObject::SvDDEObject()
134     : pConnection( 0 ), pLink( 0 ), pRequest( 0 ), pGetData( 0 ), nError( 0 )
135 {
136     SetUpdateTimeout( 100 );
137     bWaitForData = sal_False;
138 }
139 
140 SvDDEObject::~SvDDEObject()
141 {
142     delete pLink;
143     delete pRequest;
144     delete pConnection;
145 }
146 
147 sal_Bool SvDDEObject::GetData( ::com::sun::star::uno::Any & rData /*out param*/,
148                             const String & rMimeType,
149                             sal_Bool bSynchron )
150 {
151     if( !pConnection )
152         return sal_False;
153 
154     if( pConnection->GetError() )       // dann versuchen wir es nochmal
155     {
156         String sServer( pConnection->GetServiceName() );
157         String sTopic( pConnection->GetTopicName() );
158 
159         delete pConnection;
160         pConnection = new DdeConnection( sServer, sTopic );
161         if( pConnection->GetError() )
162             nError = DDELINK_ERROR_APP;
163     }
164 
165     if( bWaitForData )      // wir sind rekursiv drin, wieder raus
166         return sal_False;
167 
168     // Verriegeln gegen Reentrance
169     bWaitForData = sal_True;
170 
171     // falls gedruckt werden soll, warten wir bis die Daten vorhanden sind
172     if( bSynchron )
173     {
174         DdeRequest aReq( *pConnection, sItem, 5000 );
175         aReq.SetDataHdl( LINK( this, SvDDEObject, ImplGetDDEData ) );
176         aReq.SetFormat( SotExchange::GetFormatIdFromMimeType( rMimeType ));
177 
178         pGetData = &rData;
179 
180         do {
181             aReq.Execute();
182         } while( aReq.GetError() && ImplHasOtherFormat( aReq ) );
183 
184         if( pConnection->GetError() )
185             nError = DDELINK_ERROR_DATA;
186 
187         bWaitForData = sal_False;
188     }
189     else
190     {
191         // ansonsten wird es asynchron ausgefuehrt
192 //      if( !pLink || !pLink->IsBusy() )
193         {
194             if( pRequest )
195                 delete pRequest;
196 
197             pRequest = new DdeRequest( *pConnection, sItem );
198             pRequest->SetDataHdl( LINK( this, SvDDEObject, ImplGetDDEData ) );
199             pRequest->SetDoneHdl( LINK( this, SvDDEObject, ImplDoneDDEData ) );
200             pRequest->SetFormat( SotExchange::GetFormatIdFromMimeType(
201                                     rMimeType ) );
202             pRequest->Execute();
203         }
204 
205         ::rtl::OUString aEmptyStr;
206         rData <<= aEmptyStr;
207     }
208     return 0 == pConnection->GetError();
209 }
210 
211 
212 sal_Bool SvDDEObject::Connect( SvBaseLink * pSvLink )
213 {
214 #if defined(WNT)
215     static sal_Bool bInWinExec = sal_False;
216 #endif
217     sal_uInt16 nLinkType = pSvLink->GetUpdateMode();
218     if( pConnection )       // Verbindung steht ja schon
219     {
220         // tja, dann nur noch als Abhaengig eintragen
221         AddDataAdvise( pSvLink,
222                 SotExchange::GetFormatMimeType( pSvLink->GetContentType()),
223                 LINKUPDATE_ONCALL == nLinkType
224                         ? ADVISEMODE_ONLYONCE
225                         : 0 );
226         AddConnectAdvise( pSvLink );
227 
228         return sal_True;
229     }
230 
231     if( !pSvLink->GetLinkManager() )
232         return sal_False;
233 
234     String sServer, sTopic;
235     pSvLink->GetLinkManager()->GetDisplayNames( pSvLink, &sServer, &sTopic, &sItem );
236 
237     if( !sServer.Len() || !sTopic.Len() || !sItem.Len() )
238         return sal_False;
239 
240     pConnection = new DdeConnection( sServer, sTopic );
241     if( pConnection->GetError() )
242     {
243         // kann man denn das System-Topic ansprechen ?
244         // dann ist der Server oben, kennt nur nicht das Topic!
245         if( sTopic.EqualsIgnoreCaseAscii( "SYSTEM" ) )
246         {
247             sal_Bool bSysTopic;
248             {
249                 DdeConnection aTmp( sServer, String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "SYSTEM" ) ) );
250                 bSysTopic = !aTmp.GetError();
251             }
252 
253             if( bSysTopic )
254             {
255                 nError = DDELINK_ERROR_DATA;
256                 return sal_False;
257             }
258             // ansonsten unter Win/WinNT die Applikation direkt starten
259         }
260 
261 #if defined(WNT)
262 
263         // Server nicht da, starten und nochmal versuchen
264         if( !bInWinExec )
265         {
266             ByteString aCmdLine( sServer, RTL_TEXTENCODING_ASCII_US );
267             aCmdLine.Append( ".exe " );
268             aCmdLine.Append( ByteString( sTopic, RTL_TEXTENCODING_ASCII_US ) );
269 
270             if( WinExec( aCmdLine.GetBuffer(), SW_SHOWMINIMIZED ) < 32 )
271                 nError = DDELINK_ERROR_APP;
272             else
273             {
274                 sal_uInt16 i;
275                 for( i=0; i<5; i++ )
276                 {
277                     bInWinExec = sal_True;
278                     Application::Reschedule();
279                     bInWinExec = sal_False;
280 
281                     delete pConnection;
282                     pConnection = new DdeConnection( sServer, sTopic );
283                     if( !pConnection->GetError() )
284                         break;
285                 }
286 
287                 if( i == 5 )
288                 {
289                     nError = DDELINK_ERROR_APP;
290                 }
291             }
292         }
293         else
294 #endif  // WNT
295         {
296             nError = DDELINK_ERROR_APP;
297         }
298     }
299 
300     if( LINKUPDATE_ALWAYS == nLinkType && !pLink && !pConnection->GetError() )
301     {
302         // Hot Link einrichten, Daten kommen irgendwann spaeter
303         pLink = new DdeHotLink( *pConnection, sItem );
304         pLink->SetDataHdl( LINK( this, SvDDEObject, ImplGetDDEData ) );
305         pLink->SetDoneHdl( LINK( this, SvDDEObject, ImplDoneDDEData ) );
306         pLink->SetFormat( pSvLink->GetContentType() );
307         pLink->Execute();
308     }
309 
310     if( pConnection->GetError() )
311         return sal_False;
312 
313     AddDataAdvise( pSvLink,
314                 SotExchange::GetFormatMimeType( pSvLink->GetContentType()),
315                 LINKUPDATE_ONCALL == nLinkType
316                         ? ADVISEMODE_ONLYONCE
317                         : 0 );
318     AddConnectAdvise( pSvLink );
319     SetUpdateTimeout( 0 );
320     return sal_True;
321 }
322 
323 void SvDDEObject::Edit( Window* pParent, sfx2::SvBaseLink* pBaseLink, const Link& rEndEditHdl )
324 {
325     SvDDELinkEditDialog aDlg( pParent, pBaseLink );
326     if ( RET_OK == aDlg.Execute() && rEndEditHdl.IsSet() )
327     {
328         String sCommand = aDlg.GetCmd();
329         rEndEditHdl.Call( &sCommand );
330     }
331 }
332 
333 sal_Bool SvDDEObject::ImplHasOtherFormat( DdeTransaction& rReq )
334 {
335     sal_uInt16 nFmt = 0;
336     switch( rReq.GetFormat() )
337     {
338     case FORMAT_RTF:
339         nFmt = FORMAT_STRING;
340         break;
341 
342     case SOT_FORMATSTR_ID_HTML_SIMPLE:
343     case SOT_FORMATSTR_ID_HTML:
344         nFmt = FORMAT_RTF;
345         break;
346 
347     case FORMAT_GDIMETAFILE:
348         nFmt = FORMAT_BITMAP;
349         break;
350 
351     case SOT_FORMATSTR_ID_SVXB:
352         nFmt = FORMAT_GDIMETAFILE;
353         break;
354 
355     // sonst noch irgendwas ??
356     }
357     if( nFmt )
358         rReq.SetFormat( nFmt );     // damit nochmal versuchen
359     return 0 != nFmt;
360 }
361 
362 sal_Bool SvDDEObject::IsPending() const
363 /*  [Beschreibung]
364 
365     Die Methode stellt fest, ob aus einem DDE-Object die Daten gelesen
366     werden kann.
367     Zurueckgegeben wird:
368         ERRCODE_NONE            wenn sie komplett gelesen wurde
369         ERRCODE_SO_PENDING      wenn sie noch nicht komplett gelesen wurde
370         ERRCODE_SO_FALSE        sonst
371 */
372 {
373     return bWaitForData;
374 }
375 
376 sal_Bool SvDDEObject::IsDataComplete() const
377 {
378     return bWaitForData;
379 }
380 
381 IMPL_LINK( SvDDEObject, ImplGetDDEData, DdeData*, pData )
382 {
383     sal_uIntPtr nFmt = pData->GetFormat();
384     switch( nFmt )
385     {
386     case FORMAT_GDIMETAFILE:
387         break;
388 
389     case FORMAT_BITMAP:
390         break;
391 
392     default:
393         {
394             const sal_Char* p = (sal_Char*)( pData->operator const void*() );
395             long nLen = FORMAT_STRING == nFmt ? (p ? strlen( p ) : 0) : (long)*pData;
396 
397             Sequence< sal_Int8 > aSeq( (const sal_Int8*)p, nLen );
398             if( pGetData )
399             {
400                 *pGetData <<= aSeq;     // Daten kopieren
401                 pGetData = 0;           // und den Pointer bei mir zuruecksetzen
402             }
403             else
404             {
405                 Any aVal;
406                 aVal <<= aSeq;
407                 DataChanged( SotExchange::GetFormatMimeType(
408                                                 pData->GetFormat() ), aVal );
409                 bWaitForData = sal_False;
410             }
411         }
412     }
413 
414     return 0;
415 }
416 
417 IMPL_LINK( SvDDEObject, ImplDoneDDEData, void*, pData )
418 {
419     sal_Bool bValid = (sal_Bool)(sal_uIntPtr)pData;
420     if( !bValid && ( pRequest || pLink ))
421     {
422         DdeTransaction* pReq = 0;
423         if( !pLink || ( pLink && pLink->IsBusy() ))
424             pReq = pRequest;        // dann kann nur der fertig sein
425         else if( pRequest && pRequest->IsBusy() )
426             pReq = pLink;           // dann kann nur der fertig sein
427 
428         if( pReq )
429         {
430             if( ImplHasOtherFormat( *pReq ) )
431             {
432                 pReq->Execute();
433             }
434             else if( pReq == pRequest )
435             {
436                 // das wars dann
437                 bWaitForData = sal_False;
438             }
439         }
440     }
441     else
442         // das warten ist beendet
443         bWaitForData = sal_False;
444 
445     return 0;
446 }
447 
448 }
449