00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "knewmenu.h"
00021 #include "knewmenu_p.h"
00022 #include "konq_operations.h"
00023
00024 #include <QDir>
00025 #include <QVBoxLayout>
00026 #include <QList>
00027 #include <kactioncollection.h>
00028 #include <kdebug.h>
00029 #include <kdesktopfile.h>
00030 #include <kdirwatch.h>
00031 #include <kicon.h>
00032 #include <kcomponentdata.h>
00033 #include <kinputdialog.h>
00034 #include <klocale.h>
00035 #include <kmessagebox.h>
00036 #include <kstandarddirs.h>
00037 #include <kprotocolinfo.h>
00038 #include <kprotocolmanager.h>
00039 #include <kmenu.h>
00040 #include <krun.h>
00041 #include <kio/copyjob.h>
00042 #include <kio/jobuidelegate.h>
00043 #include <kio/renamedialog.h>
00044 #include <kio/netaccess.h>
00045 #include <kio/fileundomanager.h>
00046
00047 #include <kpropertiesdialog.h>
00048 #include <ktemporaryfile.h>
00049 #include <utime.h>
00050
00051
00052 #include <QLayout>
00053 #include <klineedit.h>
00054 #include <kurlrequester.h>
00055 #include <QLabel>
00056
00057
00058 class KNewMenuSingleton
00059 {
00060 public:
00061 KNewMenuSingleton()
00062 : templatesList(0),
00063 templatesVersion(0),
00064 filesParsed(false),
00065 dirWatch(0)
00066 {
00067 }
00068 ~KNewMenuSingleton()
00069 {
00070 delete templatesList;
00071 delete dirWatch;
00072 }
00073
00074 struct Entry {
00075 QString text;
00076 QString filePath;
00077 QString templatePath;
00078 QString icon;
00079 int entryType;
00080 QString comment;
00081 };
00082
00083
00088 typedef QList<Entry> EntryList;
00089 EntryList * templatesList;
00090
00096 int templatesVersion;
00097
00102 bool filesParsed;
00103 KDirWatch * dirWatch;
00104 };
00105
00106 K_GLOBAL_STATIC(KNewMenuSingleton, kNewMenuGlobals)
00107
00108 class KNewMenu::KNewMenuPrivate
00109 {
00110 public:
00111 KNewMenuPrivate()
00112 : menuItemsVersion(0)
00113 {}
00114 KActionCollection * m_actionCollection;
00115 QWidget *m_parentWidget;
00116 KActionMenu *m_menuDev;
00117 QAction* m_newDirAction;
00118
00119 int menuItemsVersion;
00120
00125 KUrl::List popupFiles;
00126
00130 bool m_isUrlDesktopFile;
00131 QString m_tempFileToDelete;
00132
00136 QActionGroup* m_newMenuGroup;
00137 };
00138
00139 KNewMenu::KNewMenu( KActionCollection *parent, QWidget* parentWidget, const QString& name )
00140 : KActionMenu( KIcon("document-new"), i18n( "Create New" ), parentWidget )
00141 {
00142
00143
00144 d = new KNewMenuPrivate;
00145 d->m_newMenuGroup = new QActionGroup(this);
00146 d->m_actionCollection = parent;
00147 d->m_parentWidget = parentWidget;
00148
00149 d->m_actionCollection->addAction( name, this );
00150
00151 makeMenus();
00152 }
00153
00154 KNewMenu::~KNewMenu()
00155 {
00156
00157 delete d;
00158 }
00159
00160 void KNewMenu::makeMenus()
00161 {
00162 d->m_menuDev = new KActionMenu( KIcon("drive-removable-media"), i18n( "Link to Device" ), this );
00163 }
00164
00165 void KNewMenu::slotCheckUpToDate( )
00166 {
00167 KNewMenuSingleton* s = kNewMenuGlobals;
00168
00169
00170
00171 if (d->menuItemsVersion < s->templatesVersion || s->templatesVersion == 0) {
00172
00173
00174
00175 foreach (QAction* action, d->m_newMenuGroup->actions())
00176 delete action;
00177
00178 if (!s->templatesList) {
00179 s->templatesList = new KNewMenuSingleton::EntryList;
00180 slotFillTemplates();
00181 parseFiles();
00182 }
00183
00184
00185
00186 if ( !s->filesParsed )
00187 parseFiles();
00188
00189 fillMenu();
00190
00191 d->menuItemsVersion = s->templatesVersion;
00192 }
00193 }
00194
00195 void KNewMenu::parseFiles()
00196 {
00197 KNewMenuSingleton* s = kNewMenuGlobals;
00198
00199 s->filesParsed = true;
00200 KNewMenuSingleton::EntryList::iterator templ = s->templatesList->begin();
00201 const KNewMenuSingleton::EntryList::iterator templ_end = s->templatesList->end();
00202 for ( ; templ != templ_end; ++templ )
00203 {
00204 QString iconname;
00205 QString filePath = (*templ).filePath;
00206 if ( !filePath.isEmpty() )
00207 {
00208 QString text;
00209 QString templatePath;
00210
00211
00212 if ( KDesktopFile::isDesktopFile( filePath ) ) {
00213 KDesktopFile desktopFile( filePath );
00214 const KConfigGroup config = desktopFile.desktopGroup();
00215 text = config.readEntry("Name");
00216 (*templ).icon = config.readEntry("Icon");
00217 (*templ).comment = config.readEntry("Comment");
00218 QString type = config.readEntry( "Type" );
00219 if ( type == "Link" )
00220 {
00221 templatePath = config.readPathEntry("URL", QString());
00222 if ( templatePath[0] != '/' )
00223 {
00224 if ( templatePath.startsWith("file:/") )
00225 templatePath = KUrl(templatePath).path();
00226 else
00227 {
00228
00229 QString linkDir = filePath.left( filePath.lastIndexOf( '/' ) + 1 );
00230
00231 templatePath = linkDir + templatePath;
00232 }
00233 }
00234 }
00235 if ( templatePath.isEmpty() )
00236 {
00237
00238 (*templ).entryType = TEMPLATE;
00239 (*templ).templatePath = (*templ).filePath;
00240 } else {
00241 (*templ).entryType = LINKTOTEMPLATE;
00242 (*templ).templatePath = templatePath;
00243 }
00244
00245 }
00246 if (text.isEmpty())
00247 {
00248 text = KUrl(filePath).fileName();
00249 if ( text.endsWith(".desktop") )
00250 text.truncate( text.length() - 8 );
00251 }
00252 (*templ).text = text;
00253
00254
00255
00256 }
00257 else {
00258 (*templ).entryType = SEPARATOR;
00259 }
00260 }
00261 }
00262
00263 void KNewMenu::fillMenu()
00264 {
00265
00266 menu()->clear();
00267 d->m_menuDev->menu()->clear();
00268 d->m_newDirAction = 0;
00269
00270 QSet<QString> seenTexts;
00271 QAction *linkURL = 0, *linkApp = 0;
00272
00273 KNewMenuSingleton* s = kNewMenuGlobals;
00274 int i = 1;
00275 KNewMenuSingleton::EntryList::const_iterator templ = s->templatesList->begin();
00276 const KNewMenuSingleton::EntryList::const_iterator templ_end = s->templatesList->end();
00277 for ( ; templ != templ_end; ++templ, ++i)
00278 {
00279 if ( (*templ).entryType != SEPARATOR )
00280 {
00281
00282
00283
00284
00285
00286
00287 bool bSkip = seenTexts.contains((*templ).text);
00288 if ( bSkip ) {
00289 kDebug(1203) << "KNewMenu: skipping" << (*templ).filePath;
00290 } else {
00291 seenTexts.insert((*templ).text);
00292 const KNewMenuSingleton::Entry entry = s->templatesList->at( i-1 );
00293
00294
00295 if ( (*templ).templatePath.endsWith( "emptydir" ) )
00296 {
00297 QAction * act = new QAction( this );
00298 d->m_newDirAction = act;
00299 act->setIcon( KIcon((*templ).icon) );
00300 act->setText( (*templ).text );
00301 act->setActionGroup( d->m_newMenuGroup );
00302 menu()->addAction( act );
00303
00304 QAction *sep = new QAction(this);
00305 sep->setSeparator( true );
00306 menu()->addAction( sep );
00307 }
00308 else
00309 {
00310 QAction * act = new QAction(this);
00311 act->setData( i );
00312 act->setIcon( KIcon((*templ).icon) );
00313 act->setText( (*templ).text );
00314 act->setActionGroup( d->m_newMenuGroup );
00315
00316 if ( (*templ).templatePath.endsWith( "URL.desktop" ) )
00317 {
00318 linkURL = act;
00319 }
00320 else if ( (*templ).templatePath.endsWith( "Program.desktop" ) )
00321 {
00322 linkApp = act;
00323 }
00324 else if ( KDesktopFile::isDesktopFile( entry.templatePath ) )
00325 {
00326 KDesktopFile df( entry.templatePath );
00327 if(df.readType() == "FSDevice")
00328 d->m_menuDev->menu()->addAction( act );
00329 else
00330 menu()->addAction( act );
00331 }
00332 else
00333 {
00334 menu()->addAction( act );
00335 }
00336 }
00337 }
00338 } else {
00339 Q_ASSERT( (*templ).entryType != 0 );
00340
00341 QAction *sep = new QAction( this );
00342 sep->setSeparator( true );
00343 menu()->addAction( sep );
00344 }
00345 }
00346
00347 QAction *sep = new QAction( this );
00348 sep->setSeparator( true );
00349 menu()->addAction( sep );
00350 if ( linkURL ) menu()->addAction( linkURL );
00351 if ( linkApp ) menu()->addAction( linkApp );
00352 Q_ASSERT(d->m_menuDev);
00353 menu()->addAction( d->m_menuDev );
00354
00355 connect(d->m_newMenuGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotActionTriggered(QAction*)));
00356 }
00357
00358 void KNewMenu::slotFillTemplates()
00359 {
00360 KNewMenuSingleton* s = kNewMenuGlobals;
00361
00362
00363 if ( ! s->dirWatch ) {
00364 s->dirWatch = new KDirWatch;
00365 QStringList dirs = d->m_actionCollection->componentData().dirs()->resourceDirs("templates");
00366 for ( QStringList::const_iterator it = dirs.begin() ; it != dirs.end() ; ++it ) {
00367
00368 s->dirWatch->addDir( *it );
00369 }
00370 connect ( s->dirWatch, SIGNAL( dirty( const QString & ) ),
00371 this, SLOT ( slotFillTemplates() ) );
00372 connect ( s->dirWatch, SIGNAL( created( const QString & ) ),
00373 this, SLOT ( slotFillTemplates() ) );
00374 connect ( s->dirWatch, SIGNAL( deleted( const QString & ) ),
00375 this, SLOT ( slotFillTemplates() ) );
00376
00377 }
00378 ++s->templatesVersion;
00379 s->filesParsed = false;
00380
00381 s->templatesList->clear();
00382
00383
00384 QStringList files = d->m_actionCollection->componentData().dirs()->findAllResources("templates");
00385 QMap<QString, KNewMenuSingleton::Entry> slist;
00386 for ( QStringList::Iterator it = files.begin() ; it != files.end() ; ++it )
00387 {
00388
00389 if ( (*it)[0] != '.' )
00390 {
00391 KNewMenuSingleton::Entry e;
00392 e.filePath = *it;
00393 e.entryType = 0;
00394
00395 if ( (*it).endsWith( "Directory.desktop" ) ||
00396 (*it).endsWith( "linkProgram.desktop" ) ||
00397 (*it).endsWith( "linkURL.desktop" ) )
00398 s->templatesList->prepend( e );
00399 else
00400 {
00401 KDesktopFile config( *it );
00402
00403
00404
00405 QString key = config.desktopGroup().readEntry("Name");
00406 if ( (*it).endsWith( "TextFile.desktop" ) )
00407 key.prepend( '1' );
00408 else
00409 key.prepend( '2' );
00410
00411 slist.insert( key, e );
00412 }
00413 }
00414 }
00415 (*s->templatesList) += slist.values();
00416 }
00417
00418 void KNewMenu::newDir()
00419 {
00420 if (d->popupFiles.isEmpty())
00421 return;
00422
00423 KIO::SimpleJob* job = KonqOperations::newDir(d->m_parentWidget, d->popupFiles.first());
00424 if (job) {
00425
00426 job->ui()->setAutoErrorHandlingEnabled(false);
00427 connect( job, SIGNAL( result( KJob * ) ),
00428 SLOT( slotResult( KJob * ) ) );
00429 }
00430
00431 }
00432
00433 void KNewMenu::slotActionTriggered(QAction* action)
00434 {
00435 trigger();
00436
00437 if (action == d->m_newDirAction) {
00438 newDir();
00439 return;
00440 }
00441 const int id = action->data().toInt();
00442 Q_ASSERT(id > 0);
00443
00444 KNewMenuSingleton* s = kNewMenuGlobals;
00445 const KNewMenuSingleton::Entry entry = s->templatesList->at( id - 1 );
00446
00447
00448 if ( !QFile::exists( entry.templatePath ) ) {
00449 kWarning(1203) << entry.templatePath << "doesn't exist" ;
00450 KMessageBox::sorry( 0L, i18n("<qt>The template file <b>%1</b> does not exist.</qt>", entry.templatePath));
00451 return;
00452 }
00453
00454
00455 d->m_isUrlDesktopFile = false;
00456 KUrl linkUrl;
00457
00458 QString name;
00459 if ( KDesktopFile::isDesktopFile( entry.templatePath ) )
00460 {
00461 KDesktopFile df( entry.templatePath );
00462
00463 if ( df.readType() == "Link" )
00464 {
00465 d->m_isUrlDesktopFile = true;
00466
00467 KUrlDesktopFileDlg dlg( i18n("File name:"), entry.comment, d->m_parentWidget );
00468
00469 if ( dlg.exec() )
00470 {
00471 name = dlg.fileName();
00472 linkUrl = dlg.url();
00473 if ( name.isEmpty() || linkUrl.isEmpty() )
00474 return;
00475 if ( !name.endsWith( ".desktop" ) )
00476 name += ".desktop";
00477 }
00478 else
00479 return;
00480 }
00481 else
00482 {
00483 KUrl::List::Iterator it = d->popupFiles.begin();
00484 for ( ; it != d->popupFiles.end(); ++it )
00485 {
00486
00487
00488
00489 QString text = entry.text;
00490 text.replace( "...", QString() );
00491
00492 KUrl defaultFile( *it );
00493 defaultFile.addPath( KIO::encodeFileName( text ) );
00494 if ( defaultFile.isLocalFile() && QFile::exists( defaultFile.path() ) )
00495 text = KIO::RenameDialog::suggestName( *it, text);
00496
00497 KUrl templateUrl( entry.templatePath );
00498 KPropertiesDialog dlg( templateUrl, *it, text, d->m_parentWidget );
00499 dlg.exec();
00500 }
00501 return;
00502 }
00503 }
00504 else
00505 {
00506
00507
00508 bool ok;
00509 QString text = entry.text;
00510 text.replace( "...", QString() );
00511
00512 KUrl defaultFile( *(d->popupFiles.begin()) );
00513 defaultFile.addPath( KIO::encodeFileName( text ) );
00514 if ( defaultFile.isLocalFile() && QFile::exists( defaultFile.path() ) )
00515 text = KIO::RenameDialog::suggestName( *(d->popupFiles.begin()), text);
00516
00517 name = KInputDialog::getText( QString(), entry.comment,
00518 text, &ok, d->m_parentWidget );
00519 if ( !ok )
00520 return;
00521 }
00522
00523 QString src = entry.templatePath;
00524
00525 if (d->m_isUrlDesktopFile) {
00526
00527
00528 KTemporaryFile tmpFile;
00529 tmpFile.setAutoRemove(false);
00530 if (!tmpFile.open()) {
00531 kError() << "Couldn't create temp file!";
00532 return;
00533 }
00534
00535 QFile file(src);
00536 if (!file.open(QIODevice::ReadOnly)) {
00537 kError() << "Couldn't open template" << src;
00538 return;
00539 }
00540 const QByteArray data = file.readAll();
00541 tmpFile.write(data);
00542 const QString tempFileName = tmpFile.fileName();
00543 Q_ASSERT(!tempFileName.isEmpty());
00544 tmpFile.close();
00545
00546 KDesktopFile df(tempFileName);
00547 KConfigGroup group = df.desktopGroup();
00548 group.writeEntry("Icon", KProtocolInfo::icon(linkUrl.protocol()));
00549 group.writePathEntry("URL", linkUrl.prettyUrl());
00550 df.sync();
00551 src = tempFileName;
00552 d->m_tempFileToDelete = tempFileName;
00553 }
00554
00555
00556
00557 KUrl::List::const_iterator it = d->popupFiles.begin();
00558 for ( ; it != d->popupFiles.end(); ++it )
00559 {
00560 KUrl dest( *it );
00561 dest.addPath( KIO::encodeFileName(name) );
00562
00563 KUrl uSrc(src);
00564
00565 KIO::CopyJob * job = KIO::copyAs( uSrc, dest );
00566 job->setDefaultPermissions( true );
00567 job->ui()->setWindow( d->m_parentWidget );
00568 connect( job, SIGNAL( result( KJob * ) ),
00569 SLOT( slotResult( KJob * ) ) );
00570 KUrl::List lst;
00571 lst.append(uSrc);
00572 KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Copy, lst, dest, job);
00573 }
00574 }
00575
00576 void KNewMenu::slotResult( KJob * job )
00577 {
00578 if (job->error()) {
00579 static_cast<KIO::Job*>( job )->ui()->showErrorMessage();
00580 } else {
00581
00582 KIO::CopyJob* copyJob = ::qobject_cast<KIO::CopyJob*>(job);
00583 if (copyJob) {
00584 const KUrl destUrl = copyJob->destUrl();
00585 const KUrl localUrl = KIO::NetAccess::mostLocalUrl(destUrl, d->m_parentWidget);
00586 if (localUrl.isLocalFile()) {
00587
00588 (void) ::utime(QFile::encodeName(localUrl.path()), 0);
00589 }
00590 }
00591 }
00592 if (!d->m_tempFileToDelete.isEmpty())
00593 QFile::remove(d->m_tempFileToDelete);
00594 }
00595
00596 void KNewMenu::setPopupFiles(const KUrl::List& files)
00597 {
00598 d->popupFiles = files;
00599 if (files.isEmpty()) {
00600 d->m_newMenuGroup->setEnabled(false);
00601 } else {
00602 KUrl firstUrl = files.first();
00603 if (KProtocolManager::supportsWriting(firstUrl)) {
00604 d->m_newMenuGroup->setEnabled(true);
00605 if (d->m_newDirAction) {
00606 d->m_newDirAction->setEnabled(KProtocolManager::supportsMakeDir(firstUrl));
00607 }
00608 } else {
00609 d->m_newMenuGroup->setEnabled(true);
00610 }
00611 }
00612 }
00613
00615
00616 KUrlDesktopFileDlg::KUrlDesktopFileDlg( const QString& textFileName, const QString& textUrl, QWidget *parent )
00617 : KDialog( parent )
00618 {
00619 setButtons( Ok | Cancel | User1 );
00620 setButtonGuiItem( User1, KStandardGuiItem::clear() );
00621 showButtonSeparator( true );
00622
00623 initDialog( textFileName, QString(), textUrl, QString() );
00624 }
00625
00626 void KUrlDesktopFileDlg::initDialog( const QString& textFileName, const QString& defaultName, const QString& textUrl, const QString& defaultUrl )
00627 {
00628 QFrame *plainPage = new QFrame( this );
00629 setMainWidget( plainPage );
00630
00631 QVBoxLayout * topLayout = new QVBoxLayout( plainPage );
00632 topLayout->setMargin( 0 );
00633 topLayout->setSpacing( spacingHint() );
00634
00635
00636 KHBox * fileNameBox = new KHBox( plainPage );
00637 topLayout->addWidget( fileNameBox );
00638
00639 QLabel * label = new QLabel( textFileName, fileNameBox );
00640 m_leFileName = new KLineEdit( fileNameBox );
00641 m_leFileName->setMinimumWidth(m_leFileName->sizeHint().width() * 3);
00642 label->setBuddy(m_leFileName);
00643 m_leFileName->setText( defaultName );
00644 m_leFileName->setSelection(0, m_leFileName->text().length());
00645 connect( m_leFileName, SIGNAL(textChanged(const QString&)),
00646 SLOT(slotNameTextChanged(const QString&)) );
00647
00648
00649 KHBox * urlBox = new KHBox( plainPage );
00650 topLayout->addWidget( urlBox );
00651 label = new QLabel( textUrl, urlBox );
00652 m_urlRequester = new KUrlRequester( defaultUrl, urlBox);
00653 m_urlRequester->setMode( KFile::File | KFile::Directory );
00654
00655 m_urlRequester->setMinimumWidth( m_urlRequester->sizeHint().width() * 3 );
00656 connect( m_urlRequester->lineEdit(), SIGNAL(textChanged(const QString&)),
00657 SLOT(slotURLTextChanged(const QString&)) );
00658 label->setBuddy(m_urlRequester);
00659
00660 m_urlRequester->setFocus();
00661 enableButtonOk( !defaultName.isEmpty() && !defaultUrl.isEmpty() );
00662 connect( this, SIGNAL(user1Clicked()), this, SLOT(slotClear()) );
00663 m_fileNameEdited = false;
00664 }
00665
00666 KUrl KUrlDesktopFileDlg::url() const
00667 {
00668 if ( result() == QDialog::Accepted )
00669 return m_urlRequester->url();
00670 else
00671 return KUrl();
00672 }
00673
00674 QString KUrlDesktopFileDlg::fileName() const
00675 {
00676 if ( result() == QDialog::Accepted )
00677 return m_leFileName->text();
00678 else
00679 return QString();
00680 }
00681
00682 void KUrlDesktopFileDlg::slotClear()
00683 {
00684 m_leFileName->clear();
00685 m_urlRequester->clear();
00686 m_fileNameEdited = false;
00687 }
00688
00689 void KUrlDesktopFileDlg::slotNameTextChanged( const QString& )
00690 {
00691 kDebug() ;
00692 m_fileNameEdited = true;
00693 enableButtonOk( !m_leFileName->text().isEmpty() && !m_urlRequester->url().isEmpty() );
00694 }
00695
00696 void KUrlDesktopFileDlg::slotURLTextChanged( const QString& )
00697 {
00698 if ( !m_fileNameEdited )
00699 {
00700
00701
00702
00703 KUrl url( m_urlRequester->url() );
00704 if (KProtocolManager::supportsListing(url) && !url.fileName().isEmpty())
00705 m_leFileName->setText( url.fileName() );
00706 else
00707 m_leFileName->setText( url.url() );
00708 m_fileNameEdited = false;
00709 }
00710 enableButtonOk( !m_leFileName->text().isEmpty() && !m_urlRequester->url().isEmpty() );
00711 }
00712
00713
00714 #include "knewmenu.moc"
00715 #include "knewmenu_p.moc"