Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / apps / konqueror / settings / filetypes / filetypesview.cpp
blob688ad51102f03bdcd8b8227e55d24e0986b73c1a
1 /* Missing license header */
3 // Own
4 #include "filetypesview.h"
5 #include "mimetypewriter.h"
7 // Qt
8 #include <QtGui/QLabel>
9 #include <QtGui/QLayout>
10 #include <QtGui/QPushButton>
11 #include <QtCore/QTimer>
12 #include <QtGui/QGridLayout>
13 #include <QtGui/QBoxLayout>
15 // KDE
16 #include <kapplication.h>
17 #include <kbuildsycocaprogressdialog.h>
18 #include <kcursor.h>
19 #include <kdebug.h>
20 #include <kdesktopfile.h>
21 #include <klineedit.h>
22 #include <k3listview.h>
23 #include <klocale.h>
24 #include <kservicetypeprofile.h>
25 #include <kstandarddirs.h>
26 #include <ksycoca.h>
27 #include <kpluginfactory.h>
28 #include <kpluginloader.h>
30 // Local
31 #include "newtypedlg.h"
32 #include "filetypedetails.h"
33 #include "filegroupdetails.h"
36 K_PLUGIN_FACTORY(FileTypesViewFactory, registerPlugin<FileTypesView>();)
37 K_EXPORT_PLUGIN(FileTypesViewFactory("filetypes"))
40 FileTypesView::FileTypesView(QWidget *parent, const QVariantList &)
41 : KCModule(FileTypesViewFactory::componentData(), parent)
43 m_fileTypesConfig = KSharedConfig::openConfig("filetypesrc", KConfig::NoGlobals);
45 // TODO remove mention of *.kwd and use another example
46 setQuickHelp( i18n("<p><h1>File Associations</h1>"
47 " This module allows you to choose which applications are associated"
48 " with a given type of file. File types are also referred to MIME types"
49 " (MIME is an acronym which stands for \"Multipurpose Internet Mail"
50 " Extensions\".)</p><p> A file association consists of the following:"
51 " <ul><li>Rules for determining the MIME-type of a file, for example"
52 " the filename pattern *.kwd, which means 'all files with names that end"
53 " in .kwd', is associated with the MIME type \"x-kword\";</li>"
54 " <li>A short description of the MIME-type, for example the description"
55 " of the MIME type \"x-kword\" is simply 'KWord document';</li>"
56 " <li>An icon to be used for displaying files of the given MIME-type,"
57 " so that you can easily identify the type of file in, say, a Konqueror"
58 " view (at least for the types you use often);</li>"
59 " <li>A list of the applications which can be used to open files of the"
60 " given MIME-type -- if more than one application can be used then the"
61 " list is ordered by priority.</li></ul>"
62 " You may be surprised to find that some MIME types have no associated"
63 " filename patterns; in these cases, Konqueror is able to determine the"
64 " MIME-type by directly examining the contents of the file.</p>"));
66 KServiceTypeProfile::setConfigurationMode();
67 setButtons(Help | Apply);
68 QString wtstr;
70 QHBoxLayout *l = new QHBoxLayout(this);
71 QGridLayout *leftLayout = new QGridLayout((QWidget*)0L);
72 leftLayout->setSpacing(KDialog::spacingHint());
73 leftLayout->setColumnStretch(1, 1);
75 l->addLayout( leftLayout );
77 QLabel *patternFilterLBL = new QLabel(i18n("F&ind filename pattern:"), this);
78 leftLayout->addWidget(patternFilterLBL, 0, 0, 1, 3);
80 patternFilterLE = new KLineEdit(this);
81 patternFilterLE->setClearButtonShown(true);
82 patternFilterLBL->setBuddy( patternFilterLE );
83 leftLayout->addWidget(patternFilterLE, 1, 0, 1, 3);
85 connect(patternFilterLE, SIGNAL(textChanged(const QString &)),
86 this, SLOT(slotFilter(const QString &)));
88 wtstr = i18n("Enter a part of a filename pattern. Only file types with a "
89 "matching file pattern will appear in the list.");
91 patternFilterLE->setWhatsThis( wtstr );
92 patternFilterLBL->setWhatsThis( wtstr );
94 typesLV = new K3ListView(this);
95 typesLV->setRootIsDecorated(true);
96 typesLV->setFullWidth(true);
98 typesLV->addColumn(i18n("Known Types"));
99 leftLayout->addWidget(typesLV, 2, 0, 1, 3);
100 connect(typesLV, SIGNAL(selectionChanged(Q3ListViewItem *)),
101 this, SLOT(updateDisplay(Q3ListViewItem *)));
102 connect(typesLV, SIGNAL(doubleClicked(Q3ListViewItem *)),
103 this, SLOT(slotDoubleClicked(Q3ListViewItem *)));
105 typesLV->setWhatsThis( i18n("Here you can see a hierarchical list of"
106 " the file types which are known on your system. Click on the '+' sign"
107 " to expand a category, or the '-' sign to collapse it. Select a file type"
108 " (e.g. text/html for HTML files) to view/edit the information for that"
109 " file type using the controls on the right.") );
111 QPushButton *addTypeB = new QPushButton(i18n("Add..."), this);
112 connect(addTypeB, SIGNAL(clicked()), SLOT(addType()));
113 leftLayout->addWidget(addTypeB, 3, 0);
115 addTypeB->setWhatsThis( i18n("Click here to add a new file type.") );
117 m_removeTypeB = new QPushButton(i18n("&Remove"), this);
118 connect(m_removeTypeB, SIGNAL(clicked()), SLOT(removeType()));
119 leftLayout->addWidget(m_removeTypeB, 3, 2);
120 m_removeTypeB->setEnabled(false);
122 m_removeTypeB->setWhatsThis( i18n("Click here to remove the selected file type.") );
123 #if 1 // TODO remove after porting the add / remove code
124 addTypeB->hide();
125 m_removeTypeB->hide();
126 #endif
128 // For the right panel, prepare a widget stack
129 m_widgetStack = new QStackedWidget(this);
131 l->addWidget( m_widgetStack );
133 // File Type Details
134 m_details = new FileTypeDetails( m_widgetStack );
135 connect( m_details, SIGNAL( changed(bool) ),
136 this, SLOT( setDirty(bool) ) );
137 connect( m_details, SIGNAL( embedMajor(const QString &, bool &) ),
138 this, SLOT( slotEmbedMajor(const QString &, bool &)));
139 m_widgetStack->insertWidget( 1, m_details /*id*/ );
141 // File Group Details
142 m_groupDetails = new FileGroupDetails( m_widgetStack );
143 connect( m_groupDetails, SIGNAL( changed(bool) ),
144 this, SLOT( setDirty(bool) ) );
145 m_widgetStack->insertWidget( 2,m_groupDetails /*id*/ );
147 // Widget shown on startup
148 m_emptyWidget = new QLabel( i18n("Select a file type by name or by extension"), m_widgetStack);
149 m_emptyWidget->setAlignment( Qt::AlignCenter );
151 m_widgetStack->insertWidget( 3,m_emptyWidget );
153 m_widgetStack->setCurrentWidget( m_emptyWidget );
155 QTimer::singleShot( 0, this, SLOT( init() ) ); // this takes some time
157 connect( KSycoca::self(), SIGNAL( databaseChanged() ), SLOT( slotDatabaseChanged() ) );
160 FileTypesView::~FileTypesView()
164 void FileTypesView::setDirty(bool state)
166 emit changed(state);
167 m_dirty = state;
170 void FileTypesView::init()
172 show();
173 setEnabled( false );
175 setCursor( Qt::WaitCursor );
176 readFileTypes();
177 unsetCursor();
179 setDirty(false);
180 setEnabled( true );
183 // only call this method once on startup, then never again! Otherwise, newly
184 // added Filetypes will be lost.
185 void FileTypesView::readFileTypes()
187 typesLV->clear();
188 m_majorMap.clear();
189 m_itemList.clear();
191 TypesListItem *groupItem;
192 KMimeType::List mimetypes = KMimeType::allMimeTypes();
193 KMimeType::List::const_iterator it2(mimetypes.begin());
194 for (; it2 != mimetypes.end(); ++it2) {
195 QString mimetype = (*it2)->name();
196 int index = mimetype.indexOf("/");
197 QString maj = mimetype.left(index);
198 QString min = mimetype.right(mimetype.length() - index+1);
200 QMap<QString,TypesListItem*>::const_iterator mit = m_majorMap.find( maj );
201 if ( mit == m_majorMap.end() ) {
202 groupItem = new TypesListItem( typesLV, maj );
203 m_majorMap.insert( maj, groupItem );
205 else
206 groupItem = mit.value();
208 TypesListItem *item = new TypesListItem(groupItem, (*it2));
209 m_itemList.append( item );
211 updateDisplay(0L);
215 void FileTypesView::slotEmbedMajor(const QString &major, bool &embed)
217 TypesListItem *groupItem;
218 QMap<QString,TypesListItem*>::const_iterator mit = m_majorMap.find( major );
219 if ( mit == m_majorMap.end() )
220 return;
222 groupItem = mit.value();
224 embed = (groupItem->mimeTypeData().autoEmbed() == 0);
227 void FileTypesView::slotFilter(const QString & patternFilter)
229 // one of the few ways to clear a listview without destroying the
230 // listviewitems and without making QListView crash.
231 Q3ListViewItem *item;
232 while ( (item = typesLV->firstChild()) ) {
233 while ( item->firstChild() )
234 item->takeItem( item->firstChild() );
236 typesLV->takeItem( item );
239 // insert all items and their group that match the filter
240 Q3PtrListIterator<TypesListItem> it( m_itemList );
241 while ( it.current() ) {
242 const MimeTypeData& mimeTypeData = (*it)->mimeTypeData();
243 if ( patternFilter.isEmpty() ||
244 !(mimeTypeData.patterns().filter( patternFilter, Qt::CaseInsensitive )).isEmpty() ) {
246 TypesListItem *group = m_majorMap[ mimeTypeData.majorType() ];
247 // QListView makes sure we don't insert a group-item more than once
248 typesLV->insertItem( group );
249 group->insertItem( *it );
251 ++it;
255 void FileTypesView::addType()
257 QStringList allGroups;
258 QMap<QString,TypesListItem*>::iterator it = m_majorMap.begin();
259 while ( it != m_majorMap.end() ) {
260 allGroups.append( it.key() );
261 ++it;
264 NewTypeDialog m(allGroups, this);
266 // TODO write out a kde-user-mimetypes.xml - or a file per mimetype
267 // in locateLocal("xdgdata-mime", "packages")
268 // And make the code for creating it useable from keditfiletype.cpp
269 #if 0
270 if (m.exec()) {
271 Q3ListViewItemIterator it(typesLV);
272 QString loc = m.group() + '/' + m.text() + ".desktop";
273 loc = KStandardDirs::locate("mime", loc);
274 KMimeType::Ptr mimetype(new KMimeType(loc,
275 m.group() + '/' + m.text(),
276 QString(), QString(),
277 QStringList()));
279 TypesListItem *group = m_majorMap[ m.group() ];
280 if ( !group )
282 //group = new TypesListItem(
283 //TODO ! (The combo in NewTypeDialog must be made editable again when that happens)
284 Q_ASSERT(group);
287 // find out if our group has been filtered out -> insert if necessary
288 Q3ListViewItem *item = typesLV->firstChild();
289 bool insert = true;
290 while ( item ) {
291 if ( item == group ) {
292 insert = false;
293 break;
295 item = item->nextSibling();
297 if ( insert )
298 typesLV->insertItem( group );
300 TypesListItem *tli = new TypesListItem(group, mimetype, true);
301 m_itemList.append( tli );
303 group->setOpen(true);
304 typesLV->setSelected(tli, true);
306 setDirty(true);
308 #endif
311 void FileTypesView::removeType()
313 TypesListItem *current = (TypesListItem *) typesLV->currentItem();
315 if ( !current )
316 return;
318 const MimeTypeData& mimeTypeData = current->mimeTypeData();
320 // Can't delete groups
321 if ( mimeTypeData.isMeta() )
322 return;
323 // nor essential mimetypes
324 if ( mimeTypeData.isEssential() )
325 return;
327 Q3ListViewItem *li = current->itemAbove();
328 if (!li)
329 li = current->itemBelow();
330 if (!li)
331 li = current->parent();
333 removedList.append(mimeTypeData.name());
334 current->parent()->takeItem(current);
335 m_itemList.removeRef( current );
336 setDirty(true);
338 if ( li )
339 typesLV->setSelected(li, true);
342 void FileTypesView::slotDoubleClicked(Q3ListViewItem *item)
344 if ( !item ) return;
345 item->setOpen( !item->isOpen() );
348 void FileTypesView::updateDisplay(Q3ListViewItem *item)
350 if (!item)
352 m_widgetStack->setCurrentWidget( m_emptyWidget );
353 m_removeTypeB->setEnabled(false);
354 return;
357 bool wasDirty = m_dirty;
359 TypesListItem *tlitem = (TypesListItem *) item;
360 MimeTypeData& mimeTypeData = tlitem->mimeTypeData();
362 if (mimeTypeData.isMeta()) // is a group
364 m_widgetStack->setCurrentWidget( m_groupDetails );
365 m_groupDetails->setMimeTypeData( &mimeTypeData );
366 m_removeTypeB->setEnabled(false);
368 else
370 m_widgetStack->setCurrentWidget( m_details );
371 m_details->setMimeTypeData( &mimeTypeData );
372 m_removeTypeB->setEnabled( !mimeTypeData.isEssential() );
375 // Updating the display indirectly called change(true)
376 if ( !wasDirty )
377 setDirty(false);
380 void FileTypesView::save()
382 m_itemsModified.clear();
383 bool didIt = false;
384 // first, remove those items which we are asked to remove.
385 QStringList::Iterator it(removedList.begin());
386 QString loc;
388 for (; it != removedList.end(); ++it) {
389 #if 0
390 didIt = true;
391 KMimeType::Ptr m_ptr = KMimeType::mimeType(*it);
392 loc = KStandardDirs::locate("mime", m_ptr->entryPath());
394 // TODO port to XDG shared mime!
395 KDesktopFile config("mime", loc);
396 config.desktopGroup().writeEntry("Type", "MimeType");
397 config.desktopGroup().writeEntry("MimeType", m_ptr->name());
398 config.desktopGroup().writeEntry("Hidden", true);
399 #endif
402 bool needUpdateMimeDb = false;
403 // now go through all entries and sync those which are dirty.
404 // don't use typesLV, it may be filtered
405 QMap<QString,TypesListItem*>::iterator it1 = m_majorMap.begin();
406 while ( it1 != m_majorMap.end() ) {
407 TypesListItem *tli = *it1;
408 if (tli->mimeTypeData().isDirty()) {
409 kDebug() << "Entry " << tli->name() << " is dirty. Saving.";
410 if (tli->mimeTypeData().sync())
411 needUpdateMimeDb = true;
412 m_itemsModified.append( tli );
413 didIt = true;
415 ++it1;
417 Q3PtrListIterator<TypesListItem> it2( m_itemList );
418 while ( it2.current() ) {
419 TypesListItem *tli = *it2;
420 if (tli->mimeTypeData().isDirty()) {
421 kDebug() << "Entry " << tli->name() << " is dirty. Saving.";
422 if (tli->mimeTypeData().sync())
423 needUpdateMimeDb = true;
424 m_itemsModified.append( tli );
425 didIt = true;
427 ++it2;
430 m_fileTypesConfig->sync();
432 setDirty(false);
434 if (needUpdateMimeDb) {
435 MimeTypeWriter::runUpdateMimeDatabase();
437 if (didIt) {
438 KBuildSycocaProgressDialog::rebuildKSycoca(this);
439 KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged);
443 void FileTypesView::load()
445 readFileTypes();
448 void FileTypesView::slotDatabaseChanged()
450 if ( KSycoca::self()->isChanged( "xdgdata-mime" ) )
452 // ksycoca has new KMimeTypes objects for us, make sure to update
453 // our 'copies' to be in sync with it. Not important for OK, but
454 // important for Apply (how to differentiate those 2?).
455 // See BR 35071.
456 QList<TypesListItem *>::Iterator it = m_itemsModified.begin();
457 for( ; it != m_itemsModified.end(); ++it ) {
458 QString name = (*it)->name();
459 if ( removedList.indexOf( name ) == -1 ) // if not deleted meanwhile
460 (*it)->mimeTypeData().refresh();
462 m_itemsModified.clear();
466 void FileTypesView::defaults()
470 #include "filetypesview.moc"