diff options
Diffstat (limited to 'src/app/UserAction')
-rw-r--r-- | src/app/UserAction/Makefile.am | 17 | ||||
-rw-r--r-- | src/app/UserAction/expander.cpp | 1242 | ||||
-rw-r--r-- | src/app/UserAction/expander.h | 245 | ||||
-rw-r--r-- | src/app/UserAction/kraction.cpp | 586 | ||||
-rw-r--r-- | src/app/UserAction/kraction.h | 170 | ||||
-rw-r--r-- | src/app/UserAction/kractionbase.cpp | 77 | ||||
-rw-r--r-- | src/app/UserAction/kractionbase.h | 97 | ||||
-rw-r--r-- | src/app/UserAction/tstring.h | 116 | ||||
-rw-r--r-- | src/app/UserAction/useraction.cpp | 212 | ||||
-rw-r--r-- | src/app/UserAction/useraction.h | 145 | ||||
-rw-r--r-- | src/app/UserAction/useractionpopupmenu.cpp | 26 | ||||
-rw-r--r-- | src/app/UserAction/useractionpopupmenu.h | 25 |
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 |