#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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() { TDEActionCollection *ac = TopLevel::getInstance()->main()->actionCollection(); TDEAction* newAction = KStdAction::open(this, TQT_SLOT(slotOpenFile()), ac, "file_open"); newAction->setToolTip( i18n("Open file") ); newAction->setWhatsThis( i18n("Open file

Opens an existing file without adding it to the project.

") ); m_openRecentAction = KStdAction::openRecent( this, TQT_SLOT(slotOpenRecent(const KURL&) ), ac, "file_open_recent" ); m_openRecentAction->setWhatsThis(TQString("%1

%2").arg(beautifyToolTip(m_openRecentAction->text())).arg(i18n("Opens recently opened file."))); m_openRecentAction->loadEntries( kapp->config(), "RecentFiles" ); m_saveAllFilesAction = new TDEAction(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("Save all

Saves all modified files.")); m_saveAllFilesAction->setEnabled(false); m_revertAllFilesAction = new TDEAction(i18n("Rever&t All"), 0, this, TQT_SLOT(slotRevertAllFiles()), ac, "file_revert_all"); m_revertAllFilesAction->setToolTip(i18n("Revert all changes")); m_revertAllFilesAction->setWhatsThis(i18n("Revert all

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("%1

%2").arg(beautifyToolTip(m_closeWindowAction->text())).arg(i18n("Closes current file."))); m_closeWindowAction->setEnabled(false); m_closeAllWindowsAction = new TDEAction(i18n("Close All"), 0, this, TQT_SLOT(slotCloseAllWindows()), ac, "file_close_all"); m_closeAllWindowsAction->setToolTip( i18n("Close all files") ); m_closeAllWindowsAction->setWhatsThis(i18n("Close all

Close all opened files.")); m_closeAllWindowsAction->setEnabled(false); m_closeOtherWindowsAction = new TDEAction(i18n("Close All Others"), 0, this, TQT_SLOT(slotCloseOtherWindows()), ac, "file_closeother"); m_closeOtherWindowsAction->setToolTip( i18n("Close other files") ); m_closeOtherWindowsAction->setWhatsThis(i18n("Close all others

Close all opened files except current.")); m_closeOtherWindowsAction->setEnabled(false); new TDEActionSeparator(ac, "dummy_separator"); m_backAction = new TDEToolBarPopupAction(i18n("Back"), "back", 0, this, TQT_SLOT(slotBack()), ac, "history_back"); m_backAction->setEnabled( false ); m_backAction->setToolTip(i18n("Back")); m_backAction->setWhatsThis(i18n("Back

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 TDEToolBarPopupAction(i18n("Forward"), "forward", 0, this, TQT_SLOT(slotForward()), ac, "history_forward"); m_forwardAction->setEnabled( false ); m_forwardAction->setToolTip(i18n("Forward")); m_forwardAction->setWhatsThis(i18n("Forward

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 TDEAction( 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("Goto Last Edit Position

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( 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() || !TDEIO::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() && TDEIO::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() && TDEIO::NetAccess::exists(relURL, false, 0)) { url = relURL; done = true; } } } if (!done) { url = findURLInProject(url); if ( !url.isValid() || !TDEIO::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() || !TDEIO::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; } TDEConfig *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( KLibLoader::self()->factory( TQFile::encodeName( "libkdevdesignerpart" ) ) ) ) { KParts::ReadWritePart * kdevpart = static_cast( 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-TDE-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( 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( factory->createPart( TopLevel::getInstance()->main(), 0, 0, 0, className.latin1() ) ); if ( part ) { part->openURL( url ); addHistoryEntry(); if ( dynamic_cast( 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( "%1" ).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() ) { TDEConfig *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(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) { TDETrader::OfferList offers = TDETrader::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() ) { TDETrader::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(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( 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(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(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(part)); KInterfaceDesigner::Designer *designerPart = dynamic_cast(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( doc->widget() ) ) { TDEActionCollection* c = view->actionCollection(); // Be extra carefull, in case the part either don't provide those // action, or uses different shortcuts. if (TDEAction* a = c->action("view_folding_markers")) { if (a->shortcut() == TDEShortcut(Key_F9)) a->setShortcut(TDEShortcut()); } if (TDEAction* a = c->action("view_dynamic_word_wrap")) { if (a->shortcut() == TDEShortcut(Key_F10)) a->setShortcut(TDEShortcut()); } if (TDEAction* a = c->action("view_line_numbers")) { if (a->shortcut() == TDEShortcut(Key_F11)) a->setShortcut(TDEShortcut()); } } //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 list = doc->views(); TQPtrListIterator 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( part ) ) { updatePartURL( ro_part ); } updateMenuItems(); } void PartController::slotPartRemoved( KParts::Part * part ) { kdDebug(9000) << k_funcinfo << endl; _partURLMap.remove( static_cast(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( dynamic_cast(sender()) ); if ( !ro_part ) return; if ( partURLHasChanged( ro_part ) ) { emit partURLChanged( ro_part ); updatePartURL( ro_part ); } } KParts::ReadOnlyPart *PartController::partForURL(const KURL &url) { TQPtrListIterator it(*parts()); for ( ; it.current(); ++it) { KParts::ReadOnlyPart *ro_part = dynamic_cast(it.current()); if (ro_part && url.path() == ro_part->url().path()) return ro_part; } return 0; } KParts::Part * PartController::partForWidget( const TQWidget * widget ) { TQPtrListIterator 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( part ); if ( !ro_part ) return true; KURL url = ro_part->url(); if (TQWidget* w = EditorProxy::getInstance()->topWidgetForPart( part ) ) { if ( MultiBuffer *multiBuffer = dynamic_cast( 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( 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( 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( 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 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( 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( 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 it( *parts() ); while( it.current() ) { KParts::ReadWritePart *rw_part = dynamic_cast(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( activePart() ) ) { saveFile( part->url() ); } } void PartController::slotReload() { kdDebug(9000) << k_funcinfo << endl; if ( KParts::ReadWritePart * part = dynamic_cast( activePart() ) ) { reloadFile( part->url() ); } } void PartController::slotSaveAllFiles() { saveAllFiles(); } bool PartController::saveFile( const KURL & url, bool force ) { KParts::ReadWritePart * part = dynamic_cast( 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( 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 partList( *parts() ); TQPtrListIterator it( partList ); while ( KParts::Part* part = it.current() ) { KParts::ReadOnlyPart * ro_part = dynamic_cast( 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( 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: TDEConfig * 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(m_currentActivePart) ) { addHistoryEntry( ro_part ); } } m_currentActivePart = part; m_removingActivePart = false; if (part) { KXMLGUIClient* client = dynamic_cast(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 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( d ); // theoretically unsafe in MI scenario KTextEditor::Document * doc = 0; TQPtrListIterator it( *parts() ); while( it.current() ) { if ( (void*)it.current() == (void*)d ) { doc = dynamic_cast( 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( payload ); KTextEditor::Document * doc = p->doc; // let's make sure the document is still loaded bool haveDocument = false; if( const TQPtrList * partlist = parts() ) { TQPtrListIterator it( *partlist ); while ( KParts::Part * part = it.current() ) { if ( p->doc == dynamic_cast( 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( partForURL( url ) ) ); } bool PartController::reactToDirty( KURL const & url, unsigned char reason ) { TDEConfig *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( 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( sender() )); KTextEditor::View * view = dynamic_cast( senderobj ); if ( view ) { doEmitState( view->document()->url() ); } } DocumentState PartController::documentState( KURL const & url ) { KParts::ReadWritePart * rw_part = dynamic_cast( 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 it(*parts()); for ( ; it.current(); ++it) { if ( KParts::ReadOnlyPart *ro_part = dynamic_cast(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() { TDEPopupMenu *popup = m_backAction->popupMenu(); popup->clear(); if ( m_backHistory.isEmpty()) return; int i = 0; TQValueList::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( ) { TDEPopupMenu * popup = m_forwardAction->popupMenu(); popup->clear(); if ( m_forwardHistory.isEmpty() ) return; int i = 0; TQValueList::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::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::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( activePart() ); if ( !ro_part ) return HistoryEntry(); KTextEditor::ViewCursorInterface * cursorIface = dynamic_cast( 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( activePart()->widget() ) ) { if ( !activePart()->factory() ) { TQTimer::singleShot( 100, this, TQT_SLOT(slotWaitForFactoryHack()) ); return; } else { EditorProxy::getInstance()->installPopup( activePart() ); } } if ( MultiBuffer *multiBuffer = dynamic_cast( EditorProxy::getInstance()->topWidgetForPart( activePart() ) ) ) { KURL url = dynamic_cast( 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 it(*parts()); for ( ; it.current(); ++it) { KInterfaceDesigner::Designer *des = dynamic_cast(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( activePart() ) ) { if ( KTextEditor::ViewCursorInterface * vci = dynamic_cast( 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(sender())); KTextEditor::Document *doc = dynamic_cast( obj ); if (!doc) return; MultiBuffer *multiBuffer = dynamic_cast( EditorProxy::getInstance()->findPartWidget( doc )); if (!multiBuffer) return; multiBuffer->updateUrlForPart(doc, doc->url()); updatePartURL(doc); emit partURLChanged(doc); } #include "partcontroller.moc"