KDevelop API Documentation

partcontroller.cpp

Go to the documentation of this file.
00001 #include <sys/types.h>
00002 #include <sys/stat.h>
00003 #include <unistd.h>
00004 
00005 #include <qpopupmenu.h>
00006 #include <qfile.h>
00007 #include <qlayout.h>
00008 #include <qmap.h>
00009 #include <qlabel.h>
00010 #include <qradiobutton.h>
00011 #include <qcheckbox.h>
00012 
00013 #include <kmimetype.h>
00014 #include <kservice.h>
00015 #include <ktrader.h>
00016 #include <kapplication.h>
00017 #include <krun.h>
00018 #include <kdebug.h>
00019 #include <klocale.h>
00020 #include <kmessagebox.h>
00021 #include <kparts/part.h>
00022 #include <kparts/factory.h>
00023 #include <kparts/partmanager.h>
00024 #include <kparts/browserextension.h>
00025 #include <kfiledialog.h>
00026 #include <kmainwindow.h>
00027 #include <kaction.h>
00028 #include <kstatusbar.h>
00029 #include <khtml_part.h>
00030 #include <kpopupmenu.h>
00031 #include <kio/netaccess.h>
00032 #include <kdialogbase.h>
00033 #include <klineedit.h>
00034 #include <kshortcut.h>
00035 #include <kcompletion.h>
00036 #include <kdirwatch.h>
00037 #include <kdeversion.h>
00038 #include <kiconloader.h>
00039 #include <kuserprofile.h>
00040 #include <kencodingfiledialog.h>
00041 
00042 #include <ktexteditor/view.h>
00043 #include <ktexteditor/document.h>
00044 #include <ktexteditor/viewcursorinterface.h>
00045 
00046 #include "toplevel.h"
00047 #include "api.h"
00048 #include "core.h"
00049 #include "editorproxy.h"
00050 #include "documentationpart.h"
00051 #include "ksavealldialog.h"
00052 
00053 #include "kdevproject.h"
00054 #include "urlutil.h"
00055 #include "mimewarningdialog.h"
00056 
00057 #include "designer.h"
00058 #include "kdevlanguagesupport.h"
00059 
00060 #include "partcontroller.h"
00061 
00062 PartController *PartController::s_instance = 0;
00063 
00064 using namespace MainWindowUtils;
00065 
00066 struct HistoryEntry {
00067     KURL url;
00068     QString context;
00069 
00070     HistoryEntry( const KURL& u, const QString& c ): url( u ), context( c ) {}
00071 };
00072 
00073 PartController::PartController(QWidget *parent)
00074   : KDevPartController(parent), _editorFactory(0L)
00075 {
00076   connect(this, SIGNAL(partRemoved(KParts::Part*)), this, SLOT(slotPartRemoved(KParts::Part* )) );
00077   connect(this, SIGNAL(partAdded(KParts::Part*)), this, SLOT(slotPartAdded(KParts::Part* )) );
00078   connect(this, SIGNAL(activePartChanged(KParts::Part*)), this, SLOT(slotActivePartChanged(KParts::Part*)));
00079   
00080   setupActions();
00081   
00082   m_Current = m_history.end();
00083   m_isJumping = false;
00084   
00085   m_openNextAsText = false;
00086 }
00087 
00088 
00089 PartController::~PartController()
00090 {
00091 }
00092 
00093 
00094 void PartController::createInstance(QWidget *parent)
00095 {
00096   if (!s_instance)
00097     s_instance = new PartController(parent);
00098 }
00099 
00100 
00101 PartController *PartController::getInstance()
00102 {
00103   return s_instance;
00104 }
00105 
00106 
00107 void PartController::setupActions()
00108 {
00109   KActionCollection *ac = TopLevel::getInstance()->main()->actionCollection();
00110 
00111   KAction* newAction = KStdAction::open(this, SLOT(slotOpenFile()),
00112     ac, "file_open");
00113   newAction->setToolTip( i18n("Open file") );
00114   newAction->setWhatsThis( i18n("<b>Open file</b><p>Opens an existing file without adding it to the project.</p>") );
00115 
00116   m_openRecentAction = KStdAction::openRecent( this, SLOT(slotOpenRecent(const KURL&) ),
00117     ac, "file_open_recent" );
00118   m_openRecentAction->setWhatsThis(QString("<b>%1</b><p>%2").arg(beautifyToolTip(m_openRecentAction->text())).arg(i18n("Opens recently opened file.")));
00119   m_openRecentAction->loadEntries( kapp->config(), "RecentFiles" );
00120   
00121   m_saveAllFilesAction = new KAction(i18n("Save Al&l"), 0,
00122     this, SLOT(slotSaveAllFiles()),
00123     ac, "file_save_all");
00124   m_saveAllFilesAction->setToolTip( i18n("Save all modified files") );
00125   m_saveAllFilesAction->setWhatsThis(i18n("<b>Save all</b><p>Saves all modified files."));
00126   m_saveAllFilesAction->setEnabled(false);
00127 
00128   m_revertAllFilesAction = new KAction(i18n("Rever&t All"), 0,
00129     this, SLOT(slotRevertAllFiles()),
00130     ac, "file_revert_all");
00131   m_revertAllFilesAction->setToolTip(i18n("Revert all changes"));
00132   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."));
00133   m_revertAllFilesAction->setEnabled(false);
00134 
00135   m_closeWindowAction = KStdAction::close(
00136     this, SLOT(slotCloseWindow()),
00137     ac, "file_close");
00138   m_closeWindowAction->setToolTip( i18n("Close current file") );
00139   m_closeWindowAction->setWhatsThis(QString("<b>%1</b><p>%2").arg(beautifyToolTip(m_closeWindowAction->text())).arg(i18n("Closes current file.")));
00140   m_closeWindowAction->setEnabled(false);
00141 
00142   m_closeAllWindowsAction = new KAction(i18n("Close All"), 0,
00143     this, SLOT(slotCloseAllWindows()),
00144     ac, "file_close_all");
00145   m_closeAllWindowsAction->setToolTip( i18n("Close all files") );
00146   m_closeAllWindowsAction->setWhatsThis(i18n("<b>Close all</b><p>Close all opened files."));
00147   m_closeAllWindowsAction->setEnabled(false);
00148 
00149   m_closeOtherWindowsAction = new KAction(i18n("Close All Others"), 0,
00150     this, SLOT(slotCloseOtherWindows()),
00151     ac, "file_closeother");
00152   m_closeOtherWindowsAction->setToolTip( i18n("Close other files") );
00153   m_closeOtherWindowsAction->setWhatsThis(i18n("<b>Close all others</b><p>Close all opened files except current."));
00154   m_closeOtherWindowsAction->setEnabled(false);
00155 
00156   m_switchToAction = new KAction(i18n("Switch To..."), KShortcut("CTRL+/"),
00157     this, SLOT(slotSwitchTo()),
00158     ac, "file_switchto");
00159   m_switchToAction->setToolTip(i18n("Switch to"));
00160   m_switchToAction->setWhatsThis(i18n("<b>Switch to</b><p>Prompts to enter the name of previously opened file to switch to."));
00161 
00162   new KActionSeparator(ac, "dummy_separator");
00163   
00164   m_backAction = new KToolBarPopupAction(i18n("Back"), "back", 0, this, SLOT(slotBack()), ac, "history_back");
00165   m_backAction->setEnabled( false );
00166   m_backAction->setToolTip(i18n("Back"));
00167   m_backAction->setWhatsThis(i18n("<b>Back</b><p>Moves backwards one step in the navigation history."));
00168 
00169 
00170   connect(m_backAction->popupMenu(), SIGNAL(aboutToShow()),
00171          this, SLOT(slotBackAboutToShow()));
00172   connect(m_backAction->popupMenu(), SIGNAL(activated(int)),
00173          this, SLOT(slotPopupActivated(int)));
00174 
00175 
00176   m_forwardAction = new KToolBarPopupAction(i18n("Forward"), "forward", 0, this, SLOT(slotForward()), ac, "history_forward");
00177   m_forwardAction->setEnabled( false );
00178   m_forwardAction->setToolTip(i18n("Forward"));
00179   m_forwardAction->setWhatsThis(i18n("<b>Forward</b><p>Moves forward one step in the navigation history."));
00180 
00181   connect(m_forwardAction->popupMenu(), SIGNAL(aboutToShow()),
00182          this, SLOT(slotForwardAboutToShow()));
00183   connect(m_forwardAction->popupMenu(), SIGNAL(activated(int)),
00184          this, SLOT(slotPopupActivated(int)));
00185  }
00186 
00187 void PartController::setEncoding(const QString &encoding)
00188 {
00189   m_presetEncoding = encoding;
00190 }
00191 
00192 KParts::Part* PartController::findOpenDocument(const KURL& url)
00193 {
00194     // if we find it this way, all is well
00195     KParts::Part * part = partForURL( url );
00196     if ( part )
00197     {
00198         return part;
00199     }
00200     
00201     // ok, let's see if we can try harder
00202     if ( API::getInstance()->project() )
00203     {
00204         KURL partURL = findURLInProject( url );
00205         partURL.cleanPath();
00206         return partForURL( partURL );
00207     }
00208     
00209     return 0L;
00210 }
00211 
00212 KURL PartController::findURLInProject(const KURL& url)
00213 {
00214   QStringList fileList = API::getInstance()->project()->allFiles();
00215 
00216   bool filenameOnly = (url.url().find('/') == -1);
00217   QString filename = filenameOnly ? "/" : "";
00218   filename += url.url();
00219 
00220   for (QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it) {
00221     if ((*it).endsWith(filename)) {
00222       // Match! The first one is as good as any one, I guess...
00223       return KURL( API::getInstance()->project()->projectDirectory() + "/" + *it );
00224     }
00225   }
00226 
00227   return url;
00228 }
00229 
00230 void PartController::editDocument(const KURL &inputUrl, int lineNum, int col)
00231 {
00232   editDocumentInternal(inputUrl, lineNum, col);
00233 }
00234  
00235 void PartController::editDocumentInternal( const KURL & inputUrl, int lineNum, int col, bool activate )
00236 {
00237   kdDebug(9000) << k_funcinfo << inputUrl.prettyURL() << " linenum " << lineNum << " activate? " << activate << endl;
00238 
00239   KURL url = inputUrl;
00240   
00241   KConfig *config = kapp->config();
00242   config->setGroup("General Options");
00243   bool embedKDevDesigner = config->readBoolEntry("Embed KDevDesigner", true);
00244   config->setGroup("General");
00245   
00246   kdDebug(9000) << "    - embed: " << (embedKDevDesigner ? "true" : "false") << endl;
00247 
00248   // Make sure the URL exists
00249     if ( !url.isValid() || !KIO::NetAccess::exists(url, false, 0) ) 
00250     {
00251         bool done = false;
00252         
00253         // Try to find this file in the current project's list instead
00254         if ( API::getInstance()->project() ) 
00255         {
00256             if (url.isRelativeURL(url.url())) {
00257                 KURL relURL(API::getInstance()->project()->projectDirectory(), url.url());
00258         
00259                 if (relURL.isValid() && KIO::NetAccess::exists(url, false, 0)) {
00260                     url = relURL;
00261                     done = true;
00262                 }
00263                 kdDebug() << k_funcinfo << "Looking for file in project dir: " << API::getInstance()->project()->projectDirectory() << " url " << url.url() << " transformed to " << relURL.url() << ": " << done << endl;
00264             }
00265             
00266             if (!done) {
00267                 url = findURLInProject(url);
00268                     
00269                 if ( !url.isValid() || !KIO::NetAccess::exists(url, false, 0) ) 
00270                     // See if this url is relative to the current project's directory
00271                     url = API::getInstance()->project()->projectDirectory() + "/" + url.path();
00272                 
00273                 else
00274                     done = true;
00275             }
00276         }
00277     
00278         if ( !done && ( !url.isValid() || !KIO::NetAccess::exists(url, false, 0) )) 
00279         {
00280             // Not found - prompt the user to find it?
00281             kdDebug(9000) << "cannot find URL: " << url.url() << endl;
00282             return;
00283         }
00284     }
00285 
00286   // We now have a url that exists ;)
00287 
00288   // clean it and resolve possible symlink
00289     url.cleanPath(true);
00290     if (url.isLocalFile())
00291     {
00292         QString path = url.path();
00293         path = URLUtil::canonicalPath(path);
00294         if ( !path.isEmpty() )
00295             url.setPath(path);
00296     }
00297 
00298     // is it already open?
00299     KParts::Part *existingPart = partForURL(url);
00300     if (existingPart)
00301     {
00302         Q_ASSERT(activate);
00303         activatePart(existingPart);
00304         EditorProxy::getInstance()->setLineNumber(existingPart, lineNum, col);
00305         addHistoryEntry( url, lineNum, col );
00306         return;
00307     }
00308     
00309     KMimeType::Ptr MimeType = KMimeType::findByURL( url );
00310     
00311     kdDebug(9000) << "mimeType = " << MimeType->name() << endl;
00312     
00313     // is the URL pointing to a directory?
00314     if ( MimeType->is( "inode/directory" ) )
00315     {
00316         return;
00317     }
00318   
00319     if ( !m_presetEncoding.isNull() )
00320     {
00321         m_openNextAsText = true;
00322     }
00323 
00324     if ( !m_openNextAsText && embedKDevDesigner && MimeType->is( "application/x-designer" ) )
00325     {
00326         KParts::ReadOnlyPart *designerPart = qtDesignerPart();
00327         if (designerPart)
00328         {
00329             activatePart(designerPart);
00330             designerPart->openURL(url);
00331             return;
00332         }
00333     }
00334 
00335     // we generally prefer embedding, but if Qt-designer is the preferred application for this mimetype
00336     // make sure we launch designer instead of embedding KUIviewer
00337     if (!embedKDevDesigner)
00338     {
00339         if ( !m_openNextAsText && MimeType->is( "application/x-designer" ) )
00340         {
00341             KServiceTypeProfile::OfferList offers = KServiceTypeProfile::offers(MimeType->name(), "Application");
00342             for (KServiceTypeProfile::OfferList::const_iterator it = offers.begin(); it != offers.end(); ++it)
00343             {
00344                 KService::Ptr app = (*it).service();
00345                 if ( app && app->desktopEntryName() == "designer" )
00346                 {
00347                     KRun::run(*app.data(), url);
00348                     return;
00349                 }
00350             }
00351         }
00352     }
00353     
00354     QStringList texttypeslist = config->readListEntry( "TextTypes" );
00355     if ( texttypeslist.contains( MimeType->name() ) )
00356     {
00357         m_openNextAsText = true;
00358     }
00359   
00360     // is this regular text - open in editor
00361     if ( m_openNextAsText || MimeType->is( "text/plain" ) || MimeType->is( "text/html" ) || MimeType->is( "application/x-zerosize" ) )
00362     {
00363         KTextEditor::Editor * editorpart = createEditorPart(activate);
00364 
00365         if ( editorpart )
00366         {
00367             if ( !m_presetEncoding.isNull() )
00368             {
00369                 KParts::BrowserExtension * extension = KParts::BrowserExtension::childObject( editorpart );
00370                 if ( extension )
00371                 {
00372                     KParts::URLArgs args;
00373                     args.serviceType = QString( "text/plain;" ) + m_presetEncoding;
00374                     extension->setURLArgs(args);
00375                 }
00376                 m_presetEncoding = QString::null;
00377             }
00378                 
00379             editorpart->openURL( url );
00380 
00381             QWidget* widget = editorpart->widget();
00382         
00383             if (!widget) {
00384                 // We're being lazy about creating the view, but kmdi _needs_ a widget to
00385                 // create a tab for it, so use a QWidgetStack subclass instead
00386                 kdDebug() << k_lineinfo << "Creating Editor wrapper..." << endl;
00387                 widget = new EditorWrapper(static_cast<KTextEditor::Document*>(editorpart), activate, TopLevel::getInstance()->main());
00388             }
00389         
00390             integratePart(editorpart, url, widget, true, activate);
00391             EditorProxy::getInstance()->setLineNumber(editorpart, lineNum, col);
00392             
00393             addHistoryEntry( url, lineNum, col );
00394 
00395             m_openNextAsText = false;
00396             
00397             m_openRecentAction->addURL( url );
00398             m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );
00399 
00400             return;
00401         }
00402     }
00403     
00404     // OK, it's not text and it's not a designer file.. let's see what else we can come up with..
00405     
00406     KParts::Factory *factory = 0;
00407     QString className;
00408     
00409     QString services[] = { "KParts/ReadWritePart", "KParts/ReadOnlyPart" };
00410     QString classnames[] = { "KParts::ReadWritePart", "KParts::ReadOnlyPart" };
00411     for (uint i=0; i<2; ++i)
00412     {
00413         factory = findPartFactory( MimeType->name(), services[i] );
00414         if (factory)
00415         {
00416             className = classnames[i];
00417             break;
00418         }
00419     }
00420     
00421     kdDebug(9000) << "factory = " << factory << endl;
00422 
00423     if (factory)
00424     {
00425         // create the object of the desired class
00426         KParts::ReadOnlyPart *part = static_cast<KParts::ReadOnlyPart*>( factory->createPart( TopLevel::getInstance()->main(), 0, 0, 0, className.latin1() ) );
00427         if ( part )
00428         {
00429             part->openURL( url );
00430             
00431             if ( dynamic_cast<KTextEditor::Editor*>( part ) ) // we can have ended up with a texteditor, in which case need to treat it as such
00432             {
00433                 integratePart(part, url, part->widget(), true, activate);       
00434                 EditorProxy::getInstance()->setLineNumber(part, lineNum, col);
00435             }
00436             else
00437             {
00438                 integratePart( part, url );
00439             }
00440             
00441             addHistoryEntry( url, lineNum, col );
00442         
00443             m_openRecentAction->addURL( url );
00444             m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );
00445         }
00446     }
00447     else
00448     {
00449         MimeWarningDialog dlg;
00450         dlg.text->setText( dlg.text->text().arg(url.path()).arg(MimeType->name()) );
00451         
00452         if ( dlg.exec() == QDialog::Accepted )
00453         {
00454             if ( dlg.open_with_kde->isChecked() )
00455             {
00456                 KRun::runURL(url, MimeType->name() );
00457             }
00458             else
00459             {
00460                 if ( dlg.always_open_as_text->isChecked() )
00461                 {
00462                     KConfig *config = kapp->config();  
00463                     config->setGroup("General");
00464                     QStringList texttypeslist = config->readListEntry( "TextTypes" );
00465                     texttypeslist << MimeType->name();
00466                     config->writeEntry( "TextTypes", texttypeslist );
00467                 }
00468                 m_openNextAsText = true;
00469                 editDocument( url, lineNum, col );
00470             }
00471         }
00472     }
00473 }
00474 
00475 
00476 void PartController::showDocument(const KURL &url, bool newWin)
00477 {
00478   QString fixedPath = DocumentationPart::resolveEnvVarsInURL(url.url()); // possibly could env vars
00479   KURL docUrl(fixedPath);
00480   kdDebug(9000) << "SHOW: " << docUrl.url() << endl;
00481 
00482   if ( docUrl.isLocalFile() && KMimeType::findByURL(docUrl)->name() != "text/html" ) {
00483     // a link in a html-file pointed to a local text file - display
00484     // it in the editor instead of a html-view to avoid uglyness
00485     editDocument( docUrl );
00486     return;
00487   }
00488   
00489   
00490   DocumentationPart *part = dynamic_cast<DocumentationPart*>(activePart());
00491   if (!part || newWin)
00492   {
00493     part = new DocumentationPart;
00494     integratePart(part,docUrl);
00495     connect(part, SIGNAL(fileNameChanged(KParts::ReadOnlyPart* )),
00496         this, SIGNAL(partURLChanged(KParts::ReadOnlyPart* )));
00497   }
00498   else
00499   {
00500     activatePart(part);
00501   }
00502   part->openURL(docUrl);
00503 }
00504 
00505 KParts::Factory *PartController::findPartFactory(const QString &mimeType, const QString &partType, const QString &preferredName)
00506 {
00507   KTrader::OfferList offers = KTrader::self()->query(mimeType, QString("'%1' in ServiceTypes").arg(partType));
00508 
00509   if (offers.count() > 0)
00510   {
00511     KService::Ptr ptr = 0;
00512     // if there is a preferred plugin we'll take it
00513     if ( !preferredName.isEmpty() ) {
00514       KTrader::OfferList::Iterator it;
00515       for (it = offers.begin(); it != offers.end(); ++it) {
00516         if ((*it)->name() == preferredName) {
00517           ptr = (*it);
00518         }
00519       }
00520     }
00521     // else we just take the first in the list
00522     if ( !ptr ) {
00523       ptr = offers.first();
00524     }
00525     return static_cast<KParts::Factory*>(KLibLoader::self()->factory(QFile::encodeName(ptr->library())));
00526   }
00527 
00528   return 0;
00529 }
00530 
00531 KTextEditor::Editor * PartController::createEditorPart( bool activate )
00532 {
00533     
00534     if ( !_editorFactory )
00535     {
00536         kapp->config()->setGroup("Editor");
00537         QString preferred = kapp->config()->readPathEntry("EmbeddedKTextEditor");
00538         
00539         _editorFactory = findPartFactory( "text/plain", "KTextEditor/Document", preferred );
00540         
00541         if ( !_editorFactory ) return 0L;
00542     }
00543     
00544     // Don't create non-wrapped views for now, avoid two paths (== two chances for bad bugs)
00545     activate = false;
00546     
00547     return static_cast<KTextEditor::Editor*>( _editorFactory->createPart( TopLevel::getInstance()->main(), 0, 0, 0, activate ? "KTextEditor/Editor" : "KTextEditor::Document" ) );
00548 }
00549 
00550 void PartController::integratePart(KParts::Part *part, const KURL &url, QWidget* widget, bool isTextEditor, bool activate )
00551 {
00552   if (!widget) widget = part->widget();
00553 
00554   if (!widget) {
00556       kdDebug(9000) << "no widget for this part!!" << endl;
00557       return; // to avoid later crash
00558   }
00559 
00560   TopLevel::getInstance()->embedPartView(widget, url.filename(), url.url());
00561 
00562   addPart(part, activate);
00563   
00564   // tell the parts we loaded a document
00565   KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(part);
00566   if ( !ro_part ) return;
00567   
00568   emit loadedFile( ro_part->url() );
00569   
00570   if ( ro_part->url().isLocalFile() ) 
00571   {
00572     emit loadedFile(ro_part->url().path());
00573   }
00574 
00575   connect( part, SIGNAL(modifiedOnDisc(Kate::Document*, bool, unsigned char)), this, SLOT(slotDocumentDirty(Kate::Document*, bool, unsigned char)) );
00576   
00577   // let's get notified when a document has been changed
00578   connect(part, SIGNAL(completed()), this, SLOT(slotUploadFinished()));
00579   
00580   // yes, we're cheating again. this signal exists for katepart's 
00581   // Document object and our DocumentationPart
00582 //  connect(part, SIGNAL(fileNameChanged()), this, SLOT(slotFileNameChanged()));
00583 
00584   // Connect to the document's views newStatus() signal in order to keep track of the
00585   // modified-status of the document.
00586 
00587   if (isTextEditor)
00588     integrateTextEditorPart(static_cast<KTextEditor::Document*>(part));
00589   
00590   KInterfaceDesigner::Designer *designerPart = dynamic_cast<KInterfaceDesigner::Designer *>(part);
00591   if (designerPart && API::getInstance()->languageSupport())
00592   {
00593       kdDebug() << "integrating designer part with language support" << endl;
00594       connect(designerPart, SIGNAL(addedFunction(DesignerType, const QString&, Function )),
00595           API::getInstance()->languageSupport(),
00596           SLOT(addFunction(DesignerType, const QString&, Function )));
00597       connect(designerPart, SIGNAL(editedFunction(DesignerType, const QString&, Function, Function )), API::getInstance()->languageSupport(),
00598       SLOT(editFunction(DesignerType, const QString&, Function, Function )));
00599       connect(designerPart, SIGNAL(removedFunction(DesignerType, const QString&, Function )),
00600           API::getInstance()->languageSupport(),
00601           SLOT(removeFunction(DesignerType, const QString&, Function )));
00602       connect(designerPart, SIGNAL(editFunction(DesignerType, const QString&, const QString& )),
00603           API::getInstance()->languageSupport(),
00604           SLOT(openFunction(DesignerType, const QString&, const QString& )));
00605   }
00606 }
00607 
00608 void PartController::integrateTextEditorPart(KTextEditor::Document* doc)
00609 {
00610   //EditorProxy::getInstance()->installPopup(doc, contextPopupMenu());
00611 
00612   // What's potentially problematic is that this signal isn't officially part of the
00613   // KTextEditor::View interface. It is nevertheless there, and used in kate and kwrite.
00614   // There doesn't seem to be any othere way of making this work with katepart, and since
00615   // signals are dynamic, if we try to connect to an editorpart that lacks this signal,
00616   // all we get is a runtime warning. At this point in time we are only really supported
00617   // by katepart anyway so IMHO this hack is justified. //teatime
00618   QPtrList<KTextEditor::View> list = doc->views();
00619   QPtrListIterator<KTextEditor::View> it( list );
00620   while ( it.current() )
00621   {
00622     connect( it, SIGNAL( newStatus() ), this, SLOT( slotNewStatus() ) );
00623     ++it;
00624   }
00625 }
00626 
00627 void PartController::slotPartAdded( KParts::Part * part )
00628 {
00629     kdDebug(9000) << k_funcinfo << endl;
00630     
00631     if ( KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part ) )
00632     {
00633         updatePartURL( ro_part );
00634     }
00635     
00636     updateMenuItems();
00637 }
00638 
00639 void PartController::slotPartRemoved( KParts::Part * part )
00640 {
00641     kdDebug(9000) << k_funcinfo << endl;
00642     
00643     _partURLMap.remove( static_cast<KParts::ReadOnlyPart*>(part) );
00644     
00645     updateMenuItems();
00646 }
00647 
00648 void PartController::updatePartURL( KParts::ReadOnlyPart * ro_part )
00649 {
00650     if ( ro_part->url().isEmpty() )
00651     {
00652         kdDebug(9000) << "updatePartURL() called with empty URL for part: " << ro_part << endl;
00653         return;
00654     }
00655     _partURLMap[ ro_part ] = ro_part->url();
00656 }
00657 
00658 bool PartController::partURLHasChanged( KParts::ReadOnlyPart * ro_part )
00659 {
00660     if ( _partURLMap.contains( ro_part ) && !ro_part->url().isEmpty() )
00661     {
00662         if ( _partURLMap[ ro_part ] != ro_part->url() )
00663         {
00664             return true;
00665         }
00666     }
00667     return false;
00668 }
00669 
00670 KURL PartController::storedURLForPart( KParts::ReadOnlyPart * ro_part )
00671 {
00672     if ( _partURLMap.contains( ro_part ) )
00673     {
00674         return _partURLMap[ ro_part ];
00675     }
00676     return KURL();
00677 }
00678 
00679 
00680 void PartController::slotUploadFinished()
00681 {
00682     KParts::ReadOnlyPart *ro_part = const_cast<KParts::ReadOnlyPart*>( dynamic_cast<const KParts::ReadOnlyPart*>(sender()) );
00683     if ( !ro_part ) return;
00684     
00685     if ( partURLHasChanged( ro_part ) )
00686     {
00687         emit partURLChanged( ro_part );
00688         updatePartURL( ro_part );
00689     }   
00690 }
00691 
00692 KParts::ReadOnlyPart *PartController::partForURL(const KURL &url)
00693 {
00694     QPtrListIterator<KParts::Part> it(*parts());
00695     for ( ; it.current(); ++it)
00696     {
00697         KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(it.current());
00698         if (ro_part && url == ro_part->url())
00699             return ro_part;
00700     }
00701     return 0;
00702 }
00703 
00704 KParts::Part * PartController::partForWidget( const QWidget * widget )
00705 {
00706     QPtrListIterator<KParts::Part> it(*parts());
00707     for ( ; it.current(); ++it)
00708     {
00709         if ( it.current()->widget() == widget )
00710         {
00711             return *it;
00712         }
00713     }
00714     return 0;
00715 }
00716 
00717 
00718 void PartController::activatePart(KParts::Part *part)
00719 {
00720   if ( !part ) return;
00721 
00722   QWidget * widget = EditorProxy::getInstance()->widgetForPart( part );
00723   if (widget)
00724   {
00725     TopLevel::getInstance()->raiseView( widget );
00726     widget->show();
00727     widget->setFocus();
00728   }
00729   
00730   setActivePart(part);
00731 
00732   QWidget* w2 = EditorProxy::getInstance()->widgetForPart( part );
00733   if (w2 != widget)
00734     w2->setFocus();
00735 }
00736 
00737 bool PartController::closePart(KParts::Part *part)
00738 {
00739     if ( !part ) return true;
00740 
00741     if ( KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part ) )
00742     {
00743         KURL url = ro_part->url();
00744         if ( ! ro_part->closeURL() )
00745         {
00746             return false;
00747         }
00748         _dirtyDocuments.remove( static_cast<KParts::ReadWritePart*>( ro_part ) );
00749         
00750         emit closedFile( url );
00751 //      removeTimestamp( url );
00752     }
00753     
00754   // FIXME correct? relevant?
00755 
00756   // If we didn't call removePart(), KParts::PartManager::slotObjectDestroyed would
00757   // get called from the destroyed signal of the part being deleted below.
00758   // The call chain from that looks like this:
00759   // QObject::destroyed()
00760   //   KParts::PartManager::slotWidgetDestroyed() -> setActivePart() -> activePartChanged()
00761   //     TopLevelXXX::createGUI()
00762   //       KXMLGUIFactory::removeClient()
00763   // But then KXMLGUIFactory tries to remove the already-deleted part.
00764   // Normally this would work, because the factory uses a QGuardedPtr to the part.
00765   // But the QGuardedPtr is connected to the _same_ destroyed() slot that got us to
00766   // that point (slots are called in an undefined order)!
00767 
00768   // Previously, the comment was:
00769   // The following line can be removed with kdelibs HEAD! (2002-05-26)
00770   //
00771   // Now, this is needed for proper functioning, so leave...
00772   removePart( part );
00773 
00774   if (QWidget* w = EditorProxy::getInstance()->topWidgetForPart(part))
00775     TopLevel::getInstance()->removeView(w);
00776 
00777   delete part;
00778 
00779   return true;
00780 }
00781 
00782 
00783 void PartController::updateMenuItems()
00784 {
00785   bool hasWriteParts = false;
00786   bool hasReadOnlyParts = false;
00787 
00788   QPtrListIterator<KParts::Part> it(*parts());
00789   for ( ; it.current(); ++it)
00790   {
00791     if (it.current()->inherits("KParts::ReadWritePart"))
00792       hasWriteParts = true;
00793     if (it.current()->inherits("KParts::ReadOnlyPart"))
00794       hasReadOnlyParts = true;
00795   }
00796 
00797   m_saveAllFilesAction->setEnabled(hasWriteParts);
00798   m_revertAllFilesAction->setEnabled(hasWriteParts);
00799   m_closeWindowAction->setEnabled(hasReadOnlyParts);
00800   m_closeAllWindowsAction->setEnabled(hasReadOnlyParts);
00801   m_closeOtherWindowsAction->setEnabled(hasReadOnlyParts);
00802 
00803   m_backAction->setEnabled( m_Current != m_history.begin() );
00804   m_forwardAction->setEnabled( m_Current != m_history.fromLast() );
00805 }
00806 
00807 void PartController::slotRevertAllFiles()
00808 {
00809     revertAllFiles();
00810 }
00811 
00812 void PartController::reloadFile( const KURL & url, bool ) //@todo - remove 2nd arg 
00813 {
00814     KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
00815     if ( part )
00816     {
00817         if ( part->isModified() )
00818         {
00819             if ( KMessageBox::warningYesNo( TopLevel::getInstance()->main(),
00820                 i18n( "The file \"%1\" is modified in memory. Are you sure you want to reload it? (Local changes will be lost.)" ).arg( url.path() ), 
00821                 i18n( "File is Modified" ) ) == KMessageBox::Yes )
00822             {
00823                 part->setModified( false );
00824             }
00825             else
00826             {
00827                 return;
00828             }
00829         }
00830         
00831         unsigned int line = 0; unsigned int col = 0;
00832         KTextEditor::ViewCursorInterface * iface = dynamic_cast<KTextEditor::ViewCursorInterface*>( part->widget() );
00833         if (iface)
00834         {
00835             iface->cursorPositionReal( &line, &col );
00836         }
00837                 
00838         part->openURL( url );
00839         
00840         _dirtyDocuments.remove( part );
00841         emit documentChangedState( url, Clean );
00842         
00843         if ( iface )
00844         {
00845             iface->setCursorPositionReal( line, col );
00846         }
00847     }   
00848 }
00849 
00850 void PartController::revertFiles( const KURL::List & list  )
00851 {
00852     KURL::List::ConstIterator it = list.begin();
00853     while ( it != list.end() )
00854     {
00855         reloadFile( *it );
00856         ++it;
00857     }
00858 }
00859 
00860 void PartController::revertAllFiles()
00861 {
00862     revertFiles( openURLs() );
00863 }
00864 
00865 void PartController::slotCloseWindow()
00866 {
00867     closePart( activePart() );
00868 }
00869 
00870 KURL::List PartController::modifiedDocuments()
00871 {
00872     KURL::List modFiles;
00873     
00874     QPtrListIterator<KParts::Part> it( *parts() );
00875     while( it.current() )
00876     {
00877         KParts::ReadWritePart *rw_part = dynamic_cast<KParts::ReadWritePart*>(it.current());
00878         if ( rw_part && rw_part->isModified() )
00879         {
00880             modFiles << rw_part->url();
00881         }
00882         ++it;
00883     }
00884     return modFiles;
00885 }
00886 
00887 void PartController::slotSave()
00888 {
00889     kdDebug(9000) << k_funcinfo << endl;
00890         
00891     if ( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( activePart() ) )
00892     {
00893         saveFile( part->url() );
00894     }
00895 }
00896 
00897 void PartController::slotReload()
00898 {
00899     kdDebug(9000) << k_funcinfo << endl;
00900         
00901     if ( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( activePart() ) )
00902     {
00903         reloadFile( part->url() );
00904     }
00905 }
00906 
00907 void PartController::slotSaveAllFiles()
00908 {
00909   saveAllFiles();
00910 }
00911 
00912 bool PartController::saveFile( const KURL & url, bool force )
00913 {
00914     KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
00915     if ( !part ) return true;
00916     
00917     switch( documentState( url ) )
00918     {
00919         case Clean:
00920             if ( !force )
00921             {
00922                 return true;
00923             }
00924             kdDebug(9000) << "Forced save" << endl;
00925             break;
00926             
00927         case Modified:
00928             kdDebug(9000) << "Normal save" << endl;
00929             break;
00930             
00931         case Dirty:
00932         case DirtyAndModified:
00933             {
00934                 int code = KMessageBox::warningYesNoCancel( TopLevel::getInstance()->main(),
00935                     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() ),
00936                     i18n("File Externally Modified") );
00937                 if ( code == KMessageBox::Yes )
00938                 {
00939                     kdDebug(9000) << "Dirty save!!" << endl;
00940                 }
00941                 else if ( code == KMessageBox::No )
00942                 {
00943                     return true;
00944                 }
00945                 else
00946                 {
00947                     return false; // a 'false' return means to interrupt the process that caused the save
00948                 }
00949             }
00950             break;
00951             
00952         default:
00953             ;
00954     }
00955 
00956     if ( part->save() )
00957     {
00958         _dirtyDocuments.remove( part );
00959         emit documentChangedState( url, Clean );
00960         emit savedFile( url );
00961     }
00962         
00963     return true;
00964 }
00965 
00966 void PartController::saveAllFiles()
00967 {
00968     saveFiles( openURLs() );
00969 }
00970 
00971 void PartController::saveFiles( KURL::List const & filelist )
00972 {
00973     KURL::List::ConstIterator it = filelist.begin();
00974     while ( it != filelist.end() )
00975     {
00976         saveFile( *it );
00977         ++it;
00978     }
00979 }
00980 
00981 bool PartController::querySaveFiles()
00982 {
00983     return saveFilesDialog( KURL::List() );
00984 }
00985 
00986 void PartController::clearModified( KURL::List const & filelist )
00987 {
00988     KURL::List::ConstIterator it = filelist.begin();
00989     while ( it != filelist.end() )
00990     {
00991         KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( *it ) );
00992         if ( rw_part )
00993         {
00994             rw_part->setModified( false );
00995         }
00996         ++it;
00997     }
00998 }
00999 
01000 bool PartController::saveFilesDialog( KURL::List const & ignoreList )
01001 {
01002     KURL::List modList = modifiedDocuments();
01003     
01004     if ( modList.count() > 0 && modList != ignoreList ) 
01005     {
01006         KSaveSelectDialog dlg( modList, ignoreList, TopLevel::getInstance()->main() );
01007         if ( dlg.exec() == QDialog::Accepted )
01008         {
01009             saveFiles( dlg.filesToSave() );
01010             clearModified( dlg.filesNotToSave() );
01011         }
01012         else
01013         {
01014             return false;
01015         }
01016     }
01017     return true;
01018 }
01019 
01020 bool PartController::closeFilesDialog( KURL::List const & ignoreList )
01021 {
01022     if ( !saveFilesDialog( ignoreList ) ) return false;
01023                 
01024     QPtrList<KParts::Part> partList( *parts() );
01025     QPtrListIterator<KParts::Part> it( partList );
01026     while ( KParts::Part* part = it.current() )
01027     {
01028         KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part );
01029         if ( ro_part && !ignoreList.contains( ro_part->url() ) || !ro_part )
01030         {
01031             closePart( part );
01032         }
01033         ++it;
01034     }   
01035     return true;
01036 }
01037 
01038 bool PartController::closeFiles( const KURL::List & list )
01039 {
01040     KURL::List::ConstIterator it = list.begin();
01041     while ( it != list.end() )
01042     {
01043         if ( !closePart( partForURL( *it ) ) )
01044         {
01045             return false;
01046         }
01047         ++it;
01048     }
01049     return true;
01050 }
01051 
01052 bool PartController::closeFile( const KURL & url )
01053 {
01054     return closePart( partForURL( url ) );
01055 }
01056 
01057 bool PartController::closeAllFiles()
01058 {
01059     return closeFilesDialog( KURL::List() );
01060 }
01061 
01062 void PartController::slotCloseAllWindows()
01063 {
01064     closeAllFiles();
01065 }
01066 
01067 bool PartController::closeAllOthers( const KURL & url )
01068 {
01069     KURL::List ignoreList;
01070     ignoreList.append( url );
01071     
01072     return closeFilesDialog( ignoreList );  
01073 }
01074 
01075 
01076 void PartController::slotCloseOtherWindows()
01077 {
01078     KParts::ReadOnlyPart * active = dynamic_cast<KParts::ReadOnlyPart*>(activePart());
01079     if ( !active ) return;
01080     
01081     closeAllOthers( active->url() );
01082     /*  
01083     KParts::ReadOnlyPart * active = dynamic_cast<KParts::ReadOnlyPart*>(activePart());
01084     if ( !active ) return;
01085 
01086     KURL::List ignoreList;
01087     ignoreList.append( active->url() );
01088     
01089     closeFilesDialog( ignoreList );
01090     */
01091 }
01092 
01093 void PartController::slotOpenFile()
01094 {
01095     KEncodingFileDialog::Result result = KEncodingFileDialog::getOpenURLsAndEncoding(QString::null, QString::null, 
01096         QString::null, TopLevel::getInstance()->main(), QString::null);
01097     
01098     for ( KURL::List::Iterator it = result.URLs.begin(); it != result.URLs.end(); ++it )
01099     {
01100         m_presetEncoding = result.encoding;
01101         editDocument( *it );
01102     }
01103 }
01104 
01105 void PartController::slotOpenRecent( const KURL& url )
01106 {
01107   editDocument( url );
01108   // stupid bugfix - don't allow an active item in the list
01109   m_openRecentAction->setCurrentItem( -1 );
01110 }
01111 
01112 bool PartController::readyToClose()
01113 {
01114     blockSignals( true );
01115     
01116     closeAllFiles(); // this should never return false, as the files are already saved
01117     
01118     return true;
01119 }
01120 
01121 void PartController::slotActivePartChanged( KParts::Part * part )
01122 {
01123     kdDebug(9000) << k_funcinfo << endl;
01124     
01125     updateMenuItems();
01126 
01127     QTimer::singleShot( 100, this, SLOT(slotWaitForFactoryHack()) );
01128     
01129     if ( m_isJumping ) return;
01130 
01131     if ( _partURLMap.contains( m_latestPart ) )
01132     {
01133         addHistoryEntry( _partURLMap[ m_latestPart ] );
01134     }
01135 
01136     if ( dynamic_cast<DocumentationPart*>( part ) ) return;
01137 
01138     KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part );
01139     if ( ro_part )
01140     {
01141         m_latestPart = ro_part;
01142     }
01143 }
01144 
01145 void PartController::slotSwitchTo()
01146 {
01147     QMap<QString,KParts::ReadOnlyPart*> parts_map;
01148     QStringList part_list;
01149     QPtrList<KParts::Part> pl = *parts();
01150     KParts::Part *part;
01151     for(part=pl.first();part;part=pl.next()) {
01152         kdDebug(9000) << "Part..." << endl;
01153     if (part->inherits("KParts::ReadOnlyPart")) {
01154         KParts::ReadOnlyPart *ro_part = static_cast<KParts::ReadOnlyPart*>(part);
01155             QString name = ro_part->url().fileName();
01156         part_list.append(name);
01157             parts_map[name] = ro_part;
01158             kdDebug(9000) << "Found part for URL " << ro_part->url().prettyURL() << endl;
01159     }
01160     }
01161 
01162     KDialogBase dialog(KDialogBase::Plain, i18n("Switch To"), KDialogBase::Ok|KDialogBase::Cancel,
01163                KDialogBase::Ok, 0, "Switch to", true);
01164     QGridLayout *grid = new QGridLayout( dialog.plainPage(), 2, 1, 10, 10);
01165     KLineEdit *editbox = new KLineEdit(dialog.plainPage());
01166     grid->addWidget(new QLabel( i18n("Switch to buffer:"), dialog.plainPage() ), 0, 0);
01167     grid->addWidget(editbox, 1, 0);
01168     editbox->completionObject()->setItems( part_list );
01169     editbox->setFocus();
01170     int result = dialog.exec();
01171     if (result==KDialogBase::KDialogBase::Accepted) {
01172         if (parts_map.contains(editbox->text())) {
01173             activatePart(parts_map[editbox->text()]);
01174         }
01175     }
01176 }
01177 
01178 void PartController::showPart( KParts::Part* part, const QString& name, const QString& shortDescription )
01179 {
01180   if (!part->widget()) {
01182     return; // to avoid later crash
01183   }
01184 
01185   QPtrListIterator<KParts::Part> it(*parts());
01186   for ( ; it.current(); ++it)
01187   {
01188     if( it.current() == part ){
01189         // part already embedded
01190     activatePart( it.current() );
01191     return;
01192     }
01193   }
01194 
01195   // embed the part
01196   TopLevel::getInstance()->embedPartView( part->widget(), name, shortDescription );
01197   addPart( part );
01198 }
01199 
01200 void PartController::slotDocumentDirty( Kate::Document * d, bool isModified, unsigned char reason )
01201 {
01202     kdDebug(9000) << k_funcinfo << endl;
01203 
01204     //  KTextEditor::Document * doc = reinterpret_cast<KTextEditor::Document*>( d ); // theoretically unsafe in MI scenario
01205     KTextEditor::Document * doc = 0;
01206     
01207     QPtrListIterator<KParts::Part> it( *parts() );
01208     while( it.current() )
01209     {
01210         if ( (void*)it.current() == (void*)d )
01211         {
01212             doc = dynamic_cast<KTextEditor::Document*>( it.current() );
01213             break;
01214         }   
01215         ++it;
01216     }
01217     
01218     if ( !doc ) return;
01219     KURL url = storedURLForPart( doc );
01220     if ( url.isEmpty() )
01221     {
01222         kdDebug(9000) << "Warning!! the stored url is empty. Bailing out!" << endl;
01223     }
01224     
01225     if ( reason > 0 )
01226     {
01227         if ( !_dirtyDocuments.contains( doc ) )
01228         {
01229             _dirtyDocuments.append( doc );
01230         }
01231         
01232         if ( reactToDirty( url ) )
01233         {
01234             // file has been reloaded
01235             emit documentChangedState( url, Clean );
01236             _dirtyDocuments.remove( doc );
01237         }
01238         else
01239         {
01240             emit doEmitState( url );
01241         }
01242     }
01243     else
01244     {
01245         _dirtyDocuments.remove( doc );
01246         emit documentChangedState( url, Clean );
01247     }
01248     
01249     kdDebug(9000) << doc->url().url() << endl;
01250     kdDebug(9000) << isModified << endl;
01251     kdDebug(9000) << reason << endl;
01252 }
01253 
01254 bool PartController::isDirty( KURL const & url )
01255 {
01256     return _dirtyDocuments.contains( static_cast<KTextEditor::Document*>( partForURL( url ) ) );
01257 }
01258 
01259 bool PartController::reactToDirty( KURL const & url )
01260 {   
01261     KConfig *config = kapp->config();
01262     config->setGroup("Editor");
01263     QString dirtyAction = config->readEntry( "DirtyAction" );
01264 
01265     if ( dirtyAction == "nothing" ) return false;
01266     
01267     bool isModified = true;
01268     if( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) ) )
01269     {
01270         isModified = part->isModified();
01271     }
01272     else
01273     {
01274         kdDebug(9000) << k_funcinfo << " Warning. Not a ReadWritePart." << endl;
01275         return false;
01276     }
01277     
01278     if ( isModified )
01279     {
01280         KMessageBox::sorry( TopLevel::getInstance()->main(), 
01281             i18n("Conflict: The file \"%1\" has changed on disc while being modified in memory.\n\n"
01282                     "You should investigate before saving to make sure you are not losing data.").arg( url.path() ),
01283             i18n("Conflict") );
01284         return false;
01285     }
01286     
01287     if ( dirtyAction == "alert" )
01288     {
01289         if ( KMessageBox::warningYesNo( TopLevel::getInstance()->main(), 
01290             i18n("The file \"%1\" has changed on disk.\n\nDo you want to reload it?").arg( url.path() ), 
01291             i18n("File Changed") ) == KMessageBox::No )
01292         {
01293             return false;
01294         }
01295     }
01296     
01297     // here we either answered yes above or are in autoreload mode
01298     reloadFile( url );
01299     
01300     return true;
01301 }
01302 
01303 void PartController::slotNewStatus( )
01304 {
01305     kdDebug(9000) << k_funcinfo << endl;
01306     
01307     QObject * senderobj = const_cast<QObject*>( sender() );
01308     KTextEditor::View * view = dynamic_cast<KTextEditor::View*>( senderobj );
01309     if ( view )
01310     {
01311         doEmitState( view->document()->url() );
01312     }
01313 }
01314 
01315 DocumentState PartController::documentState( KURL const & url )
01316 {
01317     KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
01318     if ( !rw_part ) return Clean;
01319     
01320     DocumentState state = Clean;
01321     if ( rw_part->isModified() )
01322     {
01323         state = Modified;
01324     }
01325 
01326     if ( isDirty( url ) )
01327     {
01328         if ( state == Modified )
01329         {
01330             state = DirtyAndModified;
01331         } 
01332         else
01333         {
01334             state = Dirty;
01335         }
01336     }
01337     
01338     return state;
01339 }
01340 
01341 void PartController::doEmitState( KURL const & url )
01342 {
01343     emit documentChangedState( url, documentState( url ) );
01344 }
01345 
01346 KURL::List PartController::openURLs( )
01347 {
01348     KURL::List list;
01349     QPtrListIterator<KParts::Part> it(*parts());
01350     for ( ; it.current(); ++it)
01351     {
01352         if ( KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(it.current()) )
01353         {
01354             list << ro_part->url();
01355         }
01356     }
01357     return list;
01358 }
01359 
01361 
01362 //BEGIN History methods
01363 
01364 PartController::HistoryEntry::HistoryEntry( const KURL & u, int l, int c) 
01365     : url(u), line(l), col(c)
01366 {
01367     id = abs( QTime::currentTime().msecsTo( QTime() ) );    // should provide a reasonably unique number
01368 }
01369 
01370 void PartController::slotBack()
01371 {
01372     if ( m_Current != m_history.begin() )
01373     {
01374         jumpTo( *(--m_Current) );
01375     }
01376 }
01377 
01378 void PartController::slotForward()
01379 {
01380     if (  m_Current != m_history.fromLast() )
01381     {
01382         jumpTo( *(++m_Current) );
01383     }
01384 }
01385 
01386 void PartController::slotBackAboutToShow()
01387 {
01388     KPopupMenu *popup = m_backAction->popupMenu();
01389     popup->clear();
01390 
01391     if ( m_Current == m_history.begin() ) return;
01392 
01393     QValueList<HistoryEntry>::Iterator it = m_Current;
01394     --it;
01395     
01396     int i = 0;
01397     while( i < 10 )
01398     {
01399         if ( it == m_history.begin() )
01400         {
01401             popup->insertItem( (*it).url.fileName() + QString(" (%1,%2)").arg( (*it).line).arg((*it).col), (*it).id );
01402             return;
01403         } 
01404         
01405         popup->insertItem( (*it).url.fileName() + QString(" (%1,%2)").arg( (*it).line).arg((*it).col), (*it).id );
01406         ++i;
01407         --it;
01408     } 
01409 }
01410 
01411 void PartController::slotForwardAboutToShow()
01412 {
01413     KPopupMenu *popup = m_forwardAction->popupMenu();
01414     popup->clear();
01415 
01416     if ( m_Current == m_history.fromLast() ) return;
01417 
01418     QValueList<HistoryEntry>::Iterator it = m_Current;
01419     ++it;
01420     
01421     int i = 0;
01422     while( i < 10 )
01423     {
01424         if ( it == m_history.fromLast() )
01425         {
01426             popup->insertItem( (*it).url.fileName() + QString(" (%1,%2)").arg( (*it).line).arg((*it).col), (*it).id );
01427             return;
01428         } 
01429         
01430         popup->insertItem( (*it).url.fileName()  + QString(" (%1,%2)").arg( (*it).line).arg((*it).col), (*it).id );
01431         ++i;
01432         ++it;
01433     } 
01434 }
01435 
01436 void PartController::slotPopupActivated( int id )
01437 {
01438     QValueList<HistoryEntry>::Iterator it = m_history.begin();
01439     while( it != m_history.end() )
01440     {
01441         if ( (*it).id == id )
01442         {
01443             m_Current = it;
01444             jumpTo( *m_Current );
01445             return;
01446         }
01447         ++it;
01448     }
01449 }
01450 
01451 void PartController::jumpTo( const HistoryEntry & entry )
01452 {
01453     m_isJumping = true;
01454     editDocument( entry.url, entry.line, entry.col );
01455     m_isJumping = false;
01456 }
01457 
01458 void PartController::addHistoryEntry(const KURL & url, int line, int col )
01459 {
01460     if ( m_isJumping ) return;
01461 
01462     QValueList<HistoryEntry>::Iterator it = m_Current;
01463     // if We're not already the last entry, we truncate the list here before adding an entry
01464     if ( it != m_history.end() && it != m_history.fromLast() )
01465     {
01466         m_history.erase( ++it, m_history.end() );
01467     }
01468     
01469     HistoryEntry newEntry( url, line, col );
01470     
01471     // Only save the new entry if it is different from the last
01472     if ( newEntry.url == (*m_Current).url )
01473     {
01474         if ( newEntry.line == -1 )
01475         {
01476             return;
01477         }
01478         if ( (*m_Current).line == -1 )
01479         {
01480             (*m_Current).line = line;
01481             (*m_Current).col = col;
01482             return;
01483         }
01484     }
01485     
01486     // add entry
01487     m_history.append( newEntry );
01488     m_Current = m_history.fromLast();
01489     
01490     updateMenuItems();
01491 }
01492 
01493 //END History methods
01494 
01495 void PartController::slotWaitForFactoryHack( )
01496 {
01497     //kdDebug(9000) << k_funcinfo << endl;
01498     
01499     if ( !activePart() ) return;
01500     
01501     if ( dynamic_cast<KTextEditor::View*>( activePart()->widget() ) )
01502     {
01503         if ( !activePart()->factory() )
01504         {
01505             QTimer::singleShot( 100, this, SLOT(slotWaitForFactoryHack()) );
01506         }
01507         else
01508         {
01509             EditorProxy::getInstance()->installPopup( activePart() );
01510         }
01511     }
01512 }
01513 
01514 KParts::ReadOnlyPart *PartController::qtDesignerPart()
01515 {
01516     QPtrListIterator<KParts::Part> it(*parts());
01517     for ( ; it.current(); ++it)
01518     {
01519         KInterfaceDesigner::Designer *des =  dynamic_cast<KInterfaceDesigner::Designer*>(it.current());
01520         if (des && des->designerType() == KInterfaceDesigner::QtDesigner)
01521             return des;
01522     }
01523     return 0;
01524 }
01525 
01526 
01527 #include "partcontroller.moc"
KDE Logo
This file is part of the documentation for KDevelop Version 3.1.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Feb 22 09:22:43 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003