summaryrefslogtreecommitdiffstats
path: root/kcontrol/filetypes/filetypesview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kcontrol/filetypes/filetypesview.cpp')
-rw-r--r--kcontrol/filetypes/filetypesview.cpp440
1 files changed, 440 insertions, 0 deletions
diff --git a/kcontrol/filetypes/filetypesview.cpp b/kcontrol/filetypes/filetypesview.cpp
new file mode 100644
index 000000000..4088e9eee
--- /dev/null
+++ b/kcontrol/filetypes/filetypesview.cpp
@@ -0,0 +1,440 @@
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qtimer.h>
+#include <qwhatsthis.h>
+#include <qwidgetstack.h>
+
+#include <dcopclient.h>
+
+#include <kapplication.h>
+#include <kcursor.h>
+#include <kdebug.h>
+#include <kdesktopfile.h>
+#include <kipc.h>
+#include <klineedit.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+#include "newtypedlg.h"
+#include "filetypedetails.h"
+#include "filegroupdetails.h"
+#include "filetypesview.h"
+#include <ksycoca.h>
+
+FileTypesView::FileTypesView(QWidget *p, const char *name)
+ : KCModule(p, name)
+{
+ m_konqConfig = KSharedConfig::openConfig("konquerorrc", false, false);
+
+ setQuickHelp( i18n("<h1>File Associations</h1>"
+ " This module allows you to choose which applications are associated"
+ " with a given type of file. File types are also referred to MIME types"
+ " (MIME is an acronym which stands for \"Multipurpose Internet Mail"
+ " Extensions\".)<p> A file association consists of the following:"
+ " <ul><li>Rules for determining the MIME-type of a file, for example"
+ " the filename pattern *.kwd, which means 'all files with names that end"
+ " in .kwd', is associated with the MIME type \"x-kword\";</li>"
+ " <li>A short description of the MIME-type, for example the description"
+ " of the MIME type \"x-kword\" is simply 'KWord document';</li>"
+ " <li>An icon to be used for displaying files of the given MIME-type,"
+ " so that you can easily identify the type of file in, say, a Konqueror"
+ " view (at least for the types you use often);</li>"
+ " <li>A list of the applications which can be used to open files of the"
+ " given MIME-type -- if more than one application can be used then the"
+ " list is ordered by priority.</li></ul>"
+ " You may be surprised to find that some MIME types have no associated"
+ " filename patterns; in these cases, Konqueror is able to determine the"
+ " MIME-type by directly examining the contents of the file."));
+
+ setButtons(Help | Apply | Cancel | Ok);
+ QString wtstr;
+
+ QHBoxLayout *l = new QHBoxLayout(this, 0, KDialog::marginHint());
+ QGridLayout *leftLayout = new QGridLayout(0, 4, 3);
+ leftLayout->setSpacing( KDialog::spacingHint() );
+ leftLayout->setColStretch(1, 1);
+
+ l->addLayout( leftLayout );
+
+ QLabel *patternFilterLBL = new QLabel(i18n("F&ind filename pattern:"), this);
+ leftLayout->addMultiCellWidget(patternFilterLBL, 0, 0, 0, 2);
+
+ patternFilterLE = new KLineEdit(this);
+ patternFilterLBL->setBuddy( patternFilterLE );
+ leftLayout->addMultiCellWidget(patternFilterLE, 1, 1, 0, 2);
+
+ connect(patternFilterLE, SIGNAL(textChanged(const QString &)),
+ this, SLOT(slotFilter(const QString &)));
+
+ wtstr = i18n("Enter a part of a filename pattern. Only file types with a "
+ "matching file pattern will appear in the list.");
+
+ QWhatsThis::add( patternFilterLE, wtstr );
+ QWhatsThis::add( patternFilterLBL, wtstr );
+
+ typesLV = new KListView(this);
+ typesLV->setRootIsDecorated(true);
+ typesLV->setFullWidth(true);
+
+ typesLV->addColumn(i18n("Known Types"));
+ leftLayout->addMultiCellWidget(typesLV, 2, 2, 0, 2);
+ connect(typesLV, SIGNAL(selectionChanged(QListViewItem *)),
+ this, SLOT(updateDisplay(QListViewItem *)));
+ connect(typesLV, SIGNAL(doubleClicked(QListViewItem *)),
+ this, SLOT(slotDoubleClicked(QListViewItem *)));
+
+ QWhatsThis::add( typesLV, i18n("Here you can see a hierarchical list of"
+ " the file types which are known on your system. Click on the '+' sign"
+ " to expand a category, or the '-' sign to collapse it. Select a file type"
+ " (e.g. text/html for HTML files) to view/edit the information for that"
+ " file type using the controls on the right.") );
+
+ QPushButton *addTypeB = new QPushButton(i18n("Add..."), this);
+ connect(addTypeB, SIGNAL(clicked()), SLOT(addType()));
+ leftLayout->addWidget(addTypeB, 3, 0);
+
+ QWhatsThis::add( addTypeB, i18n("Click here to add a new file type.") );
+
+ m_removeTypeB = new QPushButton(i18n("&Remove"), this);
+ connect(m_removeTypeB, SIGNAL(clicked()), SLOT(removeType()));
+ leftLayout->addWidget(m_removeTypeB, 3, 2);
+ m_removeTypeB->setEnabled(false);
+
+ QWhatsThis::add( m_removeTypeB, i18n("Click here to remove the selected file type.") );
+
+ // For the right panel, prepare a widget stack
+ m_widgetStack = new QWidgetStack(this);
+
+ l->addWidget( m_widgetStack );
+
+ // File Type Details
+ m_details = new FileTypeDetails( m_widgetStack );
+ connect( m_details, SIGNAL( changed(bool) ),
+ this, SLOT( setDirty(bool) ) );
+ connect( m_details, SIGNAL( embedMajor(const QString &, bool &) ),
+ this, SLOT( slotEmbedMajor(const QString &, bool &)));
+ m_widgetStack->addWidget( m_details, 1 /*id*/ );
+
+ // File Group Details
+ m_groupDetails = new FileGroupDetails( m_widgetStack );
+ connect( m_groupDetails, SIGNAL( changed(bool) ),
+ this, SLOT( setDirty(bool) ) );
+ m_widgetStack->addWidget( m_groupDetails, 2 /*id*/ );
+
+ // Widget shown on startup
+ m_emptyWidget = new QLabel( i18n("Select a file type by name or by extension"), m_widgetStack);
+ m_emptyWidget->setAlignment(AlignCenter);
+
+ m_widgetStack->addWidget( m_emptyWidget, 3 /*id*/ );
+
+ m_widgetStack->raiseWidget( m_emptyWidget );
+
+ QTimer::singleShot( 0, this, SLOT( init() ) ); // this takes some time
+
+ connect( KSycoca::self(), SIGNAL( databaseChanged() ), SLOT( slotDatabaseChanged() ) );
+}
+
+FileTypesView::~FileTypesView()
+{
+}
+
+void FileTypesView::setDirty(bool state)
+{
+ emit changed(state);
+ m_dirty = state;
+}
+
+void FileTypesView::init()
+{
+ show();
+ setEnabled( false );
+
+ setCursor( KCursor::waitCursor() );
+ readFileTypes();
+ unsetCursor();
+
+ setDirty(false);
+ setEnabled( true );
+}
+
+// only call this method once on startup, then never again! Otherwise, newly
+// added Filetypes will be lost.
+void FileTypesView::readFileTypes()
+{
+ typesLV->clear();
+ m_majorMap.clear();
+ m_itemList.clear();
+ TypesListItem::reset();
+
+ TypesListItem *groupItem;
+ KMimeType::List mimetypes = KMimeType::allMimeTypes();
+ QValueListIterator<KMimeType::Ptr> it2(mimetypes.begin());
+ for (; it2 != mimetypes.end(); ++it2) {
+ QString mimetype = (*it2)->name();
+ int index = mimetype.find("/");
+ QString maj = mimetype.left(index);
+ QString min = mimetype.right(mimetype.length() - index+1);
+
+ QMapIterator<QString,TypesListItem*> mit = m_majorMap.find( maj );
+ if ( mit == m_majorMap.end() ) {
+ groupItem = new TypesListItem( typesLV, maj );
+ m_majorMap.insert( maj, groupItem );
+ }
+ else
+ groupItem = mit.data();
+
+ TypesListItem *item = new TypesListItem(groupItem, (*it2));
+ m_itemList.append( item );
+ }
+ updateDisplay(0L);
+
+}
+
+void FileTypesView::slotEmbedMajor(const QString &major, bool &embed)
+{
+ TypesListItem *groupItem;
+ QMapIterator<QString,TypesListItem*> mit = m_majorMap.find( major );
+ if ( mit == m_majorMap.end() )
+ return;
+
+ groupItem = mit.data();
+
+ embed = (groupItem->autoEmbed() == 0);
+}
+
+void FileTypesView::slotFilter(const QString & patternFilter)
+{
+ // one of the few ways to clear a listview without destroying the
+ // listviewitems and without making QListView crash.
+ QListViewItem *item;
+ while ( (item = typesLV->firstChild()) ) {
+ while ( item->firstChild() )
+ item->takeItem( item->firstChild() );
+
+ typesLV->takeItem( item );
+ }
+
+ // insert all items and their group that match the filter
+ QPtrListIterator<TypesListItem> it( m_itemList );
+ while ( it.current() ) {
+ if ( patternFilter.isEmpty() ||
+ !((*it)->patterns().grep( patternFilter, false )).isEmpty() ) {
+
+ TypesListItem *group = m_majorMap[ (*it)->majorType() ];
+ // QListView makes sure we don't insert a group-item more than once
+ typesLV->insertItem( group );
+ group->insertItem( *it );
+ }
+ ++it;
+ }
+}
+
+void FileTypesView::addType()
+{
+ QStringList allGroups;
+ QMapIterator<QString,TypesListItem*> it = m_majorMap.begin();
+ while ( it != m_majorMap.end() ) {
+ allGroups.append( it.key() );
+ ++it;
+ }
+
+ NewTypeDialog m(allGroups, this);
+
+ if (m.exec()) {
+ QListViewItemIterator it(typesLV);
+ QString loc = m.group() + "/" + m.text() + ".desktop";
+ loc = locateLocal("mime", loc);
+ KMimeType::Ptr mimetype = new KMimeType(loc,
+ m.group() + "/" + m.text(),
+ QString(), QString(),
+ QStringList());
+
+ TypesListItem *group = m_majorMap[ m.group() ];
+ if ( !group )
+ {
+ //group = new TypesListItem(
+ //TODO ! (The combo in NewTypeDialog must be made editable again when that happens)
+ Q_ASSERT(group);
+ }
+
+ // find out if our group has been filtered out -> insert if necessary
+ QListViewItem *item = typesLV->firstChild();
+ bool insert = true;
+ while ( item ) {
+ if ( item == group ) {
+ insert = false;
+ break;
+ }
+ item = item->nextSibling();
+ }
+ if ( insert )
+ typesLV->insertItem( group );
+
+ TypesListItem *tli = new TypesListItem(group, mimetype, true);
+ m_itemList.append( tli );
+
+ group->setOpen(true);
+ typesLV->setSelected(tli, true);
+
+ setDirty(true);
+ }
+}
+
+void FileTypesView::removeType()
+{
+ TypesListItem *current = (TypesListItem *) typesLV->currentItem();
+
+ if ( !current )
+ return;
+
+ // Can't delete groups
+ if ( current->isMeta() )
+ return;
+ // nor essential mimetypes
+ if ( current->isEssential() )
+ return;
+
+ QListViewItem *li = current->itemAbove();
+ if (!li)
+ li = current->itemBelow();
+ if (!li)
+ li = current->parent();
+
+ removedList.append(current->name());
+ current->parent()->takeItem(current);
+ m_itemList.removeRef( current );
+ setDirty(true);
+
+ if ( li )
+ typesLV->setSelected(li, true);
+}
+
+void FileTypesView::slotDoubleClicked(QListViewItem *item)
+{
+ if ( !item ) return;
+ item->setOpen( !item->isOpen() );
+}
+
+void FileTypesView::updateDisplay(QListViewItem *item)
+{
+ if (!item)
+ {
+ m_widgetStack->raiseWidget( m_emptyWidget );
+ m_removeTypeB->setEnabled(false);
+ return;
+ }
+
+ bool wasDirty = m_dirty;
+
+ TypesListItem *tlitem = (TypesListItem *) item;
+ if (tlitem->isMeta()) // is a group
+ {
+ m_widgetStack->raiseWidget( m_groupDetails );
+ m_groupDetails->setTypeItem( tlitem );
+ m_removeTypeB->setEnabled(false);
+ }
+ else
+ {
+ m_widgetStack->raiseWidget( m_details );
+ m_details->setTypeItem( tlitem );
+ m_removeTypeB->setEnabled( !tlitem->isEssential() );
+ }
+
+ // Updating the display indirectly called change(true)
+ if ( !wasDirty )
+ setDirty(false);
+}
+
+bool FileTypesView::sync( QValueList<TypesListItem *>& itemsModified )
+{
+ bool didIt = false;
+ // first, remove those items which we are asked to remove.
+ QStringList::Iterator it(removedList.begin());
+ QString loc;
+
+ for (; it != removedList.end(); ++it) {
+ didIt = true;
+ KMimeType::Ptr m_ptr = KMimeType::mimeType(*it);
+
+ loc = m_ptr->desktopEntryPath();
+ loc = locateLocal("mime", loc);
+
+ KDesktopFile config(loc, false, "mime");
+ config.writeEntry("Type", "MimeType");
+ config.writeEntry("MimeType", m_ptr->name());
+ config.writeEntry("Hidden", true);
+ }
+
+ // now go through all entries and sync those which are dirty.
+ // don't use typesLV, it may be filtered
+ QMapIterator<QString,TypesListItem*> it1 = m_majorMap.begin();
+ while ( it1 != m_majorMap.end() ) {
+ TypesListItem *tli = *it1;
+ if (tli->isDirty()) {
+ kdDebug() << "Entry " << tli->name() << " is dirty. Saving." << endl;
+ tli->sync();
+ itemsModified.append( tli );
+ didIt = true;
+ }
+ ++it1;
+ }
+ QPtrListIterator<TypesListItem> it2( m_itemList );
+ while ( it2.current() ) {
+ TypesListItem *tli = *it2;
+ if (tli->isDirty()) {
+ kdDebug() << "Entry " << tli->name() << " is dirty. Saving." << endl;
+ tli->sync();
+ itemsModified.append( tli );
+ didIt = true;
+ }
+ ++it2;
+ }
+
+ m_konqConfig->sync();
+
+ setDirty(false);
+ return didIt;
+}
+
+void FileTypesView::load()
+{
+ readFileTypes();
+}
+
+void FileTypesView::save()
+{
+ m_itemsModified.clear();
+ if (sync(m_itemsModified)) {
+ // only rebuild if sync() was necessary
+ KService::rebuildKSycoca(this);
+ KIPC::sendMessageAll(KIPC::SettingsChanged);
+ }
+}
+
+void FileTypesView::slotDatabaseChanged()
+{
+ if ( KSycoca::self()->isChanged( "mime" ) )
+ {
+ // ksycoca has new KMimeTypes objects for us, make sure to update
+ // our 'copies' to be in sync with it. Not important for OK, but
+ // important for Apply (how to differentiate those 2?).
+ // See BR 35071.
+ QValueList<TypesListItem *>::Iterator it = m_itemsModified.begin();
+ for( ; it != m_itemsModified.end(); ++it ) {
+ QString name = (*it)->name();
+ if ( removedList.find( name ) == removedList.end() ) // if not deleted meanwhile
+ (*it)->refresh();
+ }
+ m_itemsModified.clear();
+ }
+}
+
+void FileTypesView::defaults()
+{
+}
+
+#include "filetypesview.moc"
+