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.
tdevelop/src/partcontroller.cpp

1868 lines
53 KiB

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <tqpopupmenu.h>
#include <tqfile.h>
#include <tqlayout.h>
#include <tqmap.h>
#include <tqlabel.h>
#include <tqradiobutton.h>
#include <tqcheckbox.h>
#include <tqdom.h>
#include <kmimetype.h>
#include <kservice.h>
#include <ktrader.h>
#include <kapplication.h>
#include <krun.h>
#include <kdebug.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kparts/part.h>
#include <kparts/factory.h>
#include <kparts/partmanager.h>
#include <kparts/browserextension.h>
#include <kfiledialog.h>
#include <kmainwindow.h>
#include <kaction.h>
#include <kmessagebox.h>
#include <kstatusbar.h>
#include <khtml_part.h>
#include <kpopupmenu.h>
#include <kio/netaccess.h>
#include <kdialogbase.h>
#include <klineedit.h>
#include <kshortcut.h>
#include <kcompletion.h>
#include <kdirwatch.h>
#include <tdeversion.h>
#include <kiconloader.h>
#include <kuserprofile.h>
#include <kencodingfiledialog.h>
#include <ksqueezedtextlabel.h>
#include <ktexteditor/view.h>
#include <ktexteditor/document.h>
#include <ktexteditor/viewcursorinterface.h>
#include <ktexteditor/encodinginterface.h>
#include "toplevel.h"
#include "api.h"
#include "core.h"
#include "editorproxy.h"
#include "documentationpart.h"
#include "ksavealldialog.h"
#include "kdevproject.h"
#include "urlutil.h"
#include "mimewarningdialog.h"
#include "domutil.h"
#include "kdevjobtimer.h"
#include "designer.h"
#include "kdevlanguagesupport.h"
#include "multibuffer.h"
#include "partcontroller.h"
class TQDomDocument;
PartController *PartController::s_instance = 0;
using namespace MainWindowUtils;
struct HistoryEntry
{
KURL url;
TQString context;
HistoryEntry( const KURL& u, const TQString& c ): url( u ), context( c ) {}
};
struct ModificationData
{
KTextEditor::Document * doc;
bool isModified;
unsigned char reason;
};
PartController::PartController(TQWidget *parent)
: KDevPartController(parent), _editorFactory(0L), m_currentActivePart(0), m_removingActivePart(false)
{
connect(this, TQT_SIGNAL(partRemoved(KParts::Part*)), this, TQT_SLOT(slotPartRemoved(KParts::Part* )) );
connect(this, TQT_SIGNAL(partAdded(KParts::Part*)), this, TQT_SLOT(slotPartAdded(KParts::Part* )) );
connect(this, TQT_SIGNAL(activePartChanged(KParts::Part*)), this, TQT_SLOT(slotActivePartChanged(KParts::Part*)));
setupActions();
m_isJumping = false;
m_openNextAsText = false;
}
PartController::~PartController()
{
}
void PartController::createInstance(TQWidget *parent)
{
if (!s_instance)
s_instance = new PartController(parent);
}
PartController *PartController::getInstance()
{
return s_instance;
}
void PartController::setupActions()
{
KActionCollection *ac = TopLevel::getInstance()->main()->actionCollection();
KAction* newAction = KStdAction::open(this, TQT_SLOT(slotOpenFile()), ac, "file_open");
newAction->setToolTip( i18n("Open file") );
newAction->setWhatsThis( i18n("<b>Open file</b><p>Opens an existing file without adding it to the project.</p>") );
m_openRecentAction = KStdAction::openRecent( this, TQT_SLOT(slotOpenRecent(const KURL&) ), ac, "file_open_recent" );
m_openRecentAction->setWhatsThis(TQString("<b>%1</b><p>%2").arg(beautifyToolTip(m_openRecentAction->text())).arg(i18n("Opens recently opened file.")));
m_openRecentAction->loadEntries( kapp->config(), "RecentFiles" );
m_saveAllFilesAction = new KAction(i18n("Save Al&l"), 0, this, TQT_SLOT(slotSaveAllFiles()), ac, "file_save_all");
m_saveAllFilesAction->setToolTip( i18n("Save all modified files") );
m_saveAllFilesAction->setWhatsThis(i18n("<b>Save all</b><p>Saves all modified files."));
m_saveAllFilesAction->setEnabled(false);
m_revertAllFilesAction = new KAction(i18n("Rever&t All"), 0, this, TQT_SLOT(slotRevertAllFiles()), ac, "file_revert_all");
m_revertAllFilesAction->setToolTip(i18n("Revert all changes"));
m_revertAllFilesAction->setWhatsThis(i18n("<b>Revert all</b><p>Reverts all changes in opened files. Prompts to save changes so the reversion can be canceled for each modified file."));
m_revertAllFilesAction->setEnabled(false);
m_closeWindowAction = KStdAction::close(this, TQT_SLOT(slotCloseWindow()), ac, "file_close");
m_closeWindowAction->setToolTip( i18n("Close current file") );
m_closeWindowAction->setWhatsThis(TQString("<b>%1</b><p>%2").arg(beautifyToolTip(m_closeWindowAction->text())).arg(i18n("Closes current file.")));
m_closeWindowAction->setEnabled(false);
m_closeAllWindowsAction = new KAction(i18n("Close All"), 0, this, TQT_SLOT(slotCloseAllWindows()), ac, "file_close_all");
m_closeAllWindowsAction->setToolTip( i18n("Close all files") );
m_closeAllWindowsAction->setWhatsThis(i18n("<b>Close all</b><p>Close all opened files."));
m_closeAllWindowsAction->setEnabled(false);
m_closeOtherWindowsAction = new KAction(i18n("Close All Others"), 0, this, TQT_SLOT(slotCloseOtherWindows()), ac, "file_closeother");
m_closeOtherWindowsAction->setToolTip( i18n("Close other files") );
m_closeOtherWindowsAction->setWhatsThis(i18n("<b>Close all others</b><p>Close all opened files except current."));
m_closeOtherWindowsAction->setEnabled(false);
new KActionSeparator(ac, "dummy_separator");
m_backAction = new KToolBarPopupAction(i18n("Back"), "back", 0, this, TQT_SLOT(slotBack()), ac, "history_back");
m_backAction->setEnabled( false );
m_backAction->setToolTip(i18n("Back"));
m_backAction->setWhatsThis(i18n("<b>Back</b><p>Moves backwards one step in the navigation history."));
connect(m_backAction->popupMenu(), TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(slotBackAboutToShow()));
connect(m_backAction->popupMenu(), TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotBackPopupActivated(int)));
m_forwardAction = new KToolBarPopupAction(i18n("Forward"), "forward", 0, this, TQT_SLOT(slotForward()), ac, "history_forward");
m_forwardAction->setEnabled( false );
m_forwardAction->setToolTip(i18n("Forward"));
m_forwardAction->setWhatsThis(i18n("<b>Forward</b><p>Moves forward one step in the navigation history."));
connect(m_forwardAction->popupMenu(), TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(slotForwardAboutToShow()));
connect(m_forwardAction->popupMenu(), TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotForwardPopupActivated(int)));
m_gotoLastEditPosAction = new KAction( i18n("Goto Last Edit Position"), "bottom", 0, this, TQT_SLOT(gotoLastEditPos()), ac, "goto_last_edit_pos" );
m_gotoLastEditPosAction->setEnabled( false );
m_gotoLastEditPosAction->setToolTip( i18n("Goto Last Edit Position") );
m_gotoLastEditPosAction->setWhatsThis( i18n("<b>Goto Last Edit Position</b><p>Open the last edited file and position cursor at the point of edit") );
}
void PartController::setEncoding(const TQString &encoding)
{
m_presetEncoding = encoding;
}
KParts::Part* PartController::findOpenDocument(const KURL& url)
{
// if we find it this way, all is well
KParts::Part * part = partForURL( url );
if ( part )
{
return part;
}
// ok, let's see if we can try harder
if ( API::getInstance()->project() )
{
KURL partURL = findURLInProject( url );
partURL.cleanPath();
return partForURL( partURL );
}
return 0L;
}
KURL PartController::findURLInProject(const KURL& url)
{
TQStringList fileList = API::getInstance()->project()->allFiles();
bool filenameOnly = (url.url().find('/') == -1);
TQString filename = filenameOnly ? "/" : "";
filename += url.url();
for (TQStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it) {
if ((*it).endsWith(filename)) {
// Match! The first one is as good as any one, I guess...
return KURL( API::getInstance()->project()->projectDirectory() + "/" + *it );
}
}
return url;
}
void PartController::editDocument(const KURL &inputUrl, int lineNum, int col)
{
editDocumentInternal(inputUrl, lineNum, col);
}
void PartController::splitCurrentDocument(const KURL &inputUrl,
int lineNum, int col)
{
editDocumentInternal(inputUrl, lineNum, col, true, true);
}
void PartController::scrollToLineColumn(const KURL &inputUrl,
int lineNum, int col, bool storeHistory )
{
if ( KParts::ReadOnlyPart *existingPart = partForURL( inputUrl ) )
{
if( storeHistory ) addHistoryEntry( existingPart );
EditorProxy::getInstance()->setLineNumber( existingPart, lineNum, col );
return;
}
}
void PartController::editDocumentInternal( const KURL & inputUrl, int lineNum,
int col, bool activate,
bool addToCurrentBuffer )
{
kdDebug(9000) << k_funcinfo << "\n " << inputUrl.prettyURL()
<< " linenum " << lineNum << " activate? " << activate
<< " addToCurrentBuffer? " << addToCurrentBuffer << endl;
KURL url = inputUrl;
// is it already open?
// (Try this once before verifying the URL, we could be dealing with a file that no longer exists on disc)
if ( KParts::Part *existingPart = partForURL( url ) )
{
// if we've been asked to OpenAs an open file with a specific encoding, assume the user wants to change encoding
if ( !m_presetEncoding.isNull() )
{
if ( KTextEditor::EncodingInterface * ei = dynamic_cast<KTextEditor::EncodingInterface*>( existingPart ) )
{
ei->setEncoding( m_presetEncoding );
}
m_presetEncoding = TQString();
}
addHistoryEntry();
activatePart( existingPart );
EditorProxy::getInstance()->setLineNumber( existingPart, lineNum, col );
return;
}
// Make sure the URL exists
if ( !url.isValid() || !KIO::NetAccess::exists(url, false, 0) )
{
bool done = false;
// Try to find this file in the current project's list instead
if ( API::getInstance()->project() )
{
if (url.isRelativeURL(url.url())) {
KURL relURL(API::getInstance()->project()->projectDirectory());
relURL.addPath( url.url() );
kdDebug() << k_funcinfo << "Looking for file in project dir: " << API::getInstance()->project()->projectDirectory() << " url " << url.url() << " transformed to " << relURL.url() << ": " << done << endl;
if (relURL.isValid() && KIO::NetAccess::exists(relURL, false, 0)) {
url = relURL;
done = true;
}
else {
KURL relURL(API::getInstance()->project()->buildDirectory());
relURL.addPath( url.url() );
kdDebug() << k_funcinfo << "Looking for file in build dir: " << API::getInstance()->project()->buildDirectory() << " url " << url.url() << " transformed to " << relURL.url() << ": " << done << endl;
if (relURL.isValid() && KIO::NetAccess::exists(relURL, false, 0)) {
url = relURL;
done = true;
}
}
}
if (!done) {
url = findURLInProject(url);
if ( !url.isValid() || !KIO::NetAccess::exists(url, false, 0) )
// See if this url is relative to the current project's directory
url = API::getInstance()->project()->projectDirectory() + "/" + url.path();
else
done = true;
}
}
if ( !done && ( !url.isValid() || !KIO::NetAccess::exists(url, false, 0) ))
{
// Not found - prompt the user to find it?
kdDebug(9000) << "cannot find URL: " << url.url() << endl;
return;
}
}
// We now have a url that exists ;)
// clean it and resolve possible symlink
url.cleanPath(true);
if (url.isLocalFile())
{
TQString path = url.path();
path = URLUtil::canonicalPath(path);
if ( !path.isEmpty() )
url.setPath(path);
}
// is it already open?
KParts::Part *existingPart = partForURL(url);
if (existingPart)
{
addHistoryEntry();
activatePart(existingPart);
EditorProxy::getInstance()->setLineNumber(existingPart, lineNum, col);
return;
}
if ( !addToCurrentBuffer )
{
if ( KDevLanguageSupport *lang =
API::getInstance()->languageSupport() )
{
// Let the language part override the addToCurrentBuffer flag
// if it decides to...
addToCurrentBuffer = lang->shouldSplitDocument( inputUrl );
if ( addToCurrentBuffer )
{
kdDebug(9000) <<
"languagePart() insists addToCurrentBuffer = true" << endl;
// Set activate = true, otherwise we have hard to fix
// multi-buffer delayed activation.
// I'll re-look at this later...
activate = true;
}
}
}
KMimeType::Ptr MimeType = KMimeType::findByURL( url );
kdDebug(9000) << "mimeType = " << MimeType->name() << endl;
// is the URL pointing to a directory?
if ( MimeType->is( "inode/directory" ) )
{
return;
}
if ( !m_presetEncoding.isNull() )
{
m_openNextAsText = true;
}
KConfig *config = kapp->config();
config->setGroup("General Options");
// we don't trust KDE with designer files, let's handle it ourselves
if ( !m_openNextAsText && MimeType->is( "application/x-designer" ) )
{
TQString DesignerSetting = config->readEntry( "DesignerSetting", "ExternalDesigner" );
TQString designerExec = "designer";
TQStringList designerPluginPaths;
TQDomDocument* dom = API::getInstance()->projectDom();
if ( dom != 0 )
{
// The global option specifies a fallback if the project
// has no setting or no project is open. However for TQt4
// projects we want to use ExternalDesigner in any case.
if ( DomUtil::readIntEntry( *dom, "/kdevcppsupport/qt/version", 3 ) == 4 )
{
designerPluginPaths = DomUtil::readListEntry(*dom, "/kdevcppsupport/qt/designerpluginpaths", "path" );
DesignerSetting = "ExternalDesigner";
}
DesignerSetting = DomUtil::readEntry(*dom, "/kdevcppsupport/qt/designerintegration", DesignerSetting );
designerExec = DomUtil::readEntry(*dom, "/kdevcppsupport/qt/designer", designerExec );
}
if ( DesignerSetting == "ExternalKDevDesigner" )
{
designerExec = "kdevdesigner";
}
else if ( DesignerSetting == "EmbeddedKDevDesigner" )
{
if ( KParts::ReadOnlyPart *designerPart = qtDesignerPart() )
{
addHistoryEntry();
activatePart(designerPart);
designerPart->openURL(url);
return;
}
else if ( KParts::Factory * KDevDesignerFactory = static_cast<KParts::Factory*>( KLibLoader::self()->factory( TQFile::encodeName( "libkdevdesignerpart" ) ) ) )
{
KParts::ReadWritePart * kdevpart = static_cast<KParts::ReadWritePart*>( KDevDesignerFactory->createPart( TopLevel::getInstance()->main(), 0, 0, 0, "KParts::ReadWritePart" ) );
kdevpart->openURL( url );
addHistoryEntry();
integratePart( kdevpart, url );
m_openRecentAction->addURL( url );
m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );
return;
}
}
if( designerPluginPaths.isEmpty() )
KRun::runCommand( designerExec+" "+url.pathOrURL() );
else
KRun::runCommand( "TQT_PLUGIN_PATH=\""+designerPluginPaths.join(":")+"\" "+designerExec+" "+url.pathOrURL() );
return;
}
config->setGroup("General");
TQStringList texttypeslist = config->readListEntry( "TextTypes" );
if ( texttypeslist.contains( MimeType->name() ) )
{
m_openNextAsText = true;
}
bool isText = false;
TQVariant v = MimeType->property("X-KDE-text");
if (v.isValid())
isText = v.toBool();
// is this regular text - open in editor
if ( m_openNextAsText || isText || MimeType->is( "application/x-zerosize" ) )
{
KTextEditor::Editor *editorpart =
createEditorPart( activate, addToCurrentBuffer, url );
if ( editorpart )
{
if ( m_presetEncoding.isNull() && API::getInstance()->projectDom() )
{
TQDomDocument * projectDom = API::getInstance()->projectDom();
m_presetEncoding = DomUtil::readEntry( *projectDom, "/general/defaultencoding", TQString() );
}
if ( !m_presetEncoding.isNull() )
{
if ( KTextEditor::EncodingInterface * ei = dynamic_cast<KTextEditor::EncodingInterface*>( editorpart ) )
{
ei->setEncoding( m_presetEncoding );
}
m_presetEncoding = TQString();
}
addHistoryEntry();
TQWidget * widget = EditorProxy::getInstance()->topWidgetForPart( editorpart );
integratePart(editorpart, url, widget, true, activate, addToCurrentBuffer);
EditorProxy::getInstance()->setLineNumber(editorpart, lineNum, col);
m_openNextAsText = false;
m_openRecentAction->addURL( url );
m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );
return;
}
}
// OK, it's not text and it's not a designer file.. let's see what else we can come up with..
KParts::Factory *factory = 0;
TQString className;
TQString services[] = { "KParts/ReadWritePart", "KParts/ReadOnlyPart" };
TQString classnames[] = { "KParts::ReadWritePart", "KParts::ReadOnlyPart" };
for (uint i=0; i<2; ++i)
{
factory = findPartFactory( MimeType->name(), services[i] );
if (factory)
{
className = classnames[i];
break;
}
}
kdDebug(9000) << "factory = " << factory << endl;
if (factory)
{
// create the object of the desired class
KParts::ReadOnlyPart *part = static_cast<KParts::ReadOnlyPart*>( factory->createPart( TopLevel::getInstance()->main(), 0, 0, 0, className.latin1() ) );
if ( part )
{
part->openURL( url );
addHistoryEntry();
if ( dynamic_cast<KTextEditor::Editor*>( part ) ) // we can have ended up with a texteditor, in which case need to treat it as such
{
integratePart(part, url, part->widget(), true, activate);
EditorProxy::getInstance()->setLineNumber(part, lineNum, col);
}
else
{
integratePart( part, url );
}
m_openRecentAction->addURL( url );
m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );
}
}
else
{
MimeWarningDialog dlg;
dlg.text2->setText( TQString( "<qt><b>%1</b></qt>" ).arg(url.path()));
dlg.text3->setText( dlg.text3->text().arg(MimeType->name()) );
if ( dlg.exec() == TQDialog::Accepted )
{
if ( dlg.open_with_kde->isChecked() )
{
KRun::runURL(url, MimeType->name() );
}
else
{
if ( dlg.always_open_as_text->isChecked() )
{
KConfig *config = kapp->config();
config->setGroup("General");
TQStringList texttypeslist = config->readListEntry( "TextTypes" );
texttypeslist << MimeType->name();
config->writeEntry( "TextTypes", texttypeslist );
}
m_openNextAsText = true;
editDocument( url, lineNum, col );
}
}
}
}
void PartController::showDocument(const KURL &url, bool newWin)
{
TQString fixedPath = HTMLDocumentationPart::resolveEnvVarsInURL(url.url()); // possibly could env vars
KURL docUrl(fixedPath);
kdDebug(9000) << "SHOW: " << docUrl.url() << endl;
if ( docUrl.isLocalFile() && KMimeType::findByURL(docUrl)->name() != "text/html" ) {
// a link in a html-file pointed to a local text file - display
// it in the editor instead of a html-view to avoid uglyness
editDocument( docUrl );
return;
}
addHistoryEntry();
HTMLDocumentationPart *part = dynamic_cast<HTMLDocumentationPart*>(activePart());
if (!part || newWin)
{
part = new HTMLDocumentationPart;
integratePart(part,docUrl);
connect(part, TQT_SIGNAL(fileNameChanged(KParts::ReadOnlyPart* )),
this, TQT_SIGNAL(partURLChanged(KParts::ReadOnlyPart* )));
}
else
{
activatePart(part);
}
part->openURL(docUrl);
}
KParts::Factory *PartController::findPartFactory(const TQString &mimeType, const TQString &partType, const TQString &preferredName)
{
KTrader::OfferList offers = KTrader::self()->query(mimeType, TQString("'%1' in ServiceTypes").arg(partType));
if (offers.count() > 0)
{
KService::Ptr ptr = 0;
// if there is a preferred plugin we'll take it
if ( !preferredName.isEmpty() ) {
KTrader::OfferList::Iterator it;
for (it = offers.begin(); it != offers.end(); ++it) {
if ((*it)->desktopEntryName() == preferredName) {
ptr = (*it);
}
}
}
// else we just take the first in the list
if ( !ptr ) {
ptr = offers.first();
}
return static_cast<KParts::Factory*>(KLibLoader::self()->factory(TQFile::encodeName(ptr->library())));
}
return 0;
}
KTextEditor::Editor * PartController::createEditorPart( bool activate,
bool addToCurrentBuffer,
const KURL &url )
{
MultiBuffer *multiBuffer = 0;
if ( addToCurrentBuffer )
{
multiBuffer =
dynamic_cast<MultiBuffer*>(
EditorProxy::getInstance()->topWidgetForPart( activePart() )
);
}
if ( !multiBuffer )
{
kdDebug(9000) << "Creating a new MultiBuffer for "
<< url.fileName() << endl;
multiBuffer = new MultiBuffer( TopLevel::getInstance()->main() );
}
static bool alwaysActivate = true;
kapp->config()->setGroup("Editor");
TQString preferred = kapp->config()->readPathEntry("EmbeddedKTextEditor");
// If we are not using kyzis...
// Don't create non-wrapped views for now,
// avoid two paths (== two chances for bad bugs)
if ( preferred != "kyzispart" )
alwaysActivate = false;
KTextEditor::Editor *editorpart =
dynamic_cast<KTextEditor::Editor*>(multiBuffer->createPart( "text/plain",
"KTextEditor/Document",
alwaysActivate | activate ?
"KTextEditor::Editor" : "KTextEditor::Document",
preferred
));
if ( url.isValid() )
editorpart->openURL( url );
multiBuffer->registerURL( url, editorpart );
multiBuffer->setDelayedActivation( !activate );
return editorpart;
}
void PartController::integratePart(KParts::Part *part, const KURL &url,
TQWidget* widget, bool isTextEditor,
bool activate, bool addToCurrentBuffer )
{
if (!widget) widget = part->widget();
if (!widget) {
/// @todo error handling
kdDebug(9000) << "no widget for this part!!" << endl;
return; // to avoid later crash
}
if ( !addToCurrentBuffer )
TopLevel::getInstance()->embedPartView(widget, url.fileName(), url.url());
addPart(part, activate);
// tell the parts we loaded a document
KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(part);
if ( !ro_part ) return;
emit loadedFile( ro_part->url() );
connect( part, TQT_SIGNAL(modifiedOnDisc(Kate::Document*, bool, unsigned char)), this, TQT_SLOT(slotDocumentDirty(Kate::Document*, bool, unsigned char)) );
// let's get notified when a document has been changed
connect(part, TQT_SIGNAL(completed()), this, TQT_SLOT(slotUploadFinished()));
// yes, we're cheating again. this signal exists for katepart's
// Document object and our HTMLDocumentationPart
// connect(part, TQT_SIGNAL(fileNameChanged()), this, TQT_SLOT(slotFileNameChanged()));
// Connect to the document's views newStatus() signal in order to keep track of the
// modified-status of the document.
if (isTextEditor)
integrateTextEditorPart(static_cast<KTextEditor::Document*>(part));
KInterfaceDesigner::Designer *designerPart = dynamic_cast<KInterfaceDesigner::Designer *>(part);
if (designerPart && API::getInstance()->languageSupport())
{
kdDebug() << "integrating designer part with language support" << endl;
connect(designerPart, TQT_SIGNAL(addedFunction(DesignerType, const TQString&, Function )),
API::getInstance()->languageSupport(),
TQT_SLOT(addFunction(DesignerType, const TQString&, Function )));
connect(designerPart, TQT_SIGNAL(editedFunction(DesignerType, const TQString&, Function, Function )), API::getInstance()->languageSupport(),
TQT_SLOT(editFunction(DesignerType, const TQString&, Function, Function )));
connect(designerPart, TQT_SIGNAL(removedFunction(DesignerType, const TQString&, Function )),
API::getInstance()->languageSupport(),
TQT_SLOT(removeFunction(DesignerType, const TQString&, Function )));
connect(designerPart, TQT_SIGNAL(editFunction(DesignerType, const TQString&, const TQString& )),
API::getInstance()->languageSupport(),
TQT_SLOT(openFunction(DesignerType, const TQString&, const TQString& )));
connect(designerPart, TQT_SIGNAL(editSource(DesignerType, const TQString& )),
API::getInstance()->languageSupport(),
TQT_SLOT(openSource(DesignerType, const TQString& )));
connect(designerPart, TQT_SIGNAL(newStatus(const TQString &, int)),
this, TQT_SLOT(slotNewDesignerStatus(const TQString &, int)));
}
}
void PartController::integrateTextEditorPart(KTextEditor::Document* doc)
{
// There's shortcut conflict between Kate and the debugger, resolve
// it here. Ideally, the should be some standard mechanism, configurable
// by config files.
// However, it does not exists and situation here some rare commands
// like "Dynamic word wrap" or "Show line numbers" from Kate take
// all possible shortcuts, leaving us with IDE that has no shortcuts for
// debugger, is very bad.
//
// We could try to handle this in debugger, but that would require
// the debugger to intercept all new KTextEditor::View creations, which is
// not possible.
if ( !doc ) return;
connect( doc, TQT_SIGNAL(textChanged()), this, TQT_SLOT(textChanged()) );
connect( doc, TQT_SIGNAL(fileNameChanged()),
this, TQT_SLOT(slotDocumentUrlChanged()));
if( doc->widget() )
{
connect( doc->widget(), TQT_SIGNAL(dropEventPass(TQDropEvent *)),
TopLevel::getInstance()->main(), TQT_SLOT(slotDropEvent(TQDropEvent *)) );
}
if ( KTextEditor::View * view = dynamic_cast<KTextEditor::View*>( doc->widget() ) )
{
KActionCollection* c = view->actionCollection();
// Be extra carefull, in case the part either don't provide those
// action, or uses different shortcuts.
if (KAction* a = c->action("view_folding_markers"))
{
if (a->shortcut() == KShortcut(Key_F9))
a->setShortcut(KShortcut());
}
if (KAction* a = c->action("view_dynamic_word_wrap"))
{
if (a->shortcut() == KShortcut(Key_F10))
a->setShortcut(KShortcut());
}
if (KAction* a = c->action("view_line_numbers"))
{
if (a->shortcut() == KShortcut(Key_F11))
a->setShortcut(KShortcut());
}
}
//EditorProxy::getInstance()->installPopup(doc, contextPopupMenu());
// What's potentially problematic is that this signal isn't officially part of the
// KTextEditor::View interface. It is nevertheless there, and used in kate and kwrite.
// There doesn't seem to be any othere way of making this work with katepart, and since
// signals are dynamic, if we try to connect to an editorpart that lacks this signal,
// all we get is a runtime warning. At this point in time we are only really supported
// by katepart anyway so IMHO this hack is justified. //teatime
TQPtrList<KTextEditor::View> list = doc->views();
TQPtrListIterator<KTextEditor::View> it( list );
while ( it.current() )
{
connect( it, TQT_SIGNAL( newStatus() ), this, TQT_SLOT( slotNewStatus() ) );
++it;
}
}
void PartController::slotPartAdded( KParts::Part * part )
{
kdDebug(9000) << k_funcinfo << endl;
if ( KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part ) )
{
updatePartURL( ro_part );
}
updateMenuItems();
}
void PartController::slotPartRemoved( KParts::Part * part )
{
kdDebug(9000) << k_funcinfo << endl;
_partURLMap.remove( static_cast<KParts::ReadOnlyPart*>(part) );
if ( part == m_currentActivePart )
{
m_removingActivePart = true;
}
updateMenuItems();
}
void PartController::updatePartURL( KParts::ReadOnlyPart * ro_part )
{
if ( ro_part->url().isEmpty() )
{
kdDebug(9000) << "updatePartURL() called with empty URL for part: " << ro_part << endl;
return;
}
_partURLMap[ ro_part ] = ro_part->url();
}
bool PartController::partURLHasChanged( KParts::ReadOnlyPart * ro_part )
{
if ( _partURLMap.contains( ro_part ) && !ro_part->url().isEmpty() )
{
if ( _partURLMap[ ro_part ] != ro_part->url() )
{
return true;
}
}
return false;
}
KURL PartController::storedURLForPart( KParts::ReadOnlyPart * ro_part )
{
if ( _partURLMap.contains( ro_part ) )
{
return _partURLMap[ ro_part ];
}
return KURL();
}
void PartController::slotUploadFinished()
{
KParts::ReadOnlyPart *ro_part = const_cast<KParts::ReadOnlyPart*>( dynamic_cast<const KParts::ReadOnlyPart*>(sender()) );
if ( !ro_part ) return;
if ( partURLHasChanged( ro_part ) )
{
emit partURLChanged( ro_part );
updatePartURL( ro_part );
}
}
KParts::ReadOnlyPart *PartController::partForURL(const KURL &url)
{
TQPtrListIterator<KParts::Part> it(*parts());
for ( ; it.current(); ++it)
{
KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(it.current());
if (ro_part && url.path() == ro_part->url().path())
return ro_part;
}
return 0;
}
KParts::Part * PartController::partForWidget( const TQWidget * widget )
{
TQPtrListIterator<KParts::Part> it(*parts());
for ( ; it.current(); ++it)
{
if ( it.current()->widget() == widget )
{
return *it;
}
}
return 0;
}
void PartController::activatePart(KParts::Part *part)
{
if ( !part ) return;
TQWidget * widget = EditorProxy::getInstance()->widgetForPart( part );
if (widget)
{
TopLevel::getInstance()->raiseView( widget );
widget->show();
widget->setFocus();
}
setActivePart(part);
TQWidget* w2 = EditorProxy::getInstance()->widgetForPart( part );
if (w2 != widget)
w2->setFocus();
}
bool PartController::closePart(KParts::Part *part)
{
KParts::ReadOnlyPart * ro_part =
dynamic_cast<KParts::ReadOnlyPart*>( part );
if ( !ro_part ) return true;
KURL url = ro_part->url();
if (TQWidget* w = EditorProxy::getInstance()->topWidgetForPart( part ) )
{
if ( MultiBuffer *multiBuffer = dynamic_cast<MultiBuffer*>( w ) )
{
kdDebug(9000) << "About to delete MultiBuffered document..."
<< " numberOfBuffers: " << multiBuffer->numberOfBuffers()
<< " isActivated: " << multiBuffer->isActivated()
<< endl;
if ( !multiBuffer->closeURL( url ) )
return false;
if ( multiBuffer->numberOfBuffers() == 0
|| !multiBuffer->isActivated() )
{
TopLevel::getInstance()->removeView( w );
_dirtyDocuments.remove( static_cast<KParts::ReadWritePart*>( ro_part ) );
emit closedFile( url );
/* kdDebug(9000) << "Deleting MultiBuffer Part" << endl;*/
TopLevel::getInstance()->main()->guiFactory()->removeClient( part );
delete part;
/* kdDebug(9000) << "DeleteLater Actual MultiBuffer" << endl;*/
multiBuffer->deleteLater();
return true;
}
else
{
/* kdDebug(9000) << "Deleting MultiBuffer Part" << endl;*/
_dirtyDocuments.remove( static_cast<KParts::ReadWritePart*>( ro_part ) );
TopLevel::getInstance()->main()->guiFactory()->removeClient( part );
emit closedFile( url );
delete part;
// Switch to a remaining buffer
setActivePart( multiBuffer->activeBuffer() );
return true;
}
}
else if ( !ro_part->closeURL() )
return false;
TopLevel::getInstance()->removeView( w );
}
else if ( !ro_part->closeURL() )
return false;
TopLevel::getInstance()->main()->guiFactory()->removeClient( part );
_dirtyDocuments.remove( static_cast<KParts::ReadWritePart*>( ro_part ) );
emit closedFile( url );
/* kdDebug(9000) << "Deleting Regular Part" << endl;*/
delete part;
return true;
}
void PartController::updateMenuItems()
{
bool hasWriteParts = false;
bool hasReadOnlyParts = false;
TQPtrListIterator<KParts::Part> it(*parts());
for ( ; it.current(); ++it)
{
if (it.current()->inherits("KParts::ReadWritePart"))
hasWriteParts = true;
if (it.current()->inherits("KParts::ReadOnlyPart"))
hasReadOnlyParts = true;
}
m_saveAllFilesAction->setEnabled(hasWriteParts);
m_revertAllFilesAction->setEnabled(hasWriteParts);
m_closeWindowAction->setEnabled(hasReadOnlyParts);
m_closeAllWindowsAction->setEnabled(hasReadOnlyParts);
m_closeOtherWindowsAction->setEnabled(hasReadOnlyParts);
m_backAction->setEnabled( !m_backHistory.isEmpty() );
}
void PartController::slotRevertAllFiles()
{
revertAllFiles();
}
void PartController::reloadFile( const KURL & url )
{
KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
if ( part )
{
if ( part->isModified() )
{
if ( KMessageBox::warningYesNo( TopLevel::getInstance()->main(),
i18n( "The file \"%1\" is modified in memory. Are you sure you want to reload it? (Local changes will be lost.)" ).arg( url.path() ),
i18n( "File is Modified" ), i18n("Reload"), i18n("Do Not Reload") ) == KMessageBox::Yes )
{
part->setModified( false );
}
else
{
return;
}
}
unsigned int line = 0; unsigned int col = 0;
KTextEditor::ViewCursorInterface * iface = dynamic_cast<KTextEditor::ViewCursorInterface*>( part->widget() );
if (iface)
{
iface->cursorPositionReal( &line, &col );
}
part->openURL( url );
_dirtyDocuments.remove( part );
emit documentChangedState( url, Clean );
if ( iface )
{
iface->setCursorPositionReal( line, col );
}
}
}
void PartController::revertFiles( const KURL::List & list )
{
KURL::List::ConstIterator it = list.begin();
while ( it != list.end() )
{
reloadFile( *it );
++it;
}
}
void PartController::revertAllFiles()
{
revertFiles( openURLs() );
}
void PartController::slotCloseWindow()
{
closePart( activePart() );
}
KURL::List PartController::modifiedDocuments()
{
KURL::List modFiles;
TQPtrListIterator<KParts::Part> it( *parts() );
while( it.current() )
{
KParts::ReadWritePart *rw_part = dynamic_cast<KParts::ReadWritePart*>(it.current());
if ( rw_part && rw_part->isModified() )
{
modFiles << rw_part->url();
}
++it;
}
return modFiles;
}
void PartController::slotSave()
{
kdDebug(9000) << k_funcinfo << endl;
if ( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( activePart() ) )
{
saveFile( part->url() );
}
}
void PartController::slotReload()
{
kdDebug(9000) << k_funcinfo << endl;
if ( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( activePart() ) )
{
reloadFile( part->url() );
}
}
void PartController::slotSaveAllFiles()
{
saveAllFiles();
}
bool PartController::saveFile( const KURL & url, bool force )
{
KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
if ( !part ) return true;
switch( documentState( url ) )
{
case Clean:
if ( !force )
{
return true;
}
kdDebug(9000) << "Forced save" << endl;
break;
case Modified:
kdDebug(9000) << "Normal save" << endl;
break;
case Dirty:
case DirtyAndModified:
{
int code = KMessageBox::warningYesNoCancel( TopLevel::getInstance()->main(),
i18n("The file \"%1\" is modified on disk.\n\nAre you sure you want to overwrite it? (External changes will be lost.)").arg( url.path() ),
i18n("File Externally Modified"), i18n("Overwrite"), i18n("Do Not Overwrite") );
if ( code == KMessageBox::Yes )
{
kdDebug(9000) << "Dirty save!!" << endl;
}
else if ( code == KMessageBox::No )
{
return true;
}
else
{
return false; // a 'false' return means to interrupt the process that caused the save
}
}
break;
default:
;
}
if ( part->save() )
{
_dirtyDocuments.remove( part );
emit documentChangedState( url, Clean );
emit savedFile( url );
}
return true;
}
bool PartController::saveAllFiles()
{
return saveFiles( openURLs() );
}
bool PartController::saveFiles( KURL::List const & filelist )
{
KURL::List::ConstIterator it = filelist.begin();
while ( it != filelist.end() )
{
if (saveFile( *it )==false)
return false; //user cancelled
++it;
}
return true;
}
bool PartController::querySaveFiles()
{
return saveFilesDialog( KURL::List() );
}
void PartController::clearModified( KURL::List const & filelist )
{
KURL::List::ConstIterator it = filelist.begin();
while ( it != filelist.end() )
{
KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( *it ) );
if ( rw_part )
{
rw_part->setModified( false );
}
++it;
}
}
bool PartController::saveFilesDialog( KURL::List const & ignoreList )
{
KURL::List modList = modifiedDocuments();
if ( modList.count() > 0 && modList != ignoreList )
{
KSaveSelectDialog dlg( modList, ignoreList, TopLevel::getInstance()->main() );
if ( dlg.exec() == TQDialog::Accepted )
{
saveFiles( dlg.filesToSave() );
clearModified( dlg.filesNotToSave() );
}
else
{
return false;
}
}
return true;
}
bool PartController::closeFilesDialog( KURL::List const & ignoreList )
{
if ( !saveFilesDialog( ignoreList ) ) return false;
TQPtrList<KParts::Part> partList( *parts() );
TQPtrListIterator<KParts::Part> it( partList );
while ( KParts::Part* part = it.current() )
{
KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part );
if ( ro_part && !ignoreList.contains( ro_part->url() ) || !ro_part )
{
closePart( part );
}
++it;
}
return true;
}
bool PartController::closeFiles( const KURL::List & list )
{
KURL::List::ConstIterator it = list.begin();
while ( it != list.end() )
{
if ( !closePart( partForURL( *it ) ) )
{
return false;
}
++it;
}
return true;
}
bool PartController::closeFile( const KURL & url )
{
return closePart( partForURL( url ) );
}
bool PartController::closeAllFiles()
{
return closeFilesDialog( KURL::List() );
}
void PartController::slotCloseAllWindows()
{
closeAllFiles();
}
bool PartController::closeAllOthers( const KURL & url )
{
KURL::List ignoreList;
ignoreList.append( url );
return closeFilesDialog( ignoreList );
}
void PartController::slotCloseOtherWindows()
{
if ( KParts::ReadOnlyPart * active = dynamic_cast<KParts::ReadOnlyPart*>( activePart() ) )
{
closeAllOthers( active->url() );
}
}
void PartController::slotOpenFile()
{
TQString DefaultEncoding;
if ( TQDomDocument * projectDom = API::getInstance()->projectDom() )
{
DefaultEncoding = DomUtil::readEntry( *projectDom, "/general/defaultencoding", TQString() );
}
if ( DefaultEncoding.isEmpty() )
{
// have a peek at katepart's settings:
KConfig * config = kapp->config();
config->setGroup("Kate Document Defaults");
DefaultEncoding = config->readEntry("Encoding", TQString() );
}
KEncodingFileDialog::Result result = KEncodingFileDialog::getOpenURLsAndEncoding( DefaultEncoding, TQString(),
TQString(), TopLevel::getInstance()->main(), TQString() );
for ( KURL::List::Iterator it = result.URLs.begin(); it != result.URLs.end(); ++it )
{
m_presetEncoding = result.encoding;
editDocument( *it );
}
}
void PartController::slotOpenRecent( const KURL& url )
{
editDocument( url );
// stupid bugfix - don't allow an active item in the list
m_openRecentAction->setCurrentItem( -1 );
}
bool PartController::readyToClose()
{
blockSignals( true );
closeAllFiles(); // this should never return false, as the files are already saved
return true;
}
void PartController::slotActivePartChanged( KParts::Part *part )
{
kdDebug(9000) << k_funcinfo << part << endl;
if ( !m_isJumping && !m_removingActivePart )
{
if ( KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>(m_currentActivePart) )
{
addHistoryEntry( ro_part );
}
}
m_currentActivePart = part;
m_removingActivePart = false;
if (part) {
KXMLGUIClient* client = dynamic_cast<KXMLGUIClient*>(part->widget());
if (client) Core::setupShourtcutTips(client);
}
updateMenuItems();
TQTimer::singleShot( 100, this, TQT_SLOT(slotWaitForFactoryHack()) );
}
void PartController::showPart( KParts::Part* part, const TQString& name, const TQString& shortDescription )
{
if (!part->widget()) {
/// @todo error handling
return; // to avoid later crash
}
TQPtrListIterator<KParts::Part> it(*parts());
for ( ; it.current(); ++it)
{
if( it.current() == part ){
// part already embedded
activatePart( it.current() );
return;
}
}
// embed the part
TopLevel::getInstance()->embedPartView( part->widget(), name, shortDescription );
addPart( part );
}
void PartController::slotDocumentDirty( Kate::Document * d, bool isModified, unsigned char reason )
{
kdDebug(9000) << k_funcinfo << endl;
// KTextEditor::Document * doc = reinterpret_cast<KTextEditor::Document*>( d ); // theoretically unsafe in MI scenario
KTextEditor::Document * doc = 0;
TQPtrListIterator<KParts::Part> it( *parts() );
while( it.current() )
{
if ( (void*)it.current() == (void*)d )
{
doc = dynamic_cast<KTextEditor::Document*>( it.current() );
break;
}
++it;
}
// this is a bit strange, but in order to avoid weird crashes
// down in KDirWatcher, we want to step off the call chain before
// opening any messageboxes
if ( doc )
{
ModificationData * p = new ModificationData;
p->doc = doc;
p->isModified = isModified;
p->reason = reason;
KDevJobTimer::singleShot( 0, this, TQT_SLOT(slotDocumentDirtyStepTwo(void*)), p );
}
}
void PartController::slotDocumentDirtyStepTwo( void * payload )
{
if ( !payload ) return;
ModificationData * p = reinterpret_cast<ModificationData*>( payload );
KTextEditor::Document * doc = p->doc;
// let's make sure the document is still loaded
bool haveDocument = false;
if( const TQPtrList<KParts::Part> * partlist = parts() )
{
TQPtrListIterator<KParts::Part> it( *partlist );
while ( KParts::Part * part = it.current() )
{
if ( p->doc == dynamic_cast<KTextEditor::Document*>( part ) )
{
haveDocument = true;
break;
}
++it;
}
}
if ( !haveDocument ) return;
bool isModified = p->isModified;
unsigned char reason = p->reason;
delete p;
KURL url = storedURLForPart( doc );
if ( url.isEmpty() )
{
kdDebug(9000) << "Warning!! the stored url is empty. Bailing out!" << endl;
}
if ( reason > 0 )
{
if ( !_dirtyDocuments.contains( doc ) )
{
_dirtyDocuments.append( doc );
}
if ( reactToDirty( url, reason ) )
{
// file has been reloaded
emit documentChangedState( url, Clean );
_dirtyDocuments.remove( doc );
}
else
{
doEmitState( url );
}
}
else
{
_dirtyDocuments.remove( doc );
emit documentChangedState( url, Clean );
}
kdDebug(9000) << doc->url().url() << endl;
kdDebug(9000) << isModified << endl;
kdDebug(9000) << reason << endl;
}
bool PartController::isDirty( KURL const & url )
{
return _dirtyDocuments.contains( static_cast<KTextEditor::Document*>( partForURL( url ) ) );
}
bool PartController::reactToDirty( KURL const & url, unsigned char reason )
{
KConfig *config = kapp->config();
config->setGroup("Editor");
TQString dirtyAction = config->readEntry( "DirtyAction" );
if ( dirtyAction == "nothing" ) return false;
bool isModified = true;
if( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) ) )
{
isModified = part->isModified();
}
else
{
kdDebug(9000) << k_funcinfo << " Warning. Not a ReadWritePart." << endl;
return false;
}
if ( isModified )
{
KMessageBox::sorry( TopLevel::getInstance()->main(),
i18n("Conflict: The file \"%1\" has changed on disk while being modified in memory.\n\n"
"You should investigate before saving to make sure you are not losing data.").arg( url.path() ),
i18n("Conflict") );
return false;
}
if ( reason == 3 ) // means the file was deleted
{
KMessageBox::sorry( TopLevel::getInstance()->main(),
i18n("Warning: The file \"%1\" has been deleted on disk.\n\n"
"If this was not your intention, make sure to save this file now.").arg( url.path() ),
i18n("File Deleted") );
return false;
}
if ( dirtyAction == "alert" )
{
if ( KMessageBox::warningYesNo( TopLevel::getInstance()->main(),
i18n("The file \"%1\" has changed on disk.\n\nDo you want to reload it?").arg( url.path() ),
i18n("File Changed"), i18n("Reload"), i18n("Do Not Reload") ) == KMessageBox::No )
{
return false;
}
}
// here we either answered yes above or are in autoreload mode
reloadFile( url );
return true;
}
void PartController::slotNewDesignerStatus(const TQString &formName, int status)
{
kdDebug(9000) << k_funcinfo << endl;
kdDebug(9000) << " formName: " << formName << ", status: " << status << endl;
emit documentChangedState( KURL::fromPathOrURL(formName), DocumentState(status) );
}
void PartController::slotNewStatus( )
{
kdDebug(9000) << k_funcinfo << endl;
TQObject * senderobj = TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>( sender() ));
KTextEditor::View * view = dynamic_cast<KTextEditor::View*>( senderobj );
if ( view )
{
doEmitState( view->document()->url() );
}
}
DocumentState PartController::documentState( KURL const & url )
{
KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
if ( !rw_part ) return Clean;
DocumentState state = Clean;
if ( rw_part->isModified() )
{
state = Modified;
}
if ( isDirty( url ) )
{
if ( state == Modified )
{
state = DirtyAndModified;
}
else
{
state = Dirty;
}
}
return state;
}
void PartController::doEmitState( KURL const & url )
{
emit documentChangedState( url, documentState( url ) );
}
KURL::List PartController::openURLs( )
{
KURL::List list;
TQPtrListIterator<KParts::Part> it(*parts());
for ( ; it.current(); ++it)
{
if ( KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(it.current()) )
{
list << ro_part->url();
}
}
return list;
}
/////////////////////////////////////////////////////////////////////////////
//BEGIN History methods
PartController::HistoryEntry::HistoryEntry( const KURL & u, int l, int c)
: url(u), line(l), col(c)
{
id = abs( TQTime::currentTime().msecsTo( TQTime() ) ); // should provide a reasonably unique number
}
void PartController::slotBack()
{
HistoryEntry thatEntry = m_backHistory.front();
m_backHistory.pop_front();
m_backAction->setEnabled( !m_backHistory.isEmpty() );
HistoryEntry thisEntry = createHistoryEntry();
if ( !thisEntry.url.isEmpty() )
{
m_forwardHistory.push_front( thisEntry );
m_forwardAction->setEnabled( true );
}
jumpTo( thatEntry );
}
void PartController::slotForward()
{
HistoryEntry thatEntry = m_forwardHistory.front();
m_forwardHistory.pop_front();
m_forwardAction->setEnabled( !m_forwardHistory.isEmpty() );
HistoryEntry thisEntry = createHistoryEntry();
if ( !thisEntry.url.isEmpty() )
{
m_backHistory.push_front( thisEntry );
m_backAction->setEnabled( true );
}
jumpTo( thatEntry );
}
void PartController::slotBackAboutToShow()
{
KPopupMenu *popup = m_backAction->popupMenu();
popup->clear();
if ( m_backHistory.isEmpty()) return;
int i = 0;
TQValueList<HistoryEntry>::ConstIterator it = m_backHistory.begin();
while( i < 10 && it != m_backHistory.end() )
{
popup->insertItem( (*it).url.fileName() + TQString(" (%1)").arg( (*it).line +1), (*it).id );
++i;
++it;
}
}
void PartController::slotForwardAboutToShow( )
{
KPopupMenu * popup = m_forwardAction->popupMenu();
popup->clear();
if ( m_forwardHistory.isEmpty() ) return;
int i = 0;
TQValueList<HistoryEntry>::ConstIterator it = m_forwardHistory.begin();
while( i < 10 && it != m_forwardHistory.end() )
{
popup->insertItem( (*it).url.fileName() + TQString(" (%1)").arg( (*it).line +1), (*it).id );
++i;
++it;
}
}
void PartController::slotBackPopupActivated( int id )
{
TQValueList<HistoryEntry>::Iterator it = m_backHistory.begin();
while( it != m_backHistory.end() )
{
if ( (*it).id == id )
{
HistoryEntry entry = *it;
m_backHistory.erase( m_backHistory.begin(), ++it );
m_backAction->setEnabled( !m_backHistory.isEmpty() );
HistoryEntry thisEntry = createHistoryEntry();
if ( !thisEntry.url.isEmpty() )
{
m_forwardHistory.push_front( thisEntry );
m_forwardAction->setEnabled( true );
}
jumpTo( entry );
return;
}
++it;
}
}
void PartController::slotForwardPopupActivated( int id )
{
TQValueList<HistoryEntry>::Iterator it = m_forwardHistory.begin();
while( it != m_forwardHistory.end() )
{
if ( (*it).id == id )
{
HistoryEntry entry = *it;
m_forwardHistory.erase( m_forwardHistory.begin(), ++it );
m_forwardAction->setEnabled( !m_forwardHistory.isEmpty() );
HistoryEntry thisEntry = createHistoryEntry();
if ( !thisEntry.url.isEmpty() )
{
m_backHistory.push_front( thisEntry );
m_backAction->setEnabled( true );
}
jumpTo( entry );
return;
}
++it;
}
}
void PartController::jumpTo( const HistoryEntry & entry )
{
m_isJumping = true;
editDocument( entry.url, entry.line, entry.col );
m_isJumping = false;
}
PartController::HistoryEntry PartController::createHistoryEntry( KParts::ReadOnlyPart * ro_part ) {
if( ro_part == 0 )
ro_part = dynamic_cast<KParts::ReadOnlyPart*>( activePart() );
if ( !ro_part ) return HistoryEntry();
KTextEditor::ViewCursorInterface * cursorIface = dynamic_cast<KTextEditor::ViewCursorInterface*>( ro_part->widget() );
if ( !cursorIface ) return HistoryEntry();
uint line = 0;
uint col = 0;
cursorIface->cursorPositionReal( &line, &col );
return HistoryEntry( ro_part->url(), line, col );
}
// this should be called _before_ a jump is made
void PartController::addHistoryEntry( KParts::ReadOnlyPart * part )
{
if ( m_isJumping ) return;
HistoryEntry thisEntry = createHistoryEntry( part );
if ( !thisEntry.url.isEmpty() )
{
HistoryEntry lastEntry = m_backHistory.front();
if ( (lastEntry.url.path() != thisEntry.url.path()) || (lastEntry.line != thisEntry.line) )
{
m_backHistory.push_front( thisEntry );
m_backAction->setEnabled( true );
m_forwardHistory.clear();
m_forwardAction->setEnabled( false );
}
else
{
kdDebug(9000) << "** avoiding to create duplicate history entry **" << endl;
}
}
}
//END History methods
void PartController::slotWaitForFactoryHack( )
{
//kdDebug(9000) << k_funcinfo << endl;
if ( !activePart() ) return;
if ( dynamic_cast<KTextEditor::View*>( activePart()->widget() ) )
{
if ( !activePart()->factory() )
{
TQTimer::singleShot( 100, this, TQT_SLOT(slotWaitForFactoryHack()) );
return;
}
else
{
EditorProxy::getInstance()->installPopup( activePart() );
}
}
if ( MultiBuffer *multiBuffer =
dynamic_cast<MultiBuffer*>(
EditorProxy::getInstance()->topWidgetForPart( activePart() ) )
)
{
KURL url = dynamic_cast<KParts::ReadOnlyPart*>( activePart() )->url();
multiBuffer->activePartChanged( url );
// Really unfortunate, but the mainWindow relies upon this
// to set the tab's icon
emit documentChangedState( url, documentState( url ) );
}
}
KParts::ReadOnlyPart *PartController::qtDesignerPart()
{
TQPtrListIterator<KParts::Part> it(*parts());
for ( ; it.current(); ++it)
{
KInterfaceDesigner::Designer *des = dynamic_cast<KInterfaceDesigner::Designer*>(it.current());
if (des && des->designerType() == KInterfaceDesigner::TQtDesigner)
return des;
}
return 0;
}
KTextEditor::Editor *PartController::openTextDocument( bool activate )
{
KTextEditor::Editor *editorpart =
createEditorPart( activate, false, KURL( i18n("unnamed") ) );
if ( editorpart )
{
if ( !m_presetEncoding.isNull() )
{
KParts::BrowserExtension * extension =
KParts::BrowserExtension::childObject( editorpart );
if ( extension )
{
KParts::URLArgs args;
args.serviceType = TQString( "text/plain;" ) + m_presetEncoding;
extension->setURLArgs(args);
}
m_presetEncoding = TQString();
}
TQWidget * widget =
EditorProxy::getInstance()->topWidgetForPart( editorpart );
addHistoryEntry();
integratePart(editorpart, KURL(i18n("unnamed")), widget, true, true);
EditorProxy::getInstance()->setLineNumber(editorpart, 0, 0);
}
return editorpart;
}
void PartController::textChanged()
{
if ( KTextEditor::Document * doc = dynamic_cast<KTextEditor::Document*>( activePart() ) )
{
if ( KTextEditor::ViewCursorInterface * vci = dynamic_cast<KTextEditor::ViewCursorInterface*>( doc->widget() ) )
{
m_gotoLastEditPosAction->setEnabled( true );
m_lastEditPos.url = doc->url();
vci->cursorPosition( &m_lastEditPos.pos.first, &m_lastEditPos.pos.second );
}
}
}
void PartController::gotoLastEditPos()
{
editDocument( m_lastEditPos.url, m_lastEditPos.pos.first, m_lastEditPos.pos.second );
}
void PartController::slotDocumentUrlChanged()
{
TQObject *obj = TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(sender()));
KTextEditor::Document *doc = dynamic_cast<KTextEditor::Document*>( obj );
if (!doc)
return;
MultiBuffer *multiBuffer = dynamic_cast<MultiBuffer*>(
EditorProxy::getInstance()->findPartWidget( doc ));
if (!multiBuffer)
return;
multiBuffer->updateUrlForPart(doc, doc->url());
updatePartURL(doc);
emit partURLChanged(doc);
}
#include "partcontroller.moc"