00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <qfile.h>
00026 #include <qdir.h>
00027 #include <qdialog.h>
00028 #include <qimage.h>
00029 #include <qpixmap.h>
00030 #include <qlabel.h>
00031 #include <qlayout.h>
00032 #include <qpushbutton.h>
00033 #include <qtoolbutton.h>
00034 #include <qcheckbox.h>
00035 #include <qtooltip.h>
00036 #include <qstyle.h>
00037
00038 #include <kapplication.h>
00039 #include <kbuttonbox.h>
00040 #include <kcombobox.h>
00041 #include <kdesktopfile.h>
00042 #include <kdialog.h>
00043 #include <kglobal.h>
00044 #include <klineedit.h>
00045 #include <klocale.h>
00046 #include <kiconloader.h>
00047 #include <kmimemagic.h>
00048 #include <krun.h>
00049 #include <kstandarddirs.h>
00050 #include <kstringhandler.h>
00051 #include <kuserprofile.h>
00052 #include <kurlcompletion.h>
00053 #include <kurlrequester.h>
00054 #include <dcopclient.h>
00055 #include <kmimetype.h>
00056 #include <kservicegroup.h>
00057 #include <klistview.h>
00058 #include <ksycoca.h>
00059
00060 #include "kopenwith.h"
00061 #include "kopenwith_p.h"
00062
00063 #include <kdebug.h>
00064 #include <assert.h>
00065 #include <stdlib.h>
00066
00067 template class QPtrList<QString>;
00068
00069 #define SORT_SPEC (QDir::DirsFirst | QDir::Name | QDir::IgnoreCase)
00070
00071
00072
00073
00074 KAppTreeListItem::KAppTreeListItem( KListView* parent, const QString & name,
00075 const QPixmap& pixmap, bool parse, bool dir, QString p, QString c )
00076 : QListViewItem( parent, name )
00077 {
00078 init(pixmap, parse, dir, p, c);
00079 }
00080
00081
00082
00083
00084 KAppTreeListItem::KAppTreeListItem( QListViewItem* parent, const QString & name,
00085 const QPixmap& pixmap, bool parse, bool dir, QString p, QString c )
00086 : QListViewItem( parent, name )
00087 {
00088 init(pixmap, parse, dir, p, c);
00089 }
00090
00091
00092
00093
00094 void KAppTreeListItem::init(const QPixmap& pixmap, bool parse, bool dir, QString _path, QString _exec)
00095 {
00096 setPixmap(0, pixmap);
00097 parsed = parse;
00098 directory = dir;
00099 path = _path;
00100 exec = _exec;
00101 }
00102
00103
00104
00105
00106
00107 QString KAppTreeListItem::key(int column, bool ) const
00108 {
00109 if (directory)
00110 return QString::fromLatin1(" ") + text(column).upper();
00111 else
00112 return text(column).upper();
00113 }
00114
00115 void KAppTreeListItem::activate()
00116 {
00117 if ( directory )
00118 setOpen(!isOpen());
00119 }
00120
00121 void KAppTreeListItem::setOpen( bool o )
00122 {
00123 if( o && !parsed ) {
00124 ((KApplicationTree *) parent())->addDesktopGroup( path, this );
00125 parsed = true;
00126 }
00127 QListViewItem::setOpen( o );
00128 }
00129
00130 bool KAppTreeListItem::isDirectory()
00131 {
00132 return directory;
00133 }
00134
00135
00136
00137 KApplicationTree::KApplicationTree( QWidget *parent )
00138 : KListView( parent ), currentitem(0)
00139 {
00140 addColumn( i18n("Known Applications") );
00141 setRootIsDecorated( true );
00142
00143 addDesktopGroup( QString::null );
00144
00145 connect( this, SIGNAL( currentChanged(QListViewItem*) ),
00146 SLOT( slotItemHighlighted(QListViewItem*) ) );
00147 connect( this, SIGNAL( selectionChanged(QListViewItem*) ),
00148 SLOT( slotSelectionChanged(QListViewItem*) ) );
00149 }
00150
00151
00152
00153 bool KApplicationTree::isDirSel()
00154 {
00155 if (!currentitem) return false;
00156 return currentitem->isDirectory();
00157 }
00158
00159
00160
00161 static QPixmap appIcon(const QString &iconName)
00162 {
00163 QPixmap normal = KGlobal::iconLoader()->loadIcon(iconName, KIcon::Small, 0, KIcon::DefaultState, 0L, true);
00164
00165 if (normal.width() > 20 || normal.height() > 20)
00166 {
00167 QImage tmp = normal.convertToImage();
00168 tmp = tmp.smoothScale(20, 20);
00169 normal.convertFromImage(tmp);
00170 }
00171 return normal;
00172 }
00173
00174 void KApplicationTree::addDesktopGroup( QString relPath, KAppTreeListItem *item)
00175 {
00176 KServiceGroup::Ptr root = KServiceGroup::group(relPath);
00177 if (!root || !root->isValid()) return;
00178
00179 KServiceGroup::List list = root->entries();
00180
00181 KAppTreeListItem * newItem;
00182 for( KServiceGroup::List::ConstIterator it = list.begin();
00183 it != list.end(); it++)
00184 {
00185 QString icon;
00186 QString text;
00187 QString relPath;
00188 QString exec;
00189 bool isDir = false;
00190 KSycocaEntry *p = (*it);
00191 if (p->isType(KST_KService))
00192 {
00193 KService *service = static_cast<KService *>(p);
00194
00195 if (service->noDisplay())
00196 continue;
00197
00198 icon = service->icon();
00199 text = service->name();
00200 exec = service->exec();
00201 }
00202 else if (p->isType(KST_KServiceGroup))
00203 {
00204 KServiceGroup *serviceGroup = static_cast<KServiceGroup *>(p);
00205
00206 if (serviceGroup->noDisplay())
00207 continue;
00208
00209 icon = serviceGroup->icon();
00210 text = serviceGroup->caption();
00211 relPath = serviceGroup->relPath();
00212 isDir = true;
00213 if ( text[0] == '.' )
00214 continue;
00215 }
00216 else
00217 {
00218 kdWarning(250) << "KServiceGroup: Unexpected object in list!" << endl;
00219 continue;
00220 }
00221
00222 QPixmap pixmap = appIcon( icon );
00223
00224 if (item)
00225 newItem = new KAppTreeListItem( item, text, pixmap, false, isDir,
00226 relPath, exec );
00227 else
00228 newItem = new KAppTreeListItem( this, text, pixmap, false, isDir,
00229 relPath, exec );
00230 if (isDir)
00231 newItem->setExpandable( true );
00232 }
00233 }
00234
00235
00236
00237
00238 void KApplicationTree::slotItemHighlighted(QListViewItem* i)
00239 {
00240
00241 if(!i)
00242 return;
00243
00244 KAppTreeListItem *item = (KAppTreeListItem *) i;
00245
00246 currentitem = item;
00247
00248 if( (!item->directory ) && (!item->exec.isEmpty()) )
00249 emit highlighted( item->text(0), item->exec );
00250 }
00251
00252
00253
00254
00255 void KApplicationTree::slotSelectionChanged(QListViewItem* i)
00256 {
00257
00258 if(!i)
00259 return;
00260
00261 KAppTreeListItem *item = (KAppTreeListItem *) i;
00262
00263 currentitem = item;
00264
00265 if( ( !item->directory ) && (!item->exec.isEmpty() ) )
00266 emit selected( item->text(0), item->exec );
00267 }
00268
00269
00270
00271 void KApplicationTree::resizeEvent( QResizeEvent * e)
00272 {
00273 setColumnWidth(0, width()-QApplication::style().pixelMetric(QStyle::PM_ScrollBarExtent)
00274 -2*QApplication::style().pixelMetric(QStyle::PM_DefaultFrameWidth));
00275 KListView::resizeEvent(e);
00276 }
00277
00278
00279
00280
00281
00282
00283
00284
00285 KOpenWithDlg::KOpenWithDlg( const KURL::List& _urls, QWidget* parent )
00286 :QDialog( parent, 0L, true )
00287 {
00288 setCaption( i18n( "Open With" ) );
00289 QString text;
00290 if( _urls.count() == 1 )
00291 {
00292 text = i18n("<qt>Select the program that should be used to open <b>%1</b>. "
00293 "If the program is not listed, enter the name or click "
00294 "the browse button.</qt>").arg( _urls.first().fileName() );
00295 }
00296 else
00297
00298 text = i18n( "Choose the name of the program with which to open the selected files." );
00299 setServiceType( _urls );
00300 init( text, QString() );
00301 }
00302
00303 KOpenWithDlg::KOpenWithDlg( const KURL::List& _urls, const QString&_text,
00304 const QString& _value, QWidget *parent)
00305 :QDialog( parent, 0L, true )
00306 {
00307 QString caption = KStringHandler::csqueeze( _urls.first().prettyURL() );
00308 if (_urls.count() > 1)
00309 caption += QString::fromLatin1("...");
00310 setCaption(caption);
00311 setServiceType( _urls );
00312 init( _text, _value );
00313 }
00314
00315 KOpenWithDlg::KOpenWithDlg( const QString &serviceType, const QString& value,
00316 QWidget *parent)
00317 :QDialog( parent, 0L, true )
00318 {
00319 setCaption(i18n("Choose Application for %1").arg(serviceType));
00320 QString text = i18n("<qt>Select the program for the file type: <b>%1</b>. "
00321 "If the program is not listed, enter the name or click "
00322 "the browse button.</qt>").arg(serviceType);
00323 qServiceType = serviceType;
00324 init( text, value );
00325 if (remember)
00326 {
00327 remember->setChecked( true );
00328 remember->hide();
00329 }
00330 }
00331
00332 KOpenWithDlg::KOpenWithDlg( QWidget *parent)
00333 :QDialog( parent, 0L, true )
00334 {
00335 setCaption(i18n("Choose Application"));
00336 QString text = i18n("<qt>Select a program. "
00337 "If the program is not listed, enter the name or click "
00338 "the browse button.</qt>");
00339 qServiceType = QString::null;
00340 init( text, QString::null );
00341 }
00342
00343 void KOpenWithDlg::setServiceType( const KURL::List& _urls )
00344 {
00345 if ( _urls.count() == 1 )
00346 {
00347 qServiceType = KMimeType::findByURL( _urls.first())->name();
00348 if (qServiceType == QString::fromLatin1("application/octet-stream"))
00349 qServiceType = QString::null;
00350 }
00351 else
00352 qServiceType = QString::null;
00353 }
00354
00355 void KOpenWithDlg::init( const QString& _text, const QString& _value )
00356 {
00357 bool bReadOnly = kapp && !kapp->authorize("shell_access");
00358 m_terminaldirty = false;
00359 m_pTree = 0L;
00360 m_pService = 0L;
00361
00362 QBoxLayout *topLayout = new QVBoxLayout( this, KDialog::marginHint(),
00363 KDialog::spacingHint() );
00364 label = new QLabel( _text, this );
00365 topLayout->addWidget(label);
00366
00367 QHBoxLayout* hbox = new QHBoxLayout(topLayout);
00368
00369 QToolButton *clearButton = new QToolButton( this );
00370 clearButton->setIconSet( BarIcon( "locationbar_erase" ) );
00371 clearButton->setFixedSize( clearButton->sizeHint() );
00372 connect( clearButton, SIGNAL( pressed() ), SLOT( slotClear() ) );
00373 QToolTip::add( clearButton, i18n( "Clear input field" ) );
00374
00375 hbox->addWidget( clearButton );
00376
00377 if (!bReadOnly)
00378 {
00379
00380 KHistoryCombo *combo = new KHistoryCombo();
00381 combo->setDuplicatesEnabled( false );
00382 KConfig *kc = KGlobal::config();
00383 KConfigGroupSaver ks( kc, QString::fromLatin1("Open-with settings") );
00384 int max = kc->readNumEntry( QString::fromLatin1("Maximum history"), 15 );
00385 combo->setMaxCount( max );
00386 int mode = kc->readNumEntry(QString::fromLatin1("CompletionMode"),
00387 KGlobalSettings::completionMode());
00388 combo->setCompletionMode((KGlobalSettings::Completion)mode);
00389 QStringList list = kc->readListEntry( QString::fromLatin1("History") );
00390 combo->setHistoryItems( list, true );
00391 edit = new KURLRequester( combo, this );
00392 }
00393 else
00394 {
00395 clearButton->hide();
00396 edit = new KURLRequester( this );
00397 edit->lineEdit()->setReadOnly(true);
00398 edit->button()->hide();
00399 }
00400
00401 edit->setURL( _value );
00402
00403 hbox->addWidget(edit);
00404
00405 if ( edit->comboBox() ) {
00406 KURLCompletion *comp = new KURLCompletion( KURLCompletion::ExeCompletion );
00407 edit->comboBox()->setCompletionObject( comp );
00408 }
00409
00410 connect ( edit, SIGNAL(returnPressed()), SLOT(slotOK()) );
00411 connect ( edit, SIGNAL(textChanged(const QString&)), SLOT(slotTextChanged()) );
00412
00413 m_pTree = new KApplicationTree( this );
00414 topLayout->addWidget(m_pTree);
00415
00416 connect( m_pTree, SIGNAL( selected( const QString&, const QString& ) ),
00417 SLOT( slotSelected( const QString&, const QString& ) ) );
00418 connect( m_pTree, SIGNAL( highlighted( const QString&, const QString& ) ),
00419 SLOT( slotHighlighted( const QString&, const QString& ) ) );
00420 connect( m_pTree, SIGNAL( doubleClicked(QListViewItem*) ),
00421 SLOT( slotDbClick() ) );
00422
00423 terminal = new QCheckBox( i18n("Run in &terminal"), this );
00424 if (bReadOnly)
00425 terminal->hide();
00426 connect(terminal, SIGNAL(toggled(bool)), SLOT(slotTerminalToggled(bool)));
00427
00428 topLayout->addWidget(terminal);
00429
00430 if (!qServiceType.isNull())
00431 {
00432 remember = new QCheckBox(i18n("&Remember application association for this type of file"), this);
00433
00434 topLayout->addWidget(remember);
00435 }
00436 else
00437 remember = 0L;
00438
00439
00440 KButtonBox* b = new KButtonBox( this );
00441 b->addStretch( 2 );
00442
00443 QPushButton* ok = b->addButton( i18n ( "&OK" ) );
00444 ok->setDefault( true );
00445 connect( ok, SIGNAL( clicked() ), SLOT( slotOK() ) );
00446
00447 QPushButton* cancel = b->addButton( i18n( "&Cancel" ) );
00448 connect( cancel, SIGNAL( clicked() ), SLOT( reject() ) );
00449
00450 b->layout();
00451 topLayout->addWidget( b );
00452
00453
00454
00455
00456
00457 edit->setFocus();
00458 }
00459
00460
00461
00462
00463 KOpenWithDlg::~KOpenWithDlg()
00464 {
00465 }
00466
00467
00468
00469 void KOpenWithDlg::slotClear()
00470 {
00471 edit->setURL(QString::null);
00472 edit->setFocus();
00473 }
00474
00475
00476
00477
00478 void KOpenWithDlg::slotSelected( const QString& , const QString& _exec )
00479 {
00480 kdDebug(250)<<"KOpenWithDlg::slotSelected"<<endl;
00481 KService::Ptr pService = m_pService;
00482 edit->setURL( _exec );
00483 m_pService = pService;
00484 }
00485
00486
00487
00488
00489 void KOpenWithDlg::slotHighlighted( const QString& _name, const QString& )
00490 {
00491 kdDebug(250)<<"KOpenWithDlg::slotHighlighted"<<endl;
00492 qName = _name;
00493 m_pService = KService::serviceByName( qName );
00494 if (!m_terminaldirty)
00495 {
00496
00497 terminal->setChecked(m_pService->terminal());
00498 m_terminaldirty = false;
00499 }
00500 }
00501
00502
00503
00504 void KOpenWithDlg::slotTextChanged()
00505 {
00506 kdDebug(250)<<"KOpenWithDlg::slotTextChanged"<<endl;
00507
00508 m_pService = 0L;
00509 }
00510
00511
00512
00513 void KOpenWithDlg::slotTerminalToggled(bool)
00514 {
00515
00516 m_terminaldirty = true;
00517 }
00518
00519
00520
00521 void KOpenWithDlg::slotDbClick()
00522 {
00523 if (m_pTree->isDirSel() ) return;
00524 slotOK();
00525 }
00526
00527 void KOpenWithDlg::slotOK()
00528 {
00529 QString fullExec(edit->url());
00530
00531 QString serviceName;
00532 QString pathName;
00533 QString initialServiceName;
00534 if (!m_pService) {
00535
00536
00537
00538 serviceName = KRun::binaryName( fullExec, true );
00539 if (serviceName.isEmpty())
00540 {
00541
00542 return;
00543 }
00544 initialServiceName = serviceName;
00545 int i = 1;
00546
00547 bool ok = false;
00548 do {
00549 KService::Ptr serv = KService::serviceByDesktopName( serviceName );
00550 ok = !serv;
00551
00552 if ( serv &&
00553 serv->type() == "Application" && (
00554 serv->exec() == fullExec ||
00555 serv->exec() == fullExec + " %u" ||
00556 serv->exec() == fullExec + " %U" ||
00557 serv->exec() == fullExec + " %f" ||
00558 serv->exec() == fullExec + " %F"
00559 ) )
00560 {
00561 ok = true;
00562 m_pService = serv;
00563 }
00564 if (!ok)
00565 {
00566 ++i;
00567 serviceName = initialServiceName + "-" + QString::number(i);
00568 }
00569 }
00570 while (!ok);
00571 if ( !m_pService )
00572 {
00573
00574 pathName = ".hidden/";
00575 pathName += serviceName;
00576 }
00577 }
00578 if ( m_pService )
00579 {
00580
00581 serviceName = m_pService->name();
00582 initialServiceName = serviceName;
00583 pathName = m_pService->desktopEntryPath();
00584 }
00585
00586 if (terminal->isChecked()) {
00587 KSimpleConfig conf(QString::fromLatin1("konquerorrc"), true);
00588 conf.setGroup(QString::fromLatin1("Misc Defaults"));
00589 m_command = conf.readPathEntry(QString::fromLatin1("Terminal"), QString::fromLatin1("konsole"));
00590
00591 m_command += QString::fromLatin1(" -e ");
00592 m_command += edit->url();
00593 kdDebug(250) << "Setting m_command to " << m_command << endl;
00594 }
00595 if ( m_pService && terminal->isChecked() != m_pService->terminal() )
00596 m_pService = 0L;
00597
00598 if ( m_pService && ( !remember || !remember->isChecked() ) ) {
00599 accept();
00600 return;
00601 }
00602
00603
00604
00605
00606
00607 kdDebug(250) << "service pathName=" << pathName << endl;
00608 if (pathName.right(8) != QString::fromLatin1(".desktop"))
00609 pathName += QString::fromLatin1(".desktop");
00610 QString path(locateLocal("apps", pathName));
00611
00612 int maxPreference = 1;
00613 if (!qServiceType.isEmpty())
00614 {
00615 KServiceTypeProfile::OfferList offerList = KServiceTypeProfile::offers( qServiceType );
00616 if (!offerList.isEmpty())
00617 maxPreference = offerList.first().preference();
00618 }
00619
00620 KDesktopFile desktop(path);
00621 desktop.writeEntry(QString::fromLatin1("Type"), QString::fromLatin1("Application"));
00622 desktop.writeEntry(QString::fromLatin1("Name"), initialServiceName);
00623 desktop.writeEntry(QString::fromLatin1("Exec"), fullExec);
00624 desktop.writeEntry(QString::fromLatin1("InitialPreference"), maxPreference + 1);
00625 if (remember)
00626 if (remember->isChecked()) {
00627 QStringList mimeList;
00628 KDesktopFile oldDesktop(locate("apps", pathName), true);
00629 mimeList = oldDesktop.readListEntry(QString::fromLatin1("MimeType"), ';');
00630 if (!qServiceType.isEmpty() && !mimeList.contains(qServiceType))
00631 mimeList.append(qServiceType);
00632 desktop.writeEntry(QString::fromLatin1("MimeType"), mimeList, ';');
00633 if (terminal->isChecked())
00634 desktop.writeEntry(QString::fromLatin1("Terminal"), true);
00635 else
00636 desktop.writeEntry(QString::fromLatin1("Terminal"), false);
00637
00638 if ( !qServiceType.isEmpty() )
00639 {
00640
00641 KDesktopFile mimeDesktop( locateLocal( "mime", qServiceType + ".desktop" ) );
00642 mimeDesktop.writeEntry( QString::fromLatin1( "X-KDE-AutoEmbed" ), false );
00643 mimeDesktop.sync();
00644 }
00645 }
00646
00647
00648
00649 desktop.sync();
00650
00651 QApplication::setOverrideCursor( waitCursor );
00652
00653
00654 QStringList args;
00655 args.append("--incremental");
00656 KApplication::kdeinitExecWait( "kbuildsycoca", args );
00657
00658
00659 kdDebug(250) << "kbuildsycoca finished, looking for service " << pathName << endl;
00660
00661
00662
00663 QStringList lst;
00664 lst << QString::fromLatin1("apps");
00665 KSycoca::self()->notifyDatabaseChanged( lst );
00666
00667 m_pService = KService::serviceByDesktopPath( pathName );
00668 QApplication::restoreOverrideCursor();
00669
00670 Q_ASSERT( m_pService );
00671
00672 accept();
00673 }
00674
00675 QString KOpenWithDlg::text() const
00676 {
00677 if (!m_command.isEmpty())
00678 return m_command;
00679 else
00680 return edit->url();
00681 }
00682
00683 void KOpenWithDlg::accept()
00684 {
00685 KHistoryCombo *combo = static_cast<KHistoryCombo*>( edit->comboBox() );
00686 if ( combo ) {
00687 combo->addToHistory( edit->url() );
00688
00689 KConfig *kc = KGlobal::config();
00690 KConfigGroupSaver ks( kc, QString::fromLatin1("Open-with settings") );
00691 kc->writeEntry( QString::fromLatin1("History"), combo->historyItems() );
00692 kc->writeEntry(QString::fromLatin1("CompletionMode"),
00693 combo->completionMode());
00694
00695
00696 kc->sync();
00697 }
00698
00699 QDialog::accept();
00700 }
00701
00702
00704
00705 #ifndef KDE_NO_COMPAT
00706 bool KFileOpenWithHandler::displayOpenWithDialog( const KURL::List& urls )
00707 {
00708 KOpenWithDlg l( urls, i18n("Open with:"), QString::null, 0L );
00709 if ( l.exec() )
00710 {
00711 KService::Ptr service = l.service();
00712 if ( !!service )
00713 return KRun::run( *service, urls );
00714
00715 kdDebug(250) << "No service set, running " << l.text() << endl;
00716 return KRun::run( l.text(), urls );
00717 }
00718 return false;
00719 }
00720 #endif
00721
00722 #include "kopenwith.moc"
00723 #include "kopenwith_p.moc"
00724