Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "sal/config.h"
21 :
22 : #include <algorithm>
23 : #include <cassert>
24 :
25 : #include <ucbhelper/content.hxx>
26 : #include <osl/mutex.hxx>
27 : #include <vcl/svapp.hxx>
28 : #include <vcl/msgbox.hxx>
29 : #include <avmedia/mediawindow.hxx>
30 : #include <unotools/pathoptions.hxx>
31 : #include <sfx2/opengrf.hxx>
32 : #include <vcl/graphicfilter.hxx>
33 : #include <svx/gallery1.hxx>
34 : #include <svx/galtheme.hxx>
35 : #include "cuigaldlg.hxx"
36 : #include "helpid.hrc"
37 : #include <unotools/syslocale.hxx>
38 : #include <cppuhelper/implbase1.hxx>
39 : #include <com/sun/star/uno/Reference.hxx>
40 : #include <com/sun/star/lang/XInitialization.hpp>
41 : #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
42 : #include <comphelper/processfactory.hxx>
43 : #include <com/sun/star/sdbc/XResultSet.hpp>
44 : #include <com/sun/star/sdbc/XRow.hpp>
45 : #include <com/sun/star/ucb/XContentAccess.hpp>
46 : #include <com/sun/star/ui/dialogs/FolderPicker.hpp>
47 : #include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
48 : #include <sfx2/sfxuno.hxx>
49 : #include "dialmgr.hxx"
50 : #include "gallery.hrc"
51 : #include <svx/dialogs.hrc>
52 : #include <svx/dialmgr.hxx>
53 :
54 :
55 :
56 : // - Namespaces -
57 :
58 :
59 : using namespace ::ucbhelper;
60 : using namespace ::rtl;
61 : using namespace ::cppu;
62 : using namespace ::com::sun::star::lang;
63 : using namespace ::com::sun::star::sdbc;
64 : using namespace ::com::sun::star::ucb;
65 : using namespace ::com::sun::star::ui::dialogs;
66 : using namespace ::com::sun::star::uno;
67 :
68 :
69 : // - SearchThread -
70 :
71 :
72 0 : SearchThread::SearchThread( SearchProgress* pProgess,
73 : TPGalleryThemeProperties* pBrowser,
74 : const INetURLObject& rStartURL ) :
75 : Thread ( "cuiSearchThread" ),
76 : mpProgress ( pProgess ),
77 : mpBrowser ( pBrowser ),
78 0 : maStartURL ( rStartURL )
79 : {
80 0 : }
81 :
82 :
83 :
84 0 : SearchThread::~SearchThread()
85 : {
86 0 : }
87 :
88 :
89 :
90 0 : void SearchThread::execute()
91 : {
92 0 : const OUString aFileType( mpBrowser->m_pCbbFileType->GetText() );
93 :
94 0 : if( !aFileType.isEmpty() )
95 : {
96 0 : const sal_uInt16 nFileNumber = mpBrowser->m_pCbbFileType->GetEntryPos( aFileType );
97 : sal_uInt16 nBeginFormat, nEndFormat;
98 0 : ::std::vector< OUString > aFormats;
99 :
100 0 : if( !nFileNumber || ( nFileNumber >= mpBrowser->m_pCbbFileType->GetEntryCount() ) )
101 : {
102 0 : nBeginFormat = 1;
103 0 : nEndFormat = mpBrowser->m_pCbbFileType->GetEntryCount() - 1;
104 : }
105 : else
106 0 : nBeginFormat = nEndFormat = nFileNumber;
107 :
108 0 : for( sal_uInt16 i = nBeginFormat; i <= nEndFormat; ++i )
109 0 : aFormats.push_back( mpBrowser->aFilterEntryList[ i ]->aFilterName.toAsciiLowerCase() );
110 :
111 0 : ImplSearch( maStartURL, aFormats, mpBrowser->bSearchRecursive );
112 : }
113 :
114 0 : Application::PostUserEvent( LINK( mpProgress, SearchProgress, CleanUpHdl ) );
115 0 : }
116 :
117 :
118 :
119 0 : void SearchThread::ImplSearch( const INetURLObject& rStartURL,
120 : const ::std::vector< OUString >& rFormats,
121 : sal_Bool bRecursive )
122 : {
123 : {
124 0 : SolarMutexGuard aGuard;
125 :
126 0 : mpProgress->SetDirectory( rStartURL );
127 0 : mpProgress->Sync();
128 : }
129 :
130 : try
131 : {
132 0 : ::com::sun::star::uno::Reference< XCommandEnvironment > xEnv;
133 0 : Content aCnt( rStartURL.GetMainURL( INetURLObject::NO_DECODE ), xEnv, comphelper::getProcessComponentContext() );
134 0 : Sequence< OUString > aProps( 2 );
135 :
136 0 : aProps.getArray()[ 0 ] = "IsFolder";
137 0 : aProps.getArray()[ 1 ] = "IsDocument";
138 : ::com::sun::star::uno::Reference< XResultSet > xResultSet(
139 0 : aCnt.createCursor( aProps, INCLUDE_FOLDERS_AND_DOCUMENTS ) );
140 :
141 0 : if( xResultSet.is() )
142 : {
143 0 : ::com::sun::star::uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW );
144 0 : ::com::sun::star::uno::Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW );
145 :
146 0 : while( xResultSet->next() && schedule() )
147 : {
148 0 : INetURLObject aFoundURL( xContentAccess->queryContentIdentifierString() );
149 : DBG_ASSERT( aFoundURL.GetProtocol() != INET_PROT_NOT_VALID, "invalid URL" );
150 :
151 0 : sal_Bool bFolder = xRow->getBoolean( 1 ); // property "IsFolder"
152 0 : if ( xRow->wasNull() )
153 0 : bFolder = sal_False;
154 :
155 0 : if( bRecursive && bFolder )
156 0 : ImplSearch( aFoundURL, rFormats, sal_True );
157 : else
158 : {
159 0 : sal_Bool bDocument = xRow->getBoolean( 2 ); // property "IsDocument"
160 0 : if ( xRow->wasNull() )
161 0 : bDocument = sal_False;
162 :
163 0 : if( bDocument )
164 : {
165 0 : GraphicDescriptor aDesc( aFoundURL );
166 :
167 0 : if( ( aDesc.Detect() &&
168 : ::std::find( rFormats.begin(),
169 : rFormats.end(),
170 : aDesc.GetImportFormatShortName(
171 0 : aDesc.GetFileFormat() ).toAsciiLowerCase() )
172 0 : != rFormats.end() ) ||
173 : ::std::find( rFormats.begin(),
174 : rFormats.end(),
175 0 : aFoundURL.GetExtension().toAsciiLowerCase() )
176 0 : != rFormats.end() )
177 : {
178 0 : SolarMutexGuard aGuard;
179 :
180 : mpBrowser->aFoundList.push_back(
181 : aFoundURL.GetMainURL( INetURLObject::NO_DECODE )
182 0 : );
183 : mpBrowser->m_pLbxFound->InsertEntry(
184 : GetReducedString( aFoundURL, 50 ),
185 0 : (sal_uInt16) mpBrowser->aFoundList.size() - 1 );
186 0 : }
187 : }
188 : }
189 0 : }
190 0 : }
191 : }
192 0 : catch (const ContentCreationException&)
193 : {
194 : }
195 0 : catch (const ::com::sun::star::uno::RuntimeException&)
196 : {
197 : }
198 0 : catch (const ::com::sun::star::uno::Exception&)
199 : {
200 : }
201 0 : }
202 :
203 :
204 : // - SearchProgress -
205 :
206 :
207 0 : SearchProgress::SearchProgress( Window* pParent, const INetURLObject& rStartURL )
208 : : ModalDialog(pParent, "GallerySearchProgress", "cui/ui/gallerysearchprogress.ui")
209 : , parent_(pParent)
210 0 : , startUrl_(rStartURL)
211 : {
212 0 : get(m_pFtSearchDir, "dir");
213 0 : get(m_pFtSearchType, "file");
214 0 : m_pFtSearchType->set_width_request(m_pFtSearchType->get_preferred_size().Width());
215 0 : get(m_pBtnCancel, "cancel");
216 0 : m_pBtnCancel->SetClickHdl( LINK( this, SearchProgress, ClickCancelBtn ) );
217 0 : }
218 :
219 :
220 :
221 0 : void SearchProgress::Terminate()
222 : {
223 0 : if (maSearchThread.is())
224 0 : maSearchThread->terminate();
225 0 : }
226 :
227 :
228 :
229 0 : IMPL_LINK_NOARG(SearchProgress, ClickCancelBtn)
230 : {
231 0 : Terminate();
232 0 : return 0L;
233 : }
234 :
235 :
236 :
237 0 : IMPL_LINK_NOARG(SearchProgress, CleanUpHdl)
238 : {
239 0 : if (maSearchThread.is())
240 0 : maSearchThread->join();
241 :
242 0 : EndDialog( RET_OK );
243 :
244 0 : delete this;
245 0 : return 0L;
246 : }
247 :
248 :
249 :
250 0 : short SearchProgress::Execute()
251 : {
252 : OSL_FAIL( "SearchProgress cannot be executed via Dialog::Execute!\n"
253 : "It creates a thread that will call back to VCL apartment => deadlock!\n"
254 : "Use Dialog::StartExecuteModal to execute the dialog!" );
255 0 : return RET_CANCEL;
256 : }
257 :
258 :
259 :
260 0 : void SearchProgress::StartExecuteModal( const Link& rEndDialogHdl )
261 : {
262 : assert(!maSearchThread.is());
263 0 : maSearchThread = new SearchThread(
264 0 : this, static_cast< TPGalleryThemeProperties * >(parent_), startUrl_);
265 0 : maSearchThread->launch();
266 0 : ModalDialog::StartExecuteModal( rEndDialogHdl );
267 0 : }
268 :
269 :
270 : // - TakeThread -
271 :
272 :
273 0 : TakeThread::TakeThread(
274 : TakeProgress* pProgess,
275 : TPGalleryThemeProperties* pBrowser,
276 : TokenList_impl& rTakenList
277 : ) :
278 : Thread ( "cuiTakeThread" ),
279 : mpProgress ( pProgess ),
280 : mpBrowser ( pBrowser ),
281 0 : mrTakenList ( rTakenList )
282 : {
283 0 : }
284 :
285 :
286 :
287 0 : TakeThread::~TakeThread()
288 : {
289 0 : }
290 :
291 :
292 :
293 0 : void TakeThread::execute()
294 : {
295 0 : INetURLObject aURL;
296 : sal_uInt16 nEntries;
297 0 : GalleryTheme* pThm = mpBrowser->GetXChgData()->pTheme;
298 : sal_uInt16 nPos;
299 : GalleryProgress* pStatusProgress;
300 :
301 : {
302 0 : SolarMutexGuard aGuard;
303 0 : pStatusProgress = new GalleryProgress;
304 0 : nEntries = mpBrowser->bTakeAll ? mpBrowser->m_pLbxFound->GetEntryCount() : mpBrowser->m_pLbxFound->GetSelectEntryCount();
305 0 : pThm->LockBroadcaster();
306 : }
307 :
308 0 : for( sal_uInt16 i = 0; i < nEntries && schedule(); i++ )
309 : {
310 0 : if( mpBrowser->bTakeAll )
311 0 : aURL = INetURLObject( mpBrowser->aFoundList[ nPos = i ] );
312 : else
313 0 : aURL = INetURLObject( mpBrowser->aFoundList[ nPos = mpBrowser->m_pLbxFound->GetSelectEntryPos( i ) ]);
314 :
315 0 : mrTakenList.push_back( (sal_uLong)nPos );
316 :
317 : {
318 0 : SolarMutexGuard aGuard;
319 :
320 0 : mpProgress->SetFile( aURL );
321 0 : pStatusProgress->Update( i, nEntries - 1 );
322 0 : mpProgress->Sync();
323 0 : pThm->InsertURL( aURL );
324 : }
325 : }
326 :
327 : {
328 0 : SolarMutexGuard aGuard;
329 :
330 0 : pThm->UnlockBroadcaster();
331 0 : delete pStatusProgress;
332 : }
333 :
334 0 : Application::PostUserEvent( LINK( mpProgress, TakeProgress, CleanUpHdl ) );
335 0 : }
336 :
337 : // - TakeProgress -
338 0 : TakeProgress::TakeProgress(Window* pWindow)
339 : : ModalDialog(pWindow, "GalleryApplyProgress",
340 : "cui/ui/galleryapplyprogress.ui")
341 0 : , window_(pWindow)
342 : {
343 0 : get(m_pFtTakeFile, "file");
344 0 : get(m_pBtnCancel, "cancel");
345 :
346 0 : m_pBtnCancel->SetClickHdl( LINK( this, TakeProgress, ClickCancelBtn ) );
347 0 : }
348 :
349 0 : void TakeProgress::Terminate()
350 : {
351 0 : if (maTakeThread.is())
352 0 : maTakeThread->terminate();
353 0 : }
354 :
355 0 : IMPL_LINK_NOARG(TakeProgress, ClickCancelBtn)
356 : {
357 0 : Terminate();
358 0 : return 0L;
359 : }
360 :
361 0 : IMPL_LINK_NOARG(TakeProgress, CleanUpHdl)
362 : {
363 0 : if (maTakeThread.is())
364 0 : maTakeThread->join();
365 :
366 0 : TPGalleryThemeProperties* mpBrowser = (TPGalleryThemeProperties*) GetParent();
367 0 : ::std::vector<bool, std::allocator<bool> > aRemoveEntries( mpBrowser->aFoundList.size(), false );
368 0 : ::std::vector< OUString > aRemainingVector;
369 : sal_uInt32 i, nCount;
370 :
371 0 : GetParent()->EnterWait();
372 0 : mpBrowser->m_pLbxFound->SetUpdateMode( false );
373 0 : mpBrowser->m_pLbxFound->SetNoSelection();
374 :
375 : // mark all taken positions in aRemoveEntries
376 0 : for( i = 0, nCount = maTakenList.size(); i < nCount; ++i )
377 0 : aRemoveEntries[ maTakenList[ i ] ] = true;
378 0 : maTakenList.clear();
379 :
380 : // refill found list
381 0 : for( i = 0, nCount = aRemoveEntries.size(); i < nCount; ++i )
382 0 : if( !aRemoveEntries[ i ] )
383 0 : aRemainingVector.push_back( mpBrowser->aFoundList[i] );
384 :
385 0 : mpBrowser->aFoundList.clear();
386 :
387 0 : for( i = 0, nCount = aRemainingVector.size(); i < nCount; ++i )
388 0 : mpBrowser->aFoundList.push_back( aRemainingVector[ i ] );
389 :
390 0 : aRemainingVector.clear();
391 :
392 : // refill list box
393 0 : for( i = 0, nCount = aRemoveEntries.size(); i < nCount; ++i )
394 0 : if( !aRemoveEntries[ i ] )
395 0 : aRemainingVector.push_back( mpBrowser->m_pLbxFound->GetEntry( (sal_uInt16) i ) );
396 :
397 0 : mpBrowser->m_pLbxFound->Clear();
398 :
399 0 : for( i = 0, nCount = aRemainingVector.size(); i < nCount; ++i )
400 0 : mpBrowser->m_pLbxFound->InsertEntry( aRemainingVector[ i ] );
401 :
402 0 : aRemainingVector.clear();
403 :
404 0 : mpBrowser->m_pLbxFound->SetUpdateMode( true );
405 0 : mpBrowser->SelectFoundHdl( NULL );
406 0 : GetParent()->LeaveWait();
407 :
408 0 : EndDialog( RET_OK );
409 0 : delete this;
410 0 : return 0L;
411 : }
412 :
413 :
414 :
415 0 : short TakeProgress::Execute()
416 : {
417 : OSL_FAIL( "TakeProgress cannot be executed via Dialog::Execute!\n"
418 : "It creates a thread that will call back to VCL apartment => deadlock!\n"
419 : "Use Dialog::StartExecuteModal to execute the dialog!" );
420 0 : return RET_CANCEL;
421 : }
422 :
423 :
424 :
425 0 : void TakeProgress::StartExecuteModal( const Link& rEndDialogHdl )
426 : {
427 : assert(!maTakeThread.is());
428 0 : maTakeThread = new TakeThread(
429 0 : this, static_cast< TPGalleryThemeProperties * >(window_), maTakenList);
430 0 : maTakeThread->launch();
431 0 : ModalDialog::StartExecuteModal( rEndDialogHdl );
432 0 : }
433 :
434 :
435 : // - ActualizeProgress -
436 0 : ActualizeProgress::ActualizeProgress(Window* pWindow, GalleryTheme* pThm)
437 : : ModalDialog(pWindow, "GalleryUpdateProgress",
438 : "cui/ui/galleryupdateprogress.ui")
439 : , pTimer(NULL)
440 0 : , pTheme(pThm)
441 : {
442 0 : get(m_pFtActualizeFile, "file");
443 0 : get(m_pBtnCancel, "cancel");
444 0 : m_pBtnCancel->SetClickHdl( LINK( this, ActualizeProgress, ClickCancelBtn ) );
445 0 : }
446 :
447 0 : short ActualizeProgress::Execute()
448 : {
449 : short nRet;
450 :
451 0 : pTimer = new Timer;
452 :
453 0 : if ( pTimer )
454 : {
455 0 : pTimer->SetTimeoutHdl( LINK( this, ActualizeProgress, TimeoutHdl ) );
456 0 : pTimer->SetTimeout( 500 );
457 0 : pTimer->Start();
458 : }
459 :
460 0 : nRet = ModalDialog::Execute();
461 :
462 0 : return nRet;
463 : }
464 :
465 :
466 :
467 0 : IMPL_LINK_NOARG(ActualizeProgress, ClickCancelBtn)
468 : {
469 0 : pTheme->AbortActualize();
470 0 : EndDialog( RET_OK );
471 :
472 0 : return 0L;
473 : }
474 :
475 :
476 :
477 0 : IMPL_LINK( ActualizeProgress, TimeoutHdl, Timer*, _pTimer )
478 : {
479 0 : if ( _pTimer )
480 : {
481 0 : _pTimer->Stop();
482 0 : delete _pTimer;
483 : }
484 :
485 0 : pTheme->Actualize( LINK( this, ActualizeProgress, ActualizeHdl ), &aStatusProgress );
486 0 : ClickCancelBtn( NULL );
487 :
488 0 : return 0;
489 : }
490 :
491 :
492 :
493 0 : IMPL_LINK( ActualizeProgress, ActualizeHdl, INetURLObject*, pURL )
494 : {
495 0 : for( long i = 0; i < 128; i++ )
496 0 : Application::Reschedule();
497 :
498 0 : Flush();
499 0 : Sync();
500 :
501 0 : if( pURL )
502 : {
503 0 : m_pFtActualizeFile->SetText( GetReducedString( *pURL, 30 ) );
504 0 : m_pFtActualizeFile->Flush();
505 0 : m_pFtActualizeFile->Sync();
506 : }
507 :
508 0 : return 0;
509 : }
510 :
511 0 : TitleDialog::TitleDialog(Window* pParent, const OUString& rOldTitle)
512 0 : : ModalDialog(pParent, "GalleryTitleDialog", "cui/ui/gallerytitledialog.ui")
513 : {
514 0 : get(m_pEdit, "entry");
515 0 : m_pEdit->SetText( rOldTitle );
516 0 : m_pEdit->GrabFocus();
517 0 : }
518 :
519 :
520 : // - GalleryIdDialog -
521 :
522 :
523 0 : GalleryIdDialog::GalleryIdDialog( Window* pParent, GalleryTheme* _pThm )
524 : : ModalDialog(pParent, "GalleryThemeIDDialog", "cui/ui/gallerythemeiddialog.ui")
525 0 : , pThm(_pThm )
526 : {
527 0 : get(m_pBtnOk, "ok");
528 0 : get(m_pLbResName, "entry");
529 :
530 0 : m_pLbResName->InsertEntry( OUString( "!!! No Id !!!" ) );
531 :
532 0 : GalleryTheme::InsertAllThemes(*m_pLbResName);
533 :
534 0 : m_pLbResName->SelectEntryPos( (sal_uInt16) pThm->GetId() );
535 0 : m_pLbResName->GrabFocus();
536 :
537 0 : m_pBtnOk->SetClickHdl( LINK( this, GalleryIdDialog, ClickOkHdl ) );
538 0 : }
539 :
540 :
541 :
542 0 : IMPL_LINK_NOARG(GalleryIdDialog, ClickOkHdl)
543 : {
544 0 : Gallery* pGal = pThm->GetParent();
545 0 : const sal_uLong nId = GetId();
546 0 : sal_Bool bDifferentThemeExists = sal_False;
547 :
548 0 : for( sal_uLong i = 0, nCount = pGal->GetThemeCount(); i < nCount && !bDifferentThemeExists; i++ )
549 : {
550 0 : const GalleryThemeEntry* pInfo = pGal->GetThemeInfo( i );
551 :
552 0 : if( ( pInfo->GetId() == nId ) && ( pInfo->GetThemeName() != pThm->GetName() ) )
553 : {
554 0 : OUString aStr( CUI_RES( RID_SVXSTR_GALLERY_ID_EXISTS ) );
555 :
556 0 : aStr += " (";
557 0 : aStr += pInfo->GetThemeName();
558 0 : aStr += ")";
559 :
560 0 : InfoBox aBox( this, aStr );
561 0 : aBox.Execute();
562 0 : m_pLbResName->GrabFocus();
563 0 : bDifferentThemeExists = sal_True;
564 : }
565 : }
566 :
567 0 : if( !bDifferentThemeExists )
568 0 : EndDialog( RET_OK );
569 :
570 0 : return 0L;
571 : }
572 :
573 :
574 :
575 : // - GalleryThemeProperties -
576 :
577 :
578 0 : GalleryThemeProperties::GalleryThemeProperties(Window* pParent,
579 : ExchangeData* _pData, SfxItemSet* pItemSet)
580 : : SfxTabDialog( pParent, "GalleryThemeDialog",
581 : "cui/ui/gallerythemedialog.ui", pItemSet)
582 : , pData(_pData)
583 : , m_nGeneralPageId(0)
584 0 : , m_nFilesPageId(0)
585 : {
586 0 : m_nGeneralPageId = AddTabPage("general", TPGalleryThemeGeneral::Create, 0);
587 0 : m_nFilesPageId = AddTabPage("files", TPGalleryThemeProperties::Create, 0);
588 :
589 0 : if( pData->pTheme->IsReadOnly() )
590 0 : RemoveTabPage(m_nFilesPageId);
591 :
592 0 : OUString aText( GetText() );
593 :
594 0 : aText += pData->pTheme->GetName();
595 :
596 0 : if( pData->pTheme->IsReadOnly() )
597 0 : aText += CUI_RES( RID_SVXSTR_GALLERY_READONLY );
598 :
599 0 : SetText( aText );
600 0 : }
601 :
602 0 : void GalleryThemeProperties::PageCreated( sal_uInt16 nId, SfxTabPage &rPage )
603 : {
604 0 : if (nId == m_nGeneralPageId)
605 0 : ( (TPGalleryThemeGeneral&) rPage ).SetXChgData( pData );
606 : else
607 0 : ( (TPGalleryThemeProperties&) rPage ).SetXChgData( pData );
608 0 : }
609 :
610 : // - TPGalleryThemeGeneral -
611 0 : TPGalleryThemeGeneral::TPGalleryThemeGeneral(Window* pParent, const SfxItemSet& rSet)
612 : : SfxTabPage(pParent, "GalleryGeneralPage",
613 : "cui/ui/gallerygeneralpage.ui", rSet)
614 0 : , pData(NULL)
615 : {
616 0 : get(m_pFiMSImage, "image");
617 0 : get(m_pEdtMSName, "name");
618 0 : get(m_pFtMSShowType, "type");
619 0 : get(m_pFtMSShowPath, "location");
620 0 : get(m_pFtMSShowContent, "contents");
621 0 : get(m_pFtMSShowChangeDate, "modified");
622 0 : }
623 :
624 0 : void TPGalleryThemeGeneral::SetXChgData( ExchangeData* _pData )
625 : {
626 0 : pData = _pData;
627 :
628 0 : GalleryTheme* pThm = pData->pTheme;
629 0 : OUString aOutStr( OUString::number(pThm->GetObjectCount()) );
630 0 : OUString aObjStr( CUI_RES( RID_SVXSTR_GALLERYPROPS_OBJECT ) );
631 0 : OUString aAccess;
632 0 : OUString aType( SVX_RES( RID_SVXSTR_GALLERYPROPS_GALTHEME ) );
633 0 : sal_Bool bReadOnly = pThm->IsReadOnly();
634 :
635 0 : m_pEdtMSName->SetText( pThm->GetName() );
636 0 : m_pEdtMSName->SetReadOnly( bReadOnly );
637 :
638 0 : if( bReadOnly )
639 0 : m_pEdtMSName->Disable();
640 : else
641 0 : m_pEdtMSName->Enable();
642 :
643 0 : if( pThm->IsReadOnly() )
644 0 : aType += CUI_RES( RID_SVXSTR_GALLERY_READONLY );
645 :
646 0 : m_pFtMSShowType->SetText( aType );
647 0 : m_pFtMSShowPath->SetText( pThm->GetSdgURL().GetMainURL( INetURLObject::DECODE_UNAMBIGUOUS ) );
648 :
649 : // singular or plural?
650 0 : if ( 1 == pThm->GetObjectCount() )
651 0 : aObjStr = aObjStr.getToken( 0, ';' );
652 : else
653 0 : aObjStr = aObjStr.getToken( 1, ';' );
654 :
655 0 : aOutStr += " " + aObjStr;
656 :
657 0 : m_pFtMSShowContent->SetText( aOutStr );
658 :
659 : // get locale wrapper (singleton)
660 0 : const SvtSysLocale aSysLocale;
661 0 : const LocaleDataWrapper& aLocaleData = aSysLocale.GetLocaleData();
662 :
663 : // ChangeDate/Time
664 0 : aAccess = aLocaleData.getDate( pData->aThemeChangeDate ) + ", " + aLocaleData.getTime( pData->aThemeChangeTime );
665 0 : m_pFtMSShowChangeDate->SetText( aAccess );
666 :
667 : // set image
668 : sal_uInt16 nId;
669 :
670 0 : if( pThm->IsReadOnly() )
671 0 : nId = RID_SVXBMP_THEME_READONLY_BIG;
672 0 : else if( pThm->IsDefault() )
673 0 : nId = RID_SVXBMP_THEME_DEFAULT_BIG;
674 : else
675 0 : nId = RID_SVXBMP_THEME_NORMAL_BIG;
676 :
677 0 : m_pFiMSImage->SetImage( Image( Bitmap( CUI_RES( nId ) ), COL_LIGHTMAGENTA ) );
678 0 : }
679 :
680 :
681 :
682 0 : bool TPGalleryThemeGeneral::FillItemSet( SfxItemSet& /*rSet*/ )
683 : {
684 0 : pData->aEditedTitle = m_pEdtMSName->GetText();
685 0 : return true;
686 : }
687 :
688 :
689 :
690 0 : SfxTabPage* TPGalleryThemeGeneral::Create( Window* pParent, const SfxItemSet& rSet )
691 : {
692 0 : return new TPGalleryThemeGeneral( pParent, rSet );
693 : }
694 :
695 : // - TPGalleryThemeProperties -
696 0 : TPGalleryThemeProperties::TPGalleryThemeProperties( Window* pWindow, const SfxItemSet& rSet )
697 : : SfxTabPage(pWindow, "GalleryFilesPage", "cui/ui/galleryfilespage.ui", rSet)
698 : , pData(NULL)
699 : , nCurFilterPos(0)
700 : , nFirstExtFilterPos(0)
701 : , bEntriesFound(false)
702 : , bInputAllowed(true)
703 : , bTakeAll(false)
704 : , bSearchRecursive(false)
705 0 : , xDialogListener(new ::svt::DialogClosedListener())
706 : {
707 0 : get(m_pCbbFileType, "filetype");
708 0 : get(m_pLbxFound, "files");
709 0 : Size aSize(LogicToPixel(Size(172, 156), MAP_APPFONT));
710 0 : m_pLbxFound->set_width_request(aSize.Width());
711 0 : m_pLbxFound->set_height_request(aSize.Height());
712 0 : m_pLbxFound->EnableMultiSelection(true);
713 0 : get(m_pBtnSearch, "findfiles");
714 0 : get(m_pBtnTake, "add");
715 0 : get(m_pBtnTakeAll, "addall");
716 0 : get(m_pCbxPreview, "preview");
717 0 : get(m_pWndPreview, "image");
718 :
719 0 : xDialogListener->SetDialogClosedLink( LINK( this, TPGalleryThemeProperties, DialogClosedHdl ) );
720 0 : }
721 :
722 0 : void TPGalleryThemeProperties::SetXChgData( ExchangeData* _pData )
723 : {
724 0 : pData = _pData;
725 :
726 0 : aPreviewTimer.SetTimeoutHdl( LINK( this, TPGalleryThemeProperties, PreviewTimerHdl ) );
727 0 : aPreviewTimer.SetTimeout( 500 );
728 0 : m_pBtnSearch->SetClickHdl(LINK(this, TPGalleryThemeProperties, ClickSearchHdl));
729 0 : m_pBtnTake->SetClickHdl(LINK(this, TPGalleryThemeProperties, ClickTakeHdl));
730 0 : m_pBtnTakeAll->SetClickHdl(LINK(this, TPGalleryThemeProperties, ClickTakeAllHdl));
731 0 : m_pCbxPreview->SetClickHdl(LINK(this, TPGalleryThemeProperties, ClickPreviewHdl));
732 0 : m_pCbbFileType->SetSelectHdl(LINK(this, TPGalleryThemeProperties, SelectFileTypeHdl));
733 0 : m_pCbbFileType->EnableDDAutoWidth( false );
734 0 : m_pLbxFound->SetDoubleClickHdl(LINK(this, TPGalleryThemeProperties, DClickFoundHdl));
735 0 : m_pLbxFound->SetSelectHdl(LINK(this, TPGalleryThemeProperties, SelectFoundHdl));
736 0 : m_pLbxFound->InsertEntry(OUString(CUI_RES(RID_SVXSTR_GALLERY_NOFILES)));
737 0 : m_pLbxFound->Show();
738 :
739 0 : FillFilterList();
740 :
741 0 : m_pBtnTake->Enable();
742 0 : m_pBtnTakeAll->Disable();
743 0 : m_pCbxPreview->Disable();
744 0 : }
745 :
746 :
747 :
748 0 : void TPGalleryThemeProperties::StartSearchFiles( const OUString& _rFolderURL, short _nDlgResult )
749 : {
750 0 : if ( RET_OK == _nDlgResult )
751 : {
752 0 : aURL = INetURLObject( _rFolderURL );
753 0 : bSearchRecursive = sal_True; // UI choice no longer possible, windows file picker allows no user controls
754 0 : SearchFiles();
755 : }
756 :
757 0 : nCurFilterPos = m_pCbbFileType->GetEntryPos( m_pCbbFileType->GetText() );
758 0 : }
759 :
760 :
761 :
762 0 : TPGalleryThemeProperties::~TPGalleryThemeProperties()
763 : {
764 0 : xMediaPlayer.clear();
765 0 : xDialogListener.clear();
766 :
767 0 : for ( size_t i = 0, n = aFilterEntryList.size(); i < n; ++i ) {
768 0 : delete aFilterEntryList[ i ];
769 : }
770 0 : }
771 :
772 :
773 :
774 0 : SfxTabPage* TPGalleryThemeProperties::Create( Window* pParent, const SfxItemSet& rSet )
775 : {
776 0 : return new TPGalleryThemeProperties( pParent, rSet );
777 : }
778 :
779 :
780 :
781 0 : OUString TPGalleryThemeProperties::addExtension( const OUString& _rDisplayText, const OUString& _rExtension )
782 : {
783 0 : OUString sAllFilter( "(*.*)" );
784 0 : OUString sOpenBracket( " (" );
785 0 : OUString sCloseBracket( ")" );
786 0 : OUString sRet = _rDisplayText;
787 :
788 0 : if ( sRet.indexOf( sAllFilter ) == -1 )
789 : {
790 0 : OUString sExt = _rExtension;
791 0 : sRet += sOpenBracket;
792 0 : sRet += sExt;
793 0 : sRet += sCloseBracket;
794 : }
795 0 : return sRet;
796 : }
797 :
798 :
799 :
800 0 : void TPGalleryThemeProperties::FillFilterList()
801 : {
802 0 : GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter();
803 0 : OUString aExt;
804 0 : OUString aName;
805 : FilterEntry* pFilterEntry;
806 : FilterEntry* pTestEntry;
807 : sal_uInt16 i, nKeyCount;
808 : sal_Bool bInList;
809 :
810 : // graphic filters
811 0 : for( i = 0, nKeyCount = rFilter.GetImportFormatCount(); i < nKeyCount; i++ )
812 : {
813 0 : aExt = rFilter.GetImportFormatShortName( i );
814 0 : aName = rFilter.GetImportFormatName( i );
815 0 : size_t entryIndex = 0;
816 0 : pTestEntry = aFilterEntryList.empty() ? NULL : aFilterEntryList[ entryIndex ];
817 0 : bInList = sal_False;
818 :
819 0 : OUString aExtensions;
820 0 : int j = 0;
821 0 : OUString sWildcard;
822 : while( true )
823 : {
824 0 : sWildcard = rFilter.GetImportWildcard( i, j++ );
825 0 : if ( sWildcard.isEmpty() )
826 0 : break;
827 0 : if ( aExtensions.indexOf( sWildcard ) == -1 )
828 : {
829 0 : if ( !aExtensions.isEmpty() )
830 0 : aExtensions += ";";
831 0 : aExtensions += sWildcard;
832 : }
833 : }
834 0 : aName = addExtension( aName, aExtensions );
835 :
836 0 : while( pTestEntry )
837 : {
838 0 : if ( pTestEntry->aFilterName == aExt )
839 : {
840 0 : bInList = sal_True;
841 0 : break;
842 : }
843 0 : pTestEntry = ( ++entryIndex < aFilterEntryList.size() )
844 0 : ? aFilterEntryList[ entryIndex ] : NULL;
845 : }
846 0 : if ( !bInList )
847 : {
848 0 : pFilterEntry = new FilterEntry;
849 0 : pFilterEntry->aFilterName = aExt;
850 0 : size_t pos = m_pCbbFileType->InsertEntry( aName );
851 0 : if ( pos < aFilterEntryList.size() ) {
852 0 : aFilterEntryList.insert( aFilterEntryList.begin() + pos, pFilterEntry );
853 : } else {
854 0 : aFilterEntryList.push_back( pFilterEntry );
855 : }
856 : }
857 0 : }
858 :
859 : // media filters
860 0 : static const OUString aWildcard( "*." );
861 0 : ::avmedia::FilterNameVector aFilters;
862 0 : ::avmedia::MediaWindow::getMediaFilters( aFilters );
863 :
864 0 : for( unsigned long l = 0; l < aFilters.size(); ++l )
865 : {
866 0 : for( sal_Int32 nIndex = 0; nIndex >= 0; )
867 : {
868 0 : OUString aFilterWildcard( aWildcard );
869 :
870 0 : pFilterEntry = new FilterEntry;
871 0 : pFilterEntry->aFilterName = aFilters[ l ].second.getToken( 0, ';', nIndex );
872 : nFirstExtFilterPos = m_pCbbFileType->InsertEntry(
873 : addExtension(
874 0 : aFilters[ l ].first,
875 0 : aFilterWildcard += pFilterEntry->aFilterName
876 : )
877 0 : );
878 0 : if ( nFirstExtFilterPos < aFilterEntryList.size() ) {
879 : aFilterEntryList.insert(
880 0 : aFilterEntryList.begin() + nFirstExtFilterPos,
881 : pFilterEntry
882 0 : );
883 : } else {
884 0 : aFilterEntryList.push_back( pFilterEntry );
885 : }
886 0 : }
887 : }
888 :
889 : // 'All' filters
890 0 : OUString aExtensions;
891 :
892 : // graphic filters
893 0 : for ( i = 0; i < nKeyCount; ++i )
894 : {
895 0 : int j = 0;
896 0 : OUString sWildcard;
897 : while( true )
898 : {
899 0 : sWildcard = rFilter.GetImportWildcard( i, j++ );
900 0 : if ( sWildcard.isEmpty() )
901 0 : break;
902 0 : if ( aExtensions.indexOf( sWildcard ) == -1 )
903 : {
904 0 : if ( !aExtensions.isEmpty() )
905 0 : aExtensions += ";";
906 :
907 0 : aExtensions += sWildcard;
908 : }
909 : }
910 0 : }
911 :
912 : // media filters
913 0 : for( unsigned long k = 0; k < aFilters.size(); ++k )
914 : {
915 0 : for( sal_Int32 nIndex = 0; nIndex >= 0; )
916 : {
917 0 : if ( !aExtensions.isEmpty() )
918 0 : aExtensions += ";";
919 0 : aExtensions += aWildcard + aFilters[ k ].second.getToken( 0, ';', nIndex );
920 : }
921 : }
922 :
923 : #if defined(WNT)
924 : if (aExtensions.getLength() > 240)
925 : aExtensions = "*.*";
926 : #endif
927 :
928 0 : pFilterEntry = new FilterEntry;
929 0 : pFilterEntry->aFilterName = CUI_RESSTR(RID_SVXSTR_GALLERY_ALLFILES);
930 0 : pFilterEntry->aFilterName = addExtension( pFilterEntry->aFilterName, aExtensions );
931 0 : size_t pos = m_pCbbFileType->InsertEntry( pFilterEntry->aFilterName, 0 );
932 0 : if ( pos < aFilterEntryList.size() ) {
933 0 : aFilterEntryList.insert( aFilterEntryList.begin() + pos, pFilterEntry );
934 : } else {
935 0 : aFilterEntryList.push_back( pFilterEntry );
936 : }
937 0 : m_pCbbFileType->SetText( pFilterEntry->aFilterName );
938 0 : }
939 :
940 :
941 :
942 0 : IMPL_LINK_NOARG(TPGalleryThemeProperties, SelectFileTypeHdl)
943 : {
944 0 : OUString aText( m_pCbbFileType->GetText() );
945 :
946 0 : if( bInputAllowed && ( aLastFilterName != aText ) )
947 : {
948 0 : aLastFilterName = aText;
949 :
950 0 : if( MessageDialog( this, "QueryUpdateFileListDialog","cui/ui/queryupdategalleryfilelistdialog.ui" ).Execute() == RET_YES )
951 0 : SearchFiles();
952 : }
953 :
954 0 : return 0L;
955 : }
956 :
957 :
958 :
959 0 : void TPGalleryThemeProperties::SearchFiles()
960 : {
961 0 : SearchProgress* pProgress = new SearchProgress( this, aURL );
962 :
963 0 : aFoundList.clear();
964 0 : m_pLbxFound->Clear();
965 :
966 0 : pProgress->SetFileType( m_pCbbFileType->GetText() );
967 0 : pProgress->SetDirectory( INetURLObject() );
968 0 : pProgress->Update();
969 :
970 0 : pProgress->StartExecuteModal( LINK( this, TPGalleryThemeProperties, EndSearchProgressHdl ) );
971 0 : }
972 :
973 :
974 :
975 0 : IMPL_LINK_NOARG(TPGalleryThemeProperties, ClickSearchHdl)
976 : {
977 0 : if( bInputAllowed )
978 : {
979 : try
980 : {
981 : // setup folder picker
982 0 : com::sun::star::uno::Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
983 0 : xFolderPicker = FolderPicker::create(xContext);
984 :
985 0 : OUString aDlgPathName( SvtPathOptions().GetGraphicPath() );
986 0 : xFolderPicker->setDisplayDirectory(aDlgPathName);
987 :
988 0 : aPreviewTimer.Stop();
989 :
990 0 : com::sun::star::uno::Reference< XAsynchronousExecutableDialog > xAsyncDlg( xFolderPicker, UNO_QUERY );
991 0 : if ( xAsyncDlg.is() )
992 0 : xAsyncDlg->startExecuteModal( xDialogListener.get() );
993 : else
994 : {
995 0 : if( xFolderPicker->execute() == RET_OK )
996 : {
997 0 : aURL = INetURLObject( xFolderPicker->getDirectory() );
998 0 : bSearchRecursive = sal_True; // UI choice no longer possible, windows file picker allows no user controls
999 0 : SearchFiles();
1000 : }
1001 :
1002 0 : nCurFilterPos = m_pCbbFileType->GetEntryPos( m_pCbbFileType->GetText() );
1003 0 : }
1004 : }
1005 0 : catch (const IllegalArgumentException&)
1006 : {
1007 : OSL_FAIL( "Folder picker failed with illegal arguments" );
1008 : }
1009 : }
1010 :
1011 0 : return 0L;
1012 : }
1013 :
1014 :
1015 :
1016 0 : void TPGalleryThemeProperties::TakeFiles()
1017 : {
1018 0 : if( m_pLbxFound->GetSelectEntryCount() || ( bTakeAll && bEntriesFound ) )
1019 : {
1020 0 : TakeProgress* pTakeProgress = new TakeProgress( this );
1021 0 : pTakeProgress->Update();
1022 :
1023 : pTakeProgress->StartExecuteModal(
1024 : Link() /* no postprocessing needed, pTakeProgress
1025 0 : will be deleted in TakeProgress::CleanupHdl */ );
1026 : }
1027 0 : }
1028 :
1029 :
1030 :
1031 0 : IMPL_LINK_NOARG(TPGalleryThemeProperties, ClickPreviewHdl)
1032 : {
1033 0 : if ( bInputAllowed )
1034 : {
1035 0 : aPreviewTimer.Stop();
1036 0 : aPreviewString = "";
1037 :
1038 0 : if( !m_pCbxPreview->IsChecked() )
1039 : {
1040 0 : xMediaPlayer.clear();
1041 0 : m_pWndPreview->SetGraphic( Graphic() );
1042 0 : m_pWndPreview->Invalidate();
1043 : }
1044 : else
1045 0 : DoPreview();
1046 : }
1047 :
1048 0 : return 0;
1049 : }
1050 :
1051 :
1052 :
1053 0 : void TPGalleryThemeProperties::DoPreview()
1054 : {
1055 0 : OUString aString( m_pLbxFound->GetSelectEntry() );
1056 :
1057 0 : if( aString != aPreviewString )
1058 : {
1059 0 : INetURLObject _aURL( aFoundList[ m_pLbxFound->GetEntryPos( aString ) ] );
1060 0 : bInputAllowed = sal_False;
1061 :
1062 0 : if ( !m_pWndPreview->SetGraphic( _aURL ) )
1063 : {
1064 0 : GetParent()->LeaveWait();
1065 0 : ErrorHandler::HandleError( ERRCODE_IO_NOTEXISTSPATH );
1066 0 : GetParent()->EnterWait();
1067 : }
1068 0 : else if( ::avmedia::MediaWindow::isMediaURL( _aURL.GetMainURL( INetURLObject::DECODE_UNAMBIGUOUS ), "" ) )
1069 : {
1070 0 : xMediaPlayer = ::avmedia::MediaWindow::createPlayer( _aURL.GetMainURL( INetURLObject::NO_DECODE ), "" );
1071 0 : if( xMediaPlayer.is() )
1072 0 : xMediaPlayer->start();
1073 : }
1074 :
1075 0 : bInputAllowed = sal_True;
1076 0 : aPreviewString = aString;
1077 0 : }
1078 0 : }
1079 :
1080 :
1081 :
1082 0 : IMPL_LINK_NOARG(TPGalleryThemeProperties, ClickTakeHdl)
1083 : {
1084 0 : if( bInputAllowed )
1085 : {
1086 0 : aPreviewTimer.Stop();
1087 :
1088 0 : if( !m_pLbxFound->GetSelectEntryCount() || !bEntriesFound )
1089 : {
1090 0 : SvxOpenGraphicDialog aDlg("Gallery");
1091 0 : aDlg.EnableLink(false);
1092 0 : aDlg.AsLink(false);
1093 :
1094 0 : if( !aDlg.Execute() )
1095 0 : pData->pTheme->InsertURL( INetURLObject( aDlg.GetPath() ) );
1096 : }
1097 : else
1098 : {
1099 0 : bTakeAll = sal_False;
1100 0 : TakeFiles();
1101 : }
1102 : }
1103 :
1104 0 : return 0L;
1105 : }
1106 :
1107 :
1108 :
1109 0 : IMPL_LINK_NOARG(TPGalleryThemeProperties, ClickTakeAllHdl)
1110 : {
1111 0 : if( bInputAllowed )
1112 : {
1113 0 : aPreviewTimer.Stop();
1114 0 : bTakeAll = sal_True;
1115 0 : TakeFiles();
1116 : }
1117 :
1118 0 : return 0L;
1119 : }
1120 :
1121 :
1122 :
1123 0 : IMPL_LINK_NOARG(TPGalleryThemeProperties, SelectFoundHdl)
1124 : {
1125 0 : if( bInputAllowed )
1126 : {
1127 0 : sal_Bool bPreviewPossible = sal_False;
1128 :
1129 0 : aPreviewTimer.Stop();
1130 :
1131 0 : if( bEntriesFound )
1132 : {
1133 0 : if( m_pLbxFound->GetSelectEntryCount() == 1 )
1134 : {
1135 0 : m_pCbxPreview->Enable();
1136 0 : bPreviewPossible = sal_True;
1137 : }
1138 : else
1139 0 : m_pCbxPreview->Disable();
1140 :
1141 0 : if( !aFoundList.empty() )
1142 0 : m_pBtnTakeAll->Enable();
1143 : else
1144 0 : m_pBtnTakeAll->Disable();
1145 : }
1146 :
1147 0 : if( bPreviewPossible && m_pCbxPreview->IsChecked() )
1148 0 : aPreviewTimer.Start();
1149 : }
1150 :
1151 0 : return 0;
1152 : }
1153 :
1154 :
1155 :
1156 0 : IMPL_LINK_NOARG(TPGalleryThemeProperties, DClickFoundHdl)
1157 : {
1158 0 : if( bInputAllowed )
1159 : {
1160 0 : aPreviewTimer.Stop();
1161 :
1162 0 : return (m_pLbxFound->GetSelectEntryCount() == 1 && bEntriesFound) ?
1163 0 : ClickTakeHdl(NULL) : 0;
1164 : }
1165 : else
1166 0 : return 0;
1167 : }
1168 :
1169 :
1170 :
1171 0 : IMPL_LINK_NOARG(TPGalleryThemeProperties, PreviewTimerHdl)
1172 : {
1173 0 : aPreviewTimer.Stop();
1174 0 : DoPreview();
1175 0 : return 0L;
1176 : }
1177 :
1178 :
1179 :
1180 0 : IMPL_LINK_NOARG(TPGalleryThemeProperties, EndSearchProgressHdl)
1181 : {
1182 0 : if( !aFoundList.empty() )
1183 : {
1184 0 : m_pLbxFound->SelectEntryPos( 0 );
1185 0 : m_pBtnTakeAll->Enable();
1186 0 : m_pCbxPreview->Enable();
1187 0 : bEntriesFound = sal_True;
1188 : }
1189 : else
1190 : {
1191 0 : m_pLbxFound->InsertEntry( OUString( CUI_RES( RID_SVXSTR_GALLERY_NOFILES ) ) );
1192 0 : m_pBtnTakeAll->Disable();
1193 0 : m_pCbxPreview->Disable();
1194 0 : bEntriesFound = sal_False;
1195 : }
1196 0 : return 0L;
1197 : }
1198 :
1199 :
1200 :
1201 0 : IMPL_LINK( TPGalleryThemeProperties, DialogClosedHdl, ::com::sun::star::ui::dialogs::DialogClosedEvent*, pEvt )
1202 : {
1203 : DBG_ASSERT( xFolderPicker.is(), "TPGalleryThemeProperties::DialogClosedHdl(): no folder picker" );
1204 :
1205 0 : OUString sURL = xFolderPicker->getDirectory();
1206 0 : StartSearchFiles( sURL, pEvt->DialogResult );
1207 :
1208 0 : return 0L;
1209 0 : }
1210 :
1211 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|