summaryrefslogtreecommitdiffstats
path: root/src/app/UserAction/kraction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/UserAction/kraction.cpp')
-rw-r--r--src/app/UserAction/kraction.cpp586
1 files changed, 586 insertions, 0 deletions
diff --git a/src/app/UserAction/kraction.cpp b/src/app/UserAction/kraction.cpp
new file mode 100644
index 0000000..edfa1ac
--- /dev/null
+++ b/src/app/UserAction/kraction.cpp
@@ -0,0 +1,586 @@
+//
+// C++ Implementation: kraction
+//
+// Description:
+//
+//
+// Author: Shie Erlich and Rafi Yanai <>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include <kdialogbase.h>
+#include <kdebug.h>
+#include <tdelocale.h>
+#include <kinputdialog.h>
+#include <tqtextedit.h>
+#include <tqvbox.h>
+#include <tqlayout.h>
+#include <tqsplitter.h>
+#include <tqpushbutton.h>
+#include <tqcheckbox.h>
+#include <tqfile.h>
+#include <tqlabel.h>
+#include <tdeaction.h>
+#include <kurl.h>
+#include <tdemessagebox.h>
+#include <tdefiledialog.h>
+#include "kraction.h"
+#include "expander.h"
+#include "useraction.h"
+#include "../krusader.h"
+#include "../krusaderview.h"
+#include "../defaults.h"
+
+//for the availabilitycheck:
+#include <kmimetype.h>
+#include <tqregexp.h>
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////// KrActionProcDlg /////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+#include <tqlayout.h>
+KrActionProcDlg::KrActionProcDlg( TQString caption, bool enableStderr, TQWidget *parent ) :
+KDialogBase( parent, 0, false, caption, KDialogBase::User1 | KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Cancel ),
+_stdout(0), _stderr(0), _currentTextEdit(0) {
+
+ setButtonOK( i18n( "Close" ) );
+ enableButtonOK( false ); // disable the close button, until the process finishes
+
+ setButtonCancel( KGuiItem(i18n("Kill"), i18n( "Kill the running process" )) );
+
+ setButtonText(KDialogBase::User1, i18n("Save as") );
+
+ TQVBox *page = makeVBoxMainWidget();
+ // do we need to separate stderr and stdout?
+ if ( enableStderr ) {
+ TQSplitter *splitt = new TQSplitter( TQt::Vertical, page );
+ // create stdout
+ TQVBox *stdoutBox = new TQVBox( splitt, "stdout VBox" );
+ stdoutBox->setSpacing( 6 );
+ new TQLabel( i18n( "Standard Output (stdout)" ), stdoutBox );
+ _stdout = new TQTextEdit( stdoutBox );
+ _stdout->setReadOnly( true );
+ _stdout->setMinimumWidth( fontMetrics().maxWidth() * 40 );
+ // create stderr
+ TQVBox *stderrBox = new TQVBox( splitt, "stderr VBox" );
+ stderrBox->setSpacing( 6 );
+ new TQLabel( i18n( "Standard Error (stderr)" ), stderrBox );
+ _stderr = new TQTextEdit( stderrBox );
+ _stderr->setReadOnly( true );
+ _stderr->setMinimumWidth( fontMetrics().maxWidth() * 40 );
+ } else {
+ // create stdout
+ new TQLabel( i18n( "Output" ), page );
+ _stdout = new TQTextEdit( page );
+ _stdout->setReadOnly( true );
+ _stdout->setMinimumWidth( fontMetrics().maxWidth() * 40 );
+ }
+
+ _currentTextEdit = _stdout;
+ connect( _stdout, TQ_SIGNAL( clicked(int, int) ), TQ_SLOT( currentTextEditChanged() ) );
+ if (_stderr)
+ connect( _stderr, TQ_SIGNAL( clicked(int, int) ), TQ_SLOT( currentTextEditChanged() ) );
+
+ krConfig->setGroup( "UserActions" );
+ normalFont = krConfig->readFontEntry( "Normal Font", _UserActions_NormalFont );
+ fixedFont = krConfig->readFontEntry( "Fixed Font", _UserActions_FixedFont );
+ bool startupState = krConfig->readBoolEntry( "Use Fixed Font", _UserActions_UseFixedFont );
+ toggleFixedFont( startupState );
+
+ // HACK This fetches the layout of the buttonbox from KDialogBase, although it is not accessable with KDialogBase's API
+ // None the less it's quite save to use since this implementation hasn't changed since KDE-3.3 (I haven't looked at earlier
+ // versions since we don't support them) and now all work is done in KDE-4.
+ TQWidget* buttonBox = static_cast<TQWidget*>( actionButton(KDialogBase::Ok)->parent() );
+ TQBoxLayout* buttonBoxLayout = static_cast<TQBoxLayout*>( buttonBox->layout() );
+ TQCheckBox* useFixedFont = new TQCheckBox( i18n("Use font with fixed width"), buttonBox );
+ buttonBoxLayout->insertWidget( 0, useFixedFont );
+ useFixedFont->setChecked( startupState );
+ connect( useFixedFont, TQ_SIGNAL( toggled(bool) ), TQ_SLOT( toggleFixedFont(bool) ) );
+}
+
+void KrActionProcDlg::addStderr( TDEProcess *, char *buffer, int buflen ) {
+ if (_stderr)
+ _stderr->append( TQString::fromLatin1( buffer, buflen ) );
+ else {
+ _stdout->setItalic(true);
+ _stdout->append( TQString::fromLatin1( buffer, buflen ) );
+ _stdout->setItalic(false);
+ }
+}
+
+void KrActionProcDlg::addStdout( TDEProcess *, char *buffer, int buflen ) {
+ _stdout->append( TQString::fromLatin1( buffer, buflen ) );
+}
+
+void KrActionProcDlg::toggleFixedFont( bool state ) {
+ if ( state ) {
+ _stdout->setFont( fixedFont );
+ if ( _stderr )
+ _stderr->setFont( fixedFont );
+ }
+ else {
+ _stdout->setFont( normalFont );
+ if ( _stderr )
+ _stderr->setFont( normalFont );
+ }
+}
+
+void KrActionProcDlg::slotUser1() {
+ TQString filename = KFileDialog::getSaveFileName(TQString(), i18n("*.txt|Text files\n*|all files"), this);
+ if ( filename.isEmpty() )
+ return;
+ TQFile file( filename );
+ int answer = KMessageBox::Yes;
+ if ( file.exists() )
+ answer = KMessageBox::warningYesNoCancel( this, //parent
+ i18n("This file already exists.\nDo you want to overwrite it or append the output?"), //text
+ i18n("Overwrite or append?"), //caption
+ i18n("Overwrite"), //label for Yes-Button
+ i18n("Append") //label for No-Button
+ );
+ if ( answer == KMessageBox::Cancel )
+ return;
+ bool open;
+ if ( answer == KMessageBox::No ) // this means to append
+ open = file.open( IO_WriteOnly | IO_Append );
+ else
+ open = file.open( IO_WriteOnly );
+
+ if ( ! open ) {
+ KMessageBox::error( this,
+ i18n("Can't open %1 for writing!\nNothing exported.").arg(filename),
+ i18n("Export failed!")
+ );
+ return;
+ }
+
+ TQTextStream stream( &file );
+ stream << _currentTextEdit->text();
+ file.close();
+}
+
+void KrActionProcDlg::currentTextEditChanged() {
+ if ( _stderr && _stderr->hasFocus() )
+ _currentTextEdit = _stderr;
+ else
+ _currentTextEdit = _stdout;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////// KrActionProc ////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+KrActionProc::KrActionProc( KrActionBase* action ) : TQObject(), _action( action ), _proc( new TDEProcess(this) ), _output( 0 ) {
+ _proc->setUseShell( true );
+
+ connect( _proc, TQ_SIGNAL( processExited( TDEProcess* ) ),
+ this, TQ_SLOT( processExited( TDEProcess* ) ) ) ;
+}
+
+KrActionProc::~KrActionProc() {
+ delete _proc;
+}
+
+void KrActionProc::start( TQString cmdLine ) {
+ TQStringList list = cmdLine;
+ start( list );
+}
+
+void KrActionProc::start( TQStringList cmdLineList ) {
+ _proc->clearArguments();
+ TQString cmd;
+
+ if ( ! _action->startpath().isEmpty() )
+ _proc->setWorkingDirectory( _action->startpath() );
+
+ if ( _action->execType() == KrAction::Terminal && cmdLineList.count() > 1)
+ KMessageBox::sorry( 0, i18n("Support for more than one command doesn't work in a terminal. Only the first is executed in the terminal.") );
+
+ if ( _action->execType() == KrAction::RunInTE
+ && ( MAIN_VIEW->konsole_part == NULL || MAIN_VIEW->konsole_part->widget() == NULL ) ) {
+ KMessageBox::sorry( 0, i18n("Embedded terminal emulator does not work, using output collection instead.") );
+ }
+
+ if( _action->execType() == KrAction::Normal || _action->execType() == KrAction::Terminal
+ || ( _action->execType() == KrAction::RunInTE && MAIN_VIEW->konsole_part && MAIN_VIEW->konsole_part->widget() )
+ ) { // not collect output
+ //TODO option to run them in paralell (not available for: collect output)
+ for ( TQStringList::Iterator it = cmdLineList.begin(); it != cmdLineList.end(); ++it) {
+ if ( ! cmd.isEmpty() )
+ cmd += " ; "; //TODO make this separator configurable (users may want && or || for spec. actions)
+ cmd += *it;
+ }
+ //run in TE
+ if ( _action->execType() == KrAction::RunInTE ) {
+ //send the commandline contents to the terminal emulator
+ TQKeyEvent keyEvent( TQEvent::KeyPress, 0, -1, 0, cmd+"\n");
+ TQApplication::sendEvent( MAIN_VIEW->konsole_part->widget(), &keyEvent );
+ } else { // will start a new process
+ // run in terminal
+ if ( _action->execType() == KrAction::Terminal ) {
+ krConfig->setGroup( "UserActions" );
+ TQString term = krConfig->readEntry( "Terminal", _UserActions_Terminal );
+
+ if ( _action->user().isEmpty() )
+ ( *_proc ) << term << cmd;
+ else
+// ( *_proc ) << "tdesu" << "-u" << *_properties->user() << "-c" << TDEProcess::quote("konsole --noclose -e " + TDEProcess::quote(cmd) );
+ ( *_proc ) << "tdesu" << "-u" << _action->user() << "-c" << TDEProcess::quote( term + " " + cmd );
+ } else { // no terminal, no output collection, start&forget
+ if ( _action->user().isEmpty() )
+ ( *_proc ) << cmd;
+ else
+ ( *_proc ) << "tdesu" << "-u" << _action->user() << "-c" << TDEProcess::quote(cmd);
+ }
+ _proc->start( TDEProcess::NotifyOnExit, ( TDEProcess::Communication ) ( TDEProcess::Stdout | TDEProcess::Stderr ) );
+ }
+ }
+ else { // collect output
+ bool separateStderr = false;
+ if ( _action->execType() == KrAction::CollectOutputSeparateStderr )
+ separateStderr = true;
+ _output = new KrActionProcDlg( _action->text(), separateStderr );
+ // connect the output to the dialog
+ connect( _proc, TQ_SIGNAL( receivedStderr( TDEProcess*, char*, int ) ), _output, TQ_SLOT( addStderr( TDEProcess*, char *, int ) ) );
+ connect( _proc, TQ_SIGNAL( receivedStdout( TDEProcess*, char*, int ) ), _output, TQ_SLOT( addStdout( TDEProcess*, char *, int ) ) );
+ connect( _output, TQ_SIGNAL( cancelClicked() ), this, TQ_SLOT( kill() ) );
+ _output->show();
+ for ( TQStringList::Iterator it = cmdLineList.begin(); it != cmdLineList.end(); ++it) {
+ if ( ! cmd.isEmpty() )
+ cmd += " ; "; //TODO make this separator configurable (users may want && or ||)
+ //TODO: read header fom config or action-properties and place it on top of each command
+ if ( cmdLineList.count() > 1 )
+ cmd += "echo --------------------------------------- ; ";
+ cmd += *it;
+ }
+ if ( _action->user().isEmpty() )
+ ( *_proc ) << cmd;
+ else
+ // "-t" is nessesary that tdesu displays the terminal-output of the command
+ ( *_proc ) << "tdesu" << "-t" << "-u" << _action->user() << "-c" << TDEProcess::quote(cmd);
+ _proc->start( TDEProcess::NotifyOnExit, ( TDEProcess::Communication ) ( TDEProcess::Stdout | TDEProcess::Stderr ) );
+ }
+
+}
+
+void KrActionProc::processExited( TDEProcess * ) {
+ // enable the 'close' button on the dialog (if active), disable 'kill' button
+ if ( _output ) {
+ _output->enableButtonOK( true );
+ _output->enableButtonCancel( false);
+ }
+ delete this; // banzai!!
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////// KrAction ///////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+KrAction::KrAction( TDEActionCollection *parent, const char* name ) : TDEAction( parent, name ) {
+ connect(this, TQ_SIGNAL(activated()), this, TQ_SLOT(exec()) );
+}
+
+KrAction::~KrAction() {
+ unplugAll();
+ krUserAction->removeKrAction( this ); // Importent! Else Krusader will crash when writing the actions to file
+}
+
+bool KrAction::isAvailable( const KURL& currentURL ) {
+ bool available = true; //show per default (FIXME: make the default an attribute of <availability>)
+
+ //check protocol
+ if ( ! _showonlyProtocol.empty() ) {
+ available = false;
+ for ( TQStringList::Iterator it = _showonlyProtocol.begin(); it != _showonlyProtocol.end(); ++it ) {
+ //kdDebug() << "KrAction::isAvailable currendProtocol: " << currentURL.protocol() << " =?= " << *it << endl;
+ if ( currentURL.protocol() == *it ) { // FIXME remove trailing slashes at the xml-parsing (faster because done only once)
+ available = true;
+ break;
+ }
+ }
+ } //check protocol: done
+
+ //check the Path-list:
+ if ( ! _showonlyPath.empty() ) {
+ available = false;
+ for ( TQStringList::Iterator it = _showonlyPath.begin(); it != _showonlyPath.end(); ++it ) {
+ if ( (*it).right(1) == "*" ){
+ if ( currentURL.path().find( (*it).left( (*it).length() - 1 ) ) == 0 ) {
+ available = true;
+ break;
+ }
+ } else
+ if ( currentURL.directory() == *it ) { // FIXME remove trailing slashes at the xml-parsing (faster because done only once)
+ available = true;
+ break;
+ }
+ }
+ } //check the Path-list: done
+
+ //check mime-type
+ if ( ! _showonlyMime.empty() ) {
+ available = false;
+ KMimeType::Ptr mime = KMimeType::findByURL( currentURL );
+ for ( TQStringList::Iterator it = _showonlyMime.begin(); it != _showonlyMime.end(); ++it ) {
+ if ( (*it).contains("/") ) {
+ if ( mime->is( *it ) ) { // don't use ==; use 'is()' instead, which is aware of inheritence (ie: text/x-makefile is also text/plain)
+ available = true;
+ break;
+ }
+ }
+ else {
+ if ( mime->name().find( *it ) == 0 ) { // 0 is the beginning, -1 is not found
+ available = true;
+ break;
+ }
+ }
+ } //for
+ } //check the mime-type: done
+
+ //check filename
+ if ( ! _showonlyFile.empty() ) {
+ available = false;
+ for ( TQStringList::Iterator it = _showonlyFile.begin(); it != _showonlyFile.end(); ++it ) {
+ TQRegExp regex = TQRegExp( *it, false, true ); // case-sensitive = false; wildcards = true
+ if ( regex.exactMatch( currentURL.fileName() ) ) {
+ available = true;
+ break;
+ }
+ }
+ } //check the filename: done
+
+ return available;
+}
+
+
+bool KrAction::xmlRead( const TQDomElement& element ) {
+/*
+ This has to be done elsewhere!!
+
+ if ( element.tagName() != "action" )
+ return false;
+
+ Also the name has to be checked before the action is created!
+
+ setName( element.attribute( "name" ).latin1() );
+*/
+
+ for ( TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling() ) {
+ TQDomElement e = node.toElement();
+ if ( e.isNull() )
+ continue; // this should skip nodes which are not elements ( i.e. comments, <!-- -->, or text nodes)
+
+ if ( e.tagName() == "title" )
+ setText( e.text() );
+ else
+ if ( e.tagName() == "tooltip" )
+ setToolTip( e.text() );
+ else
+ if ( e.tagName() == "icon" )
+ setIcon( e.text() );
+ else
+ if ( e.tagName() == "category" )
+ setCategory( e.text() );
+ else
+ if ( e.tagName() == "description" )
+ setWhatsThis( e.text() );
+ else
+ if (e.tagName() == "command")
+ readCommand( e );
+ else
+ if ( e.tagName() == "startpath" )
+ setStartpath( e.text() );
+ else
+ if (e.tagName() == "availability")
+ readAvailability( e );
+ else
+ if ( e.tagName() == "defaultshortcut" )
+ setShortcut( TDEShortcut( e.text() ) );
+ else
+
+ // unknown but not empty
+ if ( ! e.tagName().isEmpty() )
+ krOut << "KrAction::xmlRead() - unrecognized tag found: <action name=\"" << name() << "\"><" << e.tagName() << ">" << endl;
+
+ } // for ( TQDomNode node = action->firstChild(); !node.isNull(); node = node.nextSibling() )
+
+ return true;
+} //KrAction::xmlRead
+
+TQDomElement KrAction::xmlDump( TQDomDocument& doc ) const {
+ TQDomElement actionElement = doc.createElement("action");
+ actionElement.setAttribute( "name", name() );
+
+#define TEXT_ELEMENT( TAGNAME, TEXT ) \
+ { \
+ TQDomElement e = doc.createElement( TAGNAME ); \
+ e.appendChild( doc.createTextNode( TEXT ) ); \
+ actionElement.appendChild( e ); \
+ }
+
+ TEXT_ELEMENT( "title", text() )
+
+ if ( ! toolTip().isEmpty() )
+ TEXT_ELEMENT( "tooltip", toolTip() )
+
+ if ( ! icon().isEmpty() )
+ TEXT_ELEMENT( "icon", icon() )
+
+ if ( ! category().isEmpty() )
+ TEXT_ELEMENT( "category", category() )
+
+ if ( ! whatsThis().isEmpty() )
+ TEXT_ELEMENT( "description", whatsThis() )
+
+ actionElement.appendChild( dumpCommand( doc ) );
+
+ if ( ! startpath().isEmpty() )
+ TEXT_ELEMENT( "startpath", startpath() )
+
+ TQDomElement availabilityElement = dumpAvailability( doc );
+ if ( availabilityElement.hasChildNodes() )
+ actionElement.appendChild( availabilityElement );
+
+ if ( ! shortcut().isNull() )
+ TEXT_ELEMENT( "defaultshortcut", shortcut().toStringInternal() ) //.toString() would return a localised string which can't be read again
+
+ return actionElement;
+} //KrAction::xmlDump
+
+void KrAction::readCommand( const TQDomElement& element ) {
+ TQString attr;
+
+ attr = element.attribute( "executionmode", "normal" ); // default: "normal"
+ if ( attr == "normal")
+ setExecType( Normal );
+ else
+ if ( attr == "terminal" )
+ setExecType( Terminal );
+ else if ( attr == "collect_output")
+ setExecType( CollectOutput );
+ else if ( attr == "collect_output_separate_stderr")
+ setExecType( CollectOutputSeparateStderr );
+ else
+ krOut << "KrAction::readCommand() - unrecognized attribute value found: <action name=\"" << name() << "\"><command executionmode=\"" << attr << "\""<< endl;
+
+ attr = element.attribute( "accept", "local" ); // default: "local"
+ if ( attr == "local" )
+ setAcceptURLs( false );
+ else if ( attr == "url")
+ setAcceptURLs( true );
+ else
+ krOut << "KrAction::readCommand() - unrecognized attribute value found: <action name=\"" << name() << "\"><command accept=\"" << attr << "\""<< endl;
+
+ attr = element.attribute( "confirmexecution", "false" ); // default: "false"
+ if ( attr == "true" )
+ setConfirmExecution( true );
+ else
+ setConfirmExecution( false );
+
+ setUser( element.attribute( "run_as" ) );
+
+ setCommand( element.text() );
+
+} //KrAction::readCommand
+
+TQDomElement KrAction::dumpCommand( TQDomDocument& doc ) const {
+ TQDomElement commandElement = doc.createElement("command");
+
+ switch ( execType() ) {
+ case Terminal:
+ commandElement.setAttribute( "executionmode", "terminal" );
+ break;
+ case CollectOutput:
+ commandElement.setAttribute( "executionmode", "collect_output" );
+ break;
+ case CollectOutputSeparateStderr:
+ commandElement.setAttribute( "executionmode", "collect_output_separate_stderr" );
+ break;
+ default:
+ // don't write the default to file
+ break;
+ }
+
+ if ( acceptURLs() )
+ commandElement.setAttribute( "accept", "url" );
+
+ if ( confirmExecution() )
+ commandElement.setAttribute( "confirmexecution", "true" );
+
+ if ( ! user().isEmpty() )
+ commandElement.setAttribute( "run_as", user() );
+
+ commandElement.appendChild( doc.createTextNode( command() ) );
+
+ return commandElement;
+} //KrAction::dumpCommand
+
+void KrAction::readAvailability( const TQDomElement& element ) {
+ for ( TQDomNode node = element.firstChild(); ! node.isNull(); node = node.nextSibling() ) {
+ TQDomElement e = node.toElement();
+ if ( e.isNull() )
+ continue; // this should skip nodes which are not elements ( i.e. comments, <!-- -->, or text nodes)
+
+ TQStringList* showlist = 0;
+
+ if ( e.tagName() == "protocol" )
+ showlist = &_showonlyProtocol;
+ else
+ if ( e.tagName() == "path" )
+ showlist = &_showonlyPath;
+ else
+ if ( e.tagName() == "mimetype" )
+ showlist = & _showonlyMime;
+ else
+ if ( e.tagName() == "filename" )
+ showlist = & _showonlyFile;
+ else {
+ krOut << "KrAction::readAvailability() - unrecognized element found: <action name=\"" << name() << "\"><availability><" << e.tagName() << ">"<< endl;
+ showlist = 0;
+ }
+
+ if ( showlist ) {
+ for ( TQDomNode subnode = e.firstChild(); ! subnode.isNull(); subnode = subnode.nextSibling() ) {
+ TQDomElement subelement = subnode.toElement();
+ if ( subelement.tagName() == "show" )
+ showlist->append( subelement.text() );
+ } // for
+ } // if ( showlist )
+
+ } // for
+} //KrAction::readAvailability
+
+TQDomElement KrAction::dumpAvailability( TQDomDocument& doc ) const {
+ TQDomElement availabilityElement = doc.createElement("command");
+
+# define LIST_ELEMENT( TAGNAME, LIST ) \
+ { \
+ TQDomElement e = doc.createElement( TAGNAME ); \
+ for ( TQStringList::const_iterator it = LIST.constBegin(); it != LIST.constEnd(); ++it ) { \
+ TQDomElement show = doc.createElement( "show" ); \
+ show.appendChild( doc.createTextNode( *it ) ); \
+ e.appendChild( show ); \
+ } \
+ availabilityElement.appendChild( e ); \
+ }
+
+ if ( ! _showonlyProtocol.isEmpty() )
+ LIST_ELEMENT( "protocol", _showonlyProtocol )
+
+ if ( ! _showonlyPath.isEmpty() )
+ LIST_ELEMENT( "path", _showonlyPath )
+
+ if ( ! _showonlyMime.isEmpty() )
+ LIST_ELEMENT( "mimetype", _showonlyMime )
+
+ if ( ! _showonlyFile.isEmpty() )
+ LIST_ELEMENT( "filename", _showonlyFile )
+
+ return availabilityElement;
+} //KrAction::dumpAvailability
+
+#include "kraction.moc"