1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <kdecommandthread.hxx>
23 #include <kdefilepicker.hxx>
25 #include <config_vclplug.h>
29 #include <tqcheckbox.h>
30 #include <tqcombobox.h>
35 #include <tqobjectlist.h>
36 #include <tqpushbutton.h>
44 #include <tdeversion.h>
45 #include <tdediroperator.h>
46 #include <tdefiledialog.h>
47 #include <tdefilefiltercombo.h>
48 #include <tdeio/netaccess.h>
49 #include <tdelocale.h>
50 #include <tdemessagebox.h>
51 #include <tdetempfile.h>
55 #include <qcheckbox.h>
56 #include <qcombobox.h>
61 #include <qobjectlist.h>
62 #include <qpushbutton.h>
70 #include <kdeversion.h>
71 #include <kdiroperator.h>
72 #include <kfiledialog.h>
73 #include <kfilefiltercombo.h>
74 #include <kio/netaccess.h>
76 #include <kmessagebox.h>
77 #include <ktempfile.h>
82 #define QCheckBox_String "TQCheckBox"
83 #define QComboBox_String "TQComboBox"
85 #define QCheckBox_String "QCheckBox"
86 #define QComboBox_String "QComboBox"
94 KDEFileDialog::KDEFileDialog( const QString
&startDir
, const QString
&filter
,
95 QWidget
*parent
, const char *name
)
96 : KFileDialog( startDir
, filter
, parent
, name
, true, m_pCustomWidget
= new QVBox() ),
97 m_pCombosAndButtons( new QHBox( m_pCustomWidget
) ),
98 m_pLabels( new QVBox( m_pCombosAndButtons
) ),
99 m_pComboBoxes( new QVBox( m_pCombosAndButtons
) ),
100 m_pPushButtons( new QVBox( m_pCombosAndButtons
) ),
101 m_pCheckBoxes( new QGrid( 2, m_pCustomWidget
) ),
103 m_bIsExecuting( false ),
104 m_bCanNotifySelection( true )
107 connect( this, SIGNAL( fileHighlighted( const TQString
& ) ),
108 this, SLOT( fileHighlightedCommand( const TQString
& ) ) );
110 connect( this, SIGNAL( fileHighlighted( const QString
& ) ),
111 this, SLOT( fileHighlightedCommand( const QString
& ) ) );
114 connect( this, SIGNAL( selectionChanged() ),
115 this, SLOT( selectionChangedCommand() ) );
117 m_pCustomWidget
->setSpacing( KDialog::spacingHint() );
118 m_pCombosAndButtons
->setSpacing( KDialog::spacingHint() );
120 updateCustomWidgetLayout();
123 KDEFileDialog::~KDEFileDialog()
127 void KDEFileDialog::resizeEvent( QResizeEvent
*pEvent
)
129 KFileDialog::resizeEvent( pEvent
);
131 updateCustomWidgetLayout();
134 void KDEFileDialog::showEvent( QShowEvent
*pEvent
)
136 KFileDialog::showEvent( pEvent
);
138 updateCustomWidgetLayout();
141 void KDEFileDialog::updateCustomWidgetLayout()
143 QPoint qReferencePoint
= filterWidget
->mapTo( this, QPoint( 0, 0 ) );
144 QPoint qCustomPoint
= m_pCustomWidget
->mapTo( this, QPoint( 0, 0 ) );
146 int nLeft
= qReferencePoint
.x() - qCustomPoint
.x();
147 int nRight
= m_pCustomWidget
->width() - filterWidget
->width() - nLeft
;
149 nLeft
-= KDialog::spacingHint();
150 nRight
-= KDialog::spacingHint();
151 m_pLabels
->setFixedWidth( ( nLeft
> 0 )? nLeft
: 80 );
152 // FIXME The following call sets the width of m_pPushButtons all right,
153 // but it also increases the width of m_pComboBoxes rapidly. Can we do
154 // anything about it?
155 m_pPushButtons
->setFixedWidth( ( nRight
> 0 )? nRight
: 100 );
158 void KDEFileDialog::customEvent( QCustomEvent
*pEvent
)
160 if ( pEvent
&& pEvent
->type() == KDECommandEvent::TypeId
)
162 KDECommandEvent
*pCommandEvent
= static_cast< KDECommandEvent
* >( pEvent
);
163 QStringList
*pStringList
= pCommandEvent
->stringList();
167 nListSize
= pStringList
->size();
169 switch ( pCommandEvent
->command() )
171 case KDECommandEvent::AppendControl
:
172 if ( nListSize
>= 3 )
174 appendControl( (*pStringList
)[0], (*pStringList
)[1], (*pStringList
)[2] );
177 case KDECommandEvent::EnableControl
:
178 if ( nListSize
>= 2 )
180 enableControl( (*pStringList
)[0], (*pStringList
)[1] );
183 case KDECommandEvent::GetValue
:
184 if ( nListSize
>= 2 )
186 getValue( (*pStringList
)[0], (*pStringList
)[1] );
189 case KDECommandEvent::SetValue
:
190 if ( nListSize
>= 2 )
192 QStringList qStringList
= (*pStringList
);
193 qStringList
.pop_front();
194 qStringList
.pop_front();
196 setValue( (*pStringList
)[0], (*pStringList
)[1], qStringList
);
199 case KDECommandEvent::AppendFilter
:
200 if ( nListSize
>= 2 )
202 appendFilter( (*pStringList
)[0], (*pStringList
)[1] );
204 // update the filters widget
205 setFilter( filters() );
208 case KDECommandEvent::AppendFilterGroup
:
209 if ( nListSize
>= 1 )
211 QStringList::const_iterator it
= pStringList
->begin();
212 ++it
; // We ignore the filter group name
214 while ( it
!= pStringList
->end() )
216 QString qTitle
= *it
;
218 if ( it
!= pStringList
->end() )
220 appendFilter( qTitle
, (*it
) );
225 // update the filters widget
226 setFilter( filters() );
229 case KDECommandEvent::GetCurrentFilter
:
231 QString qCurrentFilter
= filterWidget
->currentText();
232 sendCommand( "currentFilter " + escapeString( qCurrentFilter
) );
235 case KDECommandEvent::SetCurrentFilter
:
236 if ( nListSize
>= 1 )
238 static_cast< KDEFileFilterComboHack
* >( filterWidget
)->setCurrentFilter( pStringList
->front() );
241 case KDECommandEvent::GetDirectory
:
243 QString qDirectory
= baseURL().url();
244 if ( qDirectory
.startsWith( "file:/" ) && qDirectory
.mid( 6, 1 ) != "/" )
245 qDirectory
.replace( "file:/", "file:///" );
246 sendCommand( "currentDirectory " + escapeString( qDirectory
) );
249 case KDECommandEvent::SetDirectory
:
250 if ( nListSize
>= 1 )
252 setURL( pStringList
->front() );
255 case KDECommandEvent::GetFiles
:
258 qString
.reserve( 1024 );
260 qString
.append( "files" );
262 if ( result() == QDialog::Accepted
)
264 KURL::List
qList( selectedURLs() );
265 for ( KURL::List::const_iterator it
= qList
.begin(); it
!= qList
.end(); ++it
)
266 appendURL( qString
, (*it
) );
270 // we have to return the selected files anyway
271 const KFileItemList
*pItems
= ops
->selectedItems();
272 for ( KFileItemListIterator
it( *pItems
); it
.current(); ++it
)
273 appendURL( qString
, (*it
)->url() );
276 sendCommand( qString
);
277 setCanNotifySelection( true );
280 case KDECommandEvent::SetTitle
:
281 if ( nListSize
>= 1 )
283 setCaption( pStringList
->front() );
286 case KDECommandEvent::SetType
:
287 if ( nListSize
>= 1 )
289 QString
qType( pStringList
->front() );
290 if ( qType
== "open" )
293 setCaption( i18n( "Open" ) );
295 else if ( qType
== "save" )
298 setCaption( i18n( "Save As" ) );
302 case KDECommandEvent::SetDefaultName
:
303 if ( nListSize
>= 1 )
305 setKeepLocation( true );
306 setSelection( pStringList
->front() );
309 case KDECommandEvent::SetMultiSelection
:
310 if ( nListSize
>= 1 )
312 if ( pStringList
->front() == "true" )
313 setMode( KFile::Files
);
315 setMode( KFile::File
);
318 case KDECommandEvent::Exec
:
320 filterWidget
->setEditable( false );
321 setIsExecuting( true );
322 bool bCanExit
= false;
324 setCanNotifySelection( true );
327 KURL qLocalSelectedURL
= mostLocalURL( selectedURL() );
328 QString
qProtocol( qLocalSelectedURL
.protocol() );
330 if ( isSave() && result() == QDialog::Accepted
)
332 if ( qProtocol
== "file" )
334 QString
qFileName( addExtension( qLocalSelectedURL
.path() ) );
336 !QFile::exists( qFileName
) ||
337 ( KMessageBox::warningYesNo( 0,
338 i18n( "A file named \"%1\" already exists. "
339 "Are you sure you want to overwrite it?" ).arg( qFileName
),
340 i18n( "Overwrite File?" ),
341 i18n( "Overwrite" ), KStdGuiItem::cancel() ) == KMessageBox::Yes
);
343 else if ( !isSupportedProtocol( qProtocol
) )
345 KMessageBox::sorry( 0,
346 i18n( "Saving using protocol \"%1\" is not supported." ).arg( qProtocol
) );
352 else if ( !isSave() && result() == QDialog::Accepted
&& !isSupportedProtocol( qProtocol
) )
354 KMessageBox::information( 0,
355 i18n( "Protocol \"%1\" is supported only partially. "
356 "Local copy of the file will be created." ).arg( qProtocol
) );
361 } while ( !bCanExit
);
362 setIsExecuting( false );
364 if ( result() == QDialog::Accepted
)
365 sendCommand( "accept" );
367 sendCommand( "reject" );
374 // FIXME Some cleanup of pEvent? delete something, etc.?
378 void KDEFileDialog::appendControl( const QString
&rId
, const QString
&rType
, const QString
&rTitle
)
380 QString
qLabel( rTitle
);
381 qLabel
.replace( '~', '&' );
383 if ( rType
== "checkbox" )
385 QCheckBox
*pCheckBox
= new QCheckBox( qLabel
, m_pCheckBoxes
, rId
.utf8() );
387 pCheckBox
->setEnabled( true );
388 pCheckBox
->setChecked( false );
390 else if ( rType
== "listbox" )
392 QLabel
*pComboLabel
= new QLabel( qLabel
, m_pLabels
);
393 QComboBox
*pComboBox
= new QComboBox( m_pComboBoxes
, rId
.utf8() );
395 pComboLabel
->setBuddy( pComboBox
);
396 pComboBox
->setEnabled( true );
398 else if ( rType
== "pushbutton" )
400 QPushButton
*pPushButton
= new QPushButton( qLabel
, m_pPushButtons
, rId
.utf8() );
401 pPushButton
->setEnabled( true );
405 QWidget
* KDEFileDialog::findControl( const QString
&rId
) const
407 QObjectList
*pList
= m_pCustomWidget
->queryList();
408 QCString
qName( rId
.utf8() );
409 QObjectList::const_iterator it
= pList
->begin();
411 for ( ; it
!= pList
->end() && qName
!= (*it
)->name(); ++it
)
414 QWidget
*pWidget
= NULL
;
415 if ( it
!= pList
->end() )
416 pWidget
= static_cast< QWidget
* >( *it
);
423 void KDEFileDialog::enableControl( const QString
&rId
, const QString
&rValue
)
425 QWidget
*pWidget
= findControl( rId
);
428 pWidget
->setEnabled( rValue
.lower() == "true" );
431 void KDEFileDialog::getValue( const QString
&rId
, const QString
&rAction
)
433 QWidget
*pWidget
= findControl( rId
);
435 qString
.reserve( 1024 );
436 qString
.append( "value" );
440 QCString qClassName
= pWidget
->className();
441 if ( qClassName
== QCheckBox_String
)
443 QCheckBox
*pCheckBox
= static_cast< QCheckBox
* >( pWidget
);
445 if ( pCheckBox
->isChecked() )
446 qString
.append( " bool true" );
448 qString
.append( " bool false" );
450 else if ( qClassName
== QComboBox_String
)
452 QComboBox
*pComboBox
= static_cast< QComboBox
* >( pWidget
);
453 if ( rAction
== "getItems" )
455 qString
.append( " stringList" );
456 for ( int nIdx
= 0; nIdx
< pComboBox
->count(); ++nIdx
)
458 qString
.append( ' ' );
459 appendEscaped( qString
, pComboBox
->text( nIdx
) );
462 else if ( rAction
== "getSelectedItem" )
464 qString
.append( " string " );
465 appendEscaped( qString
, pComboBox
->currentText() );
467 else if ( rAction
== "getSelectedItemIndex" )
469 qString
.append( " int " );
470 qString
.append( QString().setNum( pComboBox
->currentItem() ) );
477 sendCommand( qString
);
480 void KDEFileDialog::setValue( const QString
&rId
, const QString
&rAction
, const QStringList
&rValue
)
482 QWidget
*pWidget
= findControl( rId
);
486 QCString qClassName
= pWidget
->className();
487 if ( qClassName
== QCheckBox_String
)
489 QCheckBox
*pCheckBox
= static_cast< QCheckBox
* >( pWidget
);
491 bool bValue
= ( !rValue
.isEmpty() ) && ( rValue
.front().lower() == "true" );
492 pCheckBox
->setChecked( bValue
);
494 else if ( qClassName
== QComboBox_String
)
496 QComboBox
*pComboBox
= static_cast< QComboBox
* >( pWidget
);
497 if ( rAction
== "addItem" )
499 if ( !rValue
.isEmpty() )
500 pComboBox
->insertItem( rValue
.front() );
502 else if ( rAction
== "addItems" )
504 pComboBox
->insertStringList( rValue
);
506 else if ( rAction
== "deleteItem" )
508 if ( !rValue
.isEmpty() )
509 pComboBox
->removeItem( rValue
.front().toInt() );
511 else if ( rAction
== "deleteItems" )
515 else if ( rAction
== "setSelectedItem" )
517 if ( !rValue
.isEmpty() )
518 pComboBox
->setCurrentItem( rValue
.front().toInt() );
520 // FIXME setHelpURL is ignored
526 void KDEFileDialog::appendFilter( const QString
&rTitle
, const QString
&rFilter
)
528 // Filters are separated by ';'
529 QString
qFilter( rFilter
);
530 qFilter
.replace( QChar( ';' ), QChar( ' ' ) ).replace( "*.*", "*" );
532 // Workaround for too wide <All formats> (*.bmp;...) entry
533 QString
qTitle( rTitle
);
534 qTitle
.replace( QRegExp( "^<([^>]*)> \\(.*" ), "<\\1>" );
536 m_aFilters
.push_back( qMakePair( qTitle
, qFilter
) );
539 QString
KDEFileDialog::filters() const
541 QString qString
, qTmp
;
542 bool bFirstFilter
= true;
544 for ( FilterList::const_iterator it
= m_aFilters
.begin(); it
!= m_aFilters
.end(); ++it
)
547 bFirstFilter
= false;
549 qString
.append( '\n' );
551 qString
.append( (*it
).second
);
552 qString
.append( '|' );
555 qString
.append( qTmp
.replace( '/', "\\/" ) );
561 QString
KDEFileDialog::addExtension( const QString
&rFileName
) const
568 QWidget
*pExtensionWidget
= findControl( "100" ); // CHECKBOX_AUTOEXTENSION
569 QCheckBox
*pExtensionCB
= pExtensionWidget
? static_cast< QCheckBox
* >( pExtensionWidget
->qt_cast( QCheckBox_String
) ): NULL
;
570 if ( pExtensionCB
&& pExtensionCB
->isChecked() )
572 // FIXME: qFilter can be a MIME; we ignore it now...
573 QStringList qFilterList
= QStringList::split( " ", currentFilter() );
574 for ( QStringList::const_iterator it
= qFilterList
.begin();
575 qExtension
.isEmpty() && it
!= qFilterList
.end();
578 int nUnwanted
= (*it
).findRev( '*' );
580 nUnwanted
= (*it
).findRev( '?' );
582 nUnwanted
= ::std::max( nUnwanted
, (*it
).find( '?', nUnwanted
) );
584 int nIdx
= (*it
).find( '.', ::std::max( nUnwanted
, 0 ) );
586 qExtension
= (*it
).mid( nIdx
).lower();
590 if ( qExtension
.isEmpty() || qExtension
== "." || rFileName
.endsWith( qExtension
) )
593 return rFileName
+ qExtension
;
596 bool KDEFileDialog::isSupportedProtocol( const QString
&rProtocol
) const
598 // TODO Get this information directly from OOo
599 const char * pOOoProtocols
[] = { "", "smb", "ftp", "http", "file", "mailto",
600 "vnd.sun.star.webdav", "news", "private", "vnd.sun.star.help",
601 "https", "slot", "macro", "javascript", "imap", "pop3", "data",
602 "cid", "out", "vnd.sun.star.wfs", "vnd.sun.star.hier", "vim",
603 ".uno", ".component", "vnd.sun.star.pkg", "ldap", "db",
604 "vnd.sun.star.cmd", "vnd.sun.star.script",
608 for ( const char **pIndex
= pOOoProtocols
; *pIndex
!= NULL
; ++pIndex
)
610 if ( rProtocol
== *pIndex
)
614 // TODO gnome-vfs bits here
619 KURL
KDEFileDialog::mostLocalURL( const KURL
&rURL
) const
621 #if KDE_IS_VERSION(3,5,0)
622 KURL
qMostLocalURL( KIO::NetAccess::mostLocalURL( rURL
, const_cast<KDEFileDialog
*>( this ) ) );
623 if ( qMostLocalURL
.isLocalFile() )
624 return qMostLocalURL
;
627 // Terrible hack to get even non-existing media:// files right
628 qMostLocalURL
.cd( ".." );
629 KURL
qMostLocalPath( KIO::NetAccess::mostLocalURL( qMostLocalURL
, const_cast<KDEFileDialog
*>( this ) ) );
630 if ( qMostLocalPath
.isLocalFile() )
632 qMostLocalPath
.addPath( rURL
.fileName() );
633 return qMostLocalPath
;
641 QString
KDEFileDialog::localCopy( const QString
&rFileName
) const
643 // 106 == MIB enum for UTF-8
644 KURL qLocalURL
= mostLocalURL( KURL( rFileName
, 106 ) );
645 if ( qLocalURL
.isLocalFile() )
646 return qLocalURL
.url();
648 int nExtensionPos
= rFileName
.findRev( '/' );
649 if ( nExtensionPos
>= 0 )
650 nExtensionPos
= rFileName
.find( '.', nExtensionPos
);
652 nExtensionPos
= rFileName
.find( '.' );
654 KTempFile
qTempFile( QString::null
, ( nExtensionPos
< 0 )? QString(): rFileName
.mid( nExtensionPos
) );
656 qDestURL
.setPath( qTempFile
.name() );
658 if ( !KIO::NetAccess::file_copy( rFileName
, qDestURL
, 0600, true, false, NULL
) )
660 KMessageBox::error( 0, KIO::NetAccess::lastErrorString() );
661 return QString::null
;
664 return qDestURL
.url();
668 void KDEFileDialog::fileHighlightedCommand( const TQString
& )
670 void KDEFileDialog::fileHighlightedCommand( const QString
& )
673 if ( canNotifySelection() )
675 sendCommand( "fileSelectionChanged" );
676 setCanNotifySelection( false );
680 void KDEFileDialog::selectionChangedCommand()
682 if ( canNotifySelection() )
684 sendCommand( "fileSelectionChanged" );
685 setCanNotifySelection( false );
689 void KDEFileDialog::sendCommand( const QString
&rCommand
)
691 #if OSL_DEBUG_LEVEL > 1
692 ::std::cerr
<< "kdefilepicker sent: " << rCommand
.latin1() << ::std::endl
;
695 //m_aOutputStream << rCommand << endl;
696 ::std::cout
<< rCommand
.utf8() << ::std::endl
;
699 void KDEFileDialog::appendURL( QString
&rBuffer
, const KURL
&rURL
)
701 // From Martin Kretzschmar:
702 // file:///path/to/test%E0.odt is not a valid URL from OOo's point of
703 // view. (?Most modern parts of?) OOo assume(s) that the URL contains only
704 // ASCII characters (which test%E0.odt does) and is UTF-8 after unescaping
705 // (which file:///path/test%E0.odt is not).
706 // Cf. the comment in sal/inc/osl/file.h.
707 // 106 == MIB enum for UTF-8
708 QString qUrlStr
= addExtension( rURL
.url( 0, 106 ) );
710 if ( !isExecuting() && !isSupportedProtocol( rURL
.protocol() ) )
711 qUrlStr
= localCopy( qUrlStr
);
713 if ( qUrlStr
.startsWith( "file:/" ) && qUrlStr
.mid( 6, 1 ) != "/" )
714 qUrlStr
.replace( "file:/", "file:///" );
716 rBuffer
.append( " " );
717 if ( !qUrlStr
.isEmpty() )
718 appendEscaped( rBuffer
, qUrlStr
);
721 void KDEFileDialog::appendEscaped( QString
&rBuffer
, const QString
&rString
)
723 const QChar
*pUnicode
= rString
.unicode();
724 const QChar
*pEnd
= pUnicode
+ rString
.length();
726 rBuffer
.append( '"' );
727 for ( ; pUnicode
!= pEnd
; ++pUnicode
)
729 if ( *pUnicode
== '\\' )
730 rBuffer
.append( "\\\\" );
731 else if ( *pUnicode
== '"' )
732 rBuffer
.append( "\\\"" );
733 else if ( *pUnicode
== '\n' )
734 rBuffer
.append( "\\\n" );
736 rBuffer
.append( *pUnicode
);
738 rBuffer
.append( '"' );
741 QString
KDEFileDialog::escapeString( const QString
&rString
)
744 qString
.reserve( 2*rString
.length() + 2 ); // every char escaped + quotes
746 appendEscaped( qString
, rString
);
751 void KDEFileFilterComboHack::setCurrentFilter( const QString
& filter
)
753 setCurrentText( filter
);
756 // Workaround for 'Filter name (*.blah)' vs. 'Filter name'
757 if ( currentText() != text( currentItem() ) )
760 for ( ; nItem
< count() && !text( nItem
).startsWith( filter
); ++nItem
);
762 if ( nItem
< count() )
763 setCurrentItem( nItem
);
771 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */