diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 84da08d7b7fcda12c85caeb5a10b4903770a6f69 (patch) | |
tree | 2a6aea76f2dfffb4cc04bb907c4725af94f70e72 /kate/filetemplates/plugin/filetemplates.cpp | |
download | tdeaddons-84da08d7b7fcda12c85caeb5a10b4903770a6f69.tar.gz tdeaddons-84da08d7b7fcda12c85caeb5a10b4903770a6f69.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeaddons@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kate/filetemplates/plugin/filetemplates.cpp')
-rw-r--r-- | kate/filetemplates/plugin/filetemplates.cpp | 1254 |
1 files changed, 1254 insertions, 0 deletions
diff --git a/kate/filetemplates/plugin/filetemplates.cpp b/kate/filetemplates/plugin/filetemplates.cpp new file mode 100644 index 0000000..e1eb847 --- /dev/null +++ b/kate/filetemplates/plugin/filetemplates.cpp @@ -0,0 +1,1254 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) 2004, Anders Lund <anders@alweb.dk> + */ + +//BEGIN Includes +#include "filetemplates.h" + +#include <kate/document.h> + +#include <kaboutdata.h> +#include <kaction.h> +#include <kactioncollection.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kdialogbase.h> +#include <kdirwatch.h> +#include <kfiledialog.h> +#include <kglobal.h> +#include <kicondialog.h> +#include <kiconloader.h> +#include <kio/netaccess.h> +#include <klistview.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <knewstuff/knewstuff.h> +#include <knewstuff/entry.h> +#include <kprocess.h> +#include <kstandarddirs.h> +#include <kstandarddirs.h> +#include <ktempfile.h> +#include <kurlrequester.h> +#include <kuser.h> +#include <kxmlguifactory.h> + +#include <qbuttongroup.h> +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qcursor.h> +#include <qdatetime.h> +#include <qdict.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qlineedit.h> +#include <qpopupmenu.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qregexp.h> +#include <qstyle.h> +#include <qwhatsthis.h> + +#include <stdlib.h> + +#include <kdebug.h> +#include <ktexteditor/templateinterface.h> +//END Includes + +//BEGIN plugin + factory stuff +class PluginView : public KXMLGUIClient +{ + friend class KateFileTemplates; + + public: + Kate::MainWindow *win; +}; + +extern "C" +{ + void* init_katefiletemplates() + { + KGlobal::locale()->insertCatalogue("katefiletemplates"); + return new KatePluginFactory; + } +} + +KatePluginFactory::KatePluginFactory() +{ + s_instance = new KInstance( "kate" ); +} + +KatePluginFactory::~KatePluginFactory() +{ + delete s_instance; +} + +QObject* KatePluginFactory::createObject( QObject* parent, const char* name, const char*, const QStringList & ) +{ + return new KateFileTemplates( parent, name ); +} + +KInstance* KatePluginFactory::s_instance = 0L; +//END + +//BEGIN TemplateInfo +class TemplateInfo +{ + public: + TemplateInfo( const QString& fn, const QString &t, const QString &g ) + : filename( fn ), tmplate ( t ), group( g ) { ; } + ~TemplateInfo() { ; } + + QString filename; + QString tmplate; + QString group; + QString description; + QString author; + QString highlight; + QString icon; +}; +//END TemplateInfo + +//BEGIN KateFileTemplates +KateFileTemplates::KateFileTemplates( QObject* parent, const char* name ) + : Kate::Plugin ( (Kate::Application*)parent, name ), + m_actionCollection( new KActionCollection( this, "template_actions", new KInstance("kate") ) ) +{ + // create actions, so that they are shared. + // We plug them into each view's menus, and update them centrally, so that + // new plugins can automatically become visible in all windows. + (void) new KAction ( i18n("Any File..."), 0, this, + SLOT( slotAny() ), m_actionCollection, + "file_template_any" ); + // recent templates + m_acRecentTemplates = new KRecentFilesAction( i18n("&Use Recent"), 0, this, + SLOT(slotOpenTemplate(const KURL &)), + m_actionCollection, + "file_templates_recent" ); + m_acRecentTemplates->loadEntries( kapp->config(), "Recent Templates" ); + + // template menu + m_dw = new KDirWatch( this, "template_dirwatch" ); + QStringList dirs = KGlobal::dirs()->findDirs("data", "kate/plugins/katefiletemplates/templates"); + for ( QStringList::Iterator it = dirs.begin(); it != dirs.end(); ++it ) + { + m_dw->addDir( *it, true ); + } + + connect( m_dw, SIGNAL(dirty(const QString&)), + this, SLOT(updateTemplateDirs(const QString&)) ); + connect( m_dw, SIGNAL(created(const QString&)), + this, SLOT(updateTemplateDirs(const QString&)) ); + connect( m_dw, SIGNAL(deleted(const QString&)), + this, SLOT(updateTemplateDirs(const QString&)) ); + + m_templates.setAutoDelete( true ); + updateTemplateDirs(); + + m_user = 0; + m_emailstuff = 0; +} + +/** + * Called whenever the template dir is changed. Recreates the templates list. + */ +void KateFileTemplates::updateTemplateDirs(const QString &d) +{ + kdDebug()<<"updateTemplateDirs called with arg "<<d<<endl; + + QStringList templates = KGlobal::dirs()->findAllResources( + "data","kate/plugins/katefiletemplates/templates/*.katetemplate", + false,true); + + m_templates.clear(); + + QRegExp re( "\\b(\\w+)\\s*=\\s*(.+)(?:\\s+\\w+=|$)" ); + re.setMinimal( true ); + + KConfig *config = kapp->config(); + QStringList hidden; + config->readListEntry( "Hidden", hidden, ';' ); + + for ( QStringList::Iterator it=templates.begin(); it != templates.end(); ++it ) + { + QFile _f( *it ); + if ( _f.open( IO_ReadOnly ) ) + { + QString fname = (*it).section( '/', -1 ); + + // skip if hidden + if ( hidden.contains( fname ) ) + continue; + + // Read the first line of the file, to get the group/name + TemplateInfo *tmp = new TemplateInfo( *it, fname, "Other" ); + bool trymore ( true ); + QTextStream stream(&_f); + while ( trymore ) + { + QString _line = stream.readLine(); + trymore = _line.startsWith( "katetemplate:" ); + if ( ! trymore ) break; + + int pos ( 0 ); + while ( ( ( pos = re.search( _line, pos ) ) >= 0 ) ) + { + pos += re.cap( 1 ).length(); + if ( re.cap( 1 ).lower() == "template" ) + tmp->tmplate = re.cap( 2 ); + if ( re.cap( 1 ).lower() == "group" ) + tmp->group = re.cap( 2 ); + if ( re.cap( 1 ).lower() == "description" ) + tmp->description = re.cap( 2 ); + if ( re.cap( 1 ).lower() == "author" ) + tmp->author = re.cap( 2 ); + if ( re.cap( 1 ).lower() == "highlight" ) + tmp->highlight = re.cap( 2 ); + if ( re.cap( 1 ) == "icon" ) + tmp->icon = re.cap( 2 ); + } + } + + m_templates.append( tmp ); + _f.close(); + } + } + + // update the menus of all views + for (uint z=0; z < m_views.count(); z++) + { + PluginView *view = m_views.at(z); + refreshMenu( view ); + } +} + +KateFileTemplates::~KateFileTemplates() +{ + m_acRecentTemplates->saveEntries( kapp->config(), "Recent Templates" ); + delete m_emailstuff; + delete m_user; +} + +void KateFileTemplates::addView(Kate::MainWindow *win) +{ + PluginView *view = new PluginView (); + + (void) new KAction( i18n("&Manage Templates..."), 0, + this, SLOT(slotEditTemplate()), + view->actionCollection(), "settings_manage_templates" ); + + (void)new KActionMenu( i18n("New From &Template"), "make", + view->actionCollection(), "file_new_fromtemplate" ); + refreshMenu( view ); + + view->setInstance (new KInstance("kate")); + view->setXMLFile("plugins/katefiletemplates/ui.rc"); + win->guiFactory()->addClient (view); + view->win = win; + + m_views.append (view); +} + +void KateFileTemplates::removeView(Kate::MainWindow *win) +{ + for (uint z=0; z < m_views.count(); z++) + if (m_views.at(z)->win == win) + { + PluginView *view = m_views.at(z); + m_views.remove (view); + win->guiFactory()->removeClient (view); + delete view; + } +} + +QStringList KateFileTemplates::groups() +{ + QStringList l; + QString s; + + for ( uint i = 0; i < m_templates.count(); i++ ) + { + s = m_templates.at( i )->group; + if ( ! l.contains( s ) ) + l.append( s ); + } + + return l; +} + +void KateFileTemplates::refreshMenu( PluginView *v ) +{ + QPopupMenu *m = (QPopupMenu*)(((KActionMenu*)(v->actionCollection()->action("file_new_fromtemplate")))->popupMenu()); + + // clear the menu for templates + m->clear(); + + // restore it + m_actionCollection->action( "file_template_any" )->plug( m ); + m_acRecentTemplates->plug( m ); + m->insertSeparator(); + + QDict<QPopupMenu> submenus; // ### QMAP + for ( uint i = 0; i < m_templates.count(); i++ ) + { + if ( ! submenus[ m_templates.at( i )->group ] ) + { + QPopupMenu *sm = new QPopupMenu(); + submenus.insert( m_templates.at( i )->group, sm ); + m->insertItem( m_templates.at( i )->group, sm ); + } + kdDebug()<<"=== ICON: '"<<m_templates.at( i )->icon<<"'"<<endl; + if ( ! m_templates.at( i )->icon.isEmpty() ) + submenus[m_templates.at( i )->group]->insertItem( + SmallIconSet( m_templates.at( i )->icon ), + m_templates.at( i )->tmplate, this, SLOT(slotOpenTemplate( int )), 0, i ); + else + submenus[m_templates.at( i )->group]->insertItem( + m_templates.at( i )->tmplate, this, SLOT(slotOpenTemplate( int )), 0, i ); + + // add whatsthis containing the description and author + QString w ( m_templates.at( i )->description ); + if( ! m_templates.at( i )->author.isEmpty() ) + { + w.append( "<p>Author: " ); + w.append( m_templates.at( i )->author ); + } + if ( ! w.isEmpty() ) + w.prepend( "<p>" ); + + if ( ! w.isEmpty() ) + submenus[m_templates.at( i )->group]->findItem( i )->setWhatsThis( w ); + } +} + +/** + * Action slot: use any file as a template. + * Get a URL and pass it on. + */ +void KateFileTemplates::slotAny() +{ + if (!application()->activeMainWindow()) + return; + + // get a URL and pass that to slotOpenTemplate + QString fn = KFileDialog::getOpenFileName( + "katefiletemplate", + QString::null, + application()->activeMainWindow()->viewManager()->activeView(), + i18n("Open as Template") ); + if ( ! fn.isEmpty() ) + slotOpenTemplate( KURL( fn ) ); +} + +/** + * converts template [index] to a URL and passes that + */ +void KateFileTemplates::slotOpenTemplate( int index ) +{ + kdDebug()<<"slotOpenTemplate( "<<index<<" )"<<endl; + if ( index < 0 || (uint)index > m_templates.count() ) return; + slotOpenTemplate( KURL( m_templates.at( index )->filename ) ); +} + +void KateFileTemplates::slotOpenTemplate( const KURL &url ) +{ + // check if the file can be opened + QString tmpfile; + QString filename = url.fileName(); + kdDebug()<<"file: "<<filename<<endl; + if ( KIO::NetAccess::download( url, tmpfile, 0L ) ) + { + bool isTemplate ( filename.endsWith( ".katetemplate" ) ); + QString docname; + + // open the file and parse for template variables and macros + QFile file(tmpfile); + if ( ! file.open( IO_ReadOnly ) ) + { + KMessageBox::sorry( application()->activeMainWindow()->viewManager()->activeView(), + i18n("<qt>Error opening the file<br><strong>%1</strong><br>for reading. The document will not be created.</qt>").arg(filename), + i18n("Template Plugin"), 0 ); + KIO::NetAccess::removeTempFile( tmpfile ); + return; + } + + // this may take a moment.. + kapp->setOverrideCursor( QCursor(QCursor::WaitCursor) ); + + // create a new document + application()->activeMainWindow()->viewManager()->openURL( KURL() ); + Kate::View *view = application()->activeMainWindow()->viewManager()->activeView(); + Kate::Document *doc = view->getDoc(); + + + QTextStream stream(&file); + QString str, tmp; + uint numlines = 0; + uint doneheader = 0; + while ( !stream.eof() ) { + tmp = stream.readLine(); + if ( ! numlines && isTemplate && tmp.startsWith( "katetemplate:" ) ) + { + // look for document name, highlight + if ( ! (doneheader & 1) ) + { + QRegExp reName( "\\bdocumentname\\s*=\\s*(.+)(?:\\s+\\w+\\s*=|$)", false ); + reName.setMinimal( true ); + if ( reName.search( tmp ) > -1 ) + { + docname = reName.cap( 1 ); + docname = docname.replace( "%N", "%1" ); + doneheader |= 1; + } + } + + if ( ! (doneheader & 2) ) + { + QRegExp reHl( "\\bhighlight\\s*=\\s*(.+)(?:\\s+\\w+\\s*=|$)", false ); + reHl.setMinimal( true ); + kdDebug()<<"looking for a hl mode"<<endl; + if ( reHl.search( tmp ) > -1 ) + { + kdDebug()<<"looking for a hl mode -- "<<reHl.cap()<<endl; + // this is overly complex, too bad the interface is + // not providing a resonable method.. + QString hlmode = reHl.cap( 1 ); + uint _i = 0; + while ( _i < doc->hlModeCount() ) + { + if ( doc->hlModeName( _i ) == hlmode ) + { + doc->setHlMode( _i ); + break; + } + _i++; + } + + doneheader |= 2; + } + } + + continue; // skip this line + } + if ( numlines ) + str += "\n"; + str += tmp; + numlines++; + } + file.close(); + KIO::NetAccess::removeTempFile( tmpfile ); + + uint line, col; + line = col = 0; + + if ( ! isTemplate ) + { + int d = filename.findRev('.'); + docname = i18n("Untitled %1"); + if ( d > 0 ) docname += filename.mid( d ); + } else if ( docname.isEmpty() ) + docname = filename.left( filename.length() - 13 ); + + // check for other documents matching this naming scheme, + // and do a count before chosing a name for this one + QString p = docname; + p.replace( "%1", "\\d+" ); + p.replace( ".", "\\." ); + p.prepend( "^" ); + p.append( "$" ); + QRegExp reName( p ); + + int count = 1; + for ( uint i=0; i < application()->documentManager()->documents(); i++ ) + if ( ( reName.search ( application()->documentManager()->document( i )->docName() ) > -1 ) ) + count++; + + if ( docname.contains( "%1" ) ) + docname = docname.arg( count ); + + doc->setDocName( docname ); + + doc->setModified( false ); + + kapp->restoreOverrideCursor(); + m_acRecentTemplates->addURL( url ); + + // clean up + delete m_user; + m_user = 0; + delete m_emailstuff; + m_emailstuff = 0; + if (isTemplate) { + KTextEditor::TemplateInterface *ti=KTextEditor::templateInterface(doc); + ti->insertTemplateText(0,0,str,QMap<QString,QString>()); + } else { + doc->insertText( 0, 0, str ); + view->setCursorPosition( line, col ); + } + } +} + + +QWidget *KateFileTemplates::parentWindow() +{ + return dynamic_cast<QWidget*>(application()->activeMainWindow()); +} + +// The next part are tools to aid the creation and editing of templates +// ///////////////////////////////////////////////////////////////////// +// Steps to produce a template +// * Choose a file to start from (optional) +// * Ask for a location to store the file -- suggesting either the file +// directory, or the local template directory. +// Set the URL +// * Get the template properties -- provide a dialog, which has filled in what +// we already know -- the author name, list of known groups +// +// Combine those data into the editor, and tell the user to position the cursor +// and edit the file as she wants to... +void KateFileTemplates::slotCreateTemplate() +{ + KateTemplateWizard w( parentWindow(), this ); + w.exec(); + + updateTemplateDirs(); +} + +// Tools for editing the existing templates +// Editing a template: +// * Select the template to edit +// * Open the template +// * Set the URL to a writable one if required +void KateFileTemplates::slotEditTemplate() +{ + KDialogBase dlg( parentWindow(), "templatemanager", false, i18n("Manage File Templates"), KDialogBase::Close); + dlg.setMainWidget( new KateTemplateManager( this, &dlg ) ); + dlg.exec(); +} +//END KateFileTemplates + +//BEGIN KateTemplateInfoWidget +// This widget can be used to change the data of a TemplateInfo object +KateTemplateInfoWidget::KateTemplateInfoWidget( QWidget *parent, TemplateInfo *info, KateFileTemplates *kft ) + : QWidget( parent ), + info( info ), + kft( kft ) +{ + QGridLayout *lo = new QGridLayout( this, 6, 2 ); + lo->setAutoAdd( true ); + lo->setSpacing( KDialogBase::spacingHint() ); + + QLabel *l = new QLabel( i18n("&Template:"), this ); + QHBox *hb = new QHBox( this ); + hb->setSpacing( KDialogBase::spacingHint() ); + leTemplate = new QLineEdit( hb ); + l->setBuddy( leTemplate ); + QWhatsThis::add( leTemplate, i18n("<p>This string is used as the template's name " + "and is displayed, for example, in the Template menu. It should describe the " + "meaning of the template, for example 'HTML Document'.</p>") ); + ibIcon = new KIconButton( hb ); + QWhatsThis::add( ibIcon, i18n( + "Press to select or change the icon for this template") ); + + l = new QLabel( i18n("&Group:"), this ); + cmbGroup = new QComboBox( true, this ); + cmbGroup->insertStringList( kft->groups() ); + l->setBuddy( cmbGroup ); + QWhatsThis::add( cmbGroup, i18n("<p>The group is used for chosing a " + "submenu for the plugin. If it is empty, 'Other' is used.</p>" + "<p>You can type any string to add a new group to your menu.</p>") ); + + l = new QLabel( i18n("Document &name:"), this ); + leDocumentName = new QLineEdit( this ); + l->setBuddy( leDocumentName ); + QWhatsThis::add( leDocumentName, i18n("<p>This string will be used to set a name " + "for the new document, to display in the title bar and file list.</p>" + "<p>If the string contains '%N', that will be replaced with a number " + "increasing with each similarly named file.</p><p> For example, if the " + "Document Name is 'New shellscript (%N).sh', the first document will be " + "named 'New shellscript (1).sh', the second 'New shellscipt (2).sh', and " + "so on.</p>") ); + + l = new QLabel( i18n( "&Highlight:"), this ); + btnHighlight = new QPushButton( i18n("None"), this ); + l->setBuddy( btnHighlight ); + QWhatsThis::add( btnHighlight, i18n("<p>Select the highlight to use for the " + "template. If 'None' is chosen, the property will not be set.</p>") ); + + l = new QLabel( i18n("&Description:"), this ); + leDescription = new QLineEdit( this ); + l->setBuddy( leDescription ); + QWhatsThis::add( leDescription, i18n("<p>This string is used, for example, as " + "context help for this template (such as the 'whatsthis' help for the " + "menu item.)</p>") ); + + l = new QLabel( i18n("&Author:"), this ); + leAuthor = new QLineEdit( this ); + l->setBuddy( leAuthor ); + QWhatsThis::add( leAuthor, i18n("<p>You can set this if you want to share your " + "template with other users.</p>" + "<p>the recommended form is like an Email " + "address: 'Anders Lund <anders@alweb.dk>'</p>") ); + + // if we have a object ! null + if ( info ) + { + if ( ! info->icon.isEmpty() ) + ibIcon->setIcon( info->icon ); + leTemplate->setText( info->tmplate ); + cmbGroup->setCurrentText( info->group ); + leDescription->setText( info->description ); + leAuthor->setText( info->author ); + if ( ! info->highlight.isEmpty() ) + btnHighlight->setText( info->highlight ); + } + + // fill in the Hl menu + Kate::Document *doc = kft->application()->documentManager()->activeDocument(); + if ( doc ) + { + QPopupMenu *m = new QPopupMenu( btnHighlight ); + connect( m, SIGNAL( activated( int ) ), this, SLOT( slotHlSet( int ) ) ); + QDict<QPopupMenu> submenus; + for ( uint n = 0; n < doc->hlModeCount(); n++ ) + { + // create the sub menu if it does not exist + QString text( doc->hlModeSectionName( n ) ); + if ( ! text.isEmpty() ) + { + if ( ! submenus[ text ] ) + { + QPopupMenu *sm = new QPopupMenu(); + submenus.insert( text, sm ); + connect( sm, SIGNAL( activated( int ) ), this, SLOT( slotHlSet( int ) ) ); + m->insertItem( text, sm ); + } + + // create the item + submenus[ text ]->insertItem( doc->hlModeName( n ), n ); + } + else + m->insertItem( doc->hlModeName( n ), n ); + } + + btnHighlight->setPopup( m ); + } +} + +void KateTemplateInfoWidget::slotHlSet( int id ) +{ + btnHighlight->setText( + kft->application()->documentManager()->activeDocument()->hlModeName( id ) ); +} +//END KateTemplateInfoWidget + +//BEGIN KateTemplateWizard +// A simple wizard to help create a new template :-) +KateTemplateWizard::KateTemplateWizard( QWidget *parent, KateFileTemplates *kft ) + : KWizard( parent ), + kft( kft ) +{ + // Hide the help button for now + helpButton()->hide(); + + // 1) Optionally choose a file or existing template to start from + QWidget *page = new QWidget( this ); + QGridLayout *glo = new QGridLayout( page ); + //lo->setAutoAdd( true ); + glo->setSpacing( KDialogBase::spacingHint() ); + + glo->addMultiCellWidget( new QLabel( i18n("<p>If you want to base this " + "template on an existing file or template, select the appropriate option " + "below.</p>"), page ), 1, 1, 1, 2); + bgOrigin = new QButtonGroup( page ); + bgOrigin->hide(); + bgOrigin->setRadioButtonExclusive( true ); + + QRadioButton *rb = new QRadioButton( i18n("Start with an &empty document" ), page ); + bgOrigin->insert( rb, 1 ); + glo->addMultiCellWidget( rb, 2, 2, 1, 2 ); + rb->setChecked( true ); + + rb = new QRadioButton( i18n("Use an existing file:"), page ); + bgOrigin->insert( rb, 2 ); + glo->addMultiCellWidget( rb, 3, 3, 1, 2 ); + int marg = rb->style().subRect( QStyle::SR_RadioButtonIndicator, rb ).width(); + glo->addItem( new QSpacerItem( marg, 1, QSizePolicy::Fixed ), 4, 1 ); + urOrigin = new KURLRequester( page ); + glo->addWidget( urOrigin, 4, 2 ); + + rb = new QRadioButton( i18n("Use an existing template:"), page ); + bgOrigin->insert( rb, 3 ); + glo->addMultiCellWidget( rb, 5, 5, 1, 2 ); + glo->addItem( new QSpacerItem( marg, 1, QSizePolicy::Fixed ), 6, 1 ); + btnTmpl = new QPushButton( page ); + glo->addWidget( btnTmpl, 6, 2 ); + QPopupMenu *m = new QPopupMenu( btnTmpl ); + connect( m, SIGNAL( activated( int ) ), this, SLOT( slotTmplateSet( int ) ) ); + + QDict<QPopupMenu> submenus; + for ( uint i = 0; i < kft->templates().count(); i++ ) + { + if ( ! submenus[ kft->templates().at( i )->group ] ) + { + QPopupMenu *sm = new QPopupMenu(); + connect( sm, SIGNAL( activated( int ) ), this, SLOT( slotTmplateSet( int ) ) ); + submenus.insert( kft->templates().at( i )->group, sm ); + m->insertItem( kft->templates().at( i )->group, sm ); + } + + submenus[kft->templates().at( i )->group]->insertItem( + kft->templates().at( i )->tmplate, i ); + } + btnTmpl->setPopup( m ); + + connect( bgOrigin, SIGNAL(clicked(int)), this, SLOT(slotStateChanged(int)) ); + connect( urOrigin, SIGNAL(textChanged(const QString&)), this, SLOT(slotStateChanged(const QString&)) ); + + glo->addMultiCell( new QSpacerItem( 1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding ), 7, 7, 1, 2 ); + + addPage( page, i18n("Choose Template Origin") ); + kdDebug()<<"=== Adding template origin page at "<<page<<endl; + // 2) edit the template properties + kti = new KateTemplateInfoWidget( this, 0, kft ); + kdDebug()<<"=== Adding template info page at "<<kti<<endl; + addPage( kti, i18n("Edit Template Properties") ); + // get liekly values from KTE + QMap<QString, QString> map; + map[ "fullname" ] = ""; + map[ "email" ] = ""; + + KTextEditor::TemplateInterface::expandMacros( map, parent ); + QString sFullname = map["fullname"]; + QString sEmail = map["email"]; + QString _s = sFullname; + if ( ! sEmail.isEmpty() ) + _s += " <" + sEmail + ">"; + kti->leAuthor->setText( _s ); + + // 3) choose a location - either the template directory (default) or + // a custom location + page = new QWidget( this ); + glo = new QGridLayout( page, 7, 2 ); + glo->setSpacing( KDialogBase::spacingHint() ); + + glo->addMultiCellWidget( new QLabel( i18n("<p>Choose a location for the " + "template. If you store it in the template directory, it will " + "automatically be added to the template menu.</p>"), page ), 1, 1, 1, 2); + + bgLocation = new QButtonGroup( page ); + bgLocation->hide(); + bgLocation->setRadioButtonExclusive( true ); + + rb = new QRadioButton( i18n("Template directory"), page ); + bgLocation->insert( rb, 1 ); + glo->addMultiCellWidget( rb, 2, 2, 1, 2 ); + rb->setChecked( true ); + + glo->addMultiCell( new QSpacerItem( marg, 1, QSizePolicy::Fixed ), 3, 4, 1, 1 ); + leTemplateFileName = new QLineEdit( page ); + QLabel *l = new QLabel( leTemplateFileName, i18n("Template &file name:"), page ); + + glo->addWidget( l, 3, 2 ); + glo->addWidget( leTemplateFileName, 4, 2 ); + + rb = new QRadioButton( i18n("Custom location:"), page ); + bgLocation->insert( rb, 2 ); + glo->addMultiCellWidget( rb, 5, 5, 1, 2 ); + + glo->addItem( new QSpacerItem( marg, 1, QSizePolicy::Fixed ), 6, 1 ); + urLocation = new KURLRequester( page ); + glo->addWidget( urLocation, 6, 2 ); + + connect( bgLocation, SIGNAL(clicked(int)), this, SLOT(slotStateChanged(int)) ); + connect( urLocation, SIGNAL(textChanged(const QString&)), this, SLOT(slotStateChanged(const QString&)) ); + connect( leTemplateFileName, SIGNAL(textChanged(const QString &)), this, SLOT(slotStateChanged(const QString &)) ); + + glo->addMultiCell( new QSpacerItem( 1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding ), 7, 7, 1, 2 ); + + addPage( page, i18n("Choose Location") ); + kdDebug()<<"=== Adding location page at "<<page<<endl; + // 4) Should we edit the text to add some macros, replacing username etc? + // This is *only* relevant if the origin is a non-template file. + page = new QWidget( this ); + QVBoxLayout *lo = new QVBoxLayout( page ); + lo->setSpacing( KDialogBase::spacingHint() ); + + lo->addWidget( + new QLabel( i18n( "<p>You can replace certain strings in the text with " + "template macros.<p>If any of the data below is incorrect or missing, " + "edit the data in the KDE email information."), page ) ); + + cbRRealname = new QCheckBox( i18n("Replace full name '%1' with the " + "'%{fullname}' macro").arg( sFullname ), page ); + cbRRealname->setEnabled( ! sFullname.isEmpty() ); + lo->addWidget( cbRRealname ); + + cbREmail = new QCheckBox( i18n("Replace email address '%1' with the " + "'%email' macro").arg( sEmail ), page); + cbREmail->setEnabled( ! sEmail.isEmpty() ); + lo->addWidget( cbREmail ); + + lo->addStretch(); + + addPage( page, i18n("Autoreplace Macros") ); + kdDebug()<<"=== Adding autoreplace page at "<<page<<endl; + // 5) Display a summary + page = new QWidget( this ); + lo = new QVBoxLayout( page ); + lo->setSpacing( KDialogBase::spacingHint() ); + + QString s = i18n("<p>The template will now be created and saved to the chosen " + "location. To position the cursor put a caret ('^') character where you " + "want it in files created from the template.</p>"); + + lo->addWidget( new QLabel( s, page ) ); + + cbOpenTemplate = new QCheckBox( i18n("Open the template for editing"), page ); + + lo->addWidget( cbOpenTemplate ); + + lo->addStretch(); + + addPage( page, i18n("Create Template") ); + kdDebug()<<"=== Adding summary page at "<<endl; + connect( this, SIGNAL(selected(const QString&)), this, SLOT(slotStateChanged(const QString&)) ); +} + +void KateTemplateWizard::slotTmplateSet( int idx ) +{ + btnTmpl->setText( kft->templates().at( idx )->tmplate ); + selectedTemplateIdx = idx; + slotStateChanged(); +} + +/** + * When the state of any button in any setup page is changed, set the + * enabled state of the next button accordingly. + * + * Origin: + * if file is chosen, the URLRequester must have a valid URL in it + * if template is chosen, one must be selected in the menu button. + * + * Props: + * anything goes, but if the user wants to store the template in the template + * directory, she should be encouraged to fill in information. +*/ +void KateTemplateWizard::slotStateChanged() +{ + bool sane( true ); + switch ( indexOf( currentPage() ) ) + { + case 0: // origin + { + int _t = bgOrigin->selectedId(); + sane = ( _t == 1 || + ( _t == 2 && ! urOrigin->url().isEmpty() ) || + ( _t == 3 && ! btnTmpl->text().isEmpty() ) ); + setAppropriate( page(3), _t == 2 ); + } + break; + case 1: // template properties + // if origin is a existing template, let us try setting some of the properties + if ( bgOrigin->selectedId() == 3 ) + { + TemplateInfo *info = kft->templateInfo( selectedTemplateIdx ); + kti->cmbGroup->setCurrentText( info->group ); + } + break; + case 2: // location + { + // If there is a template name, and the user did not enter text into + // the template file name entry, we will construct the name from the + // template name. + int _t = bgLocation->selectedId(); + sane = ( ( _t == 1 && (! leTemplateFileName->text().isEmpty() || ! kti->leTemplate->text().isEmpty() ) ) || + ( _t == 2 && ! urLocation->url().isEmpty() ) ); + } + break; + case 4: // summary + setFinishEnabled( currentPage(), true ); + break; + default: + break; + } + nextButton()->setEnabled( sane ); +} + +/** + * This will create the new template based on the collected information. + */ +void KateTemplateWizard::accept() +{ + // TODO check that everything is kosher, so that we can get a save location + // etc. + + // check that we can combine a valid URL + KURL templateUrl; + if ( bgLocation->selectedId() == 1 ) + { + QString suggestion; + if ( ! leTemplateFileName->text().isEmpty() ) + suggestion = leTemplateFileName->text(); + else + suggestion = kti->leTemplate->text(); + + suggestion.replace(" ", ""); + + if ( ! suggestion.endsWith(".katetemplate") ) + suggestion.append(".katetemplate"); + + QString dir = KGlobal::dirs()->saveLocation( "data", "kate/plugins/katefiletemplates/templates/", true ); + + templateUrl = dir + suggestion; + + if ( QFile::exists( templateUrl.path() ) ) + { + if ( KMessageBox::warningContinueCancel( this, i18n( + "<p>The file <br><strong>'%1'</strong><br> already exists; if you " + "do not want to overwrite it, change the template file name to " + "something else.").arg( templateUrl.prettyURL() ), + i18n("File Exists"), i18n("Overwrite") ) + == KMessageBox::Cancel ) + return; + } + } + else + { + templateUrl = urLocation->url(); + } + + QWizard::accept(); + // The following must be done: + // 1) add the collected template information to the top + uint ln = 0; + QString s, str; + if ( ! kti->leTemplate->text().isEmpty() ) + s += " Template=" + kti->leTemplate->text(); + if ( ! kti->cmbGroup->currentText().isEmpty() ) + s += " Group=" + kti->cmbGroup->currentText(); + if ( ! kti->leDocumentName->text().isEmpty() ) + s += " Documentname=" + kti->leDocumentName->text(); + if ( ! kti->ibIcon->icon().isEmpty() ) + s += " Icon=" + kti->ibIcon->icon(); + if ( ! kti->btnHighlight->text().isEmpty() ) + s += " Highlight=" + kti->btnHighlight->text(); + + str = "katetemplate:" + s; + + if ( ! (s = kti->leAuthor->text()).isEmpty() ) + str += "\nkatetemplate: Author=" + s; + + if ( ! (s = kti->leDescription->text()).isEmpty() ) + str += "\nkatetemplate: Description=" + s; + + // 2) If a file or template is chosen, open that. and fill the data into a string + int toid = bgOrigin->selectedId(); // 1 = blank, 2 = file, 3 = template + kdDebug()<<"=== create template: origin type "<<toid<<endl; + if ( toid > 1 ) + { + KURL u; + if ( toid == 2 ) // file + u = KURL( urOrigin->url() ); + else // template + u = KURL( kft->templates().at( selectedTemplateIdx )->filename ); + + QString tmpfile, tmp; + if ( KIO::NetAccess::download( u, tmpfile, 0L ) ) + { + QFile file(tmpfile); + if ( ! file.open( IO_ReadOnly ) ) + { + KMessageBox::sorry( this, i18n( + "<qt>Error opening the file<br><strong>%1</strong><br>for reading. " + "The document will not be created</qt>").arg(u.prettyURL()), + i18n("Template Plugin"), 0 ); + + KIO::NetAccess::removeTempFile( tmpfile ); + return; + } + + QTextStream stream(&file); + QString ln; + bool trymore = true; + while ( !stream.eof() ) + { + // skip template headers + ln = stream.readLine(); + if ( trymore && ln.startsWith("katetemplate:") ) + continue; + + trymore = false; + tmp += "\n" + ln; + } + + file.close(); + KIO::NetAccess::removeTempFile( tmpfile ); + } + + if ( toid == 2 ) // file + { + // 3) if the file is not already a template, escape any "%" and "^" in it, + // and try do do some replacement of the authors username, name and email. + tmp.replace( QRegExp("%(?=\\{[^}]+\\})"), "\\%" ); + tmp.replace( QRegExp("\\$(?=\\{[^}]+\\})"), "\\$" ); + //tmp.replace( "^", "\\^" ); + + if ( cbRRealname->isChecked() && ! sFullname.isEmpty() ) + tmp.replace( sFullname, "%{realname}" ); + + + if ( cbREmail->isChecked() && ! sEmail.isEmpty() ) + tmp.replace( sEmail, "%{email}" ); + } + + str += tmp; + } + + // 4) Save the document to the suggested URL if possible + + bool succes = false; + + if ( templateUrl.isValid() ) + { + if ( templateUrl.isLocalFile() ) + { + QFile file( templateUrl.path() ); + if ( file.open(IO_WriteOnly) ) + { + kdDebug()<<"file opened with succes"<<endl; + QTextStream stream( &file ); + stream << str; + file.close(); + succes = true; + } + } + else + { + KTempFile tmp; + QString fn = tmp.name(); + QTextStream stream( tmp.file() ); + stream << str; + tmp.close(); + + succes = KIO::NetAccess::upload( fn, templateUrl, 0 ); + KIO::NetAccess::removeTempFile( fn ); + } + } + + if ( ! succes ) + { + KMessageBox::sorry( this, i18n( + "Unable to save the template to '%1'.\n\nThe template will be opened, " + "so you can save it from the editor.").arg( templateUrl.prettyURL() ), + i18n("Save Failed") ); + + kft->application()->activeMainWindow()->viewManager()->openURL( KURL() ); + Kate::View *view = kft->application()->activeMainWindow()->viewManager()->activeView(); + Kate::Document *doc = view->getDoc(); + doc->insertText( ln++, 0, str ); + } + else if ( cbOpenTemplate->isChecked() ) + kft->application()->activeMainWindow()->viewManager()->openURL( templateUrl ); +} +//END KateTemplateWizard + +//BEGIN KateTemplateItem +class KateTemplateItem : public KListViewItem +{ + public: + KateTemplateItem( KListViewItem *parent, TemplateInfo *templateinfo ) + : KListViewItem( parent, templateinfo->tmplate ), templateinfo( templateinfo ) + { + } + TemplateInfo *templateinfo; +}; +//END KateTemplateItem + +//BEGIN KFTNewStuff +class KFTNewStuff : public KNewStuff { + public: + KFTNewStuff( const QString &type, QWidget *parent=0 ) : KNewStuff( type, parent ), m_win( parent ) {} + ~KFTNewStuff() {} + bool install( const QString &/*filename*/ ) { return true; } + bool createUploadFile( const QString &/*filename*/ ) { return false; } + QString downloadDestination( KNS::Entry *entry ) + { + QString dir = KGlobal::dirs()->saveLocation( "data", "kate/plugins/katefiletemplates/templates/", true ); + return dir.append( entry->payload().filename() ); + } + + private: + QWidget *m_win; +}; +//END KTNewStuff + +//BEGIN KateTemplateManager +KateTemplateManager::KateTemplateManager( KateFileTemplates *kft, QWidget *parent, const char *name ) + : QWidget( parent, name ) + , kft( kft ) +{ + QGridLayout *lo = new QGridLayout( this, 2, 6 ); + lo->setSpacing( KDialogBase::spacingHint() ); + lvTemplates = new KListView( this ); + lvTemplates->addColumn( i18n("Template") ); + lo->addMultiCellWidget( lvTemplates, 1, 1, 1, 6 ); + connect( lvTemplates, SIGNAL(selectionChanged()), this, SLOT(slotUpdateState()) ); + + btnNew = new QPushButton( i18n("New..."), this ); + connect( btnNew, SIGNAL(clicked()), kft, SLOT(slotCreateTemplate()) ); + lo->addWidget( btnNew, 2, 2 ); + + btnEdit = new QPushButton( i18n("Edit..."), this ); + connect( btnEdit, SIGNAL(clicked()), this, SLOT( slotEditTemplate()) ); + lo->addWidget( btnEdit, 2, 3 ); + + btnRemove = new QPushButton( i18n("Remove"), this ); + connect( btnRemove, SIGNAL(clicked()), this, SLOT(slotRemoveTemplate()) ); + lo->addWidget( btnRemove, 2, 4 ); + + btnUpload = new QPushButton( i18n("Upload..."), this ); + connect( btnUpload, SIGNAL(clicked()), this, SLOT(slotUpload()) ); + lo->addWidget( btnUpload, 2, 5 ); + + btnDownload = new QPushButton( i18n("Download..."), this ); + connect( btnDownload, SIGNAL(clicked()), this, SLOT(slotDownload()) ); + lo->addWidget( btnDownload, 2, 6 ); + + lo->setColStretch( 1, 1 ); + + reload(); + slotUpdateState(); +} + +void KateTemplateManager::apply() +{ + // if any files were removed, delete them unless they are not writeable, in + // which case a link .filename should be put in the writable directory. +} + +void KateTemplateManager::reload() +{ + lvTemplates->clear(); + + QDict<KListViewItem> groupitems; // FIXME QMAP + for ( uint i = 0; i < kft->templates().count(); i++ ) + { + if ( ! groupitems[ kft->templates().at( i )->group ] ) + { + groupitems.insert( kft->templates().at( i )->group , new KListViewItem( lvTemplates, kft->templates().at( i )->group ) ); + groupitems[ kft->templates().at( i )->group ]->setOpen( true ); + } + new KateTemplateItem( groupitems[ kft->templates().at( i )->group ], kft->templates().at( i ) ); + } +} + +void KateTemplateManager::slotUpdateState() +{ + // enable/disable buttons wrt the current item in the list view. + // we are in single selection mode, so currentItem() is selected. + bool cool = false; + if ( dynamic_cast<KateTemplateItem*>( lvTemplates->currentItem() ) ) + cool = true; + + btnEdit->setEnabled( cool ); + btnRemove->setEnabled( cool ); + btnUpload->setEnabled( cool ); +} + +void KateTemplateManager::slotEditTemplate() +{ + // open the template file in kate + // TODO show the properties dialog, and modify the file if the data was changed. + KateTemplateItem *item = dynamic_cast<KateTemplateItem*>( lvTemplates->currentItem() ); + if ( item ) + kft->application()->activeMainWindow()->viewManager()->openURL( item->templateinfo->filename ); +} + +void KateTemplateManager::slotRemoveTemplate() +{ + KateTemplateItem *item = dynamic_cast<KateTemplateItem*>( lvTemplates->currentItem() ); + if ( item ) + { + // Find all instances of filename, and try to delete them. + // If it fails (there was a global, unwritable instance), add to a + // list of removed templates + KConfig *config = kapp->config(); + QString fname = item->templateinfo->filename.section( '/', -1 ); + QStringList templates = KGlobal::dirs()->findAllResources( + "data", fname.prepend( "kate/plugins/katefiletemplates/templates/" ), + false,true); + int failed = 0; + int removed = 0; + for ( QStringList::Iterator it=templates.begin(); it!=templates.end(); ++it ) + { + if ( ! QFile::remove(*it) ) + failed++; + else + removed++; + } + + if ( failed ) + { + config->setGroup( "KateFileTemplates" ); + QStringList l; + config->readListEntry( "Hidden", l, ';' ); + l << fname; + config->writeEntry( "Hidden", l, ';' ); + } + + // If we removed any files, we should delete a KNewStuff key + // for this template, so the template is installable again. + // ### This assumes that the knewstuff name is similar to the template name. + kdDebug()<<"trying to remove knewstuff key '"<<item->templateinfo->tmplate<<"'"<<endl; + config->setGroup("KNewStuffStatus"); + config->deleteEntry( item->templateinfo->tmplate ); + + + kft->updateTemplateDirs(); + reload(); + } +} + +// KNewStuff upload +void KateTemplateManager::slotUpload() +{ + // TODO something nicer, like preparing the meta data from the template info. + KateTemplateItem *item = dynamic_cast<KateTemplateItem*>( lvTemplates->currentItem() ); + if ( item ) + { + KFTNewStuff *ns = new KFTNewStuff( "katefiletemplates/template", this ); + ns->upload( item->templateinfo->filename, QString::null ); + } +} + +// KNewStuff download +void KateTemplateManager::slotDownload() +{ + KFTNewStuff *ns = new KFTNewStuff( "katefiletemplates/template", this ); + ns->download(); + + kft->updateTemplateDirs(); + reload(); +} + +//END KateTemplateManager + +// kate: space-indent on; indent-width 2; replace-tabs on; + +#include "filetemplates.moc" |