diff --git a/addons/CMakeLists.txt b/addons/CMakeLists.txt --- a/addons/CMakeLists.txt +++ b/addons/CMakeLists.txt @@ -49,6 +49,9 @@ # pipe text through some external command ecm_optional_add_subdirectory (textfilter) +# external tools +ecm_optional_add_subdirectory (externaltools) + # Rust complection plugin ecm_optional_add_subdirectory (rustcompletion) diff --git a/addons/externaltools/CMakeLists.txt b/addons/externaltools/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/addons/externaltools/CMakeLists.txt @@ -0,0 +1,29 @@ +project(kateexternaltoolsplugin) +add_definitions(-DTRANSLATION_DOMAIN=\"externaltoolsplugin\") + +include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) + +set(kateexternaltoolsplugin_PART_SRCS + kateexternaltoolsplugin.cpp + kateexternaltools.cpp +) + +# resource for ui file and stuff +qt5_add_resources(kateexternaltoolsplugin_PART_SRCS plugin.qrc) + +set(kateexternaltoolsplugin_PART_UI + configwidget.ui +) +ki18n_wrap_ui(kateexternaltoolsplugin_PART_SRCS ${katebacktracebrowserplugin_PART_UI} ) +add_library(kateexternaltoolsplugin MODULE ${kateexternaltoolsplugin_PART_SRCS}) + +# we compile in the .desktop file +kcoreaddons_desktop_to_json (kateexternaltoolsplugin kateexternaltoolsplugin.desktop) + +target_link_libraries(kateexternaltoolsplugin + KF5::TextEditor + KF5::I18n +) + +########### install files ############### +install(TARGETS kateexternaltoolsplugin DESTINATION ${PLUGIN_INSTALL_DIR}/ktexteditor ) diff --git a/addons/externaltools/Messages.sh b/addons/externaltools/Messages.sh new file mode 100644 --- /dev/null +++ b/addons/externaltools/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc *.ui >> rc.cpp +$XGETTEXT *.cpp -o $podir/kateexternaltoolsplugin.pot diff --git a/addons/externaltools/configwidget.ui b/addons/externaltools/configwidget.ui new file mode 100644 --- /dev/null +++ b/addons/externaltools/configwidget.ui @@ -0,0 +1,115 @@ + + ExternalToolsConfigWidget + + + + 0 + 0 + 504 + 296 + + + + + 0 + + + + + + + This list shows all the configured tools, represented by their menu text. + + + + + + + Qt::Vertical + + + + 20 + 98 + + + + + + + + Qt::Vertical + + + + 17 + 108 + + + + + + + + + + New... + + + + + + + Edit... + + + + + + + Remove + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Insert Separator + + + + + + + + + + + + + + + + + + KListWidget + QListWidget +
klistwidget.h
+
+
+ + +
diff --git a/addons/externaltools/externaltools b/addons/externaltools/externaltools new file mode 100644 --- /dev/null +++ b/addons/externaltools/externaltools @@ -0,0 +1,23 @@ +[Global] +tools=externaltool_RunScript,---,externaltool_GoogleSelection +version=1 + +[externaltool_GoogleSelection] +acname=externaltool_GoogleSelection +cmdname=google-selection +command=[ -n "%selection" ] && kfmclient openURL "gg:%selection" +executable=kfmclient +icon=globe +mimetypes= +name=Google Selection +save=0 + +[externaltool_RunScript] +acname=externaltool_RunScript +cmdname=runscript +command=konsole -e sh -c "cd %directory && pwd && chmod -vc a+x %filename && ./%filename ; echo Press any key to continue. && read -n 1" +executable=konsole +icon=run +mimetypes= +name=Run Script +save=1 diff --git a/addons/externaltools/kateexternaltools.h b/addons/externaltools/kateexternaltools.h new file mode 100644 --- /dev/null +++ b/addons/externaltools/kateexternaltools.h @@ -0,0 +1,253 @@ +/* + This file is part of the Kate text editor of the KDE project. + It describes a "external tools" action for kate and provides a dialog + page to configure that. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) 2004, Anders Lund +*/ + +#ifndef _KATE_EXTERNAL_TOOLS_H_ +#define _KATE_EXTERNAL_TOOLS_H_ + +#include "ui_configwidget.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +class KateExternalToolsPlugin; + +/** + * The external tools action + * This action creates a menu, in which each item will launch a process + * with the provided arguments, which may include the following macros: + * - %URLS: the URLs of all open documents. + * - %URL: The URL of the active document. + * - %filedir: The directory of the current document, if that is a local file. + * - %selection: The selection of the active document. + * - %text: The text of the active document. + * - %line: The line number of the cursor in the active view. + * - %column: The column of the cursor in the active view. + * + * Each item has the following properties: + * - Name: The friendly text for the menu + * - Exec: The command to execute, including arguments. + * - TryExec: the name of the executable, if not available, the + * item will not be displayed. + * - MimeTypes: An optional list of mimetypes. The item will be disabled or + * hidden if the current file is not of one of the indicated types. + * + */ +class KateExternalToolsMenuAction : public KActionMenu +{ + friend class KateExternalToolAction; + + Q_OBJECT + public: + KateExternalToolsMenuAction( const QString &text, class KActionCollection *collection, QObject *parent, class Kate::MainWindow *mw = 0 ); + virtual ~KateExternalToolsMenuAction(); + + /** + * This will load all the confiured services. + */ + void reload(); + + class KActionCollection *actionCollection() + { + return m_actionCollection; + } + + private Q_SLOTS: + void slotDocumentChanged(); + + private: + class KActionCollection *m_actionCollection; + Kate::MainWindow *mainwindow; // for the actions to access view/doc managers +}; + +/** + * This Action contains a KateExternalTool + */ +class KateExternalToolAction : public KAction, public KWordMacroExpander +{ + Q_OBJECT + public: + KateExternalToolAction( QObject *parent, class KateExternalTool *t ); + ~KateExternalToolAction(); + protected: + virtual bool expandMacro( const QString &str, QStringList &ret ); + + private Q_SLOTS: + void slotRun(); + + public: + class KateExternalTool *tool; +}; + +/** + * This class defines a single external tool. + */ +class KateExternalTool +{ + public: + explicit KateExternalTool( const QString &name = QString(), + const QString &command = QString(), + const QString &icon = QString(), + const QString &tryexec = QString(), + const QStringList &mimetypes = QStringList(), + const QString &acname = QString(), + const QString &cmdname = QString(), + int save = 0 ); + ~KateExternalTool() + {} + + QString name; ///< The name used in the menu. + QString command; ///< The command to execute. + QString icon; ///< the icon to use in the menu. + QString tryexec; ///< The name or path of the executable. + QStringList mimetypes; ///< Optional list of mimetypes for which this action is valid. + bool hasexec; ///< This is set by the constructor by calling checkExec(), if a value is present. + QString acname; ///< The name for the action. This is generated first time the action is is created. + QString cmdname; ///< The name for the commandline. + int save; ///< We can save documents prior to activating the tool command: 0 = nothing, 1 = current document, 2 = all documents. + + /** + * @return true if mimetypes is empty, or the @p mimetype matches. + */ + bool valid( const QString &mimetype ) const; + /** + * @return true if "tryexec" exists and has the executable bit set, or is + * empty. + * This is run at least once, and the tool is disabled if it fails. + */ + bool checkExec(); + + private: + QString m_exec; ///< The fully qualified path of the executable. +}; + +/** + * The config widget. + * The config widget allows the user to view a list of services of the type + * "Kate/ExternalTool" and add, remove or edit them. + */ +class KateExternalToolsConfigWidget + : public Kate::PluginConfigPage + , public Ui::ExternalToolsConfigWidget +{ + Q_OBJECT + public: + KateExternalToolsConfigWidget( QWidget *parent, KateExternalToolsPlugin *plugin, const char* name); + virtual ~KateExternalToolsConfigWidget(); + + virtual void apply(); + virtual void reset(); + virtual void defaults() + { + reset(); + } // double sigh + + private Q_SLOTS: + void slotNew(); + void slotEdit(); + void slotRemove(); + void slotInsertSeparator(); + + void slotMoveUp(); + void slotMoveDown(); + + void slotSelectionChanged(); + + private: + QPixmap blankIcon(); + + QStringList m_removed; + + class KConfig *config; + + bool m_changed; + KateExternalToolsPlugin *m_plugin; +}; + +/** + * A Singleton class for invoking external tools with the view command line + */ +class KateExternalToolsCommand : public KTextEditor::Command +{ + public: + KateExternalToolsCommand (KateExternalToolsPlugin *plugin); + virtual ~KateExternalToolsCommand () + {} + void reload(); + public: + virtual const QStringList &cmds (); + virtual bool exec (KTextEditor::View *view, const QString &cmd, QString &msg); + virtual bool help (KTextEditor::View *view, const QString &cmd, QString &msg); + private: + QStringList m_list; + QHash m_map; + QHash m_name; + bool m_inited; + KateExternalToolsPlugin *m_plugin; +}; + +/** + * A Dialog to edit a single KateExternalTool object + */ +class KateExternalToolServiceEditor : public KDialog +{ + Q_OBJECT + + public: + + explicit KateExternalToolServiceEditor( KateExternalTool *tool = 0, + QWidget *parent = 0, const char *name = 0 ); + + class KLineEdit *leName, *leExecutable, *leMimetypes, *leCmdLine; + class QTextEdit *teCommand; + class KIconButton *btnIcon; + class KComboBox *cmbSave; + + private Q_SLOTS: + /** + * Run when the OK/Cancel button is clicked, to ensure critical values are provided + */ + void slotButtonClicked(int button); + /** + * show a mimetype chooser dialog + */ + void showMTDlg(); + + private: + KateExternalTool *tool; +}; +#endif //_KATE_EXTERNAL_TOOLS_H_ +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/addons/externaltools/kateexternaltools.cpp b/addons/externaltools/kateexternaltools.cpp new file mode 100644 --- /dev/null +++ b/addons/externaltools/kateexternaltools.cpp @@ -0,0 +1,890 @@ +/* + This file is part of the Kate text editor of the KDE project. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) 2004, Anders Lund +*/ +// TODO +// Icons +// Direct shortcut setting +//BEGIN Includes +#include "kateexternaltoolsplugin.h" +#include "kateexternaltools.h" +#include "kateexternaltools.moc" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +//END Includes + + +//BEGIN KateExternalTool +KateExternalTool::KateExternalTool( const QString &name, + const QString &command, + const QString &icon, + const QString &tryexec, + const QStringList &mimetypes, + const QString &acname, + const QString &cmdname, + int save ) + : name ( name ), + command ( command ), + icon ( icon ), + tryexec ( tryexec ), + mimetypes ( mimetypes ), + acname ( acname ), + cmdname ( cmdname ), + save ( save ) +{ + //if ( ! tryexec.isEmpty() ) + hasexec = checkExec(); +} + +bool KateExternalTool::checkExec() +{ + // if tryexec is empty, it is the first word of command + if ( tryexec.isEmpty() ) + tryexec = command.section( ' ', 0, 0, QString::SectionSkipEmpty ); + + // NOTE this code is modified taken from kdesktopfile.cpp, from KDesktopFile::tryExec() + if (!tryexec.isEmpty()) + { + m_exec = KStandardDirs::findExe(tryexec); + return !m_exec.isEmpty(); + } + return false; +} + +bool KateExternalTool::valid( const QString &mt ) const +{ + return mimetypes.isEmpty() || mimetypes.contains( mt ); +} +//END KateExternalTool + +//BEGIN KateExternalToolsCommand +KateExternalToolsCommand::KateExternalToolsCommand(KateExternalToolsPlugin *plugin) : KTextEditor::Command(),m_plugin(plugin) +{ + m_inited = false; + reload(); +} + +const QStringList &KateExternalToolsCommand::cmds () +{ + return m_list; +} + + +void KateExternalToolsCommand::reload () +{ + m_list.clear(); + m_map.clear(); + m_name.clear(); + + KConfig _config( "externaltools", KConfig::NoGlobals, "appdata" ); + KConfigGroup config(&_config, "Global"); + const QStringList tools = config.readEntry("tools", QStringList()); + + + for( QStringList::const_iterator it = tools.begin(); it != tools.end(); ++it ) + { + if ( *it == "---" ) + continue; + + + config= KConfigGroup(&_config, *it ); + + KateExternalTool t = KateExternalTool( + config.readEntry( "name", "" ), + config.readEntry( "command", ""), + config.readEntry( "icon", ""), + config.readEntry( "executable", ""), + config.readEntry( "mimetypes", QStringList() ), + config.readEntry( "acname", "" ), + config.readEntry( "cmdname", "" ) ); + // FIXME test for a command name first! + if ( t.hasexec && (!t.cmdname.isEmpty())) + { + m_list.append("exttool-" + t.cmdname); + m_map.insert("exttool-" + t.cmdname, t.acname); + m_name.insert("exttool-" + t.cmdname, t.name); + } + } + if (m_inited) + { + KTextEditor::CommandInterface* cmdIface = + qobject_cast( Kate::application()->editor() ); + if( cmdIface ) + { + // reregister commands, in case of something has changed + cmdIface->unregisterCommand( this ); + cmdIface->registerCommand( this ); + } + } + else m_inited = true; +} + +bool KateExternalToolsCommand::exec (KTextEditor::View *view, const QString &cmd, QString &) +{ + QWidget *wv = dynamic_cast(view); + if (!wv) + { +// kDebug(13001)<<"KateExternalToolsCommand::exec: Could not get view widget"; + return false; + } + + +// kDebug(13001)<<"cmd="<(dmw->action("tools_external")); + if (!a) return false;*/ + KateExternalToolsPluginView *extview=m_plugin->extView(wv->window()); + if (!extview) return false; + if (!extview->externalTools) return false; +// kDebug(13001)<<"trying to find action"; + QAction *a1 = extview->externalTools->actionCollection()->action(actionName.toUtf8().constData ()); + if (!a1) return false; +// kDebug(13001)<<"activating action"; + a1->trigger(); + return true; +} + +bool KateExternalToolsCommand::help (KTextEditor::View *, const QString &, QString &) +{ + return false; +} +//END KateExternalToolsCommand + +//BEGIN KateExternalToolAction +KateExternalToolAction::KateExternalToolAction( QObject *parent, KateExternalTool *t) + : KAction( KIcon(t->icon), t->name, parent ), + tool ( t ) +{ + //setText( t->name ); + //if ( ! t->icon.isEmpty() ) + // setIcon( KIcon( t->icon ) ); + + connect( this, SIGNAL(triggered(bool)), SLOT(slotRun()) ); +} + +bool KateExternalToolAction::expandMacro( const QString &str, QStringList &ret ) +{ + Kate::MainWindow *mw = qobject_cast(parent()->parent()); + Q_ASSERT(mw); + + KTextEditor::View *view = mw->activeView(); + if ( ! view ) return false; + + KTextEditor::Document *doc = view->document(); + KUrl url = doc->url(); + + if ( str == "URL" ) + ret += url.url(); + else if ( str == "directory" ) // directory of current doc + ret += url.directory(); + else if ( str == "filename" ) + ret += url.fileName(); + else if ( str == "line" ) // cursor line of current doc + ret += QString::number( view->cursorPosition().line() ); + else if ( str == "col" ) // cursor col of current doc + ret += QString::number( view->cursorPosition().column() ); + else if ( str == "selection" ) // selection of current doc if any + ret += view->selectionText(); + else if ( str == "text" ) // text of current doc + ret += doc->text(); + else if ( str == "URLs" ) + { + foreach( KTextEditor::Document *it, Kate::documentManager()->documents()) + if ( ! it->url().isEmpty() ) + ret += it->url().url(); + } + else + return false; + + return true; +} + +KateExternalToolAction::~KateExternalToolAction() +{ + delete(tool); +} + +void KateExternalToolAction::slotRun() +{ + // expand the macros in command if any, + // and construct a command with an absolute path + QString cmd = tool->command; + + Kate::MainWindow *mw = qobject_cast(parent()->parent()); + if ( ! expandMacrosShellQuote( cmd ) ) + { + KMessageBox::sorry( mw->window(), + i18n("Failed to expand the command '%1'.", cmd ), + i18n( "Kate External Tools") ); + return; + } + kDebug(13001) << "externaltools: Running command: " << cmd; + + // save documents if requested + if ( tool->save == 1 ) + mw->activeView()->document()->save(); + else if ( tool->save == 2 ) + { + foreach (KXMLGUIClient *client, mw->guiFactory()->clients()) + { + if (QAction *a = client->actionCollection()->action("file_save_all")) + { + a->trigger(); + break; + } + } + } + + KRun::runCommand( cmd, tool->tryexec, tool->icon, mw->window() ); +} +//END KateExternalToolAction + +//BEGIN KateExternalToolsMenuAction +KateExternalToolsMenuAction::KateExternalToolsMenuAction( const QString &text, + KActionCollection *collection, + QObject *parent, + Kate::MainWindow *mw ) + : KActionMenu( text, parent ), + mainwindow( mw ) +{ + + m_actionCollection = collection; + + // connect to view changed... + connect(mw, SIGNAL(viewChanged()), this, SLOT(slotDocumentChanged())); + + reload(); +} + +KateExternalToolsMenuAction::~KateExternalToolsMenuAction() +{ + //kDebug() << "deleted KateExternalToolsMenuAction"; +} + +void KateExternalToolsMenuAction::reload() +{ + bool needs_readd=(m_actionCollection->takeAction(this)!=0); + m_actionCollection->clear (); + if (needs_readd) m_actionCollection->addAction("tools_external",this); + menu()->clear(); + + // load all the tools, and create a action for each of them + KSharedConfig::Ptr pConfig = KSharedConfig::openConfig( "externaltools", KConfig::NoGlobals, "appdata" ); + KConfigGroup config(pConfig, "Global" ); + QStringList tools = config.readEntry( "tools", QStringList() ); + + // if there are tools that are present but not explicitly removed, + // add them to the end of the list + pConfig->setReadDefaults( true ); + QStringList dtools = config.readEntry( "tools", QStringList() ); + int gver = config.readEntry( "version", 1 ); + pConfig->setReadDefaults( false ); + + int ver = config.readEntry( "version", 0 ); + if ( ver <= gver ) + { + QStringList removed = config.readEntry( "removed", QStringList() ); + bool sepadded = false; + for (QStringList::iterator itg = dtools.begin(); itg != dtools.end(); ++itg ) + { + if ( ! tools.contains( *itg ) && + ! removed.contains( *itg ) ) + { + if ( ! sepadded ) + { + tools << "---"; + sepadded = true; + } + tools << *itg; + } + } + + config.writeEntry( "tools", tools ); + config.sync(); + config.writeEntry( "version", gver ); + } + + for( QStringList::const_iterator it = tools.constBegin(); it != tools.constEnd(); ++it ) + { + if ( *it == "---" ) + { + menu()->addSeparator(); + // a separator + continue; + } + + config=KConfigGroup(pConfig, *it ); + + KateExternalTool *t = new KateExternalTool( + config.readEntry( "name", "" ), + config.readEntry( "command", ""), + config.readEntry( "icon", ""), + config.readEntry( "executable", ""), + config.readEntry( "mimetypes", QStringList() ), + config.readEntry( "acname", "" ), + config.readEntry( "cmdname", "" ), + config.readEntry( "save", 0 ) ); + + if ( t->hasexec ) + { + KAction *a = new KateExternalToolAction( this, t ); + m_actionCollection->addAction( t->acname.toAscii(), a ); + addAction( a ); + } + else + delete t; + } + + config=KConfigGroup(pConfig, "Shortcuts"); + m_actionCollection->readSettings( &config ); + slotDocumentChanged(); +} + +void KateExternalToolsMenuAction::slotDocumentChanged() +{ + // try to enable/disable to match current mime type + KTextEditor::View *v = mainwindow->activeView(); + + // no active view, oh oh + if (!v) + return; + + KTextEditor::Document *de = v->document(); + if ( de ) + { + QString mt = de->mimeType(); + QStringList l; + bool b; + + foreach (QAction* kaction, m_actionCollection->actions()) + { + KateExternalToolAction *action = dynamic_cast(kaction); + if ( action ) + { + l = action->tool->mimetypes; + b = ( ! l.count() || l.contains( mt ) ); + action->setEnabled( b ); + } + } + } +} +//END KateExternalToolsMenuAction + +//BEGIN ToolItem +/** + * This is a QListBoxItem, that has a KateExternalTool. The text is the Name + * of the tool. + */ +class ToolItem : public QListWidgetItem +{ + public: + ToolItem( QListWidget *lb, const QPixmap &icon, KateExternalTool *tool ) + : QListWidgetItem( icon , tool->name , lb ), + tool ( tool ) + { + } + + ~ToolItem() + {} + + KateExternalTool *tool; +}; +//END ToolItem + +//BEGIN KateExternalToolServiceEditor +KateExternalToolServiceEditor::KateExternalToolServiceEditor( KateExternalTool *tool, + QWidget *parent, const char *name ) + : KDialog( parent ), + tool( tool ) +{ + setCaption( i18n("Edit External Tool") ); + setButtons( Ok | Cancel ); + setObjectName( name ); + setModal( true ); + + // create a entry for each property + // fill in the values from the service if available + QWidget *w = new QWidget( this ); + setMainWidget( w ); + QGridLayout *lo = new QGridLayout( w ); + lo->setSpacing( KDialog::spacingHint() ); + + QLabel *l; + + leName = new KLineEdit( w ); + lo->addWidget( leName, 1, 2 ); + l = new QLabel( w ); + l->setBuddy( leName ); + l->setText( i18n("&Label:") ); + l->setAlignment( l->alignment() | Qt::AlignRight ); + lo->addWidget( l, 1, 1 ); + if ( tool ) leName->setText( tool->name ); + leName->setWhatsThis(i18n( + "The name will be displayed in the 'Tools->External' menu") ); + + btnIcon = new KIconButton( w ); + btnIcon->setIconSize( KIconLoader::SizeSmall ); + lo->addWidget( btnIcon, 1, 3 ); + if ( tool && !tool->icon.isEmpty() ) + btnIcon->setIcon( tool->icon ); + + teCommand = new QTextEdit( w ); + lo->addWidget( teCommand, 2, 2, 1, 2 ); + l = new QLabel( w ); + l->setBuddy( teCommand ); + l->setText( i18n("S&cript:") ); + l->setAlignment( Qt::AlignTop | Qt::AlignRight ); + lo->addWidget( l, 2, 1 ); + if ( tool ) teCommand->setText( tool->command ); + teCommand->setWhatsThis(i18n( + "

The script to execute to invoke the tool. The script is passed " + "to /bin/sh for execution. The following macros " + "will be expanded:

" + "
  • %URL - the URL of the current document.
  • " + "
  • %URLs - a list of the URLs of all open documents.
  • " + "
  • %directory - the URL of the directory containing " + "the current document.
  • " + "
  • %filename - the filename of the current document.
  • " + "
  • %line - the current line of the text cursor in the " + "current view.
  • " + "
  • %column - the column of the text cursor in the " + "current view.
  • " + "
  • %selection - the selected text in the current view.
  • " + "
  • %text - the text of the current document.
" ) ); + + + leExecutable = new KLineEdit( w ); + lo->addWidget( leExecutable, 3, 2, 1, 2 ); + l = new QLabel( w ); + l->setBuddy( leExecutable ); + l->setText( i18n("&Executable:") ); + l->setAlignment( l->alignment() | Qt::AlignRight ); + lo->addWidget( l, 3, 1 ); + if ( tool ) leExecutable->setText( tool->tryexec ); + leExecutable->setWhatsThis(i18n( + "The executable used by the command. This is used to check if a tool " + "should be displayed; if not set, the first word of command " + "will be used.") ); + + leMimetypes = new KLineEdit( w ); + lo->addWidget( leMimetypes, 4, 2 ); + l = new QLabel( w ); + l->setBuddy( leMimetypes ); + l->setText( i18n("&Mime types:") ); + l->setAlignment( l->alignment() | Qt::AlignRight ); + lo->addWidget( l, 4, 1 ); + if ( tool ) leMimetypes->setText( tool->mimetypes.join("; ") ); + leMimetypes->setWhatsThis(i18n( + "A semicolon-separated list of mime types for which this tool should " + "be available; if this is left empty, the tool is always available. " + "To choose from known mimetypes, press the button on the right.") ); + + QToolButton *btnMTW = new QToolButton(w); + lo->addWidget( btnMTW, 4, 3 ); + btnMTW->setIcon(QIcon(SmallIcon("wizard"))); + connect(btnMTW, SIGNAL(clicked()), this, SLOT(showMTDlg())); + btnMTW->setWhatsThis(i18n( + "Click for a dialog that can help you create a list of mimetypes.") ); + + cmbSave = new KComboBox(w); + lo->addWidget( cmbSave, 5, 2, 1, 2 ); + l = new QLabel( w ); + l->setBuddy( cmbSave ); + l->setText( i18n("&Save:") ); + l->setAlignment( l->alignment() | Qt::AlignRight ); + lo->addWidget( l, 5, 1 ); + QStringList sl; + sl << i18n("None") << i18n("Current Document") << i18n("All Documents"); + cmbSave->addItems( sl ); + if ( tool ) cmbSave->setCurrentIndex( tool->save ); + cmbSave->setWhatsThis(i18n( + "You can choose to save the current or all [modified] documents prior to " + "running the command. This is helpful if you want to pass URLs to " + "an application like, for example, an FTP client.") ); + + + leCmdLine = new KLineEdit( w ); + lo->addWidget( leCmdLine, 6, 2, 1, 2 ); + l = new QLabel( i18n("&Command line name:"), w ); + l->setBuddy( leCmdLine ); + l->setAlignment( l->alignment() | Qt::AlignRight ); + lo->addWidget( l, 6, 1 ); + if ( tool ) leCmdLine->setText( tool->cmdname ); + leCmdLine->setWhatsThis(i18n( + "If you specify a name here, you can invoke the command from the view " + "command line with exttool-the_name_you_specified_here. " + "Please do not use spaces or tabs in the name.")); +} + +void KateExternalToolServiceEditor::slotButtonClicked(int button) +{ + switch( button ) { + case Ok: + if ( leName->text().isEmpty() || teCommand->document()->isEmpty() ) + { + KMessageBox::information( this, i18n("You must specify at least a name and a command") ); + return; + } + accept(); + break; + case Cancel: + reject(); + break; + } + +} + +void KateExternalToolServiceEditor::showMTDlg() +{ + QString text = i18n("Select the MimeTypes for which to enable this tool."); + QStringList list = leMimetypes->text().split( QRegExp("\\s*;\\s*"), QString::SkipEmptyParts ); + KMimeTypeChooserDialog d( i18n("Select Mime Types"), text, list, "text", this ); + if ( d.exec() == KDialog::Accepted ) + { + leMimetypes->setText( d.chooser()->mimeTypes().join(";") ); + } +} +//END KateExternalToolServiceEditor + +//BEGIN KateExternalToolsConfigWidget +KateExternalToolsConfigWidget::KateExternalToolsConfigWidget( QWidget *parent, KateExternalToolsPlugin *plugin, const char* name) + : Kate::PluginConfigPage( parent, name ) + , m_changed( false ) + , m_plugin(plugin) +{ + setupUi(this); + + btnMoveUp->setIcon(KIcon("go-up")); + btnMoveDown->setIcon(KIcon("go-down")); + + connect( lbTools, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged()) ); + connect( lbTools, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotEdit()) ); + connect( btnNew, SIGNAL(clicked()), this, SLOT(slotNew()) ); + connect( btnRemove, SIGNAL(clicked()), this, SLOT(slotRemove()) ); + connect( btnEdit, SIGNAL(clicked()), this, SLOT(slotEdit()) ); + connect( btnSeparator, SIGNAL(clicked()), this, SLOT(slotInsertSeparator()) ); + connect( btnMoveUp, SIGNAL(clicked()), this, SLOT(slotMoveUp()) ); + connect( btnMoveDown, SIGNAL(clicked()), this, SLOT(slotMoveDown()) ); + + config = new KConfig( "externaltools", KConfig::NoGlobals, "appdata" ); + reset(); + slotSelectionChanged(); +} + +KateExternalToolsConfigWidget::~KateExternalToolsConfigWidget() +{ + delete config; +} + +void KateExternalToolsConfigWidget::reset() +{ + //m_tools.clear(); + lbTools->clear(); + + // load the files from a KConfig + const QStringList tools = config->group("Global").readEntry("tools", QStringList()); + + for( QStringList::const_iterator it = tools.begin(); it != tools.end(); ++it ) + { + if ( *it == "---" ) + { + new QListWidgetItem( "---" , lbTools); + } + else + { + KConfigGroup cg( config, *it ); + + KateExternalTool *t = new KateExternalTool( + cg.readEntry( "name", "" ), + cg.readEntry( "command", ""), + cg.readEntry( "icon", ""), + cg.readEntry( "executable", ""), + cg.readEntry( "mimetypes", QStringList() ), + cg.readEntry( "acname" ), + cg.readEntry( "cmdname"), + cg.readEntry( "save", 0 ) ); + + if ( t->hasexec ) // we only show tools that are also in the menu. + new ToolItem( lbTools, t->icon.isEmpty() ? blankIcon() : SmallIcon( t->icon ), t ); + else + delete t; + } + } + m_changed = false; +} + +QPixmap KateExternalToolsConfigWidget::blankIcon() +{ + QPixmap pm( KIconLoader::SizeSmall, KIconLoader::SizeSmall ); + pm.fill(); + pm.setMask( pm.createHeuristicMask() ); + return pm; +} + +void KateExternalToolsConfigWidget::apply() +{ + if ( ! m_changed ) + return; + m_changed = false; + + // save a new list + // save each item + QStringList tools; + for ( int i = 0; i < lbTools->count(); i++ ) + { + if ( lbTools->item(i)->text() == "---" ) + { + tools << "---"; + continue; + } + KateExternalTool *t = static_cast(lbTools->item( i ))->tool; +// kDebug(13001)<<"adding tool: "<name; + tools << t->acname; + + KConfigGroup cg( config, t->acname ); + + cg.writeEntry( "name", t->name ); + cg.writeEntry( "command", t->command ); + cg.writeEntry( "icon", t->icon ); + cg.writeEntry( "executable", t->tryexec ); + cg.writeEntry( "mimetypes", t->mimetypes ); + cg.writeEntry( "acname", t->acname ); + cg.writeEntry( "cmdname", t->cmdname ); + cg.writeEntry( "save", t->save ); + } + + config->group("Global").writeEntry( "tools", tools ); + + // if any tools was removed, try to delete their groups, and + // add the group names to the list of removed items. + if ( m_removed.count() ) + { + for ( QStringList::iterator it = m_removed.begin(); it != m_removed.end(); ++it ) + { + if ( config->hasGroup( *it ) ) + config->deleteGroup( *it ); + } + QStringList removed = config->group("Global").readEntry( "removed", QStringList() ); + removed += m_removed; + + // clean up the list of removed items, so that it does not contain + // non-existing groups (we can't remove groups from a non-owned global file). + config->sync(); + QStringList::iterator it1 = removed.begin(); + while ( it1 != removed.end() ) + { + if ( ! config->hasGroup( *it1 ) ) + it1 = removed.erase( it1 ); + else + ++it1; + } + config->group("Global").writeEntry( "removed", removed ); + } + + config->sync(); + m_plugin->reload(); +} + +void KateExternalToolsConfigWidget::slotSelectionChanged() +{ + // update button state + bool hs = lbTools->currentItem() != 0; + btnEdit->setEnabled( hs && dynamic_cast(lbTools->currentItem()) ); + btnRemove->setEnabled( hs ); + btnMoveUp->setEnabled( ( lbTools->currentRow() > 0 ) && hs ); + btnMoveDown->setEnabled( ( lbTools->currentRow() < (int)lbTools->count() - 1 ) && hs ); +} + +void KateExternalToolsConfigWidget::slotNew() +{ + // display a editor, and if it is OK'd, create a new tool and + // create a listbox item for it + KateExternalToolServiceEditor editor( 0, this ); + + if ( editor.exec() ) + { + KateExternalTool *t = new KateExternalTool( + editor.leName->text(), + editor.teCommand->toPlainText(), + editor.btnIcon->icon(), + editor.leExecutable->text(), + editor.leMimetypes->text().split( QRegExp("\\s*;\\s*"), QString::SkipEmptyParts ) ); + + // This is sticky, it does not change again, so that shortcuts sticks + // TODO check for dups + t->acname = "externaltool_" + QString(t->name).remove( QRegExp("\\W+") ); + + new ToolItem ( lbTools, t->icon.isEmpty() ? blankIcon() : SmallIcon( t->icon ), t ); + + emit changed(); + m_changed = true; + } +} + +void KateExternalToolsConfigWidget::slotRemove() +{ + // add the tool action name to a list of removed items, + // remove the current listbox item + if ( lbTools->currentRow() > -1 ) + { + ToolItem *i = dynamic_cast(lbTools->currentItem()); + if ( i ) + m_removed << i->tool->acname; + + delete lbTools->takeItem( lbTools->currentRow() ); + emit changed(); + m_changed = true; + } +} + +void KateExternalToolsConfigWidget::slotEdit() +{ + if( !dynamic_cast(lbTools->currentItem()) ) return; + // show the item in an editor + KateExternalTool *t = static_cast(lbTools->currentItem())->tool; + KateExternalToolServiceEditor editor( t, this); + editor.resize( config->group("Editor").readEntry( "Size", QSize() ) ); + if ( editor.exec() /*== KDialog::Ok*/ ) + { + + bool elementChanged = ( ( editor.btnIcon->icon() != t->icon ) || (editor.leName->text() != t->name ) ) ; + + t->name = editor.leName->text(); + t->cmdname = editor.leCmdLine->text(); + t->command = editor.teCommand->toPlainText(); + t->icon = editor.btnIcon->icon(); + t->tryexec = editor.leExecutable->text(); + t->mimetypes = editor.leMimetypes->text().split( QRegExp("\\s*;\\s*"), QString::SkipEmptyParts ); + t->save = editor.cmbSave->currentIndex(); + + //if the icon has changed or name changed, I have to renew the listbox item :S + if ( elementChanged ) + { + int idx = lbTools->row( lbTools->currentItem() ); + delete lbTools->takeItem( idx ); + lbTools->insertItem( idx , new ToolItem( 0, t->icon.isEmpty() ? blankIcon() : SmallIcon( t->icon ), t )); + } + + emit changed(); + m_changed = true; + } + + config->group("Editor").writeEntry( "Size", editor.size() ); + config->sync(); +} + +void KateExternalToolsConfigWidget::slotInsertSeparator() +{ + lbTools->insertItem( lbTools->currentRow() + 1 , "---" ); + emit changed(); + m_changed = true; +} + +void KateExternalToolsConfigWidget::slotMoveUp() +{ + // move the current item in the listbox upwards if possible + QListWidgetItem *item = lbTools->currentItem(); + if ( ! item ) return; + + int idx = lbTools->row( item ); + + if ( idx < 1 ) return; + + if ( dynamic_cast(item) ) + { + KateExternalTool *tool = static_cast(item)->tool; + delete lbTools->takeItem( idx ); + lbTools->insertItem( idx - 1 , new ToolItem( 0, tool->icon.isEmpty() ? blankIcon() : SmallIcon( tool->icon ), tool ) ); + } + else // a separator! + { + delete lbTools->takeItem( idx ); + lbTools->insertItem( idx - 1 , new QListWidgetItem( "---" )); + } + + lbTools->setCurrentRow( idx - 1 ); + slotSelectionChanged(); + emit changed(); + m_changed = true; +} + +void KateExternalToolsConfigWidget::slotMoveDown() +{ + // move the current item in the listbox downwards if possible + QListWidgetItem *item = lbTools->currentItem(); + if ( ! item ) return; + + int idx = lbTools->row( item ); + + if ( idx > lbTools->count() - 1 ) return; + + if ( dynamic_cast(item) ) + { + KateExternalTool *tool = static_cast(item)->tool; + delete lbTools->takeItem( idx ); + lbTools->insertItem( idx + 1 , new ToolItem( 0, tool->icon.isEmpty() ? blankIcon() : SmallIcon( tool->icon ), tool ) ); + } + else // a separator! + { + delete lbTools->takeItem( idx ); + lbTools->insertItem( idx + 1 , new QListWidgetItem( "---" ) ); + } + + lbTools->setCurrentRow( idx + 1 ); + slotSelectionChanged(); + emit changed(); + m_changed = true; +} +//END KateExternalToolsConfigWidget +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/addons/externaltools/kateexternaltoolsplugin.h b/addons/externaltools/kateexternaltoolsplugin.h new file mode 100644 --- /dev/null +++ b/addons/externaltools/kateexternaltoolsplugin.h @@ -0,0 +1,89 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2002 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_EXTERNALTOOLS_H__ +#define __KATE_EXTERNALTOOLS_H__ + +#include +#include +#include + +#include + +#include "kateexternaltools.h" + +class KateExternalToolsPluginView; + +class KateExternalToolsPlugin + : public Kate::Plugin + , public Kate::PluginConfigPageInterface +{ + Q_OBJECT + Q_INTERFACES(Kate::PluginConfigPageInterface) + + public: + explicit KateExternalToolsPlugin( QObject* parent = 0, const QList& = QList() ); + virtual ~KateExternalToolsPlugin(); + + + void reload(); + + Kate::PluginView *createView (Kate::MainWindow *mainWindow); + KateExternalToolsPluginView *extView(QWidget *widget); + private: + QList m_views; + KateExternalToolsCommand *m_command; + private Q_SLOT: + void viewDestroyed(QObject *view); + // + // ConfigInterface + // + public: + virtual uint configPages() const; + virtual Kate::PluginConfigPage *configPage (uint number = 0, QWidget *parent = 0, const char *name = 0 ); + virtual QString configPageName (uint number = 0) const; + virtual QString configPageFullName (uint number = 0) const; + virtual KIcon configPageIcon (uint number = 0) const; + +}; + +class KateExternalToolsPluginView : public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + + public: + /** + * Constructor. + */ + KateExternalToolsPluginView (Kate::MainWindow *mainWindow); + + /** + * Virtual destructor. + */ + ~KateExternalToolsPluginView (); + + void rebuildMenu(); + + KateExternalToolsMenuAction *externalTools; +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/addons/externaltools/kateexternaltoolsplugin.cpp b/addons/externaltools/kateexternaltoolsplugin.cpp new file mode 100644 --- /dev/null +++ b/addons/externaltools/kateexternaltoolsplugin.cpp @@ -0,0 +1,186 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2002 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateexternaltoolsplugin.h" +#include "kateexternaltoolsplugin.moc" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +K_PLUGIN_FACTORY(KateExternalToolsFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateExternalToolsFactory(KAboutData("kateexternaltools","kateexternaltoolsplugin",ki18n("External Tools"), "0.1", ki18n("Run external tools"), KAboutData::License_LGPL)) ) + + +KateExternalToolsPlugin::KateExternalToolsPlugin( QObject* parent, const QList& ): + Kate::Plugin ( (Kate::Application*)parent ),m_command(0) +{ + if (KAuthorized::authorizeKAction("shell_access")) + { + KTextEditor::CommandInterface* cmdIface = + qobject_cast( Kate::application()->editor() ); + if( cmdIface ) { + m_command=new KateExternalToolsCommand(this); + cmdIface->registerCommand( m_command ); + } + } +} + +KateExternalToolsPlugin::~KateExternalToolsPlugin() +{ + if (KAuthorized::authorizeKAction("shell_access")) + { + if (m_command) { + KTextEditor::CommandInterface* cmdIface = + qobject_cast( Kate::application()->editor() ); + if( cmdIface ) + cmdIface->unregisterCommand( m_command ); + delete m_command; + } + } +} + +Kate::PluginView *KateExternalToolsPlugin::createView (Kate::MainWindow *mainWindow) +{ + KateExternalToolsPluginView *view= new KateExternalToolsPluginView (mainWindow); + connect(view,SIGNAL(destroyed(QObject*)),this,SLOT(viewDestroyed(QObject*))); + m_views.append(view); + return view; +} + +KateExternalToolsPluginView *KateExternalToolsPlugin::extView(QWidget *widget) +{ + foreach (KateExternalToolsPluginView* view, m_views) + { + if (view->mainWindow()->window()==widget) return view; + } + return 0; +} + +void KateExternalToolsPlugin::viewDestroyed(QObject *view) +{ + m_views.removeAll(dynamic_cast(view)); +} + +void KateExternalToolsPlugin::reload() { + if (KAuthorized::authorizeKAction("shell_access")) + { + KTextEditor::CommandInterface* cmdIface = + qobject_cast( Kate::application()->editor() ); + if (cmdIface) + if(m_command) m_command->reload(); + } + foreach(KateExternalToolsPluginView* view,m_views) { + view->rebuildMenu(); + } +} + +uint KateExternalToolsPlugin::configPages() const +{ + return 1; +} + +Kate::PluginConfigPage *KateExternalToolsPlugin::configPage (uint number, QWidget *parent, const char *name ) +{ + if (number == 0) { + return new KateExternalToolsConfigWidget(parent, this, name); + } + return 0; +} + +QString KateExternalToolsPlugin::configPageName (uint number) const +{ + if (number == 0) { + return i18n("External Tools"); + } + return QString(); +} + +QString KateExternalToolsPlugin::configPageFullName (uint number) const +{ + if (number == 0) { + return i18n("External Tools"); + } + return QString(); +} + +KIcon KateExternalToolsPlugin::configPageIcon (uint number) const +{ + if (number == 0) { + return KIcon(); + } + return KIcon(); +} + + +KateExternalToolsPluginView::KateExternalToolsPluginView (Kate::MainWindow *mainWindow) + : Kate::PluginView (mainWindow), Kate::XMLGUIClient(KateExternalToolsFactory::componentData()) +{ + externalTools = 0; + + if (KAuthorized::authorizeKAction("shell_access")) + { + externalTools = new KateExternalToolsMenuAction( i18n("External Tools"), actionCollection(), mainWindow, mainWindow ); + actionCollection()->addAction("tools_external", externalTools); + externalTools->setWhatsThis( i18n("Launch external helper applications") ); + + } + + mainWindow->guiFactory()->addClient (this); +} + +void KateExternalToolsPluginView::rebuildMenu() { + kDebug(13001); + if (externalTools) { + KXMLGUIFactory *f=factory(); + f->removeClient(this); + reloadXML(); + externalTools->reload(); + kDebug(13001)<<"has just returned from externalTools->reload()"; + f->addClient(this); + } +} + +KateExternalToolsPluginView::~KateExternalToolsPluginView () +{ + mainWindow()->guiFactory()->removeClient (this); + + delete externalTools; + externalTools = 0; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/addons/externaltools/kateexternaltoolsplugin.desktop b/addons/externaltools/kateexternaltoolsplugin.desktop new file mode 100644 --- /dev/null +++ b/addons/externaltools/kateexternaltoolsplugin.desktop @@ -0,0 +1,121 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=kateexternaltoolsplugin +X-Kate-Version=2.8 +Name=External Tools +Name[ar]=الأدوات الخارجية +Name[ast]=Ferramientes esternes +Name[be]=Знешнія інструменты +Name[bg]=Външни инструменти +Name[bs]=Spoljašnje alatke +Name[ca]=Eines externes +Name[ca@valencia]=Eines externes +Name[cs]=Externí nástroje +Name[da]=Eksterne værktøjer +Name[de]=Externe Programme +Name[el]=Εξωτερικά εργαλεία +Name[en_GB]=External Tools +Name[es]=Herramientas externas +Name[et]=Välised tööriistad +Name[eu]=Kanpoko tresnak +Name[fi]=Ulkoiset työkalut +Name[fr]=Outils externes +Name[ga]=Uirlisí Seachtracha +Name[gl]=Utilidades externas +Name[he]=כלים חיצוניים +Name[hu]=Külső eszközök +Name[ia]=Instrumentos externe +Name[it]=Strumenti esterni +Name[ja]=外部ツール +Name[kk]=Сыртқы құралдар +Name[km]=ឧបករណ៍​ខាង​ក្រៅ +Name[ko]=외부 도구 +Name[lt]=Išoriniai įrankiai +Name[lv]=Ārējie rīki +Name[mai]=बाहरी अओजार +Name[nb]=Eksterne verktøy +Name[nds]=Extern Warktüüch +Name[ne]=बाह्य उपकरण +Name[nl]=Externe hulpmiddelen +Name[nn]=Eksterne verktøy +Name[pa]=ਬਾਹਰੀ ਟੂਲ +Name[pl]=Narzędzia zewnętrzne +Name[pt]=Ferramentas Externas +Name[pt_BR]=Ferramentas externas +Name[ro]=Unelte externe +Name[ru]=Внешние инструменты +Name[si]=බාහිර මෙවලම් +Name[sk]=Externé nástroje +Name[sl]=Zunanja orodja +Name[sr]=Спољашње алатке +Name[sr@ijekavian]=Спољашње алатке +Name[sr@ijekavianlatin]=Spoljašnje alatke +Name[sr@latin]=Spoljašnje alatke +Name[sv]=Externa verktyg +Name[tg]=Асбобҳои берунӣ +Name[tr]=Dış Araçlar +Name[ug]=سىرتقى قوراللار +Name[uk]=Зовнішні засоби +Name[wa]=Divantrinnès usteyes +Name[x-test]=xxExternal Toolsxx +Name[zh_CN]=外部工具 +Name[zh_TW]=外部工具 +Comment=External Tools +Comment[ar]=الأدوات الخارجية +Comment[ast]=Ferramientes esternes +Comment[be]=Знешнія інструменты +Comment[bg]=Външни инструменти +Comment[bs]=Spoljašnje alatke +Comment[ca]=Eines externes +Comment[ca@valencia]=Eines externes +Comment[cs]=Externí nástroje +Comment[da]=Eksterne værktøjer +Comment[de]=Externe Programme +Comment[el]=Εξωτερικά εργαλεία +Comment[en_GB]=External Tools +Comment[es]=Herramientas externas +Comment[et]=Välised tööriistad +Comment[eu]=Kanpoko tresnak +Comment[fi]=Ulkoiset työkalut +Comment[fr]=Outils externes +Comment[ga]=Uirlisí Seachtracha +Comment[gl]=Utilidades externas +Comment[he]=כלים חיצוניים +Comment[hu]=Külső eszközök +Comment[ia]=Instrumentos externe +Comment[it]=Strumenti esterni +Comment[ja]=外部ツール +Comment[kk]=Сыртқы құралдар +Comment[km]=ឧបករណ៍​ខាង​ក្រៅ +Comment[ko]=외부 도구 +Comment[lt]=Išoriniai įrankiai +Comment[lv]=Ārējie rīki +Comment[mai]=बाहरी अओजार +Comment[nb]=Eksterne verktøy +Comment[nds]=Extern Warktüüch +Comment[ne]=बाह्य उपकरण +Comment[nl]=Externe hulpmiddelen +Comment[nn]=Eksterne verktøy +Comment[pa]=ਬਾਹਰੀ ਟੂਲ +Comment[pl]=Narzędzia zewnętrzne +Comment[pt]=Ferramentas Externas +Comment[pt_BR]=Ferramentas externas +Comment[ro]=Unelte externe +Comment[ru]=Внешние инструменты +Comment[si]=බාහිර මෙවලම් +Comment[sk]=Externé nástroje +Comment[sl]=Zunanja orodja +Comment[sr]=Спољашње алатке +Comment[sr@ijekavian]=Спољашње алатке +Comment[sr@ijekavianlatin]=Spoljašnje alatke +Comment[sr@latin]=Spoljašnje alatke +Comment[sv]=Externa verktyg +Comment[tg]=Асбобҳои берунӣ +Comment[tr]=Dış Araçlar +Comment[ug]=سىرتقى قوراللار +Comment[uk]=Зовнішні інструменти +Comment[wa]=Divantrinnès usteyes +Comment[x-test]=xxExternal Toolsxx +Comment[zh_CN]=调用外部工具 +Comment[zh_TW]=外部工具 diff --git a/addons/externaltools/plugin.qrc b/addons/externaltools/plugin.qrc new file mode 100644 --- /dev/null +++ b/addons/externaltools/plugin.qrc @@ -0,0 +1,6 @@ + + + + ui.rc + + diff --git a/addons/externaltools/ui.rc b/addons/externaltools/ui.rc new file mode 100644 --- /dev/null +++ b/addons/externaltools/ui.rc @@ -0,0 +1,11 @@ + + + + &File + + + &Tools + + + +