1 /* Missing license header */
4 #include "filetypesview.h"
5 #include "mimetypewriter.h"
8 #include <QtGui/QLabel>
9 #include <QtGui/QLayout>
10 #include <QtGui/QPushButton>
11 #include <QtCore/QTimer>
12 #include <QtGui/QGridLayout>
13 #include <QtGui/QBoxLayout>
16 #include <kapplication.h>
17 #include <kbuildsycocaprogressdialog.h>
20 #include <kdesktopfile.h>
21 #include <klineedit.h>
22 #include <k3listview.h>
24 #include <kservicetypeprofile.h>
25 #include <kstandarddirs.h>
27 #include <kpluginfactory.h>
28 #include <kpluginloader.h>
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
);
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
125 m_removeTypeB
->hide();
128 // For the right panel, prepare a widget stack
129 m_widgetStack
= new QStackedWidget(this);
131 l
->addWidget( m_widgetStack
);
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
)
170 void FileTypesView::init()
175 setCursor( Qt::WaitCursor
);
183 // only call this method once on startup, then never again! Otherwise, newly
184 // added Filetypes will be lost.
185 void FileTypesView::readFileTypes()
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
);
206 groupItem
= mit
.value();
208 TypesListItem
*item
= new TypesListItem(groupItem
, (*it2
));
209 m_itemList
.append( item
);
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() )
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
);
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() );
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
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(),
279 TypesListItem
*group
= m_majorMap
[ m
.group() ];
282 //group = new TypesListItem(
283 //TODO ! (The combo in NewTypeDialog must be made editable again when that happens)
287 // find out if our group has been filtered out -> insert if necessary
288 Q3ListViewItem
*item
= typesLV
->firstChild();
291 if ( item
== group
) {
295 item
= item
->nextSibling();
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);
311 void FileTypesView::removeType()
313 TypesListItem
*current
= (TypesListItem
*) typesLV
->currentItem();
318 const MimeTypeData
& mimeTypeData
= current
->mimeTypeData();
320 // Can't delete groups
321 if ( mimeTypeData
.isMeta() )
323 // nor essential mimetypes
324 if ( mimeTypeData
.isEssential() )
327 Q3ListViewItem
*li
= current
->itemAbove();
329 li
= current
->itemBelow();
331 li
= current
->parent();
333 removedList
.append(mimeTypeData
.name());
334 current
->parent()->takeItem(current
);
335 m_itemList
.removeRef( current
);
339 typesLV
->setSelected(li
, true);
342 void FileTypesView::slotDoubleClicked(Q3ListViewItem
*item
)
345 item
->setOpen( !item
->isOpen() );
348 void FileTypesView::updateDisplay(Q3ListViewItem
*item
)
352 m_widgetStack
->setCurrentWidget( m_emptyWidget
);
353 m_removeTypeB
->setEnabled(false);
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);
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)
380 void FileTypesView::save()
382 m_itemsModified
.clear();
384 // first, remove those items which we are asked to remove.
385 QStringList::Iterator
it(removedList
.begin());
388 for (; it
!= removedList
.end(); ++it
) {
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);
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
);
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
);
430 m_fileTypesConfig
->sync();
434 if (needUpdateMimeDb
) {
435 MimeTypeWriter::runUpdateMimeDatabase();
438 KBuildSycocaProgressDialog::rebuildKSycoca(this);
439 KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged
);
443 void FileTypesView::load()
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?).
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"