/* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org> Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //BEGIN Includes #include "katefilelist.h" #include "katefilelist.moc" #include "katedocmanager.h" #include "kateviewmanager.h" #include "katemainwindow.h" #include <qapplication.h> #include <qpainter.h> #include <qpopupmenu.h> #include <qheader.h> #include <qcolor.h> #include <qcheckbox.h> #include <qhbox.h> #include <qlayout.h> #include <qgroupbox.h> #include <qlabel.h> #include <qwhatsthis.h> #include <kiconloader.h> #include <kconfig.h> #include <klocale.h> #include <kglobalsettings.h> #include <kpassivepopup.h> #include <kdebug.h> #include <kapplication.h> #include <kstringhandler.h> #include <kcolorbutton.h> #include <kdialog.h> //END Includes //BEGIN ToolTip class ToolTip : public QToolTip { public: ToolTip( QWidget *parent, KateFileList *lv ) : QToolTip( parent ), m_listView( lv ) { } virtual ~ToolTip() {}; void maybeTip( const QPoint &pos ) { QListViewItem *i = m_listView->itemAt( pos ); if ( ! i ) return; KateFileListItem *item = ((KateFileListItem*)i); if ( ! item ) return; tip( m_listView->itemRect( i ), m_listView->tooltip( item, 0 ) ); } private: KateFileList *m_listView; }; //END ToolTip //BEGIN KateFileList KateFileList::KateFileList (KateMainWindow *main, KateViewManager *_viewManager, QWidget * parent, const char * name ) : KListView (parent, name) , m_sort( KateFileList::sortByID ) { m_main = main; m_tooltip = new ToolTip( viewport(), this ); // default colors m_viewShade = QColor( 51, 204, 255 ); m_editShade = QColor( 255, 102, 153 ); m_enableBgShading = false; setFocusPolicy ( QWidget::NoFocus ); viewManager = _viewManager; header()->hide(); addColumn("Document Name"); setSelectionMode( QListView::Single ); setSorting( 0, true ); setShowToolTips( false ); setupActions (); for (uint i = 0; i < KateDocManager::self()->documents(); i++) { slotDocumentCreated (KateDocManager::self()->document(i)); slotModChanged (KateDocManager::self()->document(i)); } connect(KateDocManager::self(),SIGNAL(documentCreated(Kate::Document *)), this,SLOT(slotDocumentCreated(Kate::Document *))); connect(KateDocManager::self(),SIGNAL(documentDeleted(uint)), this,SLOT(slotDocumentDeleted(uint))); // don't Honour KDE single/double click setting, this files are already open, // no need for hassle of considering double-click connect(this,SIGNAL(selectionChanged(QListViewItem *)), this,SLOT(slotActivateView(QListViewItem *))); connect(viewManager,SIGNAL(viewChanged()), this,SLOT(slotViewChanged())); connect(this,SIGNAL(contextMenuRequested( QListViewItem *, const QPoint &, int )), this,SLOT(slotMenu ( QListViewItem *, const QPoint &, int ))); } KateFileList::~KateFileList () { delete m_tooltip; } void KateFileList::setupActions () { windowNext = KStdAction::back(this, SLOT(slotPrevDocument()), m_main->actionCollection()); windowPrev = KStdAction::forward(this, SLOT(slotNextDocument()), m_main->actionCollection()); sortAction = new KSelectAction( i18n("Sort &By"), 0, m_main->actionCollection(), "filelist_sortby" ); QStringList l; l << i18n("Opening Order") << i18n("Document Name") << i18n("URL"); sortAction->setItems( l ); connect( sortAction, SIGNAL(activated(int)), this, SLOT(setSortType(int)) ); } void KateFileList::updateActions () { windowNext->setEnabled(KateDocManager::self()->documents() > 1); windowPrev->setEnabled(KateDocManager::self()->documents() > 1); } void KateFileList::keyPressEvent(QKeyEvent *e) { if ( ( e->key() == Key_Return ) || ( e->key() == Key_Enter ) ) { e->accept(); slotActivateView( currentItem() ); } else { KListView::keyPressEvent(e); } } // Protect single mode selection: don't let them // leftclick outside items. // ### if we get to accept keyboard navigation, set focus before // returning void KateFileList::contentsMousePressEvent( QMouseEvent *e ) { if ( ! itemAt( contentsToViewport( e->pos() ) ) ) return; KListView::contentsMousePressEvent( e ); } void KateFileList::resizeEvent( QResizeEvent *e ) { KListView::resizeEvent( e ); // ### We may want to actually calculate the widest field, // since it's not automatically scrinked. If I add support for // tree or marks, the changes of the required width will vary // a lot with opening/closing of files and display changes for // the mark branches. int w = viewport()->width(); if ( columnWidth( 0 ) < w ) setColumnWidth( 0, w ); } void KateFileList::slotNextDocument() { if ( ! currentItem() || childCount() == 0 ) return; // ### more checking once more item types are added if ( currentItem()->nextSibling() ) viewManager->activateView( ((KateFileListItem*)currentItem()->nextSibling())->documentNumber() ); else viewManager->activateView( ((KateFileListItem *)firstChild())->documentNumber() ); } void KateFileList::slotPrevDocument() { if ( ! currentItem() || childCount() == 0 ) return; // ### more checking once more item types are added if ( currentItem()->itemAbove() ) viewManager->activateView( ((KateFileListItem*)currentItem()->itemAbove())->documentNumber() ); else viewManager->activateView( ((KateFileListItem *)lastItem())->documentNumber() ); } void KateFileList::slotDocumentCreated (Kate::Document *doc) { new KateFileListItem( this, doc/*, doc->documentNumber()*/ ); connect(doc,SIGNAL(modStateChanged(Kate::Document *)),this,SLOT(slotModChanged(Kate::Document *))); connect(doc,SIGNAL(nameChanged(Kate::Document *)),this,SLOT(slotNameChanged(Kate::Document *))); connect(doc,SIGNAL(modifiedOnDisc(Kate::Document *, bool, unsigned char)),this,SLOT(slotModifiedOnDisc(Kate::Document *, bool, unsigned char))); sort(); updateActions (); } void KateFileList::slotDocumentDeleted (uint documentNumber) { QListViewItem * item = firstChild(); while( item ) { if ( ((KateFileListItem *)item)->documentNumber() == documentNumber ) { // m_viewHistory.removeRef( (KateFileListItem *)item ); // m_editHistory.removeRef( (KateFileListItem *)item ); removeItem( item ); break; } item = item->nextSibling(); } updateActions (); } void KateFileList::slotActivateView( QListViewItem *item ) { if ( ! item || item->rtti() != RTTI_KateFileListItem ) return; viewManager->activateView( ((KateFileListItem *)item)->documentNumber() ); } void KateFileList::slotModChanged (Kate::Document *doc) { if (!doc) return; QListViewItem * item = firstChild(); while( item ) { if ( ((KateFileListItem *)item)->documentNumber() == doc->documentNumber() ) break; item = item->nextSibling(); } if ( ((KateFileListItem *)item)->document()->isModified() ) { m_editHistory.removeRef( (KateFileListItem *)item ); m_editHistory.prepend( (KateFileListItem *)item ); for ( uint i=0; i < m_editHistory.count(); i++ ) { m_editHistory.at( i )->setEditHistPos( i+1 ); repaintItem( m_editHistory.at( i ) ); } } else repaintItem( item ); } void KateFileList::slotModifiedOnDisc (Kate::Document *doc, bool, unsigned char) { slotModChanged( doc ); } void KateFileList::slotNameChanged (Kate::Document *doc) { if (!doc) return; // ### using nextSibling to *only* look at toplevel items. // child items could be marks for example QListViewItem * item = firstChild(); while( item ) { if ( ((KateFileListItem*)item)->document() == doc ) { item->setText( 0, doc->docName() ); repaintItem( item ); break; } item = item->nextSibling(); } updateSort(); } void KateFileList::slotViewChanged () { if (!viewManager->activeView()) return; Kate::View *view = viewManager->activeView(); uint dn = view->getDoc()->documentNumber(); QListViewItem * i = firstChild(); while( i ) { if ( ((KateFileListItem *)i)->documentNumber() == dn ) { break; } i = i->nextSibling(); } if ( ! i ) return; KateFileListItem *item = (KateFileListItem*)i; setCurrentItem( item ); // ### During load of file lists, all the loaded views gets active. // Do something to avoid shading them -- maybe not creating views, just // open the documents??? // int p = 0; // if ( m_viewHistory.count() ) // { // int p = m_viewHistory.findRef( item ); // only repaint items that needs it // } m_viewHistory.removeRef( item ); m_viewHistory.prepend( item ); for ( uint i=0; i < m_viewHistory.count(); i++ ) { m_viewHistory.at( i )->setViewHistPos( i+1 ); repaintItem( m_viewHistory.at( i ) ); } } void KateFileList::slotMenu ( QListViewItem *item, const QPoint &p, int /*col*/ ) { if (!item) return; QPopupMenu *menu = (QPopupMenu*) ((viewManager->mainWindow())->factory()->container("filelist_popup", viewManager->mainWindow())); if (menu) menu->exec(p); } QString KateFileList::tooltip( QListViewItem *item, int ) { KateFileListItem *i = ((KateFileListItem*)item); if ( ! i ) return QString::null; QString str; const KateDocumentInfo *info = KateDocManager::self()->documentInfo(i->document()); if (info && info->modifiedOnDisc) { if (info->modifiedOnDiscReason == 1) str += i18n("<b>This file was changed (modified) on disk by another program.</b><br />"); else if (info->modifiedOnDiscReason == 2) str += i18n("<b>This file was changed (created) on disk by another program.</b><br />"); else if (info->modifiedOnDiscReason == 3) str += i18n("<b>This file was changed (deleted) on disk by another program.</b><br />"); } str += i->document()->url().prettyURL(); return str; } void KateFileList::setSortType (int s) { m_sort = s; updateSort (); } void KateFileList::updateSort () { sort (); } void KateFileList::readConfig( KConfig *config, const QString &group ) { QString oldgroup = config->group(); config->setGroup( group ); setSortType( config->readNumEntry( "Sort Type", sortByID ) ); m_viewShade = config->readColorEntry( "View Shade", &m_viewShade ); m_editShade = config->readColorEntry( "Edit Shade", &m_editShade ); m_enableBgShading = config->readBoolEntry( "Shading Enabled", &m_enableBgShading ); sortAction->setCurrentItem( sortType() ); config->setGroup( oldgroup ); } void KateFileList::writeConfig( KConfig *config, const QString &group ) { QString oldgroup = config->group(); config->setGroup( group ); config->writeEntry( "Sort Type", m_sort ); config->writeEntry( "View Shade", m_viewShade ); config->writeEntry( "Edit Shade", m_editShade ); config->writeEntry( "Shading Enabled", m_enableBgShading ); config->setGroup( oldgroup ); } void KateFileList::takeItem( QListViewItem *item ) { if ( item->rtti() == RTTI_KateFileListItem ) { m_editHistory.removeRef( (KateFileListItem*)item ); m_viewHistory.removeRef( (KateFileListItem*)item ); } QListView::takeItem( item ); } //END KateFileList //BEGIN KateFileListItem KateFileListItem::KateFileListItem( QListView* lv, Kate::Document *_doc ) : QListViewItem( lv, _doc->docName() ), doc( _doc ), m_viewhistpos( 0 ), m_edithistpos( 0 ), m_docNumber( _doc->documentNumber() ) { } KateFileListItem::~KateFileListItem() { } const QPixmap *KateFileListItem::pixmap ( int column ) const { if ( column == 0) { static QPixmap noPm = SmallIcon ("null"); static QPixmap modPm = SmallIcon("modified"); static QPixmap discPm = SmallIcon("modonhd"); static QPixmap modmodPm = SmallIcon("modmod"); const KateDocumentInfo *info = KateDocManager::self()->documentInfo(doc); if (info && info->modifiedOnDisc) return doc->isModified() ? &modmodPm : &discPm; else return doc->isModified() ? &modPm : &noPm; } return 0; } void KateFileListItem::paintCell( QPainter *painter, const QColorGroup & cg, int column, int width, int align ) { KateFileList *fl = (KateFileList*)listView(); if ( ! fl ) return; if ( column == 0 ) { QColorGroup cgNew = cg; // replace the base color with a different shading if necessary... if ( fl->shadingEnabled() && m_viewhistpos > 1 ) { QColor b( cg.base() ); QColor shade = fl->viewShade(); QColor eshade = fl->editShade(); int hc = fl->histCount(); // If this file is in the edit history, blend in the eshade // color. The blend is weighted by the position in the editing history if ( fl->shadingEnabled() && m_edithistpos > 0 ) { int ec = fl->editHistCount(); int v = hc-m_viewhistpos; int e = ec-m_edithistpos+1; e = e*e; int n = QMAX(v + e, 1); shade.setRgb( ((shade.red()*v) + (eshade.red()*e))/n, ((shade.green()*v) + (eshade.green()*e))/n, ((shade.blue()*v) + (eshade.blue()*e))/n ); } // blend in the shade color. // max transperancy < .5, latest is most colored. float t = (0.5/hc)*(hc-m_viewhistpos+1); b.setRgb( (int)((b.red()*(1-t)) + (shade.red()*t)), (int)((b.green()*(1-t)) + (shade.green()*t)), (int)((b.blue()*(1-t)) + (shade.blue()*t)) ); cgNew.setColor(QColorGroup::Base, b); } QListViewItem::paintCell( painter, cgNew, column, width, align ); } else QListViewItem::paintCell( painter, cg, column, width, align ); } int KateFileListItem::compare ( QListViewItem * i, int col, bool ascending ) const { if ( i->rtti() == RTTI_KateFileListItem ) { switch( ((KateFileList*)listView())->sortType() ) { case KateFileList::sortByID: { int d = (int)doc->documentNumber() - ((KateFileListItem*)i)->documentNumber(); return ascending ? d : -d; break; } case KateFileList::sortByURL: return doc->url().prettyURL().compare( ((KateFileListItem*)i)->document()->url().prettyURL() ); break; default: return QListViewItem::compare( i, col, ascending ); } } return 0; } //END KateFileListItem //BEGIN KFLConfigPage KFLConfigPage::KFLConfigPage( QWidget* parent, const char *name, KateFileList *fl ) : Kate::ConfigPage( parent, name ), m_filelist( fl ), m_changed( false ) { QVBoxLayout *lo1 = new QVBoxLayout( this ); int spacing = KDialog::spacingHint(); lo1->setSpacing( spacing ); QGroupBox *gb = new QGroupBox( 1, Qt::Horizontal, i18n("Background Shading"), this ); lo1->addWidget( gb ); QWidget *g = new QWidget( gb ); QGridLayout *lo = new QGridLayout( g, 2, 2 ); lo->setSpacing( KDialog::spacingHint() ); cbEnableShading = new QCheckBox( i18n("&Enable background shading"), g ); lo->addMultiCellWidget( cbEnableShading, 1, 1, 0, 1 ); kcbViewShade = new KColorButton( g ); lViewShade = new QLabel( kcbViewShade, i18n("&Viewed documents' shade:"), g ); lo->addWidget( lViewShade, 2, 0 ); lo->addWidget( kcbViewShade, 2, 1 ); kcbEditShade = new KColorButton( g ); lEditShade = new QLabel( kcbEditShade, i18n("&Modified documents' shade:"), g ); lo->addWidget( lEditShade, 3, 0 ); lo->addWidget( kcbEditShade, 3, 1 ); // sorting QHBox *hbSorting = new QHBox( this ); lo1->addWidget( hbSorting ); lSort = new QLabel( i18n("&Sort by:"), hbSorting ); cmbSort = new QComboBox( hbSorting ); lSort->setBuddy( cmbSort ); QStringList l; l << i18n("Opening Order") << i18n("Document Name") << i18n("URL"); cmbSort->insertStringList( l ); lo1->insertStretch( -1, 10 ); QWhatsThis::add( cbEnableShading, i18n( "When background shading is enabled, documents that have been viewed " "or edited within the current session will have a shaded background. " "The most recent documents have the strongest shade.") ); QWhatsThis::add( kcbViewShade, i18n( "Set the color for shading viewed documents.") ); QWhatsThis::add( kcbEditShade, i18n( "Set the color for modified documents. This color is blended into " "the color for viewed files. The most recently edited documents get " "most of this color.") ); QWhatsThis::add( cmbSort, i18n( "Set the sorting method for the documents.") ); reload(); slotEnableChanged(); connect( cbEnableShading, SIGNAL(toggled(bool)), this, SLOT(slotMyChanged()) ); connect( cbEnableShading, SIGNAL(toggled(bool)), this, SLOT(slotEnableChanged()) ); connect( kcbViewShade, SIGNAL(changed(const QColor&)), this, SLOT(slotMyChanged()) ); connect( kcbEditShade, SIGNAL(changed(const QColor&)), this, SLOT(slotMyChanged()) ); connect( cmbSort, SIGNAL(activated(int)), this, SLOT(slotMyChanged()) ); } void KFLConfigPage::apply() { if ( ! m_changed ) return; m_changed = false; // Change settings in the filelist m_filelist->m_viewShade = kcbViewShade->color(); m_filelist->m_editShade = kcbEditShade->color(); m_filelist->m_enableBgShading = cbEnableShading->isChecked(); m_filelist->setSortType( cmbSort->currentItem() ); // repaint the affected items m_filelist->triggerUpdate(); } void KFLConfigPage::reload() { // read in from config file KConfig *config = kapp->config(); config->setGroup( "Filelist" ); cbEnableShading->setChecked( config->readBoolEntry("Shading Enabled", &m_filelist->m_enableBgShading ) ); kcbViewShade->setColor( config->readColorEntry("View Shade", &m_filelist->m_viewShade ) ); kcbEditShade->setColor( config->readColorEntry("Edit Shade", &m_filelist->m_editShade ) ); cmbSort->setCurrentItem( m_filelist->sortType() ); m_changed = false; } void KFLConfigPage::slotEnableChanged() { kcbViewShade->setEnabled( cbEnableShading->isChecked() ); kcbEditShade->setEnabled( cbEnableShading->isChecked() ); lViewShade->setEnabled( cbEnableShading->isChecked() ); lEditShade->setEnabled( cbEnableShading->isChecked() ); } void KFLConfigPage::slotMyChanged() { m_changed = true; slotChanged(); } //END KFLConfigPage // kate: space-indent on; indent-width 2; replace-tabs on;