KDevelop API Documentation

src/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 00011 #include <kmimetype.h> 00012 #include <kservice.h> 00013 #include <ktrader.h> 00014 #include <kapplication.h> 00015 #include <krun.h> 00016 #include <kdebug.h> 00017 #include <klocale.h> 00018 #include <kmessagebox.h> 00019 #include <kparts/part.h> 00020 #include <kparts/factory.h> 00021 #include <kparts/partmanager.h> 00022 #include <kparts/browserextension.h> 00023 #include <kfiledialog.h> 00024 #include <kmainwindow.h> 00025 #include <kaction.h> 00026 #include <kstatusbar.h> 00027 #include <khtml_part.h> 00028 #include <kpopupmenu.h> 00029 #include <kio/netaccess.h> 00030 #include <kdialogbase.h> 00031 #include <klineedit.h> 00032 #include <kshortcut.h> 00033 #include <kcompletion.h> 00034 #include <kdirwatch.h> 00035 #include <kdeversion.h> 00036 #include <kiconloader.h> 00037 #include <kuserprofile.h> 00038 00039 #include <ktexteditor/view.h> 00040 #include <ktexteditor/document.h> 00041 00042 #include "toplevel.h" 00043 #include "api.h" 00044 #include "core.h" 00045 #include "editorproxy.h" 00046 #include "documentationpart.h" 00047 #include "ksavealldialog.h" 00048 00049 #include "kdevproject.h" 00050 #include "urlutil.h" 00051 00052 #include "partcontroller.h" 00053 00054 #ifdef KDE_MAKE_VERSION 00055 # if KDE_VERSION < KDE_MAKE_VERSION(3,1,90) 00056 # define OLD__KDE 00057 # endif 00058 #else 00059 # define OLD__KDE 00060 #endif 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) 00075 { 00076 dirWatcher = new KDirWatch( this ); 00077 00078 connect(this, SIGNAL(partRemoved(KParts::Part*)), this, SLOT(updateMenuItems())); 00079 connect(this, SIGNAL(partAdded(KParts::Part*)), this, SLOT(updateMenuItems())); 00080 connect(this, SIGNAL(activePartChanged(KParts::Part*)), this, SLOT(slotActivePartChanged(KParts::Part*))); 00081 connect(dirWatcher, SIGNAL(dirty(const QString&)), this, SLOT(dirty(const QString&))); 00082 connect(this, SIGNAL(fileDirty(const KURL& )), this, SLOT(slotFileDirty(const KURL&)) ); 00083 00084 m_history.setAutoDelete( true ); 00085 m_restoring = false; 00086 setupActions(); 00087 } 00088 00089 00090 PartController::~PartController() 00091 { 00092 } 00093 00094 00095 void PartController::createInstance(QWidget *parent) 00096 { 00097 if (!s_instance) 00098 s_instance = new PartController(parent); 00099 } 00100 00101 00102 PartController *PartController::getInstance() 00103 { 00104 return s_instance; 00105 } 00106 00107 00108 void PartController::setupActions() 00109 { 00110 KActionCollection *ac = TopLevel::getInstance()->main()->actionCollection(); 00111 00112 KAction* newAction = KStdAction::open(this, SLOT(slotOpenFile()), 00113 ac, "file_open"); 00114 newAction->setToolTip( i18n("Open file") ); 00115 newAction->setWhatsThis( i18n("<b>Open file</b><p>Opens an existing file without adding it to the project.</p>") ); 00116 00117 m_openRecentAction = KStdAction::openRecent( this, SLOT(slotOpenRecent(const KURL&) ), 00118 ac, "file_open_recent" ); 00119 m_openRecentAction->setWhatsThis(QString("<b>%1</b><p>%2").arg(beautifyToolTip(m_openRecentAction->text())).arg(i18n("Opens recently opened file."))); 00120 m_openRecentAction->loadEntries( kapp->config(), "RecentFiles" ); 00121 00122 m_saveAllFilesAction = new KAction(i18n("Save Al&l"), 0, 00123 this, SLOT(slotSaveAllFiles()), 00124 ac, "file_save_all"); 00125 m_saveAllFilesAction->setToolTip( i18n("Save all modified files") ); 00126 m_saveAllFilesAction->setWhatsThis(i18n("<b>Save all</b><p>Saves all modified files.")); 00127 m_saveAllFilesAction->setEnabled(false); 00128 00129 m_revertAllFilesAction = new KAction(i18n("Rever&t All"), 0, 00130 this, SLOT(slotRevertAllFiles()), 00131 ac, "file_revert_all"); 00132 m_revertAllFilesAction->setToolTip(i18n("Revert all changes")); 00133 m_revertAllFilesAction->setWhatsThis(i18n("<b>Revert all</b><p>Reverts all changes in opened files. Prompts to save changes so the revert can be cancelled for each modified file.")); 00134 m_revertAllFilesAction->setEnabled(false); 00135 00136 m_closeWindowAction = KStdAction::close( 00137 this, SLOT(slotCloseWindow()), 00138 ac, "file_close"); 00139 m_closeWindowAction->setToolTip( i18n("Close current file") ); 00140 m_closeWindowAction->setWhatsThis(QString("<b>%1</b><p>%2").arg(beautifyToolTip(m_closeWindowAction->text())).arg(i18n("Closes current file."))); 00141 m_closeWindowAction->setEnabled(false); 00142 00143 m_closeAllWindowsAction = new KAction(i18n("Close All"), 0, 00144 this, SLOT(slotCloseAllWindows()), 00145 ac, "file_close_all"); 00146 m_closeAllWindowsAction->setToolTip( i18n("Close all files") ); 00147 m_closeAllWindowsAction->setWhatsThis(i18n("<b>Close all</b><p>Close all opened files.")); 00148 m_closeAllWindowsAction->setEnabled(false); 00149 00150 m_closeOtherWindowsAction = new KAction(i18n("Close All Others"), 0, 00151 this, SLOT(slotCloseOtherWindows()), 00152 ac, "file_closeother"); 00153 m_closeOtherWindowsAction->setToolTip( i18n("Close other files") ); 00154 m_closeOtherWindowsAction->setWhatsThis(i18n("<b>Close all others</b><p>Close all opened files except current.")); 00155 m_closeOtherWindowsAction->setEnabled(false); 00156 00157 m_backAction = new KToolBarPopupAction(i18n("Back"), "back", 0, 00158 this, SLOT(slotBack()), 00159 ac, "browser_back"); 00160 m_backAction->setEnabled( false ); 00161 m_backAction->setToolTip(i18n("Back")); 00162 m_backAction->setWhatsThis(i18n("<b>Back</b><p>Moves backwards one step in the <b>documentation</b> browsing history.")); 00163 00164 m_switchToAction = new KAction(i18n("Switch To..."), KShortcut("CTRL+/"), 00165 this, SLOT(slotSwitchTo()), 00166 ac, "file_switchto"); 00167 m_switchToAction->setToolTip(i18n("Switch to")); 00168 m_switchToAction->setWhatsThis(i18n("<b>Switch to</b><p>Prompts to enter the name of previously opened file to switch to.")); 00169 00170 new KActionSeparator(ac, "dummy_separator"); 00171 00172 connect(m_backAction->popupMenu(), SIGNAL(aboutToShow()), 00173 this, SLOT(slotBackAboutToShow())); 00174 connect(m_backAction->popupMenu(), SIGNAL(activated(int)), 00175 this, SLOT(slotBackPopupActivated(int))); 00176 00177 00178 m_forwardAction = new KToolBarPopupAction(i18n("Forward"), "forward", 0, 00179 this, SLOT(slotForward()), 00180 ac, "browser_forward"); 00181 m_forwardAction->setEnabled( false ); 00182 m_forwardAction->setToolTip(i18n("Forward")); 00183 m_forwardAction->setWhatsThis(i18n("<b>Forward</b><p>Moves forward one step in the <b>documentation</b> browsing history.")); 00184 00185 connect(m_forwardAction->popupMenu(), SIGNAL(aboutToShow()), 00186 this, SLOT(slotForwardAboutToShow())); 00187 connect(m_forwardAction->popupMenu(), SIGNAL(activated(int)), 00188 this, SLOT(slotForwardPopupActivated(int))); 00189 } 00190 00191 00192 void PartController::setEncoding(const QString &encoding) 00193 { 00194 m_presetEncoding = encoding; 00195 } 00196 00197 KParts::Part* PartController::findOpenDocument(const KURL& url) 00198 { 00199 KURL partURL = API::getInstance()->project() ? findURLInProject(url) : url; 00200 00201 partURL.cleanPath(); 00202 00203 return partForURL(partURL); 00204 } 00205 00206 KURL PartController::findURLInProject(const KURL& url) 00207 { 00208 QStringList fileList = API::getInstance()->project()->allFiles(); 00209 00210 bool filenameOnly = (url.url().find('/') == -1); 00211 QString filename = filenameOnly ? "/" : ""; 00212 filename += url.url(); 00213 00214 for (QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it) { 00215 if ((*it).endsWith(filename)) { 00216 // Match! The first one is as good as any one, I guess... 00217 return API::getInstance()->project()->projectDirectory() + "/" + *it; 00218 } 00219 } 00220 00221 return url; 00222 } 00223 00224 void PartController::editDocument(const KURL &inputUrl, int lineNum, int col) 00225 { 00226 kdDebug(9000) << k_funcinfo << inputUrl.prettyURL() << " linenum " << lineNum << endl; 00227 00228 KURL url = inputUrl; 00229 bool localUrl = url.url().startsWith("file:/"); 00230 00231 // Make sure the URL exists 00232 // KDE 3.0 compatibility hack: use KIO::NetAccess for everything >= KDE 3.1 00233 #ifdef OLD__KDE 00234 if (!url.isValid() || (localUrl ? !QFile(url.path()).exists() : !KIO::NetAccess::exists(url))) { 00235 #else 00236 if (!url.isValid() || (localUrl ? !QFile(url.path()).exists() : !KIO::NetAccess::exists(url, false, 0))) { 00237 #endif 00238 // Try to find this file in the current project's list instead 00239 KDevProject* project = API::getInstance()->project(); 00240 00241 if (project) { 00242 url = findURLInProject(url); 00243 00244 localUrl = url.url().startsWith("file:/"); 00245 #ifdef OLD__KDE 00246 if (!url.isValid() || (localUrl ? !QFile(url.path()).exists() : !KIO::NetAccess::exists(url))) { 00247 #else 00248 if (!url.isValid() || (localUrl ? !QFile(url.path()).exists() : !KIO::NetAccess::exists(url, false, 0))) { 00249 #endif 00250 // See if this url is relative to the current project's directory 00251 url = project->projectDirectory() + "/" + url.url(); 00252 } 00253 } 00254 00255 localUrl = url.url().startsWith("file:/"); 00256 #ifdef OLD__KDE 00257 if (!url.isValid() || (localUrl ? !QFile(url.path()).exists() : !KIO::NetAccess::exists(url))) { 00258 #else 00259 if (!url.isValid() || (localUrl ? !QFile(url.path()).exists() : !KIO::NetAccess::exists(url, false, 0))) { 00260 #endif 00261 // Here perhaps we should prompt the user to find the file? 00262 return; 00263 } 00264 } 00265 00266 // We now have a url that exists ;) 00267 00268 url.cleanPath(true); 00269 if (url.isLocalFile()) 00270 { 00271 QString path = url.path(); 00272 path = URLUtil::canonicalPath(path); 00273 if ( !path.isEmpty() ) 00274 url.setPath(path); 00275 } 00276 00277 KParts::Part *existingPart = partForURL(url); 00278 if (existingPart) 00279 { 00280 activatePart(existingPart); 00281 EditorProxy::getInstance()->setLineNumber(existingPart, lineNum, col); 00282 return; 00283 } 00284 00285 QString preferred, className; 00286 00287 QString mimeType, encoding; 00288 if (m_presetEncoding.isNull()) 00289 mimeType = KMimeType::findByURL(url)->name(); 00290 else { 00291 mimeType = "text/plain"; 00292 encoding = m_presetEncoding; 00293 m_presetEncoding = QString::null; 00294 } 00295 00296 // we generally prefer embedding, but if Qt-designer is the preferred application for this mimetype 00297 // make sure we launch designer instead of embedding KUIviewer 00298 if ( mimeType == "application/x-designer" ) 00299 { 00300 KService::Ptr preferredApp = KServiceTypeProfile::preferredService( mimeType, "Application" ); 00301 if ( preferredApp && preferredApp->desktopEntryName() == "designer" ) 00302 { 00303 KRun::runURL(url, mimeType); 00304 return; 00305 } 00306 } 00307 00308 kdDebug(9000) << "mimeType = " << mimeType << endl; 00309 00310 if ( mimeType.startsWith("text/") 00311 || mimeType.startsWith("application/x-") && mimeType != "application/x-designer" 00312 || mimeType == "image/x-xpm") 00313 { 00314 mimeType = "text/plain"; 00315 kapp->config()->setGroup("Editor"); 00316 preferred = kapp->config()->readPathEntry("EmbeddedKTextEditor"); 00317 } else if( mimeType.startsWith("inode/") ){ 00318 return; 00319 } 00320 00321 KParts::Factory *factory = 0; 00322 00323 // load the appropriate part factory like chosen in the editor-chooser part 00324 // (Note: KTextEditor/Document is the editor in MDI mode. KTextEditor/Editor is the editor in SDI mode. 00325 // But KTextEditor/Editor doesn't work for the Kate part on KDE-3.0.x, so better use KTextEditor/Document anyway.) 00326 QString services[] = {"KTextEditor/Document", "KParts/ReadWritePart", "KParts/ReadOnlyPart"}; 00327 QString classnames[] = {"KTextEditor::Document", "KParts::ReadWritePart", "KParts::ReadOnlyPart"}; 00328 for (uint i=0; i<3; ++i) 00329 { 00330 factory = findPartFactory(mimeType, services[i], preferred); 00331 if (factory) 00332 { 00333 className = classnames[i]; 00334 break; 00335 } 00336 } 00337 00338 kdDebug(9000) << "factory = " << factory << endl; 00339 00340 if (factory) 00341 { 00342 // Currently, only a single view per document is supported. 00343 // So fall back (downgrade) from MDI-mode editor to SDI-mode editor 00344 // (Note: This always works since KTextEditor::Document inherits KTextEditor::Editor) 00345 00346 if (className == "KTextEditor::Document") 00347 className = "KTextEditor::Editor"; 00348 00349 // create the object of the desired class 00350 KParts::ReadOnlyPart *part = static_cast<KParts::ReadOnlyPart*>(factory->createPart(TopLevel::getInstance()->main(), 0, 0, 0, className.latin1())); 00351 KParts::BrowserExtension *extension = KParts::BrowserExtension::childObject(part); 00352 kdDebug(9000) << "Encoding: " << encoding << ", extension: " << extension << endl; 00353 if (extension && !encoding.isNull()) 00354 { 00355 KParts::URLArgs args; 00356 args.serviceType = mimeType + ";" + encoding; 00357 extension->setURLArgs(args); 00358 } 00359 part->openURL(url); 00360 00361 bool isTextEditor = className == "KTextEditor::Editor"; 00362 integratePart(part, url, isTextEditor ); 00363 00364 if( isTextEditor ) 00365 EditorProxy::getInstance()->setLineNumber(part, lineNum, col); 00366 } 00367 else 00368 KRun::runURL(url, mimeType); 00369 } 00370 00371 00372 void PartController::showDocument(const KURL &url, const QString &context) 00373 { 00374 QString fixedPath = DocumentationPart::resolveEnvVarsInURL(url.url()); // possibly could env vars 00375 KURL docUrl(fixedPath); 00376 kdDebug(9000) << "SHOW: " << docUrl.url() << " context=" << context << endl; 00377 00378 if ( docUrl.isLocalFile() && KMimeType::findByURL(docUrl)->name() != "text/html" ) { 00379 // a link in a html-file pointed to a local text file - display 00380 // it in the editor instead of a html-view to avoid uglyness 00381 editDocument( docUrl ); 00382 return; 00383 } 00384 00385 DocumentationPart *part = 0; 00386 00387 if (!context.isEmpty()) 00388 part = findDocPart(context); 00389 00390 if (!part) 00391 { 00392 part = new DocumentationPart; 00393 part->setContext(context); 00394 integratePart(part,docUrl); 00395 } 00396 else 00397 activatePart(part); 00398 00399 if( !m_restoring ) 00400 addHistoryEntry( new HistoryEntry(docUrl, context) ); 00401 00402 bool bSuccess = part->openURL(docUrl); 00403 if (!bSuccess) { 00404 // part->showError(...); 00405 } 00406 } 00407 00408 00409 DocumentationPart *PartController::findDocPart(const QString &context) 00410 { 00411 QPtrListIterator<KParts::Part> it(*parts()); 00412 for ( ; it.current(); ++it) 00413 { 00414 DocumentationPart *part = dynamic_cast<DocumentationPart*>(it.current()); 00415 if (part && (part->context() == context)) 00416 return part; 00417 } 00418 00419 return 0; 00420 } 00421 00422 00423 KParts::Factory *PartController::findPartFactory(const QString &mimeType, const QString &partType, const QString &preferredName) 00424 { 00425 KTrader::OfferList offers = KTrader::self()->query(mimeType, QString("'%1' in ServiceTypes").arg(partType)); 00426 00427 if (offers.count() > 0) 00428 { 00429 KService::Ptr ptr = 0; 00430 // if there is a preferred plugin we'll take it 00431 if ( !preferredName.isEmpty() ) { 00432 KTrader::OfferList::Iterator it; 00433 for (it = offers.begin(); it != offers.end(); ++it) { 00434 if ((*it)->name() == preferredName) { 00435 ptr = (*it); 00436 } 00437 } 00438 } 00439 // else we just take the first in the list 00440 if ( !ptr ) { 00441 ptr = offers.first(); 00442 } 00443 return static_cast<KParts::Factory*>(KLibLoader::self()->factory(QFile::encodeName(ptr->library()))); 00444 } 00445 00446 return 0; 00447 } 00448 00449 00450 void PartController::integratePart(KParts::Part *part, const KURL &url, bool isTextEditor ) 00451 { 00452 if (!part->widget()) { 00454 kdDebug(9000) << "no widget for this part!!" << endl; 00455 return; // to avoid later crash 00456 } 00457 00458 TopLevel::getInstance()->embedPartView(part->widget(), url.filename(), url.url()); 00459 savePartWidgetIcon(part); 00460 00461 addPart(part); 00462 00463 if( isTextEditor ){ 00464 EditorProxy::getInstance()->installPopup(part, contextPopupMenu()); 00465 00466 #if KDE_VERSION < 310 00467 // HACK: this is a workaround. The kate-part does not emit "completed" when 00468 // it save a file yet. 00469 connect(part, SIGNAL(fileNameChanged()), this, SLOT(slotUploadFinished())); 00470 #endif 00471 } 00472 00473 // tell the parts we loaded a document 00474 KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(part); 00475 if (ro_part && ro_part->url().isLocalFile()) { 00476 // kdDebug(9000) << "KDirWatch: adding " << url.path() << endl; 00477 dirWatcher->addFile( url.path() ); 00478 accessTimeMap[ ro_part ] = dirWatcher->ctime( url.path() ); 00479 emit loadedFile(ro_part->url().path()); 00480 } 00481 00482 // let's get notified when a document has been changed 00483 connect(part, SIGNAL(completed()), this, SLOT(slotUploadFinished())); 00484 connect(part, SIGNAL(completed()), this, SLOT(slotRestoreStatus())); 00485 00486 // yes, we're cheating again. this signal exists for katepart's 00487 // Document object and our DocumentationPart 00488 connect(part, SIGNAL(fileNameChanged()), this, SLOT(slotFileNameChanged())); 00489 00490 // Connect to the document's views newStatus() signal in order to keep track of the 00491 // modified-status of the document. 00492 00493 // What's potentially problematic is that this signal isn't officially part of the 00494 // KTextEditor::View interface. It is nevertheless there, and used in kate and kwrite. 00495 // There doesn't seem to be any othere way of making this work with katepart, and since 00496 // signals are dynamic, if we try to connect to an editorpart that lacks this signal, 00497 // all we get is a runtime warning. At this point in time we are only really supported 00498 // by katepart anyway so IMHO this hack is justified. //teatime 00499 if (isTextEditor) 00500 { 00501 KTextEditor::Document * doc = static_cast<KTextEditor::Document*>( part ); 00502 QPtrList<KTextEditor::View> list = doc->views(); 00503 QPtrListIterator<KTextEditor::View> it( list ); 00504 while ( it.current() ) 00505 { 00506 connect( it, SIGNAL( newStatus() ), this, SLOT( slotNewStatus() ) ); 00507 ++it; 00508 } 00509 } 00510 } 00511 00512 void PartController::reinstallPopups( ){ 00513 00514 EditorProxy* editorProxy = EditorProxy::getInstance(); 00515 QPopupMenu* popup = contextPopupMenu(); 00516 00517 QPtrListIterator<KParts::Part> it(*parts()); 00518 for ( ; it.current(); ++it) 00519 editorProxy->installPopup( it.current(), popup, true ); 00520 } 00521 00522 void PartController::slotUploadFinished() 00523 { 00524 const KParts::ReadOnlyPart *ro_part = dynamic_cast<const KParts::ReadOnlyPart*>(sender()); 00525 00526 if (!ro_part || !ro_part->url().isLocalFile()) 00527 return; 00528 00529 QString path = ro_part->url().path(); 00530 // can't use KDirWatch's ctime here since it might not be updated yet 00531 QFileInfo fi( path ); 00532 // kdDebug(9000) << "*** uploadFinished() " << fi.lastModified().toString( "mm:ss:zzz" ) << endl; 00533 accessTimeMap[ ro_part ] = fi.lastModified(); 00534 emit savedFile( path ); 00535 } 00536 00537 void PartController::slotFileNameChanged() 00538 { 00539 const KParts::ReadOnlyPart *ro_part = dynamic_cast<const KParts::ReadOnlyPart*>(sender()); 00540 00541 if ( !ro_part || !ro_part->url().isLocalFile() ) 00542 return; 00543 00544 emit partURLChanged( const_cast<KParts::ReadOnlyPart*>(ro_part) ); 00545 00546 QString path = ro_part->url().path(); 00547 accessTimeMap[ ro_part ] = dirWatcher->ctime( path ); 00548 emit fileDirty( ro_part->url() ); 00549 } 00550 00551 QPopupMenu *PartController::contextPopupMenu() 00552 { 00553 QPopupMenu * popup = (QPopupMenu*)(TopLevel::getInstance()->main())->factory()->container("rb_popup", TopLevel::getInstance()->main()); 00554 00555 kdDebug( 9000 ) << "PartController::contextPopupMenu() will return " << popup << endl; 00556 00557 return popup; 00558 } 00559 00560 00561 /*static bool urlIsEqual(const KURL &a, const KURL &b) 00562 { 00563 if (a.isLocalFile() && b.isLocalFile()) 00564 { 00565 struct stat aStat, bStat; 00566 00567 if ((::stat(QFile::encodeName(a.fileName()), &aStat) == 0) 00568 && (::stat(QFile::encodeName(b.fileName()), &bStat) == 0)) 00569 { 00570 return (aStat.st_dev == bStat.st_dev) && (aStat.st_ino == bStat.st_ino); 00571 } 00572 } 00573 00574 return a == b; 00575 }*/ 00576 00577 KParts::Part *PartController::partForURL(const KURL &url) 00578 { 00579 QPtrListIterator<KParts::Part> it(*parts()); 00580 for ( ; it.current(); ++it) 00581 { 00582 KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(it.current()); 00583 if (ro_part && url == ro_part->url()) 00584 return ro_part; 00585 } 00586 00587 return 0; 00588 } 00589 00590 00591 void PartController::activatePart(KParts::Part *part) 00592 { 00593 setActivePart(part); 00594 00595 if (part->widget()) 00596 { 00597 TopLevel::getInstance()->raiseView(part->widget()); 00598 part->widget()->setFocus(); 00599 } 00600 } 00601 00602 00603 void PartController::closeActivePart() 00604 { 00605 if (!activePart()) 00606 return; 00607 00608 closePart(activePart()); 00609 } 00610 00611 00612 bool PartController::closePart(KParts::Part *part) 00613 { 00614 if (part->inherits("KParts::ReadOnlyPart")) 00615 { 00616 KParts::ReadOnlyPart *ro_part = static_cast<KParts::ReadOnlyPart*>(part); 00617 00618 if (!ro_part->closeURL()) 00619 { 00620 return false; 00621 } 00622 } 00623 partWidgetIcons.remove(part); 00624 00625 // If we didn't call removePart(), KParts::PartManager::slotObjectDestroyed would 00626 // get called from the destroyed signal of the part being deleted below. 00627 // The call chain from that looks like this: 00628 // QObject::destroyed() 00629 // KParts::PartManager::slotWidgetDestroyed() -> setActivePart() -> activePartChanged() 00630 // TopLevelXXX::createGUI() 00631 // KXMLGUIFactory::removeClient() 00632 // But then KXMLGUIFactory tries to remove the already-deleted part. 00633 // Normally this would work, because the factory uses a QGuardedPtr to the part. 00634 // But the QGuardedPtr is connected to the _same_ destroyed() slot that got us to 00635 // that point (slots are called in an undefined order)! 00636 00637 // The following line can be removed with kdelibs HEAD! (2002-05-26) 00638 removePart( part ); 00639 00640 if (part->widget()) 00641 TopLevel::getInstance()->removeView(part->widget()); 00642 00643 delete part; 00644 00645 return true; 00646 } 00647 00648 00649 void PartController::updateMenuItems() 00650 { 00651 bool hasWriteParts = false; 00652 bool hasReadOnlyParts = false; 00653 00654 QPtrListIterator<KParts::Part> it(*parts()); 00655 for ( ; it.current(); ++it) 00656 { 00657 if (it.current()->inherits("KParts::ReadWritePart")) 00658 hasWriteParts = true; 00659 if (it.current()->inherits("KParts::ReadOnlyPart")) 00660 hasReadOnlyParts = true; 00661 } 00662 00663 m_saveAllFilesAction->setEnabled(hasWriteParts); 00664 m_revertAllFilesAction->setEnabled(hasWriteParts); 00665 m_closeWindowAction->setEnabled(hasReadOnlyParts); 00666 m_closeAllWindowsAction->setEnabled(hasReadOnlyParts); 00667 m_closeOtherWindowsAction->setEnabled(hasReadOnlyParts); 00668 00669 m_backAction->setEnabled(m_history.current() != m_history.getFirst()); 00670 m_forwardAction->setEnabled(m_history.current() != m_history.getLast()); 00671 } 00672 00673 00674 void PartController::slotSaveAllFiles() 00675 { 00676 saveAllFiles(); 00677 } 00678 00679 00680 void PartController::saveFile(KParts::Part *part) 00681 { 00682 KParts::ReadWritePart *rw_part = dynamic_cast<KParts::ReadWritePart*>(part); 00683 if ( !rw_part ) 00684 return; 00685 if ( isDirty( rw_part ) ) { 00686 kdDebug(9000) << "DIRTY SAVE" << endl; 00687 } 00688 00689 if( rw_part->isModified() ) { 00690 rw_part->save(); 00691 TopLevel::getInstance()->statusBar()->message(i18n("Saved %1").arg(rw_part->url().prettyURL()), 2000); 00692 } 00693 } 00694 00695 void PartController::saveAllFiles() 00696 { 00697 QPtrListIterator<KParts::Part> it(*parts()); 00698 for ( ; it.current(); ++it) 00699 saveFile( it.current() ); 00700 } 00701 00702 00703 void PartController::slotRevertAllFiles() 00704 { 00705 revertAllFiles(); 00706 } 00707 00708 void PartController::revertFile(KParts::Part *part) 00709 { 00710 if ( !part ) 00711 return; 00712 00713 if (part->inherits("KParts::ReadWritePart")) { 00714 KParts::ReadWritePart *rw_part = static_cast<KParts::ReadWritePart*>(part); 00715 if ( rw_part->url().isLocalFile() ) 00716 accessTimeMap[ static_cast<KParts::ReadOnlyPart*>(part) ] = dirWatcher->ctime( rw_part->url().path() ); 00717 rw_part->openURL(rw_part->url()); 00718 } 00719 } 00720 00721 void PartController::revertAllFiles() 00722 { 00723 QPtrListIterator<KParts::Part> it(*parts()); 00724 for ( ; it.current(); ++it) 00725 revertFile( it.current() ); 00726 } 00727 00728 00729 void PartController::slotCloseWindow() 00730 { 00731 closeActivePart(); 00732 } 00733 00734 KURL::List PartController::modifiedDocuments() 00735 { 00736 KURL::List modFiles; 00737 00738 QPtrListIterator<KParts::Part> it( *parts() ); 00739 while( it.current() ) 00740 { 00741 KParts::ReadWritePart *rw_part = dynamic_cast<KParts::ReadWritePart*>(it.current()); 00742 if ( rw_part && rw_part->isModified() ) 00743 { 00744 modFiles << rw_part->url(); 00745 } 00746 ++it; 00747 } 00748 return modFiles; 00749 } 00750 00751 void PartController::saveFiles( KURL::List const & filelist ) 00752 { 00753 KURL::List::ConstIterator it = filelist.begin(); 00754 while ( it != filelist.end() ) 00755 { 00756 KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( *it ) ); 00757 if ( rw_part ) 00758 { 00759 rw_part->save(); 00760 } 00761 ++it; 00762 } 00763 } 00764 00765 void PartController::clearModified( KURL::List const & filelist ) 00766 { 00767 KURL::List::ConstIterator it = filelist.begin(); 00768 while ( it != filelist.end() ) 00769 { 00770 KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( *it ) ); 00771 if ( rw_part ) 00772 { 00773 rw_part->setModified( false ); 00774 } 00775 ++it; 00776 } 00777 } 00778 00779 bool PartController::closeWindows( KURL::List const & ignoreList ) 00780 { 00781 KURL::List modList = modifiedDocuments(); 00782 00783 if ( modList.count() > 0 && modList != ignoreList ) 00784 { 00785 KSaveSelectDialog dlg( modList, ignoreList, TopLevel::getInstance()->main() ); 00786 if ( dlg.exec() == QDialog::Accepted ) 00787 { 00788 saveFiles( dlg.filesToSave() ); 00789 clearModified( dlg.filesNotToSave() ); 00790 } 00791 else 00792 { 00793 return false; 00794 } 00795 } 00796 00797 QPtrList<KParts::Part> partList( *parts() ); 00798 QPtrListIterator<KParts::Part> it( partList ); 00799 while ( KParts::Part* part = it.current() ) 00800 { 00801 KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part ); 00802 if ( ro_part && !ignoreList.contains( ro_part->url() ) || !ro_part ) 00803 { 00804 closePart( part ); 00805 } 00806 ++it; 00807 } 00808 00809 return true; 00810 } 00811 00812 bool PartController::closeAllWindows() 00813 { 00814 return closeWindows( KURL::List() ); 00815 } 00816 00817 void PartController::slotCloseAllWindows() 00818 { 00819 closeAllWindows(); 00820 } 00821 00822 void PartController::slotCloseOtherWindows() 00823 { 00824 KParts::ReadOnlyPart * active = dynamic_cast<KParts::ReadOnlyPart*>(activePart()); 00825 if ( !active ) return; 00826 00827 KURL::List ignoreList; 00828 ignoreList.append( active->url() ); 00829 00830 closeWindows( ignoreList ); 00831 } 00832 00833 void PartController::slotCurrentChanged(QWidget *w) 00834 { 00835 00836 kdDebug()<<"slotCurrentChanged***********************************+"<<endl; 00837 QPtrListIterator<KParts::Part> it(*parts()); 00838 for ( ; it.current(); ++it) 00839 if (it.current()->widget() == w) 00840 { 00841 kdDebug()<<"found it**************************************"<<endl; 00842 setActivePart(it.current(), w); 00843 break; 00844 } 00845 } 00846 00847 void PartController::slotOpenFile() 00848 { 00849 KURL::List fileNames = KFileDialog::getOpenURLs(QString::null, QString::null, TopLevel::getInstance()->main(), QString::null); 00850 00851 for ( KURL::List::Iterator it = fileNames.begin(); it != fileNames.end(); ++it ) 00852 { 00853 editDocument( *it ); 00854 m_openRecentAction->addURL( *it ); 00855 } 00856 m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" ); 00857 } 00858 00859 void PartController::slotOpenRecent( const KURL& url ) 00860 { 00861 editDocument( url ); 00862 // stupid bugfix - don't allow an active item in the list 00863 m_openRecentAction->setCurrentItem( -1 ); 00864 } 00865 00866 void PartController::slotClosePartForWidget( const QWidget* w) 00867 { 00868 closePartForWidget(w); 00869 } 00870 00871 bool PartController::closePartForWidget( const QWidget* w ) 00872 { 00873 QPtrListIterator<KParts::Part> it(*parts()); 00874 for ( ; it.current(); ++it) 00875 if (it.current()->widget() == w) 00876 { 00877 return closePart( *it ); 00878 } 00879 return true; 00880 } 00881 00882 bool PartController::readyToClose() 00883 { 00884 return closeAllWindows(); 00885 } 00886 00887 void PartController::slotActivePartChanged( KParts::Part* part ) 00888 { 00889 updateMenuItems(); 00890 if( !part || QString(part->name()) != "DocumentationPart" ){ 00891 m_backAction->setEnabled( false ); 00892 m_forwardAction->setEnabled( false ); 00893 } 00894 } 00895 00896 void PartController::slotSwitchTo() 00897 { 00898 QMap<QString,KParts::ReadOnlyPart*> parts_map; 00899 QStringList part_list; 00900 QPtrList<KParts::Part> pl = *parts(); 00901 KParts::Part *part; 00902 for(part=pl.first();part;part=pl.next()) { 00903 kdDebug(9000) << "Part..." << endl; 00904 if (part->inherits("KParts::ReadOnlyPart")) { 00905 KParts::ReadOnlyPart *ro_part = static_cast<KParts::ReadOnlyPart*>(part); 00906 QString name = ro_part->url().fileName(); 00907 part_list.append(name); 00908 parts_map[name] = ro_part; 00909 kdDebug(9000) << "Found part for URL " << ro_part->url().prettyURL() << endl; 00910 } 00911 } 00912 00913 KDialogBase dialog(KDialogBase::Plain, i18n("Switch To..."), KDialogBase::Ok|KDialogBase::Cancel, 00914 KDialogBase::Ok, 0, "Switch to", true); 00915 QGridLayout *grid = new QGridLayout( dialog.plainPage(), 2, 1, 10, 10); 00916 KLineEdit *editbox = new KLineEdit(dialog.plainPage()); 00917 grid->addWidget(new QLabel( i18n("Switch to buffer:"), dialog.plainPage() ), 0, 0); 00918 grid->addWidget(editbox, 1, 0); 00919 editbox->completionObject()->setItems( part_list ); 00920 editbox->setFocus(); 00921 int result = dialog.exec(); 00922 if (result==KDialogBase::KDialogBase::Accepted) { 00923 if (parts_map.contains(editbox->text())) { 00924 activatePart(parts_map[editbox->text()]); 00925 } 00926 } 00927 } 00928 00929 void PartController::slotBack() 00930 { 00931 saveState(activePart()); 00932 00933 if(m_history.prev()==0L) m_history.first(); 00934 00935 restoreState(); 00936 } 00937 00938 void PartController::slotForward() 00939 { 00940 saveState(activePart()); 00941 00942 if(m_history.next()==0L) m_history.last(); 00943 00944 restoreState(); 00945 } 00946 00947 void PartController::slotBackAboutToShow() 00948 { 00949 KPopupMenu *popup = m_backAction->popupMenu(); 00950 popup->clear(); 00951 00952 int savePos = m_history.at(); 00953 for (int i=0; i<10 && m_history.prev(); ++i) 00954 popup->insertItem( m_history.current()->url.url() ); 00955 00956 m_history.at(savePos); 00957 } 00958 00959 void PartController::slotBackPopupActivated( int id ) 00960 { 00961 int by = m_backAction->popupMenu()->indexOf(id)+1; 00962 00963 saveState(activePart()); 00964 for (int i=0; i < by; ++i) 00965 m_history.prev(); 00966 if(m_history.prev()==0L) m_history.first(); 00967 00968 restoreState(); 00969 00970 updateMenuItems(); 00971 } 00972 00973 void PartController::slotForwardAboutToShow() 00974 { 00975 KPopupMenu *popup = m_forwardAction->popupMenu(); 00976 popup->clear(); 00977 00978 int savePos = m_history.at(); 00979 for (int i=0; i<10 && m_history.next(); ++i) 00980 popup->insertItem(m_history.current()->url.url()); 00981 00982 m_history.at(savePos); 00983 } 00984 00985 void PartController::slotForwardPopupActivated( int id ) 00986 { 00987 int by = m_forwardAction->popupMenu()->indexOf(id)+1; 00988 00989 saveState(activePart()); 00990 for (int i=0; i < by; ++i) 00991 m_history.next(); 00992 if(m_history.current()==0L) m_history.last(); 00993 00994 restoreState(); 00995 00996 updateMenuItems(); 00997 } 00998 00999 void PartController::addHistoryEntry( HistoryEntry* entry ) 01000 { 01001 HistoryEntry *current = m_history.current(); 01002 while (m_history.getLast() != current) 01003 m_history.removeLast(); 01004 m_history.append( entry ); 01005 m_history.last(); 01006 01007 updateMenuItems(); 01008 } 01009 01010 void PartController::saveState( KParts::Part* part ) 01011 { 01012 DocumentationPart* d = dynamic_cast<DocumentationPart*>( part ); 01013 if( !d ) 01014 return; 01015 01016 HistoryEntry *entry = m_history.current(); 01017 if (!entry) 01018 return; 01019 01020 entry->url = d->url(); 01021 } 01022 01023 01024 void PartController::restoreState() 01025 { 01026 HistoryEntry *entry = m_history.current(); 01027 if (!entry) 01028 return; 01029 01030 m_restoring = true; 01031 showDocument( entry->url, entry->context ); 01032 m_restoring = false; 01033 updateMenuItems(); 01034 } 01035 01036 void PartController::showPart( KParts::Part* part, const QString& name, const QString& shortDescription ) 01037 { 01038 if (!part->widget()) { 01040 return; // to avoid later crash 01041 } 01042 01043 QPtrListIterator<KParts::Part> it(*parts()); 01044 for ( ; it.current(); ++it) 01045 { 01046 if( it.current() == part ){ 01047 // part already embedded 01048 activatePart( it.current() ); 01049 return; 01050 } 01051 } 01052 01053 // embed the part 01054 TopLevel::getInstance()->embedPartView( part->widget(), name, shortDescription ); 01055 savePartWidgetIcon(part); 01056 addPart( part ); 01057 } 01058 01059 void PartController::dirty( const QString& fileName ) 01060 { 01061 // kdDebug(9000) << "DIRRRRRTY: " << fileName << " " << dirWatcher->ctime( fileName ).toString( "mm:ss:zzz" ) << endl; 01062 emit fileDirty( KURL( fileName ) ); 01063 } 01064 01065 bool PartController::isDirty( KParts::ReadOnlyPart* part ) 01066 { 01067 if ( !part || !part->url().isLocalFile() ) 01068 return false; 01069 01070 // kdDebug( 9000 ) << "isDirty?" << accessTimeMap[ part ].toString( "mm:zzz" ) << " : " << dirWatcher->ctime( part->url().path() ).toString( "mm:zzz" ) << endl; 01071 01072 if ( accessTimeMap.contains( part ) ) 01073 return ( accessTimeMap[ part ] < dirWatcher->ctime( part->url().path() ) ); 01074 01075 accessTimeMap[ part ] = dirWatcher->ctime( part->url().path() ); 01076 return false; 01077 } 01078 01079 void PartController::savePartWidgetIcon( KParts::Part * part ) 01080 { 01081 if ((!part->widget()) || (!part->widget()->icon())) 01082 return; 01083 QPixmap m(*(part->widget()->icon())); 01084 partWidgetIcons[part] = m; 01085 } 01086 01087 void PartController::restorePartWidgetIcon( KParts::Part * part ) 01088 { 01089 if (!part->widget()) 01090 return; 01091 if (partWidgetIcons.contains(part)) 01092 part->widget()->setIcon(partWidgetIcons[part]); 01093 } 01094 01095 void PartController::slotNewStatus( ) 01096 { 01097 kdDebug(9000) << "PartController::slotNewStatus()" << endl; 01098 01099 QObject * senderobj = const_cast<QObject*>( sender() ); 01100 KTextEditor::View * view = dynamic_cast<KTextEditor::View*>( senderobj ); 01101 01102 if ( view ) 01103 { 01104 KParts::ReadWritePart * rw_part = view->document(); 01105 if ( isDirty( rw_part ) ) { 01106 } else if ( rw_part->isModified() ) { 01107 rw_part->widget()->setIcon(SmallIcon("filesave")); 01108 } else { 01109 restorePartWidgetIcon(rw_part); 01110 } 01111 } 01112 } 01113 01114 void PartController::slotRestoreStatus( ) 01115 { 01116 KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(const_cast<QObject*>(sender())); 01117 01118 if ( !ro_part ) 01119 return; 01120 01121 if (!isDirty(ro_part)) 01122 restorePartWidgetIcon(ro_part); 01123 } 01124 01125 void PartController::slotFileDirty( const KURL & url ) 01126 { 01127 kdDebug(9000) << k_funcinfo << endl; 01128 01129 KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) ); 01130 if ( !rw_part || !rw_part->widget() ) return; 01131 01132 if ( isDirty( rw_part ) ) 01133 { 01134 rw_part->widget()->setIcon( SmallIcon("revert") ); 01135 } 01136 else if ( !rw_part->isModified() ) 01137 { 01138 rw_part->widget()->setIcon( SmallIcon("kdevelop") ); 01139 } 01140 else 01141 { 01142 rw_part->widget()->setIcon( SmallIcon("filesave") ); 01143 } 01144 } 01145 01146 #include "partcontroller.moc"
KDE Logo
This file is part of the documentation for KDevelop Version 3.0.4.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Oct 19 08:01:53 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003