diff options
Diffstat (limited to 'libkonq/knewmenu.cc')
-rw-r--r-- | libkonq/knewmenu.cc | 621 |
1 files changed, 621 insertions, 0 deletions
diff --git a/libkonq/knewmenu.cc b/libkonq/knewmenu.cc new file mode 100644 index 000000000..168ade6f7 --- /dev/null +++ b/libkonq/knewmenu.cc @@ -0,0 +1,621 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 David Faure <[email protected]> + 2003 Sven Leiber <[email protected]> + + 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 <qdir.h> + +#include <kdebug.h> +#include <kdesktopfile.h> +#include <kdirwatch.h> +#include <kinstance.h> +#include <kinputdialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> +#include <kprotocolinfo.h> +#include <kpopupmenu.h> +#include <krun.h> + +#include <kio/job.h> +#include <kio/renamedlg.h> + +#include <kpropertiesdialog.h> +#include "konq_operations.h" +#include "konq_undo.h" +#include "knewmenu.h" +#include <utime.h> + +// For KURLDesktopFileDlg +#include <qlayout.h> +#include <qhbox.h> +#include <klineedit.h> +#include <kurlrequester.h> +#include <qlabel.h> +#include <qpopupmenu.h> + +QValueList<KNewMenu::Entry> * KNewMenu::s_templatesList = 0L; +int KNewMenu::s_templatesVersion = 0; +bool KNewMenu::s_filesParsed = false; +KDirWatch * KNewMenu::s_pDirWatch = 0L; + +class KNewMenu::KNewMenuPrivate +{ +public: + KNewMenuPrivate() : m_parentWidget(0) {} + KActionCollection * m_actionCollection; + QString m_destPath; + QWidget *m_parentWidget; + KActionMenu *m_menuDev; +}; + +KNewMenu::KNewMenu( KActionCollection * _collec, const char *name ) : + KActionMenu( i18n( "Create New" ), "filenew", _collec, name ), + menuItemsVersion( 0 ) +{ + //kdDebug(1203) << "KNewMenu::KNewMenu " << this << endl; + // Don't fill the menu yet + // We'll do that in slotCheckUpToDate (should be connected to abouttoshow) + d = new KNewMenuPrivate; + d->m_actionCollection = _collec; + makeMenus(); +} + +KNewMenu::KNewMenu( KActionCollection * _collec, QWidget *parentWidget, const char *name ) : + KActionMenu( i18n( "Create New" ), "filenew", _collec, name ), + menuItemsVersion( 0 ) +{ + d = new KNewMenuPrivate; + d->m_actionCollection = _collec; + d->m_parentWidget = parentWidget; + makeMenus(); +} + +KNewMenu::~KNewMenu() +{ + //kdDebug(1203) << "KNewMenu::~KNewMenu " << this << endl; + delete d; +} + +void KNewMenu::makeMenus() +{ + d->m_menuDev = new KActionMenu( i18n( "Link to Device" ), "kcmdevices", d->m_actionCollection, "devnew" ); +} + +void KNewMenu::slotCheckUpToDate( ) +{ + //kdDebug(1203) << "KNewMenu::slotCheckUpToDate() " << this + // << " : menuItemsVersion=" << menuItemsVersion + // << " s_templatesVersion=" << s_templatesVersion << endl; + if (menuItemsVersion < s_templatesVersion || s_templatesVersion == 0) + { + //kdDebug(1203) << "KNewMenu::slotCheckUpToDate() : recreating actions" << endl; + // We need to clean up the action collection + // We look for our actions using the group + QValueList<KAction*> actions = d->m_actionCollection->actions( "KNewMenu" ); + for( QValueListIterator<KAction*> it = actions.begin(); it != actions.end(); ++it ) + { + remove( *it ); + d->m_actionCollection->remove( *it ); + } + + if (!s_templatesList) { // No templates list up to now + s_templatesList = new QValueList<Entry>(); + slotFillTemplates(); + parseFiles(); + } + + // This might have been already done for other popupmenus, + // that's the point in s_filesParsed. + if ( !s_filesParsed ) + parseFiles(); + + fillMenu(); + + menuItemsVersion = s_templatesVersion; + } +} + +void KNewMenu::parseFiles() +{ + //kdDebug(1203) << "KNewMenu::parseFiles()" << endl; + s_filesParsed = true; + QValueList<Entry>::Iterator templ = s_templatesList->begin(); + for ( /*++templ*/; templ != s_templatesList->end(); ++templ) + { + QString iconname; + QString filePath = (*templ).filePath; + if ( !filePath.isEmpty() ) + { + QString text; + QString templatePath; + // If a desktop file, then read the name from it. + // Otherwise (or if no name in it?) use file name + if ( KDesktopFile::isDesktopFile( filePath ) ) { + KSimpleConfig config( filePath, true ); + config.setDesktopGroup(); + text = config.readEntry("Name"); + (*templ).icon = config.readEntry("Icon"); + (*templ).comment = config.readEntry("Comment"); + QString type = config.readEntry( "Type" ); + if ( type == "Link" ) + { + templatePath = config.readPathEntry("URL"); + if ( templatePath[0] != '/' ) + { + if ( templatePath.startsWith("file:/") ) + templatePath = KURL(templatePath).path(); + else + { + // A relative path, then (that's the default in the files we ship) + QString linkDir = filePath.left( filePath.findRev( '/' ) + 1 /*keep / */ ); + //kdDebug(1203) << "linkDir=" << linkDir << endl; + templatePath = linkDir + templatePath; + } + } + } + if ( templatePath.isEmpty() ) + { + // No dest, this is an old-style template + (*templ).entryType = TEMPLATE; + (*templ).templatePath = (*templ).filePath; // we'll copy the file + } else { + (*templ).entryType = LINKTOTEMPLATE; + (*templ).templatePath = templatePath; + } + + } + if (text.isEmpty()) + { + text = KURL(filePath).fileName(); + if ( text.endsWith(".desktop") ) + text.truncate( text.length() - 8 ); + else if ( text.endsWith(".kdelnk") ) + text.truncate( text.length() - 7 ); + } + (*templ).text = text; + /*kdDebug(1203) << "Updating entry with text=" << text + << " entryType=" << (*templ).entryType + << " templatePath=" << (*templ).templatePath << endl;*/ + } + else { + (*templ).entryType = SEPARATOR; + } + } +} + +void KNewMenu::fillMenu() +{ + //kdDebug(1203) << "KNewMenu::fillMenu()" << endl; + popupMenu()->clear(); + d->m_menuDev->popupMenu()->clear(); + + KAction *linkURL = 0, *linkApp = 0; // these shall be put at special positions + + int i = 1; // was 2 when there was Folder + QValueList<Entry>::Iterator templ = s_templatesList->begin(); + for ( ; templ != s_templatesList->end(); ++templ, ++i) + { + if ( (*templ).entryType != SEPARATOR ) + { + // There might be a .desktop for that one already, if it's a kdelnk + // This assumes we read .desktop files before .kdelnk files ... + + // In fact, we skip any second item that has the same text as another one. + // Duplicates in a menu look bad in any case. + + bool bSkip = false; + + QValueList<KAction*> actions = d->m_actionCollection->actions(); + QValueListIterator<KAction*> it = actions.begin(); + for( ; it != actions.end() && !bSkip; ++it ) + { + if ( (*it)->text() == (*templ).text ) + { + kdDebug(1203) << "KNewMenu: skipping " << (*templ).filePath << endl; + bSkip = true; + } + } + + if ( !bSkip ) + { + Entry entry = *(s_templatesList->at( i-1 )); + + // The best way to identify the "Create Directory", "Link to Location", "Link to Application" was the template + if ( (*templ).templatePath.endsWith( "emptydir" ) ) + { + KAction * act = new KAction( (*templ).text, (*templ).icon, 0, this, SLOT( slotNewDir() ), + d->m_actionCollection, QCString().sprintf("newmenu%d", i ) ); + act->setGroup( "KNewMenu" ); + act->plug( popupMenu() ); + + KActionSeparator *sep = new KActionSeparator(); + sep->plug( popupMenu() ); + } + else + { + KAction * act = new KAction( (*templ).text, (*templ).icon, 0, this, SLOT( slotNewFile() ), + d->m_actionCollection, QCString().sprintf("newmenu%d", i ) ); + act->setGroup( "KNewMenu" ); + + if ( (*templ).templatePath.endsWith( "URL.desktop" ) ) + { + linkURL = act; + } + else if ( (*templ).templatePath.endsWith( "Program.desktop" ) ) + { + linkApp = act; + } + else if ( KDesktopFile::isDesktopFile( entry.templatePath ) ) + { + KDesktopFile df( entry.templatePath ); + if(df.readType() == "FSDevice") + act->plug( d->m_menuDev->popupMenu() ); + else + act->plug( popupMenu() ); + } + else + { + act->plug( popupMenu() ); + } + } + } + } else { // Separate system from personal templates + Q_ASSERT( (*templ).entryType != 0 ); + + KActionSeparator * act = new KActionSeparator(); + act->plug( popupMenu() ); + } + } + + KActionSeparator * act = new KActionSeparator(); + act->plug( popupMenu() ); + if ( linkURL ) linkURL->plug( popupMenu() ); + if ( linkApp ) linkApp->plug( popupMenu() ); + d->m_menuDev->plug( popupMenu() ); +} + +void KNewMenu::slotFillTemplates() +{ + //kdDebug(1203) << "KNewMenu::slotFillTemplates()" << endl; + // Ensure any changes in the templates dir will call this + if ( ! s_pDirWatch ) + { + s_pDirWatch = new KDirWatch; + QStringList dirs = d->m_actionCollection->instance()->dirs()->resourceDirs("templates"); + for ( QStringList::Iterator it = dirs.begin() ; it != dirs.end() ; ++it ) + { + //kdDebug(1203) << "Templates resource dir: " << *it << endl; + s_pDirWatch->addDir( *it ); + } + connect ( s_pDirWatch, SIGNAL( dirty( const QString & ) ), + this, SLOT ( slotFillTemplates() ) ); + connect ( s_pDirWatch, SIGNAL( created( const QString & ) ), + this, SLOT ( slotFillTemplates() ) ); + connect ( s_pDirWatch, SIGNAL( deleted( const QString & ) ), + this, SLOT ( slotFillTemplates() ) ); + // Ok, this doesn't cope with new dirs in KDEDIRS, but that's another story + } + s_templatesVersion++; + s_filesParsed = false; + + s_templatesList->clear(); + + // Look into "templates" dirs. + QStringList files = d->m_actionCollection->instance()->dirs()->findAllResources("templates"); + KSortableValueList<Entry,QString> slist; + for ( QStringList::Iterator it = files.begin() ; it != files.end() ; ++it ) + { + //kdDebug(1203) << *it << endl; + if ( (*it)[0] != '.' ) + { + Entry e; + e.filePath = *it; + e.entryType = 0; // not parsed yet + // put Directory etc. with special order (see fillMenu()) first in the list (a bit hacky) + if ( (*it).endsWith( "Directory.desktop" ) || + (*it).endsWith( "linkProgram.desktop" ) || + (*it).endsWith( "linkURL.desktop" ) ) + s_templatesList->prepend( e ); + else + { + KSimpleConfig config( *it, true ); + config.setDesktopGroup(); + + // tricky solution to ensure that TextFile is at the beginning + // because this filetype is the most used (according kde-core discussion) + QString key = config.readEntry("Name"); + if ( (*it).endsWith( "TextFile.desktop" ) ) + key = "1_" + key; + else + key = "2_" + key; + + slist.insert( key, e ); + } + } + } + slist.sort(); + for(KSortableValueList<Entry, QString>::ConstIterator it = slist.begin(); it != slist.end(); ++it) + { + s_templatesList->append( (*it).value() ); + } + +} + +void KNewMenu::slotNewDir() +{ + emit activated(); // for KDIconView::slotNewMenuActivated() + + if (popupFiles.isEmpty()) + return; + + KonqOperations::newDir(d->m_parentWidget, popupFiles.first()); +} + +void KNewMenu::slotNewFile() +{ + int id = QString( sender()->name() + 7 ).toInt(); // skip "newmenu" + if (id == 0) + { + // run the command for the templates + KRun::runCommand(QString(sender()->name())); + return; + } + + emit activated(); // for KDIconView::slotNewMenuActivated() + + Entry entry = *(s_templatesList->at( id - 1 )); + //kdDebug(1203) << QString("sFile = %1").arg(sFile) << endl; + + if ( !QFile::exists( entry.templatePath ) ) { + kdWarning(1203) << entry.templatePath << " doesn't exist" << endl; + KMessageBox::sorry( 0L, i18n("<qt>The template file <b>%1</b> does not exist.</qt>").arg(entry.templatePath)); + return; + } + m_isURLDesktopFile = false; + QString name; + if ( KDesktopFile::isDesktopFile( entry.templatePath ) ) + { + KDesktopFile df( entry.templatePath ); + //kdDebug(1203) << df.readType() << endl; + if ( df.readType() == "Link" ) + { + m_isURLDesktopFile = true; + // entry.comment contains i18n("Enter link to location (URL):"). JFYI :) + KURLDesktopFileDlg dlg( i18n("File name:"), entry.comment, d->m_parentWidget ); + // TODO dlg.setCaption( i18n( ... ) ); + if ( dlg.exec() ) + { + name = dlg.fileName(); + m_linkURL = dlg.url(); + if ( name.isEmpty() || m_linkURL.isEmpty() ) + return; + if ( !name.endsWith( ".desktop" ) ) + name += ".desktop"; + } + else + return; + } + else // any other desktop file (Device, App, etc.) + { + KURL::List::Iterator it = popupFiles.begin(); + for ( ; it != popupFiles.end(); ++it ) + { + //kdDebug(1203) << "first arg=" << entry.templatePath << endl; + //kdDebug(1203) << "second arg=" << (*it).url() << endl; + //kdDebug(1203) << "third arg=" << entry.text << endl; + QString text = entry.text; + text.replace( "...", QString::null ); // the ... is fine for the menu item but not for the default filename + + KURL defaultFile( *it ); + defaultFile.addPath( KIO::encodeFileName( text ) ); + if ( defaultFile.isLocalFile() && QFile::exists( defaultFile.path() ) ) + text = KIO::RenameDlg::suggestName( *it, text); + + KURL templateURL; + templateURL.setPath( entry.templatePath ); + (void) new KPropertiesDialog( templateURL, *it, text, d->m_parentWidget ); + } + return; // done, exit. + } + } + else + { + // The template is not a desktop file + // Show the small dialog for getting the destination filename + bool ok; + QString text = entry.text; + text.replace( "...", QString::null ); // the ... is fine for the menu item but not for the default filename + + KURL defaultFile( *(popupFiles.begin()) ); + defaultFile.addPath( KIO::encodeFileName( text ) ); + if ( defaultFile.isLocalFile() && QFile::exists( defaultFile.path() ) ) + text = KIO::RenameDlg::suggestName( *(popupFiles.begin()), text); + + name = KInputDialog::getText( QString::null, entry.comment, + text, &ok, d->m_parentWidget ); + if ( !ok ) + return; + } + + // The template is not a desktop file [or it's a URL one] + // Copy it. + KURL::List::Iterator it = popupFiles.begin(); + + QString src = entry.templatePath; + for ( ; it != popupFiles.end(); ++it ) + { + KURL dest( *it ); + dest.addPath( KIO::encodeFileName(name) ); // Chosen destination file name + d->m_destPath = dest.path(); // will only be used if m_isURLDesktopFile and dest is local + + KURL uSrc; + uSrc.setPath( src ); + //kdDebug(1203) << "KNewMenu : KIO::copyAs( " << uSrc.url() << ", " << dest.url() << ")" << endl; + KIO::CopyJob * job = KIO::copyAs( uSrc, dest ); + job->setDefaultPermissions( true ); + connect( job, SIGNAL( result( KIO::Job * ) ), + SLOT( slotResult( KIO::Job * ) ) ); + if ( m_isURLDesktopFile ) + connect( job, SIGNAL( renamed( KIO::Job *, const KURL&, const KURL& ) ), + SLOT( slotRenamed( KIO::Job *, const KURL&, const KURL& ) ) ); + KURL::List lst; + lst.append( uSrc ); + (void)new KonqCommandRecorder( KonqCommand::COPY, lst, dest, job ); + } +} + +// Special case (filename conflict when creating a link=url file) +// We need to update m_destURL +void KNewMenu::slotRenamed( KIO::Job *, const KURL& from , const KURL& to ) +{ + if ( from.isLocalFile() ) + { + kdDebug() << k_funcinfo << from.prettyURL() << " -> " << to.prettyURL() << " ( m_destPath=" << d->m_destPath << ")" << endl; + Q_ASSERT( from.path() == d->m_destPath ); + d->m_destPath = to.path(); + } +} + +void KNewMenu::slotResult( KIO::Job * job ) +{ + if (job->error()) + job->showErrorDialog(); + else + { + KURL destURL = static_cast<KIO::CopyJob*>(job)->destURL(); + if ( destURL.isLocalFile() ) + { + if ( m_isURLDesktopFile ) + { + // destURL is the original destination for the new file. + // But in case of a renaming (due to a conflict), the real path is in m_destPath + kdDebug(1203) << " destURL=" << destURL.path() << " " << " d->m_destPath=" << d->m_destPath << endl; + KDesktopFile df( d->m_destPath ); + df.writeEntry( "Icon", KProtocolInfo::icon( KURL(m_linkURL).protocol() ) ); + df.writePathEntry( "URL", m_linkURL ); + df.sync(); + } + else + { + // Normal (local) file. Need to "touch" it, kio_file copied the mtime. + (void) ::utime( QFile::encodeName( destURL.path() ), 0 ); + } + } + } +} + +////////// + +KURLDesktopFileDlg::KURLDesktopFileDlg( const QString& textFileName, const QString& textUrl ) + : KDialogBase( Plain, QString::null, Ok|Cancel|User1, Ok, 0L /*parent*/, 0L, true, + true, KStdGuiItem::clear() ) +{ + initDialog( textFileName, QString::null, textUrl, QString::null ); +} + +KURLDesktopFileDlg::KURLDesktopFileDlg( const QString& textFileName, const QString& textUrl, QWidget *parent ) + : KDialogBase( Plain, QString::null, Ok|Cancel|User1, Ok, parent, 0L, true, + true, KStdGuiItem::clear() ) +{ + initDialog( textFileName, QString::null, textUrl, QString::null ); +} + +void KURLDesktopFileDlg::initDialog( const QString& textFileName, const QString& defaultName, const QString& textUrl, const QString& defaultUrl ) +{ + QVBoxLayout * topLayout = new QVBoxLayout( plainPage(), 0, spacingHint() ); + + // First line: filename + QHBox * fileNameBox = new QHBox( plainPage() ); + topLayout->addWidget( fileNameBox ); + + QLabel * label = new QLabel( textFileName, fileNameBox ); + m_leFileName = new KLineEdit( fileNameBox, 0L ); + m_leFileName->setMinimumWidth(m_leFileName->sizeHint().width() * 3); + label->setBuddy(m_leFileName); // please "scheck" style + m_leFileName->setText( defaultName ); + m_leFileName->setSelection(0, m_leFileName->text().length()); // autoselect + connect( m_leFileName, SIGNAL(textChanged(const QString&)), + SLOT(slotNameTextChanged(const QString&)) ); + + // Second line: url + QHBox * urlBox = new QHBox( plainPage() ); + topLayout->addWidget( urlBox ); + label = new QLabel( textUrl, urlBox ); + m_urlRequester = new KURLRequester( defaultUrl, urlBox, "urlRequester" ); + m_urlRequester->setMode( KFile::File | KFile::Directory ); + + m_urlRequester->setMinimumWidth( m_urlRequester->sizeHint().width() * 3 ); + connect( m_urlRequester->lineEdit(), SIGNAL(textChanged(const QString&)), + SLOT(slotURLTextChanged(const QString&)) ); + label->setBuddy(m_urlRequester); // please "scheck" style + + m_urlRequester->setFocus(); + enableButtonOK( !defaultName.isEmpty() && !defaultUrl.isEmpty() ); + connect( this, SIGNAL(user1Clicked()), this, SLOT(slotClear()) ); + m_fileNameEdited = false; +} + +QString KURLDesktopFileDlg::url() const +{ + if ( result() == QDialog::Accepted ) + return m_urlRequester->url(); + else + return QString::null; +} + +QString KURLDesktopFileDlg::fileName() const +{ + if ( result() == QDialog::Accepted ) + return m_leFileName->text(); + else + return QString::null; +} + +void KURLDesktopFileDlg::slotClear() +{ + m_leFileName->setText( QString::null ); + m_urlRequester->clear(); + m_fileNameEdited = false; +} + +void KURLDesktopFileDlg::slotNameTextChanged( const QString& ) +{ + kdDebug() << k_funcinfo << endl; + m_fileNameEdited = true; + enableButtonOK( !m_leFileName->text().isEmpty() && !m_urlRequester->url().isEmpty() ); +} + +void KURLDesktopFileDlg::slotURLTextChanged( const QString& ) +{ + if ( !m_fileNameEdited ) + { + // use URL as default value for the filename + // (we copy only its filename if protocol supports listing, + // but for HTTP we don't want tons of index.html links) + KURL url( m_urlRequester->url() ); + if ( KProtocolInfo::supportsListing( url ) ) + m_leFileName->setText( url.fileName() ); + else + m_leFileName->setText( url.url() ); + m_fileNameEdited = false; // slotNameTextChanged set it to true erroneously + } + enableButtonOK( !m_leFileName->text().isEmpty() && !m_urlRequester->url().isEmpty() ); +} + + +#include "knewmenu.moc" |