You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kile/src/kile/kileviewmanager.cpp

585 lines
18 KiB

//
// C++ Implementation: kileviewmanager
//
// Description:
//
//
// Author: Jeroen Wijnhout <Jeroen.Wijnhout@kdemail.net>, (C) 2004
// Michel Ludwig <michel.ludwig@kdemail.net>, (C) 2006, 2007
/***************************************************************************
* *
* 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 "kileviewmanager.h"
#include <tqpopupmenu.h>
#include <tqtimer.h> //for TQTimer::singleShot trick
#include <tqpixmap.h>
#include <tqclipboard.h>
#include <tdeversion.h>
#include <kglobal.h>
#include <kate/view.h>
#include <kate/document.h>
#include <tdeparts/componentfactory.h>
#include <kxmlguiclient.h>
#include <kxmlguifactory.h>
#include <kiconloader.h>
#include <kmimetype.h>
#include <klocale.h>
#include <tdetexteditor/editinterfaceext.h>
#include <kapplication.h>
#include <kurldrag.h>
#include "editorkeysequencemanager.h"
#include "kileinfo.h"
#include "kileconstants.h"
#include "kiledocmanager.h"
#include "kileextensions.h"
#include "kileprojectview.h"
#include "kileeventfilter.h"
#include "kilestructurewidget.h"
#include "kileedit.h"
#include "plaintolatexconverter.h"
#include "previewwidget.h"
#include "quickpreview.h"
namespace KileView
{
Manager::Manager(KileInfo *info, TQObject *parent, const char *name) :
TQObject(parent, name),
m_ki(info),
m_activeTextView(0L),
// m_projectview(0L),
m_tabs(0L),
m_widgetStack(0L),
m_emptyDropWidget(0L)
{
}
Manager::~Manager()
{
}
void Manager::setClient(TQObject *receiver, KXMLGUIClient *client)
{
m_receiver = receiver;
m_client = client;
if(NULL == m_client->actionCollection()->action("popup_pasteaslatex"))
new KAction(i18n("Paste as LaTe&X"), 0, this,
TQT_SLOT(pasteAsLaTeX()), m_client->actionCollection(), "popup_pasteaslatex");
if(NULL == m_client->actionCollection()->action("popup_converttolatex"))
new KAction(i18n("Convert Selection to &LaTeX"), 0, this,
TQT_SLOT(convertSelectionToLaTeX()), m_client->actionCollection(), "popup_converttolatex");
if(NULL == m_client->actionCollection()->action("popup_quickpreview"))
new KAction(i18n("&QuickPreview Selection"), 0, this,
TQT_SLOT(quickPreviewPopup()), m_client->actionCollection(), "popup_quickpreview");
}
void Manager::createTabs(TQWidget *parent)
{
m_widgetStack = new TQWidgetStack(parent);
m_emptyDropWidget = new DropWidget(parent);
m_widgetStack->addWidget(m_emptyDropWidget);
connect(m_emptyDropWidget, TQT_SIGNAL(testCanDecode(const TQDragMoveEvent *, bool &)), this, TQT_SLOT(testCanDecodeURLs(const TQDragMoveEvent *, bool &)));
connect(m_emptyDropWidget, TQT_SIGNAL(receivedDropEvent(TQDropEvent *)), m_ki->docManager(), TQT_SLOT(openDroppedURLs(TQDropEvent *)));
m_tabs = new KTabWidget(parent);
m_widgetStack->addWidget(m_tabs);
m_tabs->setFocusPolicy(TQ_ClickFocus);
m_tabs->setTabReorderingEnabled(true);
m_tabs->setHoverCloseButton(true);
m_tabs->setHoverCloseButtonDelayed(true);
m_tabs->setFocus();
connect( m_tabs, TQT_SIGNAL( currentChanged( TQWidget * ) ), m_receiver, TQT_SLOT(newCaption()) );
connect( m_tabs, TQT_SIGNAL( currentChanged( TQWidget * ) ), m_receiver, TQT_SLOT(activateView( TQWidget * )) );
connect( m_tabs, TQT_SIGNAL( currentChanged( TQWidget * ) ), m_receiver, TQT_SLOT(updateModeStatus()) );
connect( m_tabs, TQT_SIGNAL( closeRequest(TQWidget *) ), this, TQT_SLOT(closeWidget(TQWidget *)));
connect( m_tabs, TQT_SIGNAL( testCanDecode( const TQDragMoveEvent *, bool & ) ), this, TQT_SLOT(testCanDecodeURLs( const TQDragMoveEvent *, bool & )) );
connect( m_tabs, TQT_SIGNAL( receivedDropEvent( TQDropEvent * ) ), m_ki->docManager(), TQT_SLOT(openDroppedURLs( TQDropEvent * )) );
connect( m_tabs, TQT_SIGNAL( receivedDropEvent( TQWidget*, TQDropEvent * ) ), this, TQT_SLOT(replaceLoadedURL( TQWidget *, TQDropEvent * )) );
m_widgetStack->raiseWidget(m_emptyDropWidget); // there are no tabs, so show the DropWidget
}
void Manager::closeWidget(TQWidget *widget)
{
if (widget->inherits( "Kate::View" ))
{
Kate::View *view = static_cast<Kate::View*>(widget);
m_ki->docManager()->fileClose(view->getDoc());
}
}
Kate::View* Manager::createTextView(KileDocument::TextInfo *info, int index)
{
Kate::Document *doc = info->getDoc();
Kate::View *view = static_cast<Kate::View*>(info->createView (m_tabs, 0L));
//install a key sequence recorder on the view
view->focusProxy()->installEventFilter(new KileEditorKeySequence::Recorder(view, m_ki->editorKeySequenceManager()));
// in the case of simple text documents, we mimic the behaviour of LaTeX documents
if(info->getType() == KileDocument::Text)
{
view->focusProxy()->installEventFilter(m_ki->eventFilter());
}
//insert the view in the tab widget
m_tabs->insertTab( view, m_ki->getShortName(doc), index );
#if TDE_VERSION >= TDE_MAKE_VERSION(3,4,0)
m_tabs->setTabToolTip(view, doc->url().pathOrURL() );
#else
m_tabs->setTabToolTip(view, doc->url().prettyURL() );
#endif
m_tabs->showPage( view );
m_textViewList.insert((index < 0 || (uint)index >= m_textViewList.count()) ? m_textViewList.count() : index, view);
connect(view, TQT_SIGNAL(viewStatusMsg(const TQString&)), m_receiver, TQT_SLOT(newStatus(const TQString&)));
connect(view, TQT_SIGNAL(newStatus()), m_receiver, TQT_SLOT(newCaption()));
connect(view, TQT_SIGNAL(dropEventPass(TQDropEvent *)), m_ki->docManager(), TQT_SLOT(openDroppedURLs(TQDropEvent *)));
connect(info, TQT_SIGNAL(urlChanged(KileDocument::Info*, const KURL&)), this, TQT_SLOT(urlChanged(KileDocument::Info*, const KURL&)));
connect( doc, TQT_SIGNAL(charactersInteractivelyInserted (int,int,const TQString&)), m_ki->editorExtension()->complete(), TQT_SLOT(slotCharactersInserted(int,int,const TQString&)) );
connect( view, TQT_SIGNAL(completionDone(KTextEditor::CompletionEntry)), m_ki->editorExtension()->complete(), TQT_SLOT( slotCompletionDone(KTextEditor::CompletionEntry)) );
connect( view, TQT_SIGNAL(completionAborted()), m_ki->editorExtension()->complete(), TQT_SLOT( slotCompletionAborted()) );
connect( view, TQT_SIGNAL(filterInsertString(KTextEditor::CompletionEntry*,TQString *)), m_ki->editorExtension()->complete(), TQT_SLOT(slotFilterCompletion(KTextEditor::CompletionEntry*,TQString *)) );
// install a working kate part popup dialog thingy
TQPopupMenu *viewPopupMenu = (TQPopupMenu*)(m_client->factory()->container("tdetexteditor_popup", m_client));
if((NULL != view) && (NULL != viewPopupMenu))
view->installPopup(viewPopupMenu);
if(NULL != viewPopupMenu)
connect(viewPopupMenu, TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(onKatePopupMenuRequest()));
//activate the newly created view
emit(activateView(view, false));
TQTimer::singleShot(0, m_receiver, TQT_SLOT(newCaption())); //make sure the caption gets updated
reflectDocumentStatus(view->getDoc(), false, 0);
view->setFocusPolicy(TQ_StrongFocus);
view->setFocus();
emit(prepareForPart("Editor"));
unplugKatePartMenu(view);
// use Kile's save and save-as functions instead of Katepart's
KAction *action = view->actionCollection()->action(KStdAction::stdName(KStdAction::Save));
if ( action )
{
KILE_DEBUG() << " reconnect action 'file_save'..." << endl;
action->disconnect(TQT_SIGNAL(activated()));
connect(action, TQT_SIGNAL(activated()), m_ki->docManager(), TQT_SLOT(fileSave()));
}
action = view->actionCollection()->action(KStdAction::stdName(KStdAction::SaveAs));
if ( action )
{
KILE_DEBUG() << " reconnect action 'file_save_as'..." << endl;
action->disconnect(TQT_SIGNAL(activated()));
connect(action, TQT_SIGNAL(activated()), m_ki->docManager(), TQT_SLOT(fileSaveAs()));
}
m_widgetStack->raiseWidget(m_tabs); // there is at least one tab, so show the KTabWidget now
return view;
}
void Manager::removeView(Kate::View *view)
{
if (view)
{
m_client->factory()->removeClient(view);
m_tabs->removePage(view);
m_textViewList.remove(view);
delete view;
TQTimer::singleShot(0, m_receiver, TQT_SLOT(newCaption())); //make sure the caption gets updated
if (textViews().isEmpty()) {
m_ki->structureWidget()->clear();
m_widgetStack->raiseWidget(m_emptyDropWidget); // there are no tabs left, so show
// the DropWidget
}
}
}
Kate::View *Manager::currentTextView() const
{
if ( m_tabs->currentPage() &&
m_tabs->currentPage()->inherits( "Kate::View" ) )
{
return (Kate::View*) m_tabs->currentPage();
}
return 0;
}
Kate::View* Manager::textView(KileDocument::TextInfo *info)
{
Kate::Document *doc = info->getDoc();
if(!doc)
{
return NULL;
}
for(Kate::View *view = m_textViewList.first(); view; view = m_textViewList.next())
{
if(view->getDoc() == doc)
{
return view;
}
}
return NULL;
}
int Manager::getIndexOf(Kate::View* view) const
{
return m_tabs->indexOf(view);
}
unsigned int Manager::getTabCount() const {
return m_tabs->count();
}
Kate::View* Manager::switchToTextView(const KURL & url, bool requestFocus)
{
Kate::View *view = 0L;
Kate::Document *doc = m_ki->docManager()->docFor(url);
if (doc)
{
view = static_cast<Kate::View*>(doc->views().first());
if(view)
{
m_tabs->showPage(view);
if(requestFocus)
view->setFocus();
}
}
return view;
}
void Manager::updateStructure(bool parse /* = false */, KileDocument::Info *docinfo /* = 0L */)
{
if (docinfo == 0L)
docinfo = m_ki->docManager()->getInfo();
if (docinfo)
m_ki->structureWidget()->update(docinfo, parse);
Kate::View *view = currentTextView();
if (view) {view->setFocus();}
if ( textViews().count() == 0 )
m_ki->structureWidget()->clear();
}
void Manager::gotoNextView()
{
if ( m_tabs->count() < 2 )
return;
int cPage = m_tabs->currentPageIndex() + 1;
if ( cPage >= m_tabs->count() )
m_tabs->setCurrentPage( 0 );
else
m_tabs->setCurrentPage( cPage );
}
void Manager::gotoPrevView()
{
if ( m_tabs->count() < 2 )
return;
int cPage = m_tabs->currentPageIndex() - 1;
if ( cPage < 0 )
m_tabs->setCurrentPage( m_tabs->count() - 1 );
else
m_tabs->setCurrentPage( cPage );
}
void Manager::reflectDocumentStatus(Kate::Document *doc, bool isModified, unsigned char reason)
{
TQPixmap icon;
if ( reason == 0 && isModified ) //nothing
icon = SmallIcon("filesave");
else if ( reason == 1 || reason == 2 ) //dirty file
icon = SmallIcon("revert");
else if ( reason == 3 ) //file deleted
icon = SmallIcon("stop");
else if ( m_ki->extensions()->isScriptFile(doc->url()) )
icon = SmallIcon("js");
else
icon = KMimeType::pixmapForURL (doc->url(), 0, KIcon::Small);
changeTab(doc->views().first(), icon, m_ki->getShortName(doc));
}
/**
* Adds/removes the "Convert to LaTeX" entry in Kate's popup menu according to the selection.
*/
void Manager::onKatePopupMenuRequest(void)
{
Kate::View *view = currentTextView();
if(NULL == view)
return;
TQPopupMenu *viewPopupMenu = (TQPopupMenu*)(m_client->factory()->container("tdetexteditor_popup", m_client));
if(NULL == viewPopupMenu)
return;
// Setting up the "QuickPreview selection" entry
KAction *quickPreviewAction = m_client->actionCollection()->action("popup_quickpreview");
if(NULL != quickPreviewAction) {
if(!quickPreviewAction->isPlugged())
quickPreviewAction->plug(viewPopupMenu);
quickPreviewAction->setEnabled( view->getDoc()->hasSelection() ||
m_ki->editorExtension()->hasMathgroup(view) ||
m_ki->editorExtension()->hasEnvironment(view)
);
}
// Setting up the "Convert to LaTeX" entry
KAction *latexCvtAction = m_client->actionCollection()->action("popup_converttolatex");
if(NULL != latexCvtAction) {
if(!latexCvtAction->isPlugged())
latexCvtAction->plug(viewPopupMenu);
latexCvtAction->setEnabled(view->getDoc()->hasSelection());
}
// Setting up the "Paste as LaTeX" entry
KAction *pasteAsLaTeXAction = m_client->actionCollection()->action("popup_pasteaslatex");
if((NULL != pasteAsLaTeXAction)) {
if(!pasteAsLaTeXAction->isPlugged())
pasteAsLaTeXAction->plug(viewPopupMenu);
TQClipboard *clip = TDEApplication::clipboard();
if(NULL != clip)
pasteAsLaTeXAction->setEnabled(!clip->text().isNull());
}
}
void Manager::convertSelectionToLaTeX(void)
{
Kate::View *view = currentTextView();
if(NULL == view)
return;
Kate::Document *doc = view->getDoc();
if(NULL == doc)
return;
// Getting the selection
uint selStartLine = doc->selStartLine(), selStartCol = doc->selStartCol();
uint selEndLine = doc->selEndLine(), selEndCol = doc->selEndCol();
/* Variable to "restore" the selection after replacement: if {} was selected,
we increase the selection of two characters */
uint newSelEndCol;
PlainToLaTeXConverter cvt;
// "Notifying" the editor that what we're about to do must be seen as ONE operation
KTextEditor::EditInterfaceExt *editInterfaceExt = KTextEditor::editInterfaceExt(doc);
if(NULL != editInterfaceExt)
editInterfaceExt->editBegin();
// Processing the first line
int firstLineLength;
if(selStartLine != selEndLine)
firstLineLength = doc->lineLength(selStartLine);
else
firstLineLength = selEndCol;
TQString firstLine = doc->text(selStartLine, selStartCol, selStartLine, firstLineLength);
TQString firstLineCvt = cvt.ConvertToLaTeX(firstLine);
doc->removeText(selStartLine, selStartCol, selStartLine, firstLineLength);
doc->insertText(selStartLine, selStartCol, firstLineCvt);
newSelEndCol = selStartCol + firstLineCvt.length();
// Processing the intermediate lines
for(uint nLine = selStartLine + 1 ; nLine < selEndLine ; ++nLine) {
TQString line = doc->textLine(nLine);
TQString newLine = cvt.ConvertToLaTeX(line);
doc->removeLine(nLine);
doc->insertLine(nLine, newLine);
}
// Processing the final line
if(selStartLine != selEndLine) {
TQString lastLine = doc->text(selEndLine, 0, selEndLine, selEndCol);
TQString lastLineCvt = cvt.ConvertToLaTeX(lastLine);
doc->removeText(selEndLine, 0, selEndLine, selEndCol);
doc->insertText(selEndLine, 0, lastLineCvt);
newSelEndCol = lastLineCvt.length();
}
// End of the "atomic edit operation"
if(NULL != editInterfaceExt)
editInterfaceExt->editEnd();
doc->setSelection(selStartLine, selStartCol, selEndLine, newSelEndCol);
}
/**
* Pastes the clipboard's contents as LaTeX (ie. % -> \%, etc.).
*/
void Manager::pasteAsLaTeX(void)
{
Kate::View *view = currentTextView();
if(NULL == view)
return;
Kate::Document *doc = view->getDoc();
if(NULL == doc)
return;
// Getting a proper text insertion point BEFORE the atomic editing operation
uint cursorLine, cursorCol;
if(doc->hasSelection()) {
cursorLine = doc->selStartLine();
cursorCol = doc->selStartCol();
} else {
view->cursorPositionReal(&cursorLine, &cursorCol);
}
// "Notifying" the editor that what we're about to do must be seen as ONE operation
KTextEditor::EditInterfaceExt *editInterfaceExt = KTextEditor::editInterfaceExt(doc);
if(NULL != editInterfaceExt)
editInterfaceExt->editBegin();
// If there is a selection, one must remove it
if(doc->hasSelection())
doc->removeSelectedText();
PlainToLaTeXConverter cvt;
TQString toPaste = cvt.ConvertToLaTeX(TDEApplication::clipboard()->text());
doc->insertText(cursorLine, cursorCol, toPaste);
// End of the "atomic edit operation"
if(NULL != editInterfaceExt)
editInterfaceExt->editEnd();
}
void Manager::quickPreviewPopup()
{
Kate::View *view = currentTextView();
if( ! view )
return;
Kate::Document *doc = view->getDoc();
if ( doc )
{
if ( doc->hasSelection() )
emit( startQuickPreview(KileTool::qpSelection) );
else if ( m_ki->editorExtension()->hasMathgroup(view) )
emit( startQuickPreview(KileTool::qpMathgroup) );
else if ( m_ki->editorExtension()->hasEnvironment(view) )
emit( startQuickPreview(KileTool::qpEnvironment) );
}
}
void Manager::testCanDecodeURLs(const TQDragMoveEvent *e, bool &accept)
{
accept = KURLDrag::canDecode(e); // only accept URL drops
}
void Manager::replaceLoadedURL(TQWidget *w, TQDropEvent *e)
{
KURL::List urls;
if(!KURLDrag::decode(e, urls)) {
return;
}
int index = m_tabs->indexOf(w);
KileDocument::Extensions *extensions = m_ki->extensions();
bool hasReplacedTab = false;
for(KURL::List::iterator i = urls.begin(); i != urls.end(); ++i) {
KURL url = *i;
if(extensions->isProjectFile(url)) {
m_ki->docManager()->projectOpen(url);
}
else if(!hasReplacedTab) {
closeWidget(w);
m_ki->docManager()->fileOpen(url, TQString(), index);
hasReplacedTab = true;
}
else {
m_ki->docManager()->fileOpen(url);
}
}
}
void Manager::urlChanged(KileDocument::Info* info, const KURL& /*url*/)
{
KileDocument::TextInfo *textInfo = dynamic_cast<KileDocument::TextInfo*>(info);
if(textInfo)
{
Kate::View *view = textView(textInfo);
if(!view)
{
return;
}
setTabLabel(view, m_ki->getShortName(textInfo->getDoc()));
}
}
DropWidget::DropWidget(TQWidget * parent, const char * name, WFlags f) : TQWidget(parent, name, f)
{
setAcceptDrops(true);
}
DropWidget::~DropWidget()
{
}
void DropWidget::dragMoveEvent(TQDragMoveEvent *e)
{
bool b;
emit testCanDecode(e, b);
e->accept(b);
}
void DropWidget::dropEvent(TQDropEvent *e)
{
emit receivedDropEvent(e);
}
// remove entries from KatePart menu:
// - menu entry to config Kate, because there is
// already one call to this configuration dialog from Kile
// - goto line, because we put it into a submenu
void Manager::unplugKatePartMenu(Kate::View* view)
{
if ( view )
{
TQStringList actionlist;
actionlist << "set_confdlg" << "go_goto_line"; // action names from katepartui.rc
for ( uint i=0; i < actionlist.count(); ++i )
{
KAction *action = view->actionCollection()->action( actionlist[i].ascii() );
if ( action )
{
action->unplugAll();
// action->setShortcut(KShortcut());
}
}
}
}
}
#include "kileviewmanager.moc"