Logo Search packages:      
Sourcecode: ktechlab version File versions

contexthelp.cpp

/***************************************************************************
 *   Copyright (C) 2003-2006 by David Saxton                               *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#include "cnitem.h"
#include "cnitemgroup.h"
#include "contexthelp.h"
#include "docmanager.h"
#include "itemlibrary.h"
#include "itemselector.h"
#include "katemdi.h"
#include "libraryitem.h"
#include "richtexteditor.h"

#include <kcolorbutton.h>
#include <kcombobox.h>
#include <kdebug.h>
#include <khtml_part.h>
#include <khtmlview.h>
#include <kiconloader.h>
#include <kmimetype.h>
#include <kinputdialog.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kparts/browserextension.h> 
#include <kpopupmenu.h>
#include <krun.h>
#include <kdirselectdialog.h>
#include <kstandarddirs.h>

#include <qfile.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qregexp.h>
#include <qtextbrowser.h>
#include <qtimer.h>
#include <qtoolbutton.h>
#include <qvalidator.h>
#include <qwhatsthis.h>
#include <qwidgetstack.h>

#include <cassert>

ContextHelp * ContextHelp::m_pSelf = 0l;

ContextHelp * ContextHelp::self( KateMDI::ToolView * parent )
{
      if (!m_pSelf)
      {
            assert(parent);
            m_pSelf = new ContextHelp(parent);
      }
      return m_pSelf;
}


ContextHelp::ContextHelp( KateMDI::ToolView * parent )
      : ContextHelpWidget( parent )
{
      QWhatsThis::add( this, i18n("Provides context-sensitive help relevant to the current editing being performed.") );
      setAcceptDrops( true );
      
      QFont font;
      font.setBold( true );
      if ( font.pointSize() != 0 )
            font.setPointSize( int(font.pointSize() * 1.4) );
      m_pNameLabel->setFont( font );
      m_pNameLabel->setTextFormat( RichText );
      
      m_pBrowser = new KHTMLPart( m_pWidgetStack->widget( 0 ) );
      m_pBrowserView = m_pBrowser->view();
      m_pBrowserView->setFocusPolicy( NoFocus );
      m_pBrowserLayout->addWidget( m_pBrowserView );
      connect( m_pBrowser->browserExtension(), SIGNAL(openURLRequest( const KURL &, const KParts::URLArgs & ) ),
                   this, SLOT( openURL(const KURL &, const KParts::URLArgs & ) ) );
      
      // Adjust appearance of browser
      m_pBrowserView->setMarginWidth( 4 );
      
      m_pEditor = new RichTextEditor( m_pWidgetStack->widget( 1 ), "ContextHelpEditor" );
      m_pTopLayout->addWidget( m_pEditor );
      
      m_pEditor->installEventFilter( this );
      m_pEditor->editorViewport()->installEventFilter( this );
      slotClear();
      
      connect( m_pEditButton, SIGNAL(clicked()), this, SLOT(slotEdit()) );
      connect( m_pSaveButton, SIGNAL(clicked()), this, SLOT(slotSave()) );
      connect( m_pResetButton, SIGNAL(clicked()), this, SLOT(slotEditReset()) );
      connect( m_pChangeDescriptionsDirectory, SIGNAL(clicked()), this, SLOT(requestItemDescriptionsDirectory()) );
      connect( m_pLanguageSelect, SIGNAL(activated(const QString &)), this, SLOT(setCurrentLanguage( const QString& )) );
      
      m_pResetButton->setPixmap( KGlobal::iconLoader()->loadIcon( "button_cancel", KIcon::Small ) );
      m_pChangeDescriptionsDirectory->setPixmap( KGlobal::iconLoader()->loadIcon( "folder", KIcon::Small ) );
      
      
      connect( ComponentSelector::self(), SIGNAL(itemSelected( const QString& )), this, SLOT(setBrowserItem( const QString& )) );
      connect( FlowPartSelector::self(), SIGNAL(itemSelected( const QString& )), this, SLOT(setBrowserItem( const QString& )) );
#ifdef MECHANICS
      connect( MechanicsSelector::self(), SIGNAL(itemSelected( const QString& )), this, SLOT(setBrowserItem( const QString& )) );
#endif
      
      QTimer::singleShot( 10, this, SLOT(slotInitializeLanguageList()) );
}


ContextHelp::~ContextHelp()
{
}


00118 bool ContextHelp::eventFilter( QObject * watched, QEvent * e )
{
//    kdDebug() << k_funcinfo << "watched="<<watched<<endl;
      
      if ( (watched != m_pEditor) && (watched != m_pEditor->editorViewport()) )
            return false;
      
      switch ( e->type() )
      {
            case QEvent::DragEnter:
            {
                  QDragEnterEvent * dragEnter = static_cast<QDragEnterEvent*>(e);
                  
                  if ( !QString( dragEnter->format() ).startsWith("ktechlab/") )
                        break;
                  
                  dragEnter->acceptAction();
                  return true;
            }
                  
            case QEvent::Drop:
            {
                  QDropEvent * dropEvent = static_cast<QDropEvent*>(e);
                  
                  if ( !QString( dropEvent->format() ).startsWith("ktechlab/") )
                        break;
                  
                  dropEvent->accept();
                  
                  QString type;
                  QDataStream stream( dropEvent->encodedData( dropEvent->format() ), IO_ReadOnly );
                  stream >> type;
                  
                  LibraryItem * li = itemLibrary()->libraryItem( type );
                  if ( !li )
                        return true;
                  
                  m_pEditor->insertURL( "ktechlab-help:///" + type, li->name() );
                  return true;
            }
                  
            default:
                  break;
      }
      
      return false;
}


00167 void ContextHelp::slotInitializeLanguageList()
{
      m_pLanguageSelect->insertStringList( itemLibrary()->descriptionLanguages() );
      m_currentLanguage = KGlobal::locale()->language();
      m_pLanguageSelect->setCurrentItem( m_currentLanguage );
}


bool ContextHelp::isEditChanged()
{
      if ( m_lastItemType.isEmpty() )
            return false;
      
      // Is the edit widget raised?
      if ( m_pWidgetStack->visibleWidget() != m_pWidgetStack->widget( 1 ) )
            return false;
      
      // We are currently editing an item. Is it changed?
      return ( m_pEditor->text() != itemLibrary()->description( m_lastItemType, m_currentLanguage ).stripWhiteSpace() );
}


void ContextHelp::slotUpdate( Item * item )
{
      if ( isEditChanged() )
            return;
      
      m_lastItemType = item ? item->type() : QString::null;
      m_pEditButton->setEnabled( item );
      
      if ( !item )
      {
            slotClear();
            return;
      }
      
      m_pWidgetStack->raiseWidget( 0 );
      setContextHelp( item->name(), itemLibrary()->description( m_lastItemType, KGlobal::locale()->language() ) );
}


00208 void ContextHelp::setBrowserItem( const QString & type )
{
      if ( isEditChanged() )
            return;
      
      QString description = itemLibrary()->description( type, KGlobal::locale()->language() );
      if ( description.isEmpty() )
            return;
      
      QString name;
      LibraryItem * li = itemLibrary()->libraryItem( type );
      if ( li )
            name = li->name();
      else
            name = type;
      
      m_lastItemType = type;
      setContextHelp( name, description );
      m_pEditButton->setEnabled( true );
}


void ContextHelp::slotClear()
{
      setContextHelp( i18n("No Item Selected"), 0 );
      m_pEditButton->setEnabled( false );
      
      // Can we go hide the edit widget?
      if ( !isEditChanged() )
            m_pWidgetStack->raiseWidget( 0 );
}


void ContextHelp::slotMultipleSelected()
{
      setContextHelp( i18n("Multiple Items"), 0 );
}


void ContextHelp::setContextHelp( QString name, QString help )
{
      //BEGIN modify help string as appropriate
      help = help.stripWhiteSpace();
      parseInfo( help );
      RichTextEditor::makeUseStandardFont( & help );
      addLinkTypeAppearances( & help );
      //END modify help string as appropriate
      
      // HACK Adjust top spacing according to whether the item description uses <p>.
      // This is because the help editor uses paragraphs, but old item help stored
      // in the items just uses <br>
      QFont f;
      int fontPixelSize = QFontInfo( f ).pixelSize();
      if ( help.contains( "<p>" ) )
            m_pBrowserView->setMarginHeight( 3-fontPixelSize );
      else
            m_pBrowserView->setMarginHeight( 3 );
      
      m_pNameLabel->setText( name );
      m_pBrowser->begin( itemLibrary()->itemDescriptionsDirectory() );
      m_pBrowser->write( help );
      m_pBrowser->end();
}


00273 void ContextHelp::parseInfo( QString &info )
{
      info.replace("<example>","<br><br><b>Example:</b><blockquote>");
      info.replace("</example>","</blockquote>");
}


00280 void ContextHelp::slotEdit()
{
      if ( m_lastItemType.isEmpty() )
            return;
      
      QStringList resourcePaths;
      QString currentResourcePath = itemLibrary()->itemDescriptionsDirectory();
      QString defaultResourcePath = locate( "appdata", "contexthelp/" );
      
      resourcePaths << currentResourcePath;
      if ( currentResourcePath != defaultResourcePath )
            resourcePaths << defaultResourcePath;
      
      m_pEditor->setResourcePaths( resourcePaths );
      QString description = itemLibrary()->description( m_lastItemType, m_currentLanguage );
      m_pEditor->setText( description );
      m_pWidgetStack->raiseWidget( 1 );
}


00300 void ContextHelp::setCurrentLanguage( const QString & language )
{
      if ( !saveDescription( m_currentLanguage ) )
      {
            m_pLanguageSelect->blockSignals( true );
            m_pLanguageSelect->setCurrentItem( m_currentLanguage );
            m_pLanguageSelect->blockSignals( false );
            return;
      }
      
      m_currentLanguage = language;
      slotEdit();
}

00314 void ContextHelp::requestItemDescriptionsDirectory()
{
      KDirSelectDialog * dlg = new KDirSelectDialog( itemLibrary()->itemDescriptionsDirectory(), true );
      if ( dlg->exec() == QDialog::Accepted )
      {
            itemLibrary()->setItemDescriptionsDirectory( dlg->url().path() );
      }
      delete dlg;
      return;
}


00326 void ContextHelp::slotEditReset()
{
      if ( isEditChanged() )
      {
            KGuiItem continueItem = KStdGuiItem::cont();
            continueItem.setText( i18n("Reset") );
            int answer = KMessageBox::warningContinueCancel( this, i18n("Reset item help to last saved changes?"), i18n("Reset"), continueItem );
            if ( answer == KMessageBox::Cancel )
                  return;
      }
      
      m_pWidgetStack->raiseWidget( 0 );
}


00341 void ContextHelp::slotSave()
{
      if ( !saveDescription( m_currentLanguage ) )
            return;
      
      setContextHelp( m_pNameLabel->text(), itemLibrary()->description( m_lastItemType, KGlobal::locale()->language() ) );
      m_pWidgetStack->raiseWidget( 0 );
}


00351 bool ContextHelp::saveDescription( const QString & language )
{
      if ( m_lastItemType.isEmpty() )
      {
            KMessageBox::sorry( 0, i18n("Cannot save item description.") );
            return false;
      }
      
      return itemLibrary()->setDescription( m_lastItemType, m_pEditor->text(), language );
}


// static function
00364 void ContextHelp::addLinkTypeAppearances( QString * html )
{
      QRegExp rx("<a href=\"([^\"]*)\">([^<]*)</a>");
      
      int pos = 0;
      
      while ( (pos = rx.search( *html, pos )) >= 0 )
      {
            QString anchorText = rx.cap( 0 ); // contains e.g.: <a href="http://ktechlab.org/">KTechlab website</a>
            QString url = rx.cap( 1 ); // contains e.g.: http://ktechlab.org/
            QString text = rx.cap( 2 ); // contains e.g.: KTechlab website
            
            int length = anchorText.length();
            
            LinkType lt = extractLinkType( url );
            
            QColor color; // default constructor gives an "invalid" color
            QString imageURL;
            
            switch ( lt )
            {
                  case HelpLink:
                        break;
                        
                  case NewHelpLink:
                        color = red;
                        break;
                        
                  case ExampleLink:
                  {
                        QString iconName = KMimeType::iconForURL( examplePathToFullPath( KURL( url ).path() ) );
                        imageURL = KGlobal::iconLoader()->iconPath( iconName, - KIcon::SizeSmall );
                        break;
                  }
                  
                  case ExternalLink:
                  {
                        imageURL = locate( "appdata", "icons/external_link.png" );
                        break;
                  }
            }
            
            QString newAnchorText;
            
            if ( color.isValid() )
            {
                  newAnchorText = QString("<a href=\"%1\" style=\"color: %2;\">%3</a>").arg( url ).arg( color.name() ).arg( text );
            }
            else if ( !imageURL.isEmpty() )
            {
                  newAnchorText = anchorText;
                  newAnchorText += QString(" <img src=\"%1\"/>").arg( imageURL );
            }
            
            if ( !newAnchorText.isEmpty() )
                  html->replace( pos, length, newAnchorText );
            
            pos++; // avoid the string we just found
      }
}


// static function
00427 ContextHelp::LinkType ContextHelp::extractLinkType( const KURL & url )
{
      QString path = url.path();
      
      if ( url.protocol() == "ktechlab-help" )
      {
            if ( itemLibrary()->haveDescription( path, KGlobal::locale()->language() ) )
                  return HelpLink;
            else
                  return NewHelpLink;
      }
      
      if ( url.protocol() == "ktechlab-example" )
            return ExampleLink;
      
      return ExternalLink;
}


// static function
00447 QString ContextHelp::examplePathToFullPath( QString path )
{
      // quick security check
      path.remove( ".." );
      
      if ( path.startsWith("/") )
            path.remove( 0, 1 );
      
      return locate( "appdata", "examples/" + path );
}


void ContextHelp::openURL( const KURL & url, const KParts::URLArgs & )
{
      QString path = url.path();
      
      switch ( extractLinkType( url ) )
      {
            case HelpLink:
            case NewHelpLink:
                  setBrowserItem( path );
                  break;
                  
            case ExampleLink:
                  DocManager::self()->openURL( examplePathToFullPath( path ) );
                  break;
                  
            case ExternalLink:
            {
                  // external url
                  KRun * r = new KRun( url );
                  connect( r, SIGNAL(finished()), r, SLOT(deleteLater()) );
                  connect( r, SIGNAL(error()), r, SLOT(deleteLater()) );
                  break;
            }
      }
            
}

#include "contexthelp.moc"

Generated by  Doxygen 1.6.0   Back to index