summaryrefslogtreecommitdiffstats
path: root/src/app/UserAction
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/UserAction')
-rw-r--r--src/app/UserAction/Makefile.am17
-rw-r--r--src/app/UserAction/expander.cpp1242
-rw-r--r--src/app/UserAction/expander.h245
-rw-r--r--src/app/UserAction/kraction.cpp586
-rw-r--r--src/app/UserAction/kraction.h170
-rw-r--r--src/app/UserAction/kractionbase.cpp77
-rw-r--r--src/app/UserAction/kractionbase.h97
-rw-r--r--src/app/UserAction/tstring.h116
-rw-r--r--src/app/UserAction/useraction.cpp212
-rw-r--r--src/app/UserAction/useraction.h145
-rw-r--r--src/app/UserAction/useractionpopupmenu.cpp26
-rw-r--r--src/app/UserAction/useractionpopupmenu.h25
12 files changed, 2958 insertions, 0 deletions
diff --git a/src/app/UserAction/Makefile.am b/src/app/UserAction/Makefile.am
new file mode 100644
index 0000000..691f60a
--- /dev/null
+++ b/src/app/UserAction/Makefile.am
@@ -0,0 +1,17 @@
+if include_libkjsembed
+AM_CPPFLAGS = -D__KJSEMBED__
+endif
+
+noinst_LIBRARIES = libUserAction.a
+
+INCLUDES = $(all_includes)
+
+libUserAction_a_METASOURCES = AUTO
+
+libUserAction_a_SOURCES = useraction.cpp \
+ kraction.cpp \
+ expander.cpp \
+ useractionpopupmenu.cpp \
+ kractionbase.cpp
+
+noinst_HEADERS = tstring.h
diff --git a/src/app/UserAction/expander.cpp b/src/app/UserAction/expander.cpp
new file mode 100644
index 0000000..4b5a745
--- /dev/null
+++ b/src/app/UserAction/expander.cpp
@@ -0,0 +1,1242 @@
+//
+// C++ Implementation: expander
+//
+// Description:
+//
+//
+// Author: Jonas B�r (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include <algorithm>
+
+#include "expander.h"
+
+#include "../krusader.h"
+#include "../krusaderview.h"
+#include "../panelmanager.h"
+#include "../Panel/listpanel.h"
+#include "../Panel/panelfunc.h"
+#include "../Panel/krview.h"
+#include "../Synchronizer/synchronizergui.h"
+#include "../Search/krsearchdialog.h"
+#include "../GUI/profilemanager.h"
+#include "../VFS/preservingcopyjob.h"
+#include "../KViewer/krviewer.h"
+#include "../krservices.h"
+
+#ifdef __KJSEMBED__
+#include "../KrJS/krjs.h"
+#endif
+
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <kstandarddirs.h>
+#include <tdemessagebox.h>
+#include <tdetempfile.h>
+#include <tqstringlist.h>
+#include <tqclipboard.h>
+
+#include <functional>
+using namespace std;
+
+#define NEED_PANEL if (panel==0) { panelMissingError(_expression,exp); return TQString(); }
+
+#include "tstring.h"
+
+TQValueList<const exp_placeholder*>& Expander::_placeholder()
+{
+ static TQValueList<const exp_placeholder*> ret;
+ return ret;
+}
+
+void exp_placeholder::panelMissingError(const TQString &s, Expander& exp)
+{
+ exp.setError( Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Needed panel specification missing in expander %1").arg(s)) );
+}
+
+TQStringList exp_placeholder::fileList(const ListPanel* const panel,const TQString& type,const TQString& mask,const bool ommitPath,const bool useUrl,Expander& exp,const TQString& error)
+{
+ TQStringList items;
+ if ( type.isEmpty() || type == "all" )
+ panel->view->getItemsByMask( mask, &items );
+ else if ( type == "files" )
+ panel->view->getItemsByMask( mask, &items, false, true );
+ else if ( type == "dirs" )
+ panel->view->getItemsByMask( mask, &items, true, false );
+ else if ( type == "selected" )
+ panel->view->getSelectedItems( &items );
+ else {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: Bad argument to %1: %2 is not valid item specifier").arg(error,type) ) );
+ return TQString();
+ }
+ if ( !ommitPath ) { // add the current path
+ // translate to urls using vfs
+ KURL::List* list = panel->func->files()->vfs_getFiles(&items);
+ items.clear();
+ // parse everything to a single qstring
+ for (KURL::List::Iterator it = list->begin(); it != list->end(); ++it) {
+ items.push_back(useUrl ? (*it).url() : (*it).path());
+ }
+ delete list;
+ }
+
+ return items;
+}
+
+namespace {
+
+class exp_simpleplaceholder : public exp_placeholder
+{
+public:
+ EXP_FUNC;
+ virtual TagString expFunc ( const ListPanel*, const TQStringList&, const bool&, Expander& ) const=0;
+};
+
+/**
+ * expands %_Path% ('_' is replaced by 'a', 'o', 'r' or 'l' to indicate the active, other, right or left panel) with the path of the specified panel
+ */
+class exp_Path : public exp_simpleplaceholder {
+ static const exp_Path instance;
+ exp_Path();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * expands %_Count% ('_' is replaced by 'a', 'o', 'r' or 'l' to indicate the active, other, right or left panel) with the number of items, which type is specified by the first Parameter
+ */
+class exp_Count : public exp_simpleplaceholder {
+ static const exp_Count instance;
+ exp_Count();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * expands %_Filter% ('_' is replaced by 'a', 'o', 'r' or 'l' to indicate the active, other, right or left panel) with the correspondend filter (ie: "*.cpp")
+ */
+class exp_Filter : public exp_simpleplaceholder {
+ static const exp_Filter instance;
+ exp_Filter();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * expands %_Current% ('_' is replaced by 'a', 'o', 'r' or 'l' to indicate the active, other, right or left panel) with the current item ( != the selected onec)
+ */
+class exp_Current : public exp_simpleplaceholder {
+ static const exp_Current instance;
+ exp_Current();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * expands %_List% ('_' is replaced by 'a', 'o', 'r' or 'l' to indicate the active, other, right or left panel) with a list of items, which type is specified by the first Parameter
+ */
+class exp_List : public exp_simpleplaceholder {
+ static const exp_List instance;
+ exp_List();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * expands %_ListFile% ('_' is replaced by 'a', 'o', 'r' or 'l' to indicate the active, other, right or left panel) with the name of a temporary file, containing a list of items, which type is specified by the first Parameter
+ */
+class exp_ListFile : public exp_simpleplaceholder {
+ static const exp_ListFile instance;
+ exp_ListFile();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * expands %_Ask% ('_' is nessesary because there is no panel needed) with the return of an input-dialog
+ */
+class exp_Ask : public exp_simpleplaceholder {
+ static const exp_Ask instance;
+ exp_Ask();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * This copies it's first Parameter to the clipboard
+ */
+class exp_Clipboard : public exp_placeholder {
+ static const exp_Clipboard instance;
+ exp_Clipboard();
+public:
+ EXP_FUNC;
+};
+
+/**
+ * This selects all items by the mask given with the first Parameter
+ */
+class exp_Select : public exp_simpleplaceholder {
+ static const exp_Select instance;
+ exp_Select();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * This changes the panel'spath to the value given with the first Parameter.
+ */
+class exp_Goto : public exp_simpleplaceholder {
+ static const exp_Goto instance;
+ exp_Goto();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * This is equal to 'cp <first Parameter> <second Parameter>'.
+ */
+class exp_Copy : public exp_placeholder {
+ static const exp_Copy instance;
+ exp_Copy();
+public:
+ EXP_FUNC;
+};
+
+/**
+ * This is equal to 'mv <first Parameter> <second Parameter>'.
+ */
+class exp_Move : public exp_placeholder {
+ static const exp_Move instance;
+ exp_Move();
+public:
+ EXP_FUNC;
+};
+
+/**
+ * This opens the synchronizer with a given profile
+ */
+class exp_Sync : public exp_simpleplaceholder {
+ static const exp_Sync instance;
+ exp_Sync();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * This opens the searchmodule with a given profile
+ */
+class exp_NewSearch : public exp_simpleplaceholder {
+ static const exp_NewSearch instance;
+ exp_NewSearch();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * This loads the panel-profile with a given name
+ */
+class exp_Profile : public exp_simpleplaceholder {
+ static const exp_Profile instance;
+ exp_Profile();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * This is setting marks in the string where he is later splitted up for each {all, selected, files, dirs}
+ */
+class exp_Each : public exp_simpleplaceholder {
+ static const exp_Each instance;
+ exp_Each();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * This sets the sorting on a specific colunm
+ */
+class exp_ColSort : public exp_simpleplaceholder {
+ static const exp_ColSort instance;
+ exp_ColSort();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+/**
+ * This sets relation between the left and right panel
+ */
+class exp_PanelSize : public exp_simpleplaceholder {
+ static const exp_PanelSize instance;
+ exp_PanelSize();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+#ifdef __KJSEMBED__
+/**
+ * This sets relation between the left and right panel
+ */
+class exp_Script : public exp_simpleplaceholder {
+ static const exp_Script instance;
+ exp_Script();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+const exp_Script exp_Script::instance;
+
+#endif
+
+/**
+ * This loads a file in the internal viewer
+ */
+class exp_View : public exp_simpleplaceholder {
+ static const exp_View instance;
+ exp_View();
+public:
+ SIMPLE_EXP_FUNC;
+};
+
+const exp_View exp_View::instance;
+const exp_PanelSize exp_PanelSize::instance;
+const exp_ColSort exp_ColSort::instance;
+const exp_Each exp_Each::instance;
+const exp_Profile exp_Profile::instance;
+const exp_NewSearch exp_NewSearch::instance;
+const exp_Sync exp_Sync::instance;
+const exp_Move exp_Move::instance;
+const exp_Copy exp_Copy::instance;
+const exp_Goto exp_Goto::instance;
+const exp_Select exp_Select::instance;
+const exp_Clipboard exp_Clipboard::instance;
+const exp_Ask exp_Ask::instance;
+const exp_ListFile exp_ListFile::instance;
+const exp_List exp_List::instance;
+const exp_Current exp_Current::instance;
+const exp_Filter exp_Filter::instance;
+const exp_Count exp_Count::instance;
+const exp_Path exp_Path::instance;
+
+////////////////////////////////////////////////////////////
+//////////////////////// utils ////////////////////////
+////////////////////////////////////////////////////////////
+
+/**
+ * escapes everything that confuses bash in filenames
+ * @param s String to manipulate
+ * @return escaped string
+ */
+TQString bashquote( TQString s ) {
+ /*
+ // we _can_not_ use this function because it _encloses_ the sting in single-quots!
+ // In this case quotes strings could not be concaternated anymore
+ return KrServices::quote(s);
+ */
+
+ static const TQString evilstuff = "\\\"'`()[]{}!?;$&<>| \t\r\n"; // stuff that should get escaped
+
+ for ( unsigned int i = 0; i < evilstuff.length(); ++i )
+ s.replace( evilstuff[ i ], (TQString("\\") + evilstuff[ i ]) );
+
+ return s;
+}
+
+TQString separateAndQuote(TQStringList list,const TQString& separator,const bool quote)
+{
+ if(quote)
+ transform(list.begin(),list.end(),list.begin(),bashquote);
+ return list.join(separator);
+}
+/////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////// expander classes ////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+exp_Path::exp_Path() {
+ _expression = "Path";
+ _description = i18n("Panel's Path...");
+ _needPanel = true;
+
+ addParameter( exp_parameter( i18n("Automatically escape spaces"), "__yes", false ) );
+}
+TagString exp_Path::expFunc( const ListPanel* panel, const TQStringList& parameter, const bool& useUrl, Expander& exp ) const {
+ NEED_PANEL
+
+ TQString result;
+
+ if ( useUrl )
+ result = panel->func->files()->vfs_getOrigin().url() + "/";
+ else
+ result = panel->func->files()->vfs_getOrigin().path() + "/";
+
+ if ( parameter[0].lower() == "no" ) // don't escape spaces
+ return TagString(result);
+ else
+ return TagString(bashquote(result));
+}
+
+exp_Count::exp_Count() {
+ _expression = "Count";
+ _description = i18n("Number of...");
+ _needPanel = true;
+
+ addParameter( exp_parameter( i18n("Count:"), "__choose:All;Files;Dirs;Selected", false ) );
+}
+TagString exp_Count::expFunc( const ListPanel* panel, const TQStringList& parameter, const bool&, Expander& exp ) const {
+ NEED_PANEL
+
+ int n = -1;
+ if ( parameter[ 0 ].isEmpty() || parameter[ 0 ].lower() == "all" )
+ n = panel->view->numDirs() + panel->view->numFiles();
+ else if ( parameter[ 0 ].lower() == "files" )
+ n = panel->view->numFiles();
+ else if ( parameter[ 0 ].lower() == "dirs" )
+ n = panel->view->numDirs();
+ else if ( parameter[ 0 ].lower() == "selected" )
+ n = panel->view->numSelected();
+ else {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: Bad argument to Count: %1 is not valid item specifier").arg(parameter[0]) ));
+ return TQString();
+ }
+
+ return TagString(TQString("%1").arg( n ));
+}
+
+exp_Filter::exp_Filter() {
+ _expression = "Filter";
+ _description = i18n("Filter Mask (*.h, *.cpp, etc.)");
+ _needPanel = true;
+}
+TagString exp_Filter::expFunc( const ListPanel* panel, const TQStringList&, const bool&, Expander& exp ) const {
+ NEED_PANEL
+
+ return panel->view->filterMask().nameFilter();
+}
+
+exp_Current::exp_Current() {
+ _expression = "Current";
+ _description = i18n("Current File (!= Selected File)...");
+ _needPanel = true;
+
+ addParameter( exp_parameter( i18n("Omit the current path (optional)"), "__no", false ) );
+ addParameter( exp_parameter( i18n("Automatically escape spaces"), "__yes", false ) );
+}
+TagString exp_Current::expFunc( const ListPanel* panel, const TQStringList& parameter, const bool& useUrl, Expander& exp ) const {
+ NEED_PANEL
+
+ TQString item = panel->view->getCurrentItem();
+
+ TQString result;
+
+ if ( parameter[0].lower() == "yes" ) // ommit the current path
+ result = item;
+ else {
+ KURL url = panel->func->files()->vfs_getFile( item );
+ if ( useUrl )
+ result = url.url();
+ else
+ result = url.path();
+ }
+
+ if ( parameter[1].lower() == "no" ) // don't escape spaces
+ return result;
+ else
+ return bashquote(result);
+}
+
+exp_List::exp_List() {
+ _expression = "List";
+ _description = i18n("Item List of...");
+ _needPanel = true;
+
+ addParameter( exp_parameter( i18n("Which items:"), "__choose:All;Files;Dirs;Selected", false ) );
+ addParameter( exp_parameter( i18n("Separator between the items (optional):"), " ", false ) );
+ addParameter( exp_parameter( i18n("Omit the current path (optional)"), "__no", false ) );
+ addParameter( exp_parameter( i18n("Mask (optional, all but 'Selected'):"), "__select", false ) );
+ addParameter( exp_parameter( i18n("Automatically escape spaces"), "__yes", false ) );
+}
+TagString exp_List::expFunc( const ListPanel* panel, const TQStringList& parameter, const bool& useUrl, Expander& exp ) const {
+ NEED_PANEL
+
+ // get selected items from view
+ TQStringList items;
+ TQString mask;
+
+ if ( parameter.count() <= 3 || parameter[3].isEmpty() )
+ mask = "*";
+ else
+ mask = parameter[3];
+
+ return separateAndQuote(
+ fileList(panel,
+ parameter.empty() ? TQString() : parameter[0].lower(),
+ mask, parameter.count() > 2 ? parameter[2].lower()=="yes" : false,
+ useUrl, exp, "List"),
+ parameter.count() > 1 ? parameter[1] : " ",
+ parameter.count() > 4 ? parameter[4].lower()=="yes" : true);
+}
+
+exp_ListFile::exp_ListFile() {
+ _expression = "ListFile";
+ _description = i18n("Filename of an Item List...");
+ _needPanel = true;
+
+ addParameter( exp_parameter( i18n("Which items:"), "__choose:All;Files;Dirs;Selected", false ) );
+ addParameter( exp_parameter( i18n("Separator between the items (optional)"), "\n", false ) );
+ addParameter( exp_parameter( i18n("Omit the current path (optional)"), "__no", false ) );
+ addParameter( exp_parameter( i18n("Mask (optional, all but 'Selected'):"), "__select", false ) );
+ addParameter( exp_parameter( i18n("Automatically escape spaces"), "__no", false ) );
+}
+TagString exp_ListFile::expFunc( const ListPanel* panel, const TQStringList& parameter, const bool& useUrl, Expander& exp ) const {
+ NEED_PANEL
+
+ // get selected items from view
+ TQStringList items;
+ TQString mask;
+
+ if ( parameter.count() <= 3 || parameter[3].isEmpty() )
+ mask = "*";
+ else
+ mask = parameter[3];
+ KTempFile tmpFile( locateLocal("tmp", "krusader"), ".itemlist" );
+
+ if ( tmpFile.status() != 0 ) {
+ setError(exp, Error(Error::S_FATAL,Error::C_WORLD, i18n("Expander: tempfile couldn't be opened (%1)" ).arg(strerror( tmpFile.status() )) ));
+ return TQString();
+ }
+
+ TQTextStream stream( tmpFile.file() );
+ stream << separateAndQuote(
+ fileList(panel,
+ parameter.empty() ? TQString() : parameter[0].lower(),
+ mask, parameter.count()>2 ? parameter[2].lower()=="yes" : false,
+ useUrl, exp, "ListFile"),
+ parameter.count() > 1 ? parameter[1] : "\n",
+ parameter.count() > 4 ? parameter[4].lower()=="yes" : true)
+ << "\n";
+ tmpFile.close();
+
+ return tmpFile.name();
+}
+
+exp_Select::exp_Select() {
+ _expression = "Select";
+ _description = i18n("Manipulate the Selection...");
+ _needPanel = true;
+
+ addParameter( exp_parameter( i18n("Selection mask:"), "__select", true ) );
+ addParameter( exp_parameter( i18n("Manipulate in which way:"), "__choose:Set;Add;Remove", false ) );
+}
+TagString exp_Select::expFunc( const ListPanel* panel, const TQStringList& parameter, const bool& , Expander& exp) const {
+ NEED_PANEL
+
+ KRQuery mask;
+ if ( parameter.count() <= 0 || parameter[0].isEmpty() )
+ mask = KRQuery( "*" );
+ else
+ mask = KRQuery( parameter[0] );
+
+ if ( parameter[1].lower() == "add")
+ panel->view->select( mask );
+ else if ( parameter[1].lower() == "remove")
+ panel->view->unselect( mask );
+ else { // parameter[1].lower() == "set" or isEmpty() or whatever
+ panel->view->unselect( KRQuery( "*" ) );
+ panel->view->select( mask );
+ }
+
+ return TQString(); // this doesn't return anything, that's normal!
+}
+
+exp_Goto::exp_Goto() {
+ _expression = "Goto";
+ _description = i18n("Jump to a Location...");
+ _needPanel = true;
+
+ addParameter( exp_parameter( i18n("Choose a path:"), "__goto", true ) );
+ addParameter( exp_parameter( i18n("Open location in a new tab"), "__no", false ) );
+}
+TagString exp_Goto::expFunc( const ListPanel* panel, const TQStringList& parameter, const bool&, Expander& exp ) const {
+ NEED_PANEL
+
+ bool newTab = false;
+ if ( parameter[1].lower() == "yes" )
+ newTab = true;
+
+ if ( newTab ) {
+ if ( panel == LEFT_PANEL)
+ krApp->mainView->leftMng->slotNewTab( parameter[0] );
+ else
+ krApp->mainView->rightMng->slotNewTab( parameter[0] );
+ }
+ else {
+ panel->func->openUrl( parameter[0], "" );
+ const_cast<ListPanel*>(panel)->slotFocusOnMe();
+ }
+
+ return TQString(); // this doesn't return anything, that's normal!
+}
+
+/*
+exp_Search::exp_Search() {
+ _expression = "Search";
+ _description = i18n("Search for files");
+ _needPanel = true;
+
+ addParameter( new exp_parameter( i18n("please choose the setting"), "__searchprofile", true ) );
+ addParameter( new exp_parameter( i18n("open the search in a new tab"), "__yes", false ) ); //TODO: add this also to panel-dependent as soon as vfs support the display of search-results
+}
+*/
+
+exp_Ask::exp_Ask() {
+ _expression = "Ask";
+ _description = i18n("Ask Parameter from User...");
+ _needPanel = false;
+
+ addParameter( exp_parameter( i18n("Question:"), "Where do you want do go today?", true ) );
+ addParameter( exp_parameter( i18n("Preset (optional):"), "", false ) );
+ addParameter( exp_parameter( i18n("Caption (optional):"), "", false ) );
+}
+TagString exp_Ask::expFunc( const ListPanel*, const TQStringList& parameter, const bool&, Expander& exp ) const {
+ TQString caption, preset, result;
+
+ if ( parameter.count() <= 2 || parameter[2].isEmpty() )
+ caption = i18n("User Action");
+ else
+ caption = parameter[2];
+ if ( parameter.count() <= 1 || parameter[1].isEmpty() )
+ preset = TQString();
+ else
+ preset = parameter[1];
+
+ bool ok;
+ result = KInputDialog::getText(
+ caption,
+ parameter[0],
+ preset,
+ &ok );
+
+ if (ok)
+ return result;
+ else {
+ setError(exp, Error(Error::S_ERROR,Error::C_USER,"User cancelled") );
+ return TQString();
+ }
+}
+
+exp_Clipboard::exp_Clipboard() {
+ _expression = "Clipboard";
+ _description = i18n("Copy to Clipboard...");
+ _needPanel = false;
+
+ addParameter( exp_parameter( i18n("What to copy:"), "__placeholder", true ) );
+ addParameter( exp_parameter( i18n("Append to current clipboard content with this separator (optional):"), "", false ) );
+}
+TagString exp_Clipboard::expFunc( const ListPanel*, const TagStringList& parameter, const bool&, Expander& exp ) const {
+// kdDebug() << "Expander::exp_Clipboard, parameter[0]: '" << parameter[0] << "', Clipboard: " << TDEApplication::clipboard()->text() << endl;
+ TQStringList lst=splitEach(parameter[0]);
+ if(!parameter[1].isSimple()) {
+ setError(exp,Error(Error::S_FATAL,Error::C_SYNTAX,i18n("Expander: %Each% may not be in the second argument of %Clipboard%")));
+ return TQString();
+ }
+ if ( parameter.count() <= 1 || parameter[1].string().isEmpty() || TDEApplication::clipboard()->text().isEmpty() )
+ TDEApplication::clipboard()->setText( lst.join("\n") );
+ else
+ TDEApplication::clipboard()->setText( TDEApplication::clipboard()->text() + parameter[1].string() + lst.join("\n") );
+
+ return TQString(); // this doesn't return anything, that's normal!
+}
+
+exp_Copy::exp_Copy() {
+ _expression = "Copy";
+ _description = i18n("Copy a File/Folder...");
+ _needPanel = false;
+
+ addParameter( exp_parameter( i18n("What to copy:"), "__placeholder", true ) );
+ addParameter( exp_parameter( i18n("Where to copy:"), "__placeholder", true ) );
+}
+TagString exp_Copy::expFunc( const ListPanel*, const TagStringList& parameter, const bool&, Expander& exp ) const {
+
+ // basically the parameter can already be used as URL, but since KURL has problems with ftp-proxy-urls (like ftp://username@proxyusername@url...) this is neccesary:
+ TQStringList lst=splitEach( parameter[0] );
+ if(!parameter[1].isSimple()) {
+ setError(exp,Error(Error::S_FATAL,Error::C_SYNTAX,i18n("Expander: %Each% may not be in the second argument of %Copy%")));
+ return TQString();
+ }
+ KURL::List src;
+ for(TQStringList::const_iterator it=lst.begin(),end=lst.end();it!=end;++it)
+ src.push_back(vfs::fromPathOrURL( *it ));
+ // or transform(...) ?
+ KURL dest = vfs::fromPathOrURL( parameter[1].string() );
+
+ if (!dest.isValid())
+ {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: invalid URL's in %_Copy(\"src\", \"dest\")%") ));
+ return TQString();
+ }
+ for (const KURL &url : src)
+ {
+ if (!url.isValid())
+ {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: invalid URL's in %_Copy(\"src\", \"dest\")%") ));
+ return TQString();
+ }
+ }
+
+ PreservingCopyJob::createCopyJob( PM_DEFAULT, src, dest, TDEIO::CopyJob::Copy, false, true );
+
+ return TQString(); // this doesn't return everything, that's normal!
+}
+
+exp_Move::exp_Move() {
+ _expression = "Move";
+ _description = i18n("Move/Rename a File/Folder...");
+ _needPanel = false;
+
+ addParameter( exp_parameter( i18n("What to move/rename:"), "__placeholder", true ) );
+ addParameter( exp_parameter( i18n("New target/name:"), "__placeholder", true ) );
+}
+TagString exp_Move::expFunc( const ListPanel*, const TagStringList& parameter, const bool& , Expander& exp ) const {
+ // basically the parameter can already be used as URL, but since KURL has problems with ftp-proxy-urls (like ftp://username@proxyusername@url...) this is neccesary:
+ TQStringList lst=splitEach( parameter[0] );
+ if(!parameter[1].isSimple()) {
+ setError(exp,Error(Error::S_FATAL,Error::C_SYNTAX,i18n("%Each% may not be in the second argument of %Move%")));
+ return TQString();
+ }
+ KURL::List src;
+ for(TQStringList::const_iterator it=lst.begin(),end=lst.end();it!=end;++it)
+ src.push_back(vfs::fromPathOrURL( *it ));
+ // or transform(...) ?
+ KURL dest = vfs::fromPathOrURL( parameter[1].string() );
+
+ if (!dest.isValid())
+ {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: invalid URL's in %_Move(\"src\", \"dest\")%") ));
+ return TQString();
+ }
+ for (const KURL &url : src)
+ {
+ if (!url.isValid())
+ {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: invalid URL's in %_Move(\"src\", \"dest\")%") ));
+ return TQString();
+ }
+ }
+
+ PreservingCopyJob::createCopyJob( PM_DEFAULT, src, dest, TDEIO::CopyJob::Move, false, true );
+
+ return TQString(); // this doesn't return anything, that's normal!
+}
+
+exp_Sync::exp_Sync() {
+ _expression = "Sync";
+ _description = i18n("Load a Synchronizer Profile...");
+ _needPanel = false;
+
+ addParameter( exp_parameter( i18n("Choose a profile:"), "__syncprofile", true ) );
+}
+TagString exp_Sync::expFunc( const ListPanel*, const TQStringList& parameter, const bool&, Expander& exp ) const {
+ if ( parameter[0].isEmpty() ) {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: no profile specified for %_Sync(profile)%") ));
+ return TQString();
+ }
+
+ new SynchronizerGUI( 0, parameter[0] );
+
+ return TQString(); // this doesn't return everything, that's normal!
+}
+
+exp_NewSearch::exp_NewSearch() {
+ _expression = "NewSearch";
+ _description = i18n("Load a Searchmodule Profile...");
+ _needPanel = false;
+
+ addParameter( exp_parameter( i18n("Choose a profile:"), "__searchprofile", true ) );
+}
+TagString exp_NewSearch::expFunc( const ListPanel*, const TQStringList& parameter, const bool&, Expander& exp ) const {
+ if ( parameter[0].isEmpty() ) {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: no profile specified for %_NewSearch(profile)%") ));
+ return TQString();
+ }
+
+ new KrSearchDialog( parameter[0], krApp );
+
+ return TQString(); // this doesn't return everything, that's normal!
+}
+
+exp_Profile::exp_Profile() {
+ _expression = "Profile";
+ _description = i18n("Load a Panel Profile...");
+ _needPanel = false;
+
+ addParameter( exp_parameter( i18n("Choose a profile:"), "__panelprofile", true ) );
+}
+TagString exp_Profile::expFunc( const ListPanel*, const TQStringList& parameter, const bool&, Expander& exp ) const {
+ if ( parameter[0].isEmpty() ) {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: no profile specified for %_Profile(profile)%; abort...") ));
+ return TQString();
+ }
+
+ MAIN_VIEW->profiles( parameter[0] );
+
+ return TQString(); // this doesn't return everything, that's normal!
+}
+
+exp_Each::exp_Each() {
+ _expression = "Each";
+ _description = i18n("Separate Program Call for Each...");
+ _needPanel = true;
+
+ addParameter( exp_parameter( i18n("Which items:"), "__choose:All;Files;Dirs;Selected", false ) );
+ addParameter( exp_parameter( i18n("Omit the current path (optional)"), "__no", false ) );
+ addParameter( exp_parameter( i18n("Mask (optional, all but 'Selected'):"), "__select", false ) );
+ addParameter( exp_parameter( i18n("Automatically escape spaces"), "__yes", false ) );
+}
+TagString exp_Each::expFunc( const ListPanel* panel, const TQStringList& parameter, const bool& useUrl, Expander& exp ) const {
+ NEED_PANEL
+
+ TQString mask;
+ if ( parameter.count() <= 2 || parameter[2].isEmpty() )
+ mask = "*";
+ else
+ mask = parameter[2];
+
+ TagString ret;
+ TQStringList l = fileList(panel,
+ parameter.empty() ? TQString() : parameter[0].lower(),
+ mask, parameter.count() > 1 && parameter[1].lower()=="yes",
+ useUrl, exp, "Each");
+
+ if(!(parameter.count()<=3 || parameter[3].lower()!="yes"))
+ transform(l.begin(),l.end(),l.begin(),bashquote);
+
+ ret.insertTag(0,l);
+ return ret;
+}
+
+exp_ColSort::exp_ColSort() {
+ _expression = "ColSort";
+ _description = i18n("Set Sorting for This Panel...");
+ _needPanel = true;
+
+ addParameter( exp_parameter( i18n("Choose a column:"), "__choose:Name;Ext;Type;Size;Modified;Perms;rwx;Owner;Group", true ) );
+ addParameter( exp_parameter( i18n("Choose a sort sequence:"), "__choose:Toggle;Asc;Desc", false ) );
+}
+TagString exp_ColSort::expFunc( const ListPanel* panel, const TQStringList& parameter, const bool&, Expander& exp ) const {
+ NEED_PANEL
+
+ if ( parameter[0].isEmpty() ) {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: no column specified for %_ColSort(column)%") ));
+ return TQString();
+ }
+
+ int mode = (int) panel->view->sortMode();
+
+ /* from Panel/krview.h:
+ enum SortSpec { Name=0x1,
+ Ext=0x2,
+ Size=0x4,
+ Type=0x8,
+ Modified=0x10,
+ Permissions=0x20,
+ KrPermissions=0x40,
+ Owner=0x80,
+ Group=0x100,
+ Descending=0x200,
+ DirsFirst=0x400,
+ IgnoreCase=0x800 };
+ */
+
+// krOut << "start: exp_ColSort::expFunc" << endl;
+ #define MODE_OUT krOut << TQString( "mode: %1" ).arg( mode, 0, 2 ) << endl; // displays mode in base-2
+ //MODE_OUT
+
+ if ( parameter.count() <= 1 || ( parameter[1].lower() != "asc" && parameter[1].lower() != "desc" ) ) { //default == toggle
+ if ( mode & KrViewProperties::Descending )
+ mode &= ~KrViewProperties::Descending; // == asc
+ else
+ mode |= KrViewProperties::Descending; // == desc
+ } else
+ if ( parameter[1].lower() == "asc" ) {
+ mode &= ~KrViewProperties::Descending;
+ }
+ else { // == desc
+ mode |= KrViewProperties::Descending;
+ }
+
+ //MODE_OUT
+
+ // clear all column-infromation:
+ mode &= ~( KrViewProperties::Name | KrViewProperties::Ext | KrViewProperties::Size | KrViewProperties::Type | KrViewProperties::Modified | KrViewProperties::Permissions | KrViewProperties::KrPermissions | KrViewProperties::Owner | KrViewProperties::Group );
+
+ MODE_OUT
+
+ if ( parameter[0].lower() == "name" ) {
+ mode |= KrViewProperties::Name;
+ } else
+ if ( parameter[0].lower() == "ext" ) {
+ mode |= KrViewProperties::Ext;
+ } else
+ if ( parameter[0].lower() == "type" ) {
+ mode |= KrViewProperties::Type;
+ } else
+ if ( parameter[0].lower() == "size" ) {
+ mode |= KrViewProperties::Size;
+ } else
+ if ( parameter[0].lower() == "modified" ) {
+ mode |= KrViewProperties::Modified;
+ } else
+ if ( parameter[0].lower() == "perms" ) {
+ mode |= KrViewProperties::Permissions;
+ } else
+ if ( parameter[0].lower() == "rwx" ) {
+ mode |= KrViewProperties::KrPermissions;
+ } else
+ if ( parameter[0].lower() == "owner" ) {
+ mode |= KrViewProperties::Owner;
+ } else
+ if ( parameter[0].lower() == "group" ) {
+ mode |= KrViewProperties::Group;
+ } else {
+ setError(exp, Error(Error::S_WARNING,Error::C_ARGUMENT,i18n("Expander: unknown column specified for %_ColSort(%1)%").arg(parameter[0]) ));
+ return TQString();
+ }
+
+ //MODE_OUT
+ panel->view->setSortMode( (KrViewProperties::SortSpec)mode );
+// krOut << "end: exp_ColSort::expFunc" << endl;
+ return TQString(); // this doesn't return anything, that's normal!
+}
+
+exp_PanelSize::exp_PanelSize() {
+ _expression = "PanelSize";
+ _description = i18n("Set Relation Between the Panels...");
+ _needPanel = true;
+
+ addParameter( exp_parameter( i18n("Set the new size in percent:"), "__int:0;100;5;50", true ) );
+}
+TagString exp_PanelSize::expFunc( const ListPanel* panel, const TQStringList& parameter, const bool&, Expander& exp ) const {
+ NEED_PANEL
+ int newSize;
+
+ if ( parameter[0].isEmpty() )
+ newSize = 50; //default is 50%
+ else
+ newSize = parameter[0].toInt();
+
+ if ( newSize < 0 || newSize > 100 ) {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: Value %1 out of range for %_PanelSize(percent)%. The first parameter has to be >0 and <100").arg(newSize)) );
+ return TQString();
+ }
+
+ TQValueList<int> panelSizes = MAIN_VIEW->horiz_splitter->sizes();
+ int totalSize = panelSizes[0] + panelSizes[1];
+
+ if ( panel == LEFT_PANEL ) {
+ panelSizes[0] = totalSize * newSize / 100;
+ panelSizes[1] = totalSize * (100 - newSize) / 100;
+ }
+ else { // == RIGHT_PANEL
+ panelSizes[0] = totalSize * (100 - newSize) / 100;
+ panelSizes[1] = totalSize * newSize / 100;
+ }
+
+ MAIN_VIEW->horiz_splitter->setSizes( panelSizes );
+
+ return TQString(); // this doesn't return everything, that's normal!
+}
+
+#ifdef __KJSEMBED__
+exp_Script::exp_Script() {
+ _expression = "Script";
+ _description = i18n("Execute a JavaScript Extension...");
+ _needPanel = false;
+
+ addParameter( exp_parameter( i18n("Location of the script"), "", true ) );
+ addParameter( exp_parameter( i18n("Set some variables for the execution (optional).\ni.e. \"return=return_var;foo=bar\", consult the handbook for more information"), "", false ) );
+}
+TagString exp_Script::expFunc( const ListPanel*, const TQStringList& parameter, const bool&, Expander& exp ) const {
+ if ( parameter[0].isEmpty() ) {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: no script specified for %_Script(script)%")) );
+ return TQString();
+ }
+
+ TQString filename = parameter[0];
+ if ( filename.find('/') && KURL::isRelativeURL(filename) ) {
+ // this return the local version of the file if this exists. else the global one is returnd
+ filename = locate( "data", "krusader/js/"+filename );
+ }
+
+ if ( ! krJS )
+ krJS = new KrJS();
+
+ KJS::ExecState *exec = krJS->globalExec();
+
+ TQString jsReturn = TQString();
+ if ( parameter[1].lower() == "yes" ) // to stay compatible with the old-style parameter
+ jsReturn = "cmd";
+ else {
+ TQStringList jsVariables = TQStringList::split( ';', parameter[1] );
+ TQString jsVariable, jsValue;
+ for ( TQStringList::Iterator it = jsVariables.begin(); it != jsVariables.end(); ++it ) {
+ jsVariable = (*it).section('=', 0, 0).stripWhiteSpace();
+ jsValue = (*it).section('=', 1);
+ if ( jsVariable == "return" )
+ jsReturn = jsValue.stripWhiteSpace();
+ else
+ krJS->putValue( jsVariable, KJSEmbed::convertToValue(exec, jsValue ) );
+ }
+ }
+
+ krJS->runFile( filename );
+
+ if ( ! jsReturn.isEmpty() )
+ return krJS->getValue( jsReturn ).toString( krJS->globalExec() ).qstring();
+ else
+ return TQString();
+}
+#endif
+
+exp_View::exp_View() {
+ _expression = "View";
+ _description = i18n("View File with Krusader's Internal Viewer...");
+ _needPanel = false;
+
+ addParameter( exp_parameter( i18n("Which file to view (normally '%aCurrent%'):"), "__placeholder", true ) );
+ addParameter( exp_parameter( i18n("Choose a view mode:"), "__choose:generic;text;hex", false ) );
+ //addParameter( exp_parameter( i18n("Choose a window-mode"), "__choose:tab;window;panel", false ) );
+ //TODO: window-mode 'panel' should open the file in the third-hand viewer
+ addParameter( exp_parameter( i18n("Choose a window mode:"), "__choose:tab;window", false ) );
+}
+TagString exp_View::expFunc( const ListPanel*, const TQStringList& parameter, const bool&, Expander& exp ) const {
+ if ( parameter[0].isEmpty() ) {
+ setError(exp, Error(Error::S_FATAL,Error::C_ARGUMENT,i18n("Expander: no file to view in %_View(filename)%")) );
+ return TQString();
+ }
+
+ TQString viewMode, windowMode;
+ if ( parameter.count() <= 1 || parameter[1].isEmpty() )
+ viewMode = "generic";
+ else
+ viewMode = parameter[1];
+
+ if ( parameter.count() <= 2 || parameter[2].isEmpty() )
+ windowMode = "tab";
+ else
+ windowMode = parameter[2];
+
+ KrViewer::Mode mode = KrViewer::Generic;
+ if( viewMode == "text" ) mode = KrViewer::Text;
+ else if( viewMode == "hex" ) mode = KrViewer::Hex;
+
+ KrViewer::view(parameter[0],mode,(windowMode == "window"));
+ //TODO: Call the viewer with viewMode and windowMode. Filename is in parameter[0].
+ // It would be nice if parameter[0] could also be a space-separated filename-list (provided if the first parameter is %aList(selected)%)
+
+ return TQString(); // this doesn't return everything, that's normal!
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////// end of expander classes ////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+TagString exp_simpleplaceholder::expFunc( const ListPanel* p, const TagStringList& parameter, const bool& useUrl, Expander& exp) const
+{
+ TQStringList lst;
+ for(TagStringList::const_iterator it=parameter.begin(),end=parameter.end();it!=end;++it)
+ if((*it).isSimple())
+ lst.push_back((*it).string());
+ else {
+ setError(exp,Error(Error::S_FATAL,Error::C_SYNTAX,i18n("%Each% is not allowed in parameter to %1").arg(description())));
+ return TQString();
+ }
+ return expFunc(p,lst,useUrl,exp);
+}
+
+}
+
+ListPanel* Expander::getPanel( const char panelIndicator, const exp_placeholder* pl, Expander& exp ) {
+ switch ( panelIndicator ) {
+ case 'a':
+ return ACTIVE_PANEL;
+ case 'o':
+ return OTHER_PANEL;
+ case 'l':
+ return LEFT_PANEL;
+ case 'r':
+ return RIGHT_PANEL;
+ case '_':
+ return 0;
+ default:
+ exp.setError(Error(Error::S_FATAL,Error::C_SYNTAX,i18n("Expander: Bad panel specifier %1 in placeholder %2").arg(panelIndicator).arg(pl->description())));
+ return 0;
+ }
+}
+
+void Expander::expand( const TQString& stringToExpand, bool useUrl ) {
+ TagString result = expandCurrent( stringToExpand, useUrl );
+ if ( error() )
+ return;
+
+ if ( !result.isSimple() )
+ resultList = splitEach( result );
+ else
+ resultList.append( result.string() );
+
+// krOut << resultList[0] << endl;
+}
+
+TagString Expander::expandCurrent( const TQString& stringToExpand, bool useUrl ) {
+ TagString result;
+ TQString exp = TQString();
+ TagString tmpResult;
+ int begin, end, i;
+// int brackets = 0;
+// bool inQuotes = false;
+ unsigned int idx = 0;
+ while ( idx < stringToExpand.length() ) {
+ if ( ( begin = stringToExpand.find( '%', idx ) ) == -1 ) break;
+ if ( ( end = findEnd( stringToExpand, begin ) ) == -1 ) {
+ setError(Error(Error::S_FATAL,Error::C_SYNTAX,i18n("Error: unterminated % in Expander::expandCurrent")) );
+ return TQString();
+ }
+
+ result += stringToExpand.mid( idx, begin - idx ); // copy until the start of %exp%
+
+ // get the expression, and expand it using the correct expander function
+ exp = stringToExpand.mid( begin + 1, end - begin - 1 );
+// kdDebug() << "------------- exp: '" << exp << "'" << endl;
+ if ( exp == "" )
+ result += TQString(TQChar('%'));
+ else {
+ TagStringList parameter = separateParameter( &exp, useUrl );
+ if ( error() )
+ return TQString();
+ char panelIndicator = exp.lower()[0].latin1();
+ exp.replace( 0, 1, "" );
+ for ( i = 0; i < placeholderCount(); ++i )
+ if ( exp == placeholder( i )->expression() ) {
+// kdDebug() << "---------------------------------------" << endl;
+ tmpResult = placeholder( i )->expFunc( getPanel( panelIndicator,placeholder(i),*this ), parameter, useUrl, *this );
+ if ( error() ) {
+ return TQString();
+ }
+ else
+ result += tmpResult;
+// kdDebug() << "---------------------------------------" << endl;
+ break;
+ }
+ if ( i == placeholderCount() ) { // didn't find an expander
+ setError(Error(Error::S_FATAL,Error::C_SYNTAX,i18n("Error: unrecognized %%%1%2%% in Expander::expand").arg(panelIndicator).arg(exp)) );
+ return TQString();
+ }
+ } //else
+ idx = end + 1;
+ }
+ // copy the rest of the string
+ result += stringToExpand.mid( idx );
+// kdDebug() << "============== result '" << result << "'" << endl;
+ return result;
+}
+
+TQStringList Expander::splitEach( TagString stringToSplit ) {
+ if(stringToSplit.isSimple()) {
+// krOut << stringToSplit.string() << endl;
+ return stringToSplit.string();
+ }
+ pair<uint,TQStringList> pl=*stringToSplit.tagsBegin();
+ stringToSplit.eraseTag(stringToSplit.tagsBegin());
+ TQStringList ret;
+ for(TQStringList::const_iterator it=pl.second.begin(),end=pl.second.end();it!=end;++it) {
+ TagString s=stringToSplit;
+ s.insert(pl.first,*it);
+ ret+=splitEach(s);
+ }
+ return ret;
+// kdDebug() << "stringToSplit: " << stringToSplit << endl;
+}
+
+TagStringList Expander::separateParameter( TQString* const exp, bool useUrl ) {
+ TagStringList parameter;
+ TQStringList parameter1;
+ TQString result;
+ int begin, end;
+ if ( ( begin = exp->find( '(' ) ) != -1 ) {
+ if ( ( end = exp->findRev( ')' ) ) == -1 ) {
+ setError(Error(Error::S_FATAL,Error::C_SYNTAX,i18n("Error: missing ')' in Expander::separateParameter") ));
+ return TagStringList();
+ }
+ result = exp->mid( begin + 1, end - begin - 1 );
+ *exp = exp->left( begin );
+
+ bool inQuotes = false;
+ unsigned int idx = 0;
+ begin = 0;
+ while ( idx < result.length() ) {
+ if ( result[ idx ].latin1() == '\\' ) {
+ if ( result[ idx+1 ].latin1() == '"')
+ result.replace( idx, 1, "" );
+ }
+ if ( result[ idx ].latin1() == '"' )
+ inQuotes = !inQuotes;
+ if ( result[ idx ].latin1() == ',' && !inQuotes ) {
+ parameter1.append( result.mid( begin, idx - begin) );
+ begin = idx + 1;
+// krOut << " ---- parameter: " << parameter.join(";") << endl;
+ }
+ idx++;
+ }
+ parameter1.append( result.mid( begin, idx - begin) ); //don't forget the last one
+
+ for (TQStringList::Iterator it = parameter1.begin(); it != parameter1.end(); ++it) {
+ *it = (*it).stripWhiteSpace();
+ if ( (*it).left(1) == "\"" )
+ *it = (*it).mid(1, (*it).length() - 2 );
+ parameter.push_back(expandCurrent( *it, useUrl ));
+ if ( error() )
+ return TagStringList();
+ }
+ }
+
+// krOut << "------- exp: " << *exp << " ---- parameter: " << parameter.join(";") << endl;
+ return parameter;
+}
+
+int Expander::findEnd( const TQString& str, int start ) {
+ int end = str.find( '%', start + 1 );
+ if ( end == -1 )
+ return end;
+ int bracket = str.find( '(', start + 1 );
+ if ( end < bracket || bracket == -1 )
+ return end;
+
+ unsigned int idx = bracket+1;
+ bool inQuotes = false;
+ int depth=1;
+ while ( idx < str.length() ) {
+ switch (str[ idx ].latin1()) {
+ case '\\':
+ idx ++;
+ break;
+ case '"':
+ inQuotes = !inQuotes;
+ break;
+ case '(':
+ if(!inQuotes)
+ depth++;
+ break;
+ case ')':
+ if(!inQuotes)
+ --depth;
+ break;
+ case '%':
+ if(depth==0)
+ return idx;
+ } //switch
+ idx++;
+ } //while
+ // failsafe
+ return -1;
+}
diff --git a/src/app/UserAction/expander.h b/src/app/UserAction/expander.h
new file mode 100644
index 0000000..3ae7f21
--- /dev/null
+++ b/src/app/UserAction/expander.h
@@ -0,0 +1,245 @@
+//
+// C++ Interface: expander
+//
+// Description:
+//
+//
+// Author: Jonas B�r (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef EXPANDER_H
+#define EXPANDER_H
+
+// class TQString;
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqvaluelist.h>
+#include "tstring.h"
+// #include <tqstringlist.h>
+class ListPanel;
+class Expander;
+class Error;
+
+typedef TagString_t<TQStringList> TagString;
+typedef TQValueList<TagString> TagStringList;
+
+/**
+ * This holds informations about each parameter
+ */
+class exp_parameter {
+public:
+ exp_parameter() {}
+ inline exp_parameter( TQString desc, TQString pre, bool ness)
+ { _description = desc; _preset = pre; _nessesary = ness; }
+ inline TQString description() const ///< A description of the parameter
+ { return _description; }
+ inline TQString preset() const ///< the default of the parameter
+ { return _preset; }
+ inline bool nessesary() const ///< false if the parameter is optional
+ { return _nessesary; }
+
+private:
+ TQString _description;
+ TQString _preset;
+ bool _nessesary;
+};
+
+#define EXP_FUNC virtual TagString expFunc ( const ListPanel*, const TagStringList&, const bool&, Expander& ) const
+#define SIMPLE_EXP_FUNC virtual TagString expFunc ( const ListPanel*, const TQStringList&, const bool&, Expander& ) const
+/**
+ * Abstract baseclass for all expander-functions (which replace placeholder).
+ * A Placeholder is an entry containing the expression, its expanding function and Parameter.
+ *
+ * Not to be created on the heap
+ *
+ * @author Jonas B�r (http://www.jonas-baehr.de)
+ */
+class exp_placeholder {
+public:
+ inline TQString expression() const ///< The placeholder (without '%' or panel-prefix)
+ { return _expression; }
+ inline TQString description() const ///< A description of the placeholder
+ { return _description; }
+ inline bool needPanel() const ///< true if the placeholder needs a panel to operate on
+ { return _needPanel; }
+ inline void addParameter( exp_parameter parameter ) ///< adds parameter to the placeholder
+ { _parameter.append(parameter); }
+ inline int parameterCount() const ///< returns the number of placeholders
+ { return _parameter.count(); }
+ inline const exp_parameter& parameter( int id ) const ///< returns a specific parameter
+ { return _parameter[ id ]; }
+
+ EXP_FUNC = 0;
+protected:
+ static void setError(Expander& exp,const Error& e) ;
+ static void panelMissingError(const TQString &s, Expander& exp);
+ static TQStringList splitEach(const TagString& s);
+ static TQStringList fileList(const ListPanel* const panel,const TQString& type,const TQString& mask,const bool ommitPath,const bool useUrl,Expander&,const TQString&);
+ exp_placeholder();
+ exp_placeholder(const exp_placeholder& p);
+ ~exp_placeholder() { }
+ TQString _expression;
+ TQString _description;
+ TQValueList <exp_parameter> _parameter;
+ bool _needPanel;
+};
+
+
+ class Error {
+ public:
+ enum Cause {
+ C_USER, C_SYNTAX, C_WORLD, C_ARGUMENT
+ };
+ enum Severity {
+ S_OK, S_WARNING, S_ERROR, S_FATAL
+ };
+ Error() : s_(S_OK) {}
+ Error(Severity s,Cause c,TQString d) : s_(s), c_(c), desc_(d) {}
+ Cause cause() const { return c_; }
+ operator bool() const { return s_!=S_OK; }
+ const TQString& what() const { return desc_; }
+ private:
+ Severity s_;
+ Cause c_;
+ TQString desc_;
+ };
+
+
+/**
+ * The Expander expands the command of an UserAction by replacing all placeholders by thier current values.@n
+ * Each placeholder begins with a '%'-sign, followed by one char indicating the panel, followed by a command which may have some paramenter enclosed in brackets and also ends with a '%'-sign.
+ * Examples are %aPath% or %rBookmark("/home/jonas/src/krusader_trinity", "yes")%.@n
+ * The panel-indicator has to be either 'a' for the active, 'o' for the other, 'r' for the right, 'l' for the left or '_' for panel-independence.
+ *
+ * Currently sopported are these commands can be ordered in three groups (childs are the parameter in the right order):
+ * - Placeholders for Krusaders panel-data (panel-indicator has to be 'a', 'o', 'r' or 'l')
+ * - @em Path is replaced by the panel's path
+ * - @em Count is replaced by a nomber of
+ * -# Either "All", "Files", "Dirs", "Selected"
+ * .
+ * - @em Filter is preplaced by the panels filter-mask (ex: "*.cpp *.h")
+ * - @em Current is replaced by the current item or, in case of onmultiple="call_each", by each selected item.
+ * -# If "yes", only the filename (without path) is returned
+ * .
+ * - @em List isreplaced by a list of
+ * -# Either "All", "Files", "Dirs", "Selected"
+ * -# A seperator between the items (default: " " [one space])
+ * -# If "yes", only the filename (without path) is returned
+ * -# (for all but "Selected") a filter-mask (default: "*")
+ * .
+ * .
+ * - Access to panel-dependent, krusader-internal, parameter-needed functions (panel-indicator has to be 'a', 'o', 'r' or 'l')
+ * - @em Select manipulates the selection of the panel
+ * -# A filter-mask (nessesary)
+ * -# Either "Add", "Remove", "Set" (default)
+ * .
+ * - @em Bookmark manipulates the selection of the panel
+ * -# A path or URL (nessesary)
+ * -# If "yes", the location is opend in a new tab
+ * .
+ * .
+ * - Access to panel-independent, krusader-internal, parameter-needed functions (panel-indicator doesn't matter but should be set to '_')
+ * - @em Ask displays a lineedit and is replaced by its return
+ * -# The question (nessesary)
+ * -# A default answer
+ * -# A cation for the popup
+ * .
+ * - @em Clipboard manipulates the system-wide clipboard
+ * -# The string copied to clip (ex: "%aCurrent%") (nessesary)
+ * -# A separator. If set, parameter1 is append with this to the current clipboard content
+ * .
+ * .
+ * .
+ * Since all placeholders are expanded in the order they appear in the command, little one-line-scripts are possible
+ *
+ * @author Jonas B�r (http://www.jonas-baehr.de), Shie Erlich
+ */
+class Expander {
+public:
+
+ inline static int placeholderCount() ///< returns the number of placeholders
+ { return _placeholder().count(); }
+ inline static const exp_placeholder* placeholder( int id )
+ { return _placeholder()[ id ]; }
+
+ /**
+ * This expands a whole commandline
+ *
+ * @param stringToExpand the commandline with the placeholder
+ * @param useUrl true iff the path's should be expanded to an URL instead of an local path
+ * @return a list of all commands
+ */
+ void expand( const TQString& stringToExpand, bool useUrl );
+
+ /**
+ * Returns the list of all commands to be executed, provided that #expand was called
+ * before, and there was no error (see #error). Otherwise, calls #abort
+ *
+ * @return The list of commands to be executed
+ */
+ const TQStringList& result() const { assert(!error()); return resultList; }
+
+ /**
+ * Returns the error object of this Expander. You can test whether there was
+ * any error by
+ * \code
+ * if(exp.error())
+ * error behaviour...
+ * else
+ * no error...
+ * \endcode
+ *
+ * @return The error object
+ */
+ const Error& error() const { return _err; }
+protected:
+ /**
+ * This expands a whole commandline by calling for each Placeholder the corresponding expander
+ *
+ * @param stringToExpand the commandline with the placeholder
+ * @param useUrl true if the path's should be expanded to an URL instead of an local path
+ * @return the expanded commanline for the current item
+ */
+ TagString expandCurrent( const TQString& stringToExpand, bool useUrl );
+ /**
+ * This function searches for "@EACH"-marks to splitt the string in a list for each %_Each%-item
+ *
+ * @param stringToSplit the string which should be splitted
+ * @return the splitted list
+ */
+ static TQStringList splitEach( TagString stringToSplit );
+ /**
+ * @param panelIndicator either '_' for panel-independent placeholders, 'a', 'o', 'r', or 'l' for the active, other (inactive), right or left panel
+ * @return a pointer to the right panel or NULL if no panel is needed.
+ */
+ static ListPanel* getPanel( const char panelIndicator ,const exp_placeholder*,Expander&);
+ /**
+ * This splits the parameter-string into separate parameter and expands each
+ * @param exp the string holding all parameter
+ * @param useUrl true if the path's should be expanded to an URL instead of an local path
+ * @return a list of all parameter
+ */
+ TagStringList separateParameter( TQString* const exp, bool useUrl );
+ /**
+ * This finds the end of a placeholder, taking care of the parameter
+ * @return the position where the placeholder ends
+ */
+ int findEnd( const TQString& str, int start );
+
+ void setError(const Error &e) { _err=e; }
+ friend class exp_placeholder;
+
+private:
+ static TQValueList <const exp_placeholder*>& _placeholder();
+ Error _err;
+ TQStringList resultList;
+};
+
+inline void exp_placeholder::setError(Expander& exp,const Error& e) { exp.setError(e); }
+inline TQStringList exp_placeholder::splitEach(const TagString& s) { return Expander::splitEach(s); }
+inline exp_placeholder::exp_placeholder() { Expander::_placeholder().push_back(this); }
+
+#endif // ifndef EXPANDER_H
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"
diff --git a/src/app/UserAction/kraction.h b/src/app/UserAction/kraction.h
new file mode 100644
index 0000000..095dcc8
--- /dev/null
+++ b/src/app/UserAction/kraction.h
@@ -0,0 +1,170 @@
+//
+// C++ Interface: kraction
+//
+// Description:
+//
+//
+// Author: Krusader Krew <http://www.krusader.org>, (C) 2004, 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef KRACTION_H
+#define KRACTION_H
+
+#include <tdeaction.h>
+#include <kprocess.h>
+#include <kdialogbase.h>
+#include "kractionbase.h"
+
+class UserActionProperties;
+class TQTextEdit;
+class TDEActionCollection;
+class TQDomElement;
+class TQDomDocument;
+
+/**
+ * This subclass of TDEAction extends it with an individual executor and a struct UserActionProperties. It is used to integrate useractions into KDE's TDEAction-System
+ * @author Jonas Bähr (http://www.jonas-baehr.de)
+ */
+class KrAction: public TDEAction, public KrActionBase {
+ TQ_OBJECT
+
+ public:
+ KrAction( TDEActionCollection *parent, const char* name );
+ ~KrAction();
+
+ /**
+ * This chekcs if the KrAction is for a specific file / location available
+ * @param currentURL Check for this file
+ * @return true if the KrAction if available
+ */
+ bool isAvailable( const KURL& currentURL );
+
+ bool xmlRead( const TQDomElement& element );
+ TQDomElement xmlDump( TQDomDocument& doc ) const;
+
+ void setName( const char* ) { /* empty reimplementation to prevent a name-change */ };
+
+ TQString category() const { return _category; };
+ void setCategory( const TQString& category ) { _category = category; };
+
+ TQString command() const { return _command; };
+ void setCommand( const TQString& command ) { _command = command; };
+
+ TQString user() const { return _user; };
+ void setUser( const TQString& user ) { _user = user; };
+
+ TQString startpath() const { return _startpath; };
+ void setStartpath( const TQString& startpath ) { _startpath = startpath; };
+
+ ExecType execType() const { return _execType; };
+ void setExecType( ExecType execType ) { _execType = execType; };
+
+ bool acceptURLs() const { return _acceptURLs; };
+ void setAcceptURLs(const bool& acceptURLs) { _acceptURLs = acceptURLs; };
+
+ bool confirmExecution() const { return _confirmExecution; };
+ void setConfirmExecution(const bool& confirmExecution) { _confirmExecution = confirmExecution; };
+
+ TQStringList showonlyProtocol() const { return _showonlyProtocol; };
+ void setShowonlyProtocol( const TQStringList& showonlyProtocol ) { _showonlyProtocol = showonlyProtocol; };
+
+ TQStringList showonlyPath() const { return _showonlyPath; };
+ void setShowonlyPath( const TQStringList& showonlyPath ) { _showonlyPath = showonlyPath; };
+
+ TQStringList showonlyMime() const { return _showonlyMime; };
+ void setShowonlyMime( const TQStringList& showonlyMime ) { _showonlyMime = showonlyMime; };
+
+ TQStringList showonlyFile() const { return _showonlyFile; };
+ void setShowonlyFile( const TQStringList& showonlyFile ) { _showonlyFile = showonlyFile; };
+
+ bool doSubstitution() const {
+ return true;
+ }
+
+ TQString text() const {
+ return TDEAction::text();
+ }
+
+ public slots:
+ void exec() {
+ KrActionBase::exec();
+ }
+
+
+ private:
+ void readCommand( const TQDomElement& element );
+ TQDomElement dumpCommand( TQDomDocument& doc ) const;
+
+ void readAvailability( const TQDomElement& element );
+ TQDomElement dumpAvailability( TQDomDocument& doc ) const;
+
+ TQString _category;
+ TQString _command;
+ TQString _user;
+ TQString _startpath;
+ ExecType _execType;
+ bool _acceptURLs;
+ bool _confirmExecution;
+ TQStringList _showonlyProtocol;
+ TQStringList _showonlyPath;
+ TQStringList _showonlyMime;
+ TQStringList _showonlyFile;
+
+};
+
+class TQFont;
+/**
+ * This displays the output of a process
+ * @author Shie Erlich, Jonas Bähr
+ */
+class KrActionProcDlg: public KDialogBase {
+ TQ_OBJECT
+
+ public:
+ KrActionProcDlg( TQString caption, bool enableStderr = false, TQWidget *parent = 0 );
+
+ protected slots:
+ void addStderr( TDEProcess *proc, char *buffer, int buflen );
+ void addStdout( TDEProcess *proc, char *buffer, int buflen );
+ void toggleFixedFont( bool state );
+ void slotUser1(); ///< This is used to save the buffer to disc
+
+ private:
+ TQTextEdit *_stdout, *_stderr, *_currentTextEdit;
+ TQFont normalFont, fixedFont;
+ private slots:
+ void currentTextEditChanged();
+};
+
+/**
+ * This executes a command of a UserAction
+ * @author Shie Erlich, Jonas Bähr
+ * @todo jonas: call a list of commands separately (I began it but it doesn't work)
+ */
+class KrActionProc: public TQObject {
+ TQ_OBJECT
+
+ public:
+
+ KrActionProc( KrActionBase* action );
+ virtual ~KrActionProc();
+ void start( TQString cmdLine );
+ void start( TQStringList cmdLineList );
+
+ protected slots:
+ void kill() { _proc->kill( SIGINT ); }
+ void processExited( TDEProcess *proc );
+
+ private:
+ KrActionBase* _action;
+ TDEProcess *_proc;
+ TQString _stdout;
+ TQString _stderr;
+ KrActionProcDlg *_output;
+};
+
+
+#endif //KRACTION_H
diff --git a/src/app/UserAction/kractionbase.cpp b/src/app/UserAction/kractionbase.cpp
new file mode 100644
index 0000000..6eb520d
--- /dev/null
+++ b/src/app/UserAction/kractionbase.cpp
@@ -0,0 +1,77 @@
+//
+// C++ Implementation: kractionbase
+//
+// Description:
+//
+//
+// Author: Shie Erlich and Rafi Yanai <>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include <kinputdialog.h>
+#include <tdelocale.h>
+
+#include <tqerrormessage.h>
+
+#include "kraction.h"
+#include "expander.h"
+#include "kractionbase.h"
+
+KrActionBase::~KrActionBase()
+{
+}
+
+void KrActionBase::exec() {
+ KrActionProc *proc;
+
+ // replace %% and prepare string
+ TQStringList commandList;
+ if(doSubstitution()) {
+ Expander exp;
+ exp.expand(command(),acceptURLs());
+ if(exp.error()) {
+ handleError(exp.error());
+ return;
+ }
+ commandList=exp.result();
+ } else
+ commandList=command();
+ //TODO: query expander for status and may skip the rest of the function
+
+ // stop here if the commandline is empty
+ if ( commandList.count() == 1 && commandList[0].stripWhiteSpace().isEmpty() )
+ return;
+
+ if ( confirmExecution() ) {
+ for ( TQStringList::iterator it = commandList.begin(); it != commandList.end(); ++it ) {
+ bool exec = true;
+ *it = KInputDialog::getText(
+ i18n( "Confirm execution" ),
+ i18n( "Command being executed:" ),
+ *it,
+ &exec, 0
+ );
+ if ( exec ) {
+ proc = actionProcFactoryMethod();
+ proc->start( *it );
+ }
+ } //for
+ } // if ( _properties->confirmExecution() )
+ else {
+ proc = actionProcFactoryMethod();
+ proc->start( commandList );
+ }
+
+}
+
+void KrActionBase::handleError(const Error& err)
+{
+ TQErrorMessage::qtHandler()->message(err.what());
+}
+
+KrActionProc* KrActionBase::actionProcFactoryMethod()
+{
+ return new KrActionProc(this);
+}
diff --git a/src/app/UserAction/kractionbase.h b/src/app/UserAction/kractionbase.h
new file mode 100644
index 0000000..6691a06
--- /dev/null
+++ b/src/app/UserAction/kractionbase.h
@@ -0,0 +1,97 @@
+//
+// C++ Interface: kractionbase
+//
+// Description:
+//
+//
+// Author: Shie Erlich and Rafi Yanai <>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef KRACTIONBASE_H
+#define KRACTIONBASE_H
+
+#include <tqstring.h>
+#include "expander.h"
+class KrActionProc;
+
+class KrActionBase
+{
+public:
+ KrActionBase() {}
+ virtual ~KrActionBase();
+
+ /** \brief Specifies the mode for executing the action */
+ enum ExecType {
+ Normal, ///< Run the command freely
+ Terminal, ///< Run the command in new terminal window
+ CollectOutput, ///< Collect output from this command
+ CollectOutputSeparateStderr, ///< Like #CollectOutput, but display stderr output separately
+ RunInTE ///< Run in built-in terminal emulator
+ };
+
+ /** \brief Command which runs this action
+ *
+ * The string of the command may contain placeholders
+ * which are parsed by the #Expander class, unless #doSubstitution
+ * returns false
+ *
+ * The command is run by the shell, which should be bash (see #Expander)
+ *
+ * @see Expander
+ * @see doSubstitution
+ *
+ * @return The command to execute
+ */
+ virtual TQString command() const =0;
+ /** \brief Execution type of the action
+ *
+ * @see #ExecType
+ */
+ virtual ExecType execType() const =0;
+ /** \brief Working directory of the command
+ *
+ * @return The working directory of the command. May be \a null,
+ * in which case the command is executed in current directory
+ */
+ virtual TQString startpath() const =0;
+ /** \brief Username under which the command is run
+ *
+ * @return The username of the command. May be \a null,
+ * in which case the command is executed under the current user
+ */
+ virtual TQString user() const=0;
+ /** \brief Name of the action
+ *
+ * @return The name of the action which will be shown to the user
+ * eg. any string will do
+ */
+ virtual TQString text() const=0;
+ /** \brief Does the command accept URLs as filenames (like KDE apps)?
+ *
+ * @return \a true iff it does
+ */
+ virtual bool acceptURLs() const=0;
+ /** \brief Confirm execution of this action by the user?
+ *
+ * @return \a true iff execution should be confirmed
+ */
+ virtual bool confirmExecution() const=0;
+ /** \brief Can #command contain placeholders?
+ *
+ * @return \a true iff #command should be expanded by #Expander
+ */
+ virtual bool doSubstitution() const=0;
+ /** \brief A factory method for creating KrActionProc
+ *
+ * @return A new instance of KrActionProc
+ */
+ virtual KrActionProc* actionProcFactoryMethod();
+ virtual void handleError(const Error& err);
+
+ void exec();
+
+};
+
+#endif
diff --git a/src/app/UserAction/tstring.h b/src/app/UserAction/tstring.h
new file mode 100644
index 0000000..9b7d57d
--- /dev/null
+++ b/src/app/UserAction/tstring.h
@@ -0,0 +1,116 @@
+//
+// C++ Interface: tstring
+//
+// Description:
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef __TAGSTRING__H
+#define __TAGSTRING__H
+
+#include <tqstring.h>
+#include <tqvaluelist.h>
+#include <utility>
+#include <cassert>
+
+using namespace std;
+
+template <class T>
+class TagString_t
+{
+ TQString str;
+ typedef TQValueList<std::pair<uint,T> > taglist; // may change
+ taglist tags;
+public:
+ TagString_t(const TQString& s) : str(s) {}
+ TagString_t() {}
+ bool isSimple() const { return tags.empty(); }
+ const TQString& string() const { return str; }
+ unsigned length() const { return str.length(); }
+ TagString_t mid(unsigned idx,unsigned len=~0) const;
+ TagString_t left(unsigned) const;
+ TagString_t right(unsigned) const;
+ void insert(uint,const TQString& s);
+ int find ( TQChar c, int index = 0, bool cs = true ) const {
+ return str.find(c,index,cs);
+ }
+ TagString_t& operator+=(const TagString_t& s);
+ typename taglist::const_iterator tagsBegin() const { return tags.begin(); }
+ typename taglist::const_iterator tagsEnd() const { return tags.end(); }
+ typename taglist::iterator tagsBegin() { return tags.begin(); }
+ typename taglist::iterator tagsEnd() { return tags.end(); }
+ void insertTag(uint pos,const T& t);
+ void eraseTag(const typename taglist::iterator& which) { tags.erase(which); }
+/* void insert(uint idx,const TQString&);
+ void insert(uint idx,const char*);
+ void addTag(uint);
+ void remove(uint start,uint len);*/
+};
+
+template <class T>
+TagString_t<T> TagString_t<T>::mid(unsigned idx,unsigned len) const
+{
+ TagString_t ret(str.mid(idx,len));
+ unsigned max=idx+len;
+ if(max<idx) max=~0;
+ for(typename taglist::const_iterator it=tags.begin(),end=tags.end();it!=end;++it) {
+ if((*it).first>=idx && (*it).first<max)
+ ret.tags.push_back(*it);
+ }
+ return ret;
+}
+
+template <class T>
+TagString_t<T> TagString_t<T>::left(unsigned len) const
+{
+ TagString_t ret(str.left(len));
+ for(typename taglist::const_iterator it=tags.begin(),end=tags.end();it!=end;++it) {
+ if((*it).first<len)
+ ret.tags.push_back(*it);
+ }
+ return ret;
+}
+
+template <class T>
+TagString_t<T> TagString_t<T>::right(unsigned len) const
+{
+ TagString_t ret(str.right(len));
+ for(typename taglist::const_iterator it=tags.begin(),end=tags.end();it!=end;++it) {
+ if((*it).first>=str.length()-len)
+ ret.tags.push_back(*it);
+ }
+ return ret;
+}
+
+template <class T>
+void TagString_t<T>::insert(uint idx,const TQString& s)
+{
+ str.insert(idx,s);
+ const unsigned disp=s.length();
+ for(typename taglist::iterator it=tags.begin(),end=tags.end();it!=end;++it) {
+ if((*it).first>=idx)
+ (*it).first+=disp;
+ }
+}
+
+template <class T>
+TagString_t<T>& TagString_t<T>::operator+=(const TagString_t& s)
+{
+ str+=s.str;
+ const unsigned disp=length();
+ for(typename taglist::const_iterator it=s.tags.begin(),end=s.tags.end();it!=end;++it) {
+ tags.push_back(make_pair((*it).first+disp,(*it).second));
+ }
+ return *this;
+}
+
+template <class T>
+void TagString_t<T>::insertTag(uint pos,const T& t)
+{
+ assert(pos<=length());
+ tags.push_back(make_pair(pos,t));
+}
+
+#endif
diff --git a/src/app/UserAction/useraction.cpp b/src/app/UserAction/useraction.cpp
new file mode 100644
index 0000000..4e412bd
--- /dev/null
+++ b/src/app/UserAction/useraction.cpp
@@ -0,0 +1,212 @@
+//
+// C++ Implementation: useraction
+//
+// Description: This manages all useractions
+//
+//
+// Author: Jonas B�r (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <tdepopupmenu.h>
+#include <kstandarddirs.h>
+#include <tdemessagebox.h>
+
+#include <tqstring.h>
+#include <tqdom.h>
+
+#include "useraction.h"
+#include "kraction.h"
+
+#include "../krusader.h"
+#include "../krusaderview.h"
+#include "../Panel/listpanel.h"
+#include "../Panel/panelfunc.h"
+
+
+UserAction::UserAction() {
+ _actions.setAutoDelete( false ); // the actions are "owned" by Krusader's TDEActionCollection, so they should not be deleted
+ krOut << "Initialisising useractions..." << endl;
+ readAllFiles();
+ krOut << _actions.count() << " useractions read." << endl;
+}
+
+UserAction::~UserAction() {
+ // KrActions are deleted by Krusader's TDEActionCollection
+}
+
+void UserAction::setAvailability() {
+ setAvailability( ACTIVE_FUNC->files()->vfs_getFile( ACTIVE_PANEL->view->getCurrentItem() ) );
+}
+
+void UserAction::setAvailability( const KURL& currentURL ) {
+ //kdDebug() << "UserAction::setAvailability currendFile: " << currentURL.url() << endl;
+ // disable the entries that should not appear in this folder
+ for ( KrAction* action = _actions.first(); action; action = _actions.next() )
+ action->setEnabled( action->isAvailable( currentURL ) );
+}
+
+void UserAction::populateMenu( TDEPopupMenu* menu ) {
+ for ( KrAction* action = _actions.first(); action; action = _actions.next() )
+ if ( ! action->isPlugged( menu ) )
+ action->plug( menu );
+}
+
+TQStringList UserAction::allCategories() {
+ TQStringList actionCategories;
+
+ for ( KrAction* action = _actions.first(); action; action = _actions.next() )
+ if ( actionCategories.find( action->category() ) == actionCategories.end() )
+ actionCategories.append( action->category() );
+
+ return actionCategories;
+}
+
+TQStringList UserAction::allNames() {
+ TQStringList actionNames;
+
+ for ( KrAction* action = _actions.first(); action; action = _actions.next() )
+ actionNames.append( action->name() );
+
+ return actionNames;
+}
+
+void UserAction::readAllFiles() {
+ TQString filename = locate( "data", ACTION_XML ); // locate returns the local file if it exists, else the global one is retrieved.
+ if ( ! filename.isEmpty() ) {
+ readFromFile( filename, renameDoublicated );
+ return;
+ }
+
+ filename = locate( "data", ACTION_XML_EXAMPLES );
+ if ( ! filename.isEmpty() )
+ readFromFile( filename, ignoreDoublicated ); // ignore samples which are already in the normal file
+}
+
+void UserAction::readFromFile( const TQString& filename, ReadMode mode, KrActionList* list ) {
+ TQDomDocument* doc = new TQDomDocument( ACTION_DOCTYPE );
+ TQFile file( filename );
+ if( file.open( IO_ReadOnly ) ) {
+ //kdDebug() << "UserAction::readFromFile - " << filename << "could be opened" << endl;
+ if( ! doc->setContent( &file ) ) {
+ //kdDebug() << "UserAction::readFromFile - content set - failed" << endl;
+ // if the file doesn't exist till now, the content CAN be set but is empty.
+ // if the content can't be set, the file exists and is NOT an xml-file.
+ file.close();
+ delete doc; doc = 0;
+ KMessageBox::error( MAIN_VIEW,
+ i18n( "The file %1 does not contain valid UserActions.\n" ).arg( filename ), // text
+ i18n("UserActions - can't read from file!") // caption
+ );
+ }
+ file.close();
+
+ if ( doc ) {
+ TQDomElement root = doc->documentElement();
+ // check if the file got the right root-element (ACTION_ROOT) - this finds out if the xml-file read to the DOM is realy an krusader useraction-file
+ if( root.tagName() != ACTION_ROOT ) {
+ KMessageBox::error( MAIN_VIEW,
+ i18n( "The actionfile's root-element isn't called " ACTION_ROOT ", using %1").arg( filename ),
+ i18n( "UserActions - can't read from file!" )
+ );
+ delete doc; doc = 0;
+ }
+ readFromElement( root, mode, list );
+ delete doc;
+ }
+
+ } // if ( file.open( IO_ReadOnly ) )
+ else {
+ KMessageBox::error( MAIN_VIEW,
+ i18n( "Unable to open actionfile %1").arg( filename ),
+ i18n( "UserActions - can't read from file!" )
+ );
+ }
+
+}
+
+void UserAction::readFromElement( const TQDomElement& element, ReadMode mode, KrActionList* list ) {
+ 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() == "action" ) {
+ TQString name = e.attribute( "name" );
+ if ( name.isEmpty() ) {
+ KMessageBox::error( MAIN_VIEW,
+ i18n( "Action without name detected. This action will not be imported!\nThis is an error in the file, you may want to correct it." ),
+ i18n( "UserActions - invalid action" )
+ );
+ continue;
+ }
+
+ if ( mode == ignoreDoublicated && krApp->actionCollection()->action( name.latin1() ) )
+ continue;
+
+ TQString basename = name + "_%1";
+ int i = 0;
+ // appent a counter till the name is unique... (this checks every action, not only useractions)
+ while ( krApp->actionCollection()->action( name.latin1() ) )
+ name = basename.arg( ++i );
+
+ KrAction* act = new KrAction( krApp->actionCollection(), name.latin1() );
+ if ( act->xmlRead( e ) ) {
+ _actions.append( act );
+ if ( list )
+ list->append( act );
+ }
+ else
+ delete act;
+ }
+ } // for
+}
+
+TQDomDocument UserAction::createEmptyDoc() {
+ TQDomDocument doc = TQDomDocument( ACTION_DOCTYPE );
+ // adding: <?xml version="1.0" encoding="UTF-8" ?>
+ doc.appendChild( doc.createProcessingInstruction( "xml", ACTION_PROCESSINSTR ) );
+ //adding root-element
+ doc.appendChild( doc.createElement( ACTION_ROOT ) ); // create new actionfile by adding a root-element ACTION_ROOT
+ return doc;
+}
+
+bool UserAction::writeActionFile() {
+ TQString filename = locateLocal( "data", ACTION_XML );
+
+ TQDomDocument doc = createEmptyDoc();
+ TQDomElement root = doc.documentElement();
+ for ( KrAction* action = _actions.first(); action; action = _actions.next() )
+ root.appendChild( action->xmlDump( doc ) );
+
+ return writeToFile( doc, filename );
+}
+
+bool UserAction::writeToFile( const TQDomDocument& doc, const TQString& filename ) {
+ TQFile file( filename );
+ if( ! file.open( IO_WriteOnly ) )
+ return false;
+
+/* // This is not needed, because each DomDocument created with UserAction::createEmptyDoc already contains the processinstruction
+ if ( ! doc.firstChild().isProcessingInstruction() ) {
+ // adding: <?xml version="1.0" encoding="UTF-8" ?> if not already present
+ TQDomProcessingInstruction instr = doc.createProcessingInstruction( "xml", ACTION_PROCESSINSTR );
+ doc.insertBefore( instr, doc.firstChild() );
+ }
+*/
+
+ TQTextStream ts( &file );
+ ts.setEncoding(ts.UnicodeUTF8);
+ ts << doc.toString();
+
+ file.close();
+ return true;
+}
+
+
+
+
diff --git a/src/app/UserAction/useraction.h b/src/app/UserAction/useraction.h
new file mode 100644
index 0000000..11ed68c
--- /dev/null
+++ b/src/app/UserAction/useraction.h
@@ -0,0 +1,145 @@
+//
+// C++ Interface: useraction
+//
+// Description: This manages all useractions
+//
+//
+// Author: Jonas Bähr (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef USERACTION_H
+#define USERACTION_H
+
+#include <tqptrlist.h>
+
+class TQDomDocument;
+class TQDomElement;
+class TQString;
+class TQStringList;
+class KrAction;
+class KURL;
+class TDEPopupMenu;
+
+/**
+ * Useractions are Krusaders backend for user-defined actions on current/selected files in its panels
+ * and for krusader's internal actions which need some parameter. @n
+ * There are several komponents:
+ * - The UserAction class as a Manager
+ * - The interface to KDE's action-system (the KrAction)
+ * - The Expander, which parses the commandline for placeholders and calls the internal actions
+ * - A widget to manipulate the UserAction's Properties via GUI (ActionProperty)
+ * .
+ * The Useractions are stored in XML-files. Currently there are two main files. The first is a global example-file
+ * which is read only (read after the other actionfiles, doublicates are ignored) and a local file where the actions are saved.
+ * This class reads only the container and passes each action-tag to the new KrAction, which reads it's data itself.
+ *
+ * @author Jonas Bähr (http://www.jonas-baehr.de)
+ */
+
+class UserAction {
+public:
+
+ typedef TQPtrList<KrAction> KrActionList;
+
+ enum ReadMode { renameDoublicated, ignoreDoublicated };
+
+ /**
+ * The constructor reads all useractions, see readAllFiles()
+ */
+ UserAction();
+ ~UserAction();
+
+ /**
+ * adds an action to the collection.
+ */
+ void addKrAction( KrAction* action ) { _actions.append( action ); };
+
+ /**
+ * Use this to access the whole list of registerd KrActions.
+ * currently only used to fill the usermenu with all available actions. This should change...
+ * @return A reference to the internal KrActionList
+ */
+ const KrActionList &actionList() { return _actions; };
+
+ /**
+ * @return how many useractions exist
+ */
+ int count() const { return _actions.count(); };
+
+ /**
+ * removes a KrAction from the internal list but does not delete it.
+ * @param action the KrAction which should be removed
+ */
+ void removeKrAction( KrAction* action ) { _actions.remove( action ); };
+
+ /**
+ * check for each KrAction if it is available for the currend location / file and disables it if not
+ */
+ void setAvailability();
+ /**
+ * same as above but check for a specitic file
+ * @param currentURL Check for this file
+ */
+ void setAvailability(const KURL& currentURL);
+
+ /**
+ * Fills a TDEPopupMenu with all available UserActions in the list
+ * @param popupmenu to populate
+ */
+ void populateMenu(TDEPopupMenu* menu);
+
+ TQStringList allCategories();
+ TQStringList allNames();
+
+ /**
+ * reads all predefined useractionfiles.
+ */
+ void readAllFiles();
+ /**
+ * writes all actions to the local actionfile
+ */
+ bool writeActionFile();
+ /**
+ * Reads UserActions from a xml-file.
+ * @param list If provided, all new actions will also be added to this list
+ */
+ void readFromFile( const TQString& filename, ReadMode mode = renameDoublicated, KrActionList* list = 0 );
+ /**
+ * Reads UserActions from a XML-Element.
+ * @param element a container with action-elements
+ * @param list If provided, all new actions will also be added to this list
+ */
+ void readFromElement( const TQDomElement& element, ReadMode mode = renameDoublicated, KrActionList* list = 0 );
+
+ /**
+ * creates an empty TQDomDocument for the UserActions
+ */
+ static TQDomDocument createEmptyDoc();
+ /**
+ * Writes a TQDomDocument to an UTF-8 encodes text-file
+ * @param doc the XML-Tree
+ * @param filename the filename where to save
+ * @return true on success, false otherwise
+ * @warning any existing file will get overwritten!
+ */
+ static bool writeToFile( const TQDomDocument& doc, const TQString& filename );
+
+private:
+ KrActionList _actions;
+};
+
+
+#define ACTION_XML "krusader/useractions.xml"
+#define ACTION_XML_EXAMPLES "krusader/useraction_examples.xml"
+
+#define ACTION_DOCTYPE "KrusaderUserActions"
+// in well formed XML the root-element has to have the same name then the doctype:
+#define ACTION_ROOT ACTION_DOCTYPE
+#define ACTION_PROCESSINSTR "version=\"1.0\" encoding=\"UTF-8\" "
+
+
+
+#endif // ifndef USERACTION_H
diff --git a/src/app/UserAction/useractionpopupmenu.cpp b/src/app/UserAction/useractionpopupmenu.cpp
new file mode 100644
index 0000000..c100992
--- /dev/null
+++ b/src/app/UserAction/useractionpopupmenu.cpp
@@ -0,0 +1,26 @@
+//
+// C++ Implementation: UserActionPopupMenu
+//
+// Description:
+//
+//
+// Author: Jonas B�r (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include "useractionpopupmenu.h"
+
+#include <kurl.h>
+
+#include "../krusader.h"
+#include "useraction.h"
+#include "kraction.h"
+
+UserActionPopupMenu::UserActionPopupMenu( KURL currentURL, TQWidget *parent ) : TDEPopupMenu( parent, "useraction popupmenu" ) {
+ UserAction::KrActionList list = krUserAction->actionList();
+ for ( KrAction* action = list.first(); action; action = list.next() )
+ if ( action->isAvailable( currentURL ) )
+ action->plug( this );
+}
diff --git a/src/app/UserAction/useractionpopupmenu.h b/src/app/UserAction/useractionpopupmenu.h
new file mode 100644
index 0000000..33ea818
--- /dev/null
+++ b/src/app/UserAction/useractionpopupmenu.h
@@ -0,0 +1,25 @@
+//
+// C++ Interface: UserActionPopupMenu
+//
+// Description:
+//
+//
+// Author: Jonas Bähr, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef USERACTIONPOPUPMENU_H
+#define USERACTIONPOPUPMENU_H
+
+#include <tdepopupmenu.h>
+
+class KURL;
+
+class UserActionPopupMenu : public TDEPopupMenu {
+public:
+ UserActionPopupMenu( KURL currentURL, TQWidget *parent = 0 );
+};
+
+#endif // ifndef USERACTIONPOPUPMENU_H