• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • KDevelop Generic Shell
 

KDevelop Generic Shell

  • src
partcontroller.cpp
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <unistd.h>
4
5#include <tqpopupmenu.h>
6#include <tqfile.h>
7#include <tqlayout.h>
8#include <tqmap.h>
9#include <tqlabel.h>
10#include <tqradiobutton.h>
11#include <tqcheckbox.h>
12#include <tqdom.h>
13
14#include <kmimetype.h>
15#include <kservice.h>
16#include <ktrader.h>
17#include <tdeapplication.h>
18#include <krun.h>
19#include <kdebug.h>
20#include <tdelocale.h>
21#include <tdemessagebox.h>
22#include <tdeparts/part.h>
23#include <tdeparts/factory.h>
24#include <tdeparts/partmanager.h>
25#include <tdeparts/browserextension.h>
26#include <tdefiledialog.h>
27#include <tdemainwindow.h>
28#include <tdeaction.h>
29#include <tdemessagebox.h>
30#include <kstatusbar.h>
31#include <tdehtml_part.h>
32#include <tdepopupmenu.h>
33#include <tdeio/netaccess.h>
34#include <kdialogbase.h>
35#include <klineedit.h>
36#include <tdeshortcut.h>
37#include <kcompletion.h>
38#include <kdirwatch.h>
39#include <tdeversion.h>
40#include <kiconloader.h>
41#include <kuserprofile.h>
42#include <kencodingfiledialog.h>
43#include <ksqueezedtextlabel.h>
44
45#include <tdetexteditor/view.h>
46#include <tdetexteditor/document.h>
47#include <tdetexteditor/viewcursorinterface.h>
48#include <tdetexteditor/encodinginterface.h>
49
50#include "toplevel.h"
51#include "api.h"
52#include "core.h"
53#include "editorproxy.h"
54#include "documentationpart.h"
55#include "ksavealldialog.h"
56
57#include "kdevproject.h"
58#include "urlutil.h"
59#include "mimewarningdialog.h"
60#include "domutil.h"
61#include "kdevjobtimer.h"
62
63#include "designer.h"
64#include "kdevlanguagesupport.h"
65
66#include "multibuffer.h"
67#include "partcontroller.h"
68
69
70class TQDomDocument;
71
72PartController *PartController::s_instance = 0;
73
74using namespace MainWindowUtils;
75
76struct HistoryEntry
77{
78 KURL url;
79 TQString context;
80
81 HistoryEntry( const KURL& u, const TQString& c ): url( u ), context( c ) {}
82};
83
84struct ModificationData
85{
86 KTextEditor::Document * doc;
87 bool isModified;
88 unsigned char reason;
89};
90
91
92PartController::PartController(TQWidget *parent)
93 : KDevPartController(parent), _editorFactory(0L), m_currentActivePart(0), m_removingActivePart(false)
94{
95 connect(this, TQ_SIGNAL(partRemoved(KParts::Part*)), this, TQ_SLOT(slotPartRemoved(KParts::Part* )) );
96 connect(this, TQ_SIGNAL(partAdded(KParts::Part*)), this, TQ_SLOT(slotPartAdded(KParts::Part* )) );
97 connect(this, TQ_SIGNAL(activePartChanged(KParts::Part*)), this, TQ_SLOT(slotActivePartChanged(KParts::Part*)));
98
99 setupActions();
100
101 m_isJumping = false;
102
103 m_openNextAsText = false;
104}
105
106
107PartController::~PartController()
108{
109}
110
111
112void PartController::createInstance(TQWidget *parent)
113{
114 if (!s_instance)
115 s_instance = new PartController(parent);
116}
117
118
119PartController *PartController::getInstance()
120{
121 return s_instance;
122}
123
124
125void PartController::setupActions()
126{
127 TDEActionCollection *ac = TopLevel::getInstance()->main()->actionCollection();
128
129 TDEAction* newAction = KStdAction::open(this, TQ_SLOT(slotOpenFile()), ac, "file_open");
130 newAction->setToolTip( i18n("Open file") );
131 newAction->setWhatsThis( i18n("<b>Open file</b><p>Opens an existing file without adding it to the project.</p>") );
132
133 m_openRecentAction = KStdAction::openRecent( this, TQ_SLOT(slotOpenRecent(const KURL&) ), ac, "file_open_recent" );
134 m_openRecentAction->setWhatsThis(TQString("<b>%1</b><p>%2").arg(beautifyToolTip(m_openRecentAction->text())).arg(i18n("Opens recently opened file.")));
135 m_openRecentAction->loadEntries( kapp->config(), "RecentFiles" );
136
137 m_saveAllFilesAction = new TDEAction(i18n("Save Al&l"), 0, this, TQ_SLOT(slotSaveAllFiles()), ac, "file_save_all");
138 m_saveAllFilesAction->setToolTip( i18n("Save all modified files") );
139 m_saveAllFilesAction->setWhatsThis(i18n("<b>Save all</b><p>Saves all modified files."));
140 m_saveAllFilesAction->setEnabled(false);
141
142 m_revertAllFilesAction = new TDEAction(i18n("Rever&t All"), 0, this, TQ_SLOT(slotRevertAllFiles()), ac, "file_revert_all");
143 m_revertAllFilesAction->setToolTip(i18n("Revert all changes"));
144 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."));
145 m_revertAllFilesAction->setEnabled(false);
146
147 m_closeWindowAction = KStdAction::close(this, TQ_SLOT(slotCloseWindow()), ac, "file_close");
148 m_closeWindowAction->setToolTip( i18n("Close current file") );
149 m_closeWindowAction->setWhatsThis(TQString("<b>%1</b><p>%2").arg(beautifyToolTip(m_closeWindowAction->text())).arg(i18n("Closes current file.")));
150 m_closeWindowAction->setEnabled(false);
151
152 m_closeAllWindowsAction = new TDEAction(i18n("Close All"), 0, this, TQ_SLOT(slotCloseAllWindows()), ac, "file_close_all");
153 m_closeAllWindowsAction->setToolTip( i18n("Close all files") );
154 m_closeAllWindowsAction->setWhatsThis(i18n("<b>Close all</b><p>Close all opened files."));
155 m_closeAllWindowsAction->setEnabled(false);
156
157 m_closeOtherWindowsAction = new TDEAction(i18n("Close All Others"), 0, this, TQ_SLOT(slotCloseOtherWindows()), ac, "file_closeother");
158 m_closeOtherWindowsAction->setToolTip( i18n("Close other files") );
159 m_closeOtherWindowsAction->setWhatsThis(i18n("<b>Close all others</b><p>Close all opened files except current."));
160 m_closeOtherWindowsAction->setEnabled(false);
161
162 new TDEActionSeparator(ac, "dummy_separator");
163
164 m_backAction = new TDEToolBarPopupAction(i18n("Back"), "back", 0, this, TQ_SLOT(slotBack()), ac, "history_back");
165 m_backAction->setEnabled( false );
166 m_backAction->setToolTip(i18n("Back"));
167 m_backAction->setWhatsThis(i18n("<b>Back</b><p>Moves backwards one step in the navigation history."));
168 connect(m_backAction->popupMenu(), TQ_SIGNAL(aboutToShow()), this, TQ_SLOT(slotBackAboutToShow()));
169 connect(m_backAction->popupMenu(), TQ_SIGNAL(activated(int)), this, TQ_SLOT(slotBackPopupActivated(int)));
170
171 m_forwardAction = new TDEToolBarPopupAction(i18n("Forward"), "forward", 0, this, TQ_SLOT(slotForward()), ac, "history_forward");
172 m_forwardAction->setEnabled( false );
173 m_forwardAction->setToolTip(i18n("Forward"));
174 m_forwardAction->setWhatsThis(i18n("<b>Forward</b><p>Moves forward one step in the navigation history."));
175 connect(m_forwardAction->popupMenu(), TQ_SIGNAL(aboutToShow()), this, TQ_SLOT(slotForwardAboutToShow()));
176 connect(m_forwardAction->popupMenu(), TQ_SIGNAL(activated(int)), this, TQ_SLOT(slotForwardPopupActivated(int)));
177
178 m_gotoLastEditPosAction = new TDEAction( i18n("Goto Last Edit Position"), "go-bottom", 0, this, TQ_SLOT(gotoLastEditPos()), ac, "goto_last_edit_pos" );
179 m_gotoLastEditPosAction->setEnabled( false );
180 m_gotoLastEditPosAction->setToolTip( i18n("Goto Last Edit Position") );
181 m_gotoLastEditPosAction->setWhatsThis( i18n("<b>Goto Last Edit Position</b><p>Open the last edited file and position cursor at the point of edit") );
182}
183
184void PartController::setEncoding(const TQString &encoding)
185{
186 m_presetEncoding = encoding;
187}
188
189KParts::Part* PartController::findOpenDocument(const KURL& url)
190{
191 // if we find it this way, all is well
192 KParts::Part * part = partForURL( url );
193 if ( part )
194 {
195 return part;
196 }
197
198 // ok, let's see if we can try harder
199 if ( API::getInstance()->project() )
200 {
201 KURL partURL = findURLInProject( url );
202 partURL.cleanPath();
203 return partForURL( partURL );
204 }
205
206 return 0L;
207}
208
209KURL PartController::findURLInProject(const KURL& url)
210{
211 TQStringList fileList = API::getInstance()->project()->allFiles();
212
213 bool filenameOnly = (url.url().find('/') == -1);
214 TQString filename = filenameOnly ? "/" : "";
215 filename += url.url();
216
217 for (TQStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it) {
218 if ((*it).endsWith(filename)) {
219 // Match! The first one is as good as any one, I guess...
220 return KURL( API::getInstance()->project()->projectDirectory() + "/" + *it );
221 }
222 }
223
224 return url;
225}
226
227void PartController::editDocument(const KURL &inputUrl, int lineNum, int col)
228{
229 editDocumentInternal(inputUrl, lineNum, col);
230}
231
232void PartController::splitCurrentDocument(const KURL &inputUrl,
233 int lineNum, int col)
234{
235 editDocumentInternal(inputUrl, lineNum, col, true, true);
236}
237
238void PartController::scrollToLineColumn(const KURL &inputUrl,
239 int lineNum, int col, bool storeHistory )
240{
241 if ( KParts::ReadOnlyPart *existingPart = partForURL( inputUrl ) )
242 {
243 if( storeHistory ) addHistoryEntry( existingPart );
244 EditorProxy::getInstance()->setLineNumber( existingPart, lineNum, col );
245 return;
246 }
247}
248
249void PartController::editDocumentInternal( const KURL & inputUrl, int lineNum,
250 int col, bool activate,
251 bool addToCurrentBuffer )
252{
253 kdDebug(9000) << k_funcinfo << "\n " << inputUrl.prettyURL()
254 << " linenum " << lineNum << " activate? " << activate
255 << " addToCurrentBuffer? " << addToCurrentBuffer << endl;
256
257 KURL url = inputUrl;
258
259 // is it already open?
260 // (Try this once before verifying the URL, we could be dealing with a file that no longer exists on disc)
261 if ( KParts::Part *existingPart = partForURL( url ) )
262 {
263 // if we've been asked to OpenAs an open file with a specific encoding, assume the user wants to change encoding
264 if ( !m_presetEncoding.isNull() )
265 {
266 if ( KTextEditor::EncodingInterface * ei = dynamic_cast<KTextEditor::EncodingInterface*>( existingPart ) )
267 {
268 ei->setEncoding( m_presetEncoding );
269 }
270 m_presetEncoding = TQString();
271 }
272
273 addHistoryEntry();
274 activatePart( existingPart );
275 EditorProxy::getInstance()->setLineNumber( existingPart, lineNum, col );
276 return;
277 }
278
279 // Make sure the URL exists
280 if ( !url.isValid() || !TDEIO::NetAccess::exists(url, false, 0) )
281 {
282 bool done = false;
283
284 // Try to find this file in the current project's list instead
285 if ( API::getInstance()->project() )
286 {
287 if (url.isRelativeURL(url.url())) {
288 KURL relURL(API::getInstance()->project()->projectDirectory());
289 relURL.addPath( url.url() );
290
291 kdDebug() << k_funcinfo << "Looking for file in project dir: " << API::getInstance()->project()->projectDirectory() << " url " << url.url() << " transformed to " << relURL.url() << ": " << done << endl;
292 if (relURL.isValid() && TDEIO::NetAccess::exists(relURL, false, 0)) {
293 url = relURL;
294 done = true;
295 }
296 else {
297 KURL relURL(API::getInstance()->project()->buildDirectory());
298 relURL.addPath( url.url() );
299 kdDebug() << k_funcinfo << "Looking for file in build dir: " << API::getInstance()->project()->buildDirectory() << " url " << url.url() << " transformed to " << relURL.url() << ": " << done << endl;
300 if (relURL.isValid() && TDEIO::NetAccess::exists(relURL, false, 0)) {
301 url = relURL;
302 done = true;
303 }
304 }
305 }
306
307 if (!done) {
308 url = findURLInProject(url);
309
310 if ( !url.isValid() || !TDEIO::NetAccess::exists(url, false, 0) )
311 // See if this url is relative to the current project's directory
312 url = API::getInstance()->project()->projectDirectory() + "/" + url.path();
313
314 else
315 done = true;
316 }
317 }
318
319 if ( !done && ( !url.isValid() || !TDEIO::NetAccess::exists(url, false, 0) ))
320 {
321 // Not found - prompt the user to find it?
322 kdDebug(9000) << "cannot find URL: " << url.url() << endl;
323 return;
324 }
325 }
326
327 // We now have a url that exists ;)
328
329 // clean it and resolve possible symlink
330 url.cleanPath(true);
331 if (url.isLocalFile())
332 {
333 TQString path = url.path();
334 path = URLUtil::canonicalPath(path);
335 if ( !path.isEmpty() )
336 url.setPath(path);
337 }
338
339 // is it already open?
340 KParts::Part *existingPart = partForURL(url);
341 if (existingPart)
342 {
343 addHistoryEntry();
344 activatePart(existingPart);
345 EditorProxy::getInstance()->setLineNumber(existingPart, lineNum, col);
346 return;
347 }
348
349 if ( !addToCurrentBuffer )
350 {
351 if ( KDevLanguageSupport *lang =
352 API::getInstance()->languageSupport() )
353 {
354 // Let the language part override the addToCurrentBuffer flag
355 // if it decides to...
356 addToCurrentBuffer = lang->shouldSplitDocument( inputUrl );
357
358 if ( addToCurrentBuffer )
359 {
360 kdDebug(9000) <<
361 "languagePart() insists addToCurrentBuffer = true" << endl;
362 // Set activate = true, otherwise we have hard to fix
363 // multi-buffer delayed activation.
364 // I'll re-look at this later...
365 activate = true;
366 }
367 }
368 }
369
370 KMimeType::Ptr MimeType = KMimeType::findByURL( url );
371
372 kdDebug(9000) << "mimeType = " << MimeType->name() << endl;
373
374 // is the URL pointing to a directory?
375 if ( MimeType->is( "inode/directory" ) )
376 {
377 return;
378 }
379
380 if ( !m_presetEncoding.isNull() )
381 {
382 m_openNextAsText = true;
383 }
384
385 TDEConfig *config = kapp->config();
386 config->setGroup("General Options");
387
388 // we don't trust KDE with designer files, let's handle it ourselves
389 if ( !m_openNextAsText && MimeType->is( "application/x-designer" ) )
390 {
391 TQString DesignerSetting = config->readEntry( "DesignerSetting", "ExternalDesigner" );
392 TQString designerExec = "designer";
393 TQStringList designerPluginPaths;
394 TQDomDocument* dom = API::getInstance()->projectDom();
395 if ( dom != 0 )
396 {
397 // The global option specifies a fallback if the project
398 // has no setting or no project is open.
399 DesignerSetting = DomUtil::readEntry(*dom, "/kdevcppsupport/qt/designerintegration", DesignerSetting );
400 designerExec = DomUtil::readEntry(*dom, "/kdevcppsupport/qt/designer", designerExec );
401 }
402 if ( DesignerSetting == "ExternalKDevDesigner" )
403 {
404 designerExec = "kdevdesigner";
405 }
406 else if ( DesignerSetting == "EmbeddedKDevDesigner" )
407 {
408 if ( KParts::ReadOnlyPart *designerPart = qtDesignerPart() )
409 {
410 addHistoryEntry();
411 activatePart(designerPart);
412 designerPart->openURL(url);
413 return;
414 }
415 else if ( KParts::Factory * KDevDesignerFactory = static_cast<KParts::Factory*>( KLibLoader::self()->factory( TQFile::encodeName( "libkdevdesignerpart" ) ) ) )
416 {
417 KParts::ReadWritePart * kdevpart = static_cast<KParts::ReadWritePart*>( KDevDesignerFactory->createPart( TopLevel::getInstance()->main(), 0, 0, 0, "KParts::ReadWritePart" ) );
418 kdevpart->openURL( url );
419 addHistoryEntry();
420 integratePart( kdevpart, url );
421 m_openRecentAction->addURL( url );
422 m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );
423 return;
424 }
425 }
426
427 if( designerPluginPaths.isEmpty() )
428 KRun::runCommand( designerExec+" "+url.pathOrURL() );
429 else
430 KRun::runCommand( "TQT_PLUGIN_PATH=\""+designerPluginPaths.join(":")+"\" "+designerExec+" "+url.pathOrURL() );
431
432 return;
433 }
434
435 config->setGroup("General");
436
437 TQStringList texttypeslist = config->readListEntry( "TextTypes" );
438 if ( texttypeslist.contains( MimeType->name() ) )
439 {
440 m_openNextAsText = true;
441 }
442
443 bool isText = false;
444 TQVariant v = MimeType->property("X-TDE-text");
445 if (v.isValid())
446 isText = v.toBool();
447 // is this regular text - open in editor
448 if ( m_openNextAsText || isText || MimeType->is( "application/x-zerosize" ) )
449 {
450 KTextEditor::Editor *editorpart =
451 createEditorPart( activate, addToCurrentBuffer, url );
452 if ( editorpart )
453 {
454 if ( m_presetEncoding.isNull() && API::getInstance()->projectDom() )
455 {
456 TQDomDocument * projectDom = API::getInstance()->projectDom();
457 m_presetEncoding = DomUtil::readEntry( *projectDom, "/general/defaultencoding", TQString() );
458 }
459
460 if ( !m_presetEncoding.isNull() )
461 {
462 if ( KTextEditor::EncodingInterface * ei = dynamic_cast<KTextEditor::EncodingInterface*>( editorpart ) )
463 {
464 ei->setEncoding( m_presetEncoding );
465 }
466 m_presetEncoding = TQString();
467 }
468
469 addHistoryEntry();
470
471 TQWidget * widget = EditorProxy::getInstance()->topWidgetForPart( editorpart );
472
473 integratePart(editorpart, url, widget, true, activate, addToCurrentBuffer);
474 EditorProxy::getInstance()->setLineNumber(editorpart, lineNum, col);
475
476 m_openNextAsText = false;
477
478 m_openRecentAction->addURL( url );
479 m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );
480
481 return;
482 }
483 }
484
485 // OK, it's not text and it's not a designer file.. let's see what else we can come up with..
486
487 KParts::Factory *factory = 0;
488 TQString className;
489
490 TQString services[] = { "KParts/ReadWritePart", "KParts/ReadOnlyPart" };
491 TQString classnames[] = { "KParts::ReadWritePart", "KParts::ReadOnlyPart" };
492 for (uint i=0; i<2; ++i)
493 {
494 factory = findPartFactory( MimeType->name(), services[i] );
495 if (factory)
496 {
497 className = classnames[i];
498 break;
499 }
500 }
501
502 kdDebug(9000) << "factory = " << factory << endl;
503
504 if (factory)
505 {
506 // create the object of the desired class
507 KParts::ReadOnlyPart *part = static_cast<KParts::ReadOnlyPart*>( factory->createPart( TopLevel::getInstance()->main(), 0, 0, 0, className.latin1() ) );
508 if ( part )
509 {
510 part->openURL( url );
511 addHistoryEntry();
512
513 if ( dynamic_cast<KTextEditor::Editor*>( part ) ) // we can have ended up with a texteditor, in which case need to treat it as such
514 {
515 integratePart(part, url, part->widget(), true, activate);
516 EditorProxy::getInstance()->setLineNumber(part, lineNum, col);
517 }
518 else
519 {
520 integratePart( part, url );
521 }
522
523 m_openRecentAction->addURL( url );
524 m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );
525 }
526 }
527 else
528 {
529 MimeWarningDialog dlg;
530 dlg.text2->setText( TQString( "<qt><b>%1</b></qt>" ).arg(url.path()));
531 dlg.text3->setText( dlg.text3->text().arg(MimeType->name()) );
532
533 if ( dlg.exec() == TQDialog::Accepted )
534 {
535 if ( dlg.open_with_kde->isChecked() )
536 {
537 KRun::runURL(url, MimeType->name() );
538 }
539 else
540 {
541 if ( dlg.always_open_as_text->isChecked() )
542 {
543 TDEConfig *config = kapp->config();
544 config->setGroup("General");
545 TQStringList texttypeslist = config->readListEntry( "TextTypes" );
546 texttypeslist << MimeType->name();
547 config->writeEntry( "TextTypes", texttypeslist );
548 }
549 m_openNextAsText = true;
550 editDocument( url, lineNum, col );
551 }
552 }
553 }
554}
555
556
557void PartController::showDocument(const KURL &url, bool newWin)
558{
559 TQString fixedPath = HTMLDocumentationPart::resolveEnvVarsInURL(url.url()); // possibly could env vars
560 KURL docUrl(fixedPath);
561 kdDebug(9000) << "SHOW: " << docUrl.url() << endl;
562
563 if ( docUrl.isLocalFile() && KMimeType::findByURL(docUrl)->name() != "text/html" ) {
564 // a link in a html-file pointed to a local text file - display
565 // it in the editor instead of a html-view to avoid uglyness
566 editDocument( docUrl );
567 return;
568 }
569
570 addHistoryEntry();
571
572 HTMLDocumentationPart *part = dynamic_cast<HTMLDocumentationPart*>(activePart());
573 if (!part || newWin)
574 {
575 part = new HTMLDocumentationPart;
576 integratePart(part,docUrl);
577 connect(part, TQ_SIGNAL(fileNameChanged(KParts::ReadOnlyPart* )),
578 this, TQ_SIGNAL(partURLChanged(KParts::ReadOnlyPart* )));
579 }
580 else
581 {
582 activatePart(part);
583 }
584 part->openURL(docUrl);
585}
586
587KParts::Factory *PartController::findPartFactory(const TQString &mimeType, const TQString &partType, const TQString &preferredName)
588{
589 TDETrader::OfferList offers = TDETrader::self()->query(mimeType, TQString("'%1' in ServiceTypes").arg(partType));
590
591 if (offers.count() > 0)
592 {
593 KService::Ptr ptr = 0;
594 // if there is a preferred plugin we'll take it
595 if ( !preferredName.isEmpty() ) {
596 TDETrader::OfferList::Iterator it;
597 for (it = offers.begin(); it != offers.end(); ++it) {
598 if ((*it)->desktopEntryName() == preferredName) {
599 ptr = (*it);
600 }
601 }
602 }
603 // else we just take the first in the list
604 if ( !ptr ) {
605 ptr = offers.first();
606 }
607 return static_cast<KParts::Factory*>(KLibLoader::self()->factory(TQFile::encodeName(ptr->library())));
608 }
609
610 return 0;
611}
612
613KTextEditor::Editor * PartController::createEditorPart( bool activate,
614 bool addToCurrentBuffer,
615 const KURL &url )
616{
617 MultiBuffer *multiBuffer = 0;
618 if ( addToCurrentBuffer )
619 {
620 multiBuffer =
621 dynamic_cast<MultiBuffer*>(
622 EditorProxy::getInstance()->topWidgetForPart( activePart() )
623 );
624 }
625 if ( !multiBuffer )
626 {
627 kdDebug(9000) << "Creating a new MultiBuffer for "
628 << url.fileName() << endl;
629 multiBuffer = new MultiBuffer( TopLevel::getInstance()->main() );
630 }
631
632 static bool alwaysActivate = true;
633
634 kapp->config()->setGroup("Editor");
635 TQString preferred = kapp->config()->readPathEntry("EmbeddedKTextEditor");
636 // If we are not using kyzis...
637 // Don't create non-wrapped views for now,
638 // avoid two paths (== two chances for bad bugs)
639 if ( preferred != "kyzispart" )
640 alwaysActivate = false;
641
642 KTextEditor::Editor *editorpart =
643 dynamic_cast<KTextEditor::Editor*>(multiBuffer->createPart( "text/plain",
644 "KTextEditor/Document",
645 alwaysActivate | activate ?
646 "KTextEditor::Editor" : "KTextEditor::Document",
647 preferred
648 ));
649
650 if ( url.isValid() )
651 editorpart->openURL( url );
652
653 multiBuffer->registerURL( url, editorpart );
654 multiBuffer->setDelayedActivation( !activate );
655 return editorpart;
656}
657
658void PartController::integratePart(KParts::Part *part, const KURL &url,
659 TQWidget* widget, bool isTextEditor,
660 bool activate, bool addToCurrentBuffer )
661{
662 if (!widget) widget = part->widget();
663
664 if (!widget) {
666 kdDebug(9000) << "no widget for this part!!" << endl;
667 return; // to avoid later crash
668 }
669
670 if ( !addToCurrentBuffer )
671 TopLevel::getInstance()->embedPartView(widget, url.fileName(), url.url());
672
673 addPart(part, activate);
674
675 // tell the parts we loaded a document
676 KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(part);
677 if ( !ro_part ) return;
678
679 emit loadedFile( ro_part->url() );
680
681 connect( part, TQ_SIGNAL(modifiedOnDisc(Kate::Document*, bool, unsigned char)), this, TQ_SLOT(slotDocumentDirty(Kate::Document*, bool, unsigned char)) );
682
683 // let's get notified when a document has been changed
684 connect(part, TQ_SIGNAL(completed()), this, TQ_SLOT(slotUploadFinished()));
685
686 // yes, we're cheating again. this signal exists for katepart's
687 // Document object and our HTMLDocumentationPart
688// connect(part, TQ_SIGNAL(fileNameChanged()), this, TQ_SLOT(slotFileNameChanged()));
689
690 // Connect to the document's views newStatus() signal in order to keep track of the
691 // modified-status of the document.
692
693 if (isTextEditor)
694 integrateTextEditorPart(static_cast<KTextEditor::Document*>(part));
695
696 KInterfaceDesigner::Designer *designerPart = dynamic_cast<KInterfaceDesigner::Designer *>(part);
697 if (designerPart && API::getInstance()->languageSupport())
698 {
699 kdDebug() << "integrating designer part with language support" << endl;
700 connect(designerPart, TQ_SIGNAL(addedFunction(DesignerType, const TQString&, Function )),
701 API::getInstance()->languageSupport(),
702 TQ_SLOT(addFunction(DesignerType, const TQString&, Function )));
703 connect(designerPart, TQ_SIGNAL(editedFunction(DesignerType, const TQString&, Function, Function )), API::getInstance()->languageSupport(),
704 TQ_SLOT(editFunction(DesignerType, const TQString&, Function, Function )));
705 connect(designerPart, TQ_SIGNAL(removedFunction(DesignerType, const TQString&, Function )),
706 API::getInstance()->languageSupport(),
707 TQ_SLOT(removeFunction(DesignerType, const TQString&, Function )));
708 connect(designerPart, TQ_SIGNAL(editFunction(DesignerType, const TQString&, const TQString& )),
709 API::getInstance()->languageSupport(),
710 TQ_SLOT(openFunction(DesignerType, const TQString&, const TQString& )));
711 connect(designerPart, TQ_SIGNAL(editSource(DesignerType, const TQString& )),
712 API::getInstance()->languageSupport(),
713 TQ_SLOT(openSource(DesignerType, const TQString& )));
714 connect(designerPart, TQ_SIGNAL(newStatus(const TQString &, int)),
715 this, TQ_SLOT(slotNewDesignerStatus(const TQString &, int)));
716 }
717}
718
719void PartController::integrateTextEditorPart(KTextEditor::Document* doc)
720{
721 // There's shortcut conflict between Kate and the debugger, resolve
722 // it here. Ideally, the should be some standard mechanism, configurable
723 // by config files.
724 // However, it does not exists and situation here some rare commands
725 // like "Dynamic word wrap" or "Show line numbers" from Kate take
726 // all possible shortcuts, leaving us with IDE that has no shortcuts for
727 // debugger, is very bad.
728 //
729 // We could try to handle this in debugger, but that would require
730 // the debugger to intercept all new KTextEditor::View creations, which is
731 // not possible.
732
733
734 if ( !doc ) return;
735
736 connect( doc, TQ_SIGNAL(textChanged()), this, TQ_SLOT(textChanged()) );
737 connect( doc, TQ_SIGNAL(fileNameChanged()),
738 this, TQ_SLOT(slotDocumentUrlChanged()));
739
740 if( doc->widget() )
741 {
742 connect( doc->widget(), TQ_SIGNAL(dropEventPass(TQDropEvent *)),
743 TopLevel::getInstance()->main(), TQ_SLOT(slotDropEvent(TQDropEvent *)) );
744 }
745
746 if ( KTextEditor::View * view = dynamic_cast<KTextEditor::View*>( doc->widget() ) )
747 {
748 TDEActionCollection* c = view->actionCollection();
749 // Be extra carefull, in case the part either don't provide those
750 // action, or uses different shortcuts.
751
752 if (TDEAction* a = c->action("view_folding_markers"))
753 {
754 if (a->shortcut() == TDEShortcut(Key_F9))
755 a->setShortcut(TDEShortcut());
756 }
757
758 if (TDEAction* a = c->action("view_dynamic_word_wrap"))
759 {
760 if (a->shortcut() == TDEShortcut(Key_F10))
761 a->setShortcut(TDEShortcut());
762 }
763
764 if (TDEAction* a = c->action("view_line_numbers"))
765 {
766 if (a->shortcut() == TDEShortcut(Key_F11))
767 a->setShortcut(TDEShortcut());
768 }
769 }
770
771 //EditorProxy::getInstance()->installPopup(doc, contextPopupMenu());
772
773 // What's potentially problematic is that this signal isn't officially part of the
774 // KTextEditor::View interface. It is nevertheless there, and used in kate and kwrite.
775 // There doesn't seem to be any othere way of making this work with katepart, and since
776 // signals are dynamic, if we try to connect to an editorpart that lacks this signal,
777 // all we get is a runtime warning. At this point in time we are only really supported
778 // by katepart anyway so IMHO this hack is justified. //teatime
779 TQPtrList<KTextEditor::View> list = doc->views();
780 TQPtrListIterator<KTextEditor::View> it( list );
781 while ( it.current() )
782 {
783 connect( it, TQ_SIGNAL( newStatus() ), this, TQ_SLOT( slotNewStatus() ) );
784 ++it;
785 }
786}
787
788void PartController::slotPartAdded( KParts::Part * part )
789{
790 kdDebug(9000) << k_funcinfo << endl;
791
792 if ( KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part ) )
793 {
794 updatePartURL( ro_part );
795 }
796
797 updateMenuItems();
798}
799
800void PartController::slotPartRemoved( KParts::Part * part )
801{
802 kdDebug(9000) << k_funcinfo << endl;
803
804 _partURLMap.remove( static_cast<KParts::ReadOnlyPart*>(part) );
805
806 if ( part == m_currentActivePart )
807 {
808 m_removingActivePart = true;
809 }
810
811 updateMenuItems();
812}
813
814void PartController::updatePartURL( KParts::ReadOnlyPart * ro_part )
815{
816 if ( ro_part->url().isEmpty() )
817 {
818 kdDebug(9000) << "updatePartURL() called with empty URL for part: " << ro_part << endl;
819 return;
820 }
821 _partURLMap[ ro_part ] = ro_part->url();
822}
823
824bool PartController::partURLHasChanged( KParts::ReadOnlyPart * ro_part )
825{
826 if ( _partURLMap.contains( ro_part ) && !ro_part->url().isEmpty() )
827 {
828 if ( _partURLMap[ ro_part ] != ro_part->url() )
829 {
830 return true;
831 }
832 }
833 return false;
834}
835
836KURL PartController::storedURLForPart( KParts::ReadOnlyPart * ro_part )
837{
838 if ( _partURLMap.contains( ro_part ) )
839 {
840 return _partURLMap[ ro_part ];
841 }
842 return KURL();
843}
844
845
846void PartController::slotUploadFinished()
847{
848 KParts::ReadOnlyPart *ro_part = const_cast<KParts::ReadOnlyPart*>( dynamic_cast<const KParts::ReadOnlyPart*>(sender()) );
849 if ( !ro_part ) return;
850
851 if ( partURLHasChanged( ro_part ) )
852 {
853 emit partURLChanged( ro_part );
854 updatePartURL( ro_part );
855 }
856}
857
858KParts::ReadOnlyPart *PartController::partForURL(const KURL &url)
859{
860 TQPtrListIterator<KParts::Part> it(*parts());
861 for ( ; it.current(); ++it)
862 {
863 KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(it.current());
864 if (ro_part && url.path() == ro_part->url().path())
865 return ro_part;
866 }
867 return 0;
868}
869
870KParts::Part * PartController::partForWidget( const TQWidget * widget )
871{
872 TQPtrListIterator<KParts::Part> it(*parts());
873 for ( ; it.current(); ++it)
874 {
875 if ( it.current()->widget() == widget )
876 {
877 return *it;
878 }
879 }
880 return 0;
881}
882
883
884void PartController::activatePart(KParts::Part *part)
885{
886 if ( !part ) return;
887
888 TQWidget * widget = EditorProxy::getInstance()->widgetForPart( part );
889 if (widget)
890 {
891 TopLevel::getInstance()->raiseView( widget );
892 widget->show();
893 widget->setFocus();
894 }
895
896 setActivePart(part);
897
898 TQWidget* w2 = EditorProxy::getInstance()->widgetForPart( part );
899 if (w2 != widget)
900 w2->setFocus();
901}
902
903bool PartController::closePart(KParts::Part *part)
904{
905 KParts::ReadOnlyPart * ro_part =
906 dynamic_cast<KParts::ReadOnlyPart*>( part );
907
908 if ( !ro_part ) return true;
909
910 KURL url = ro_part->url();
911
912 if (TQWidget* w = EditorProxy::getInstance()->topWidgetForPart( part ) )
913 {
914 if ( MultiBuffer *multiBuffer = dynamic_cast<MultiBuffer*>( w ) )
915 {
916 kdDebug(9000) << "About to delete MultiBuffered document..."
917 << " numberOfBuffers: " << multiBuffer->numberOfBuffers()
918 << " isActivated: " << multiBuffer->isActivated()
919 << endl;
920 if ( !multiBuffer->closeURL( url ) )
921 return false;
922
923 if ( multiBuffer->numberOfBuffers() == 0
924 || !multiBuffer->isActivated() )
925 {
926 TopLevel::getInstance()->removeView( w );
927 _dirtyDocuments.remove( static_cast<KParts::ReadWritePart*>( ro_part ) );
928 emit closedFile( url );
929/* kdDebug(9000) << "Deleting MultiBuffer Part" << endl;*/
930 TopLevel::getInstance()->main()->guiFactory()->removeClient( part );
931 delete part;
932/* kdDebug(9000) << "DeleteLater Actual MultiBuffer" << endl;*/
933 multiBuffer->deleteLater();
934 return true;
935 }
936 else
937 {
938/* kdDebug(9000) << "Deleting MultiBuffer Part" << endl;*/
939 _dirtyDocuments.remove( static_cast<KParts::ReadWritePart*>( ro_part ) );
940 TopLevel::getInstance()->main()->guiFactory()->removeClient( part );
941 emit closedFile( url );
942 delete part;
943 // Switch to a remaining buffer
944 setActivePart( multiBuffer->activeBuffer() );
945 return true;
946 }
947 }
948 else if ( !ro_part->closeURL() )
949 return false;
950 TopLevel::getInstance()->removeView( w );
951 }
952 else if ( !ro_part->closeURL() )
953 return false;
954 TopLevel::getInstance()->main()->guiFactory()->removeClient( part );
955
956 _dirtyDocuments.remove( static_cast<KParts::ReadWritePart*>( ro_part ) );
957 emit closedFile( url );
958
959/* kdDebug(9000) << "Deleting Regular Part" << endl;*/
960 delete part;
961 return true;
962}
963
964
965void PartController::updateMenuItems()
966{
967 bool hasWriteParts = false;
968 bool hasReadOnlyParts = false;
969
970 TQPtrListIterator<KParts::Part> it(*parts());
971 for ( ; it.current(); ++it)
972 {
973 if (it.current()->inherits("KParts::ReadWritePart"))
974 hasWriteParts = true;
975 if (it.current()->inherits("KParts::ReadOnlyPart"))
976 hasReadOnlyParts = true;
977 }
978
979 m_saveAllFilesAction->setEnabled(hasWriteParts);
980 m_revertAllFilesAction->setEnabled(hasWriteParts);
981 m_closeWindowAction->setEnabled(hasReadOnlyParts);
982 m_closeAllWindowsAction->setEnabled(hasReadOnlyParts);
983 m_closeOtherWindowsAction->setEnabled(hasReadOnlyParts);
984
985 m_backAction->setEnabled( !m_backHistory.isEmpty() );
986}
987
988void PartController::slotRevertAllFiles()
989{
990 revertAllFiles();
991}
992
993void PartController::reloadFile( const KURL & url )
994{
995 KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
996 if ( part )
997 {
998 if ( part->isModified() )
999 {
1000 if ( KMessageBox::warningYesNo( TopLevel::getInstance()->main(),
1001 i18n( "The file \"%1\" is modified in memory. Are you sure you want to reload it? (Local changes will be lost.)" ).arg( url.path() ),
1002 i18n( "File is Modified" ), i18n("Reload"), i18n("Do Not Reload") ) == KMessageBox::Yes )
1003 {
1004 part->setModified( false );
1005 }
1006 else
1007 {
1008 return;
1009 }
1010 }
1011
1012 unsigned int line = 0; unsigned int col = 0;
1013 KTextEditor::ViewCursorInterface * iface = dynamic_cast<KTextEditor::ViewCursorInterface*>( part->widget() );
1014 if (iface)
1015 {
1016 iface->cursorPositionReal( &line, &col );
1017 }
1018
1019 part->openURL( url );
1020
1021 _dirtyDocuments.remove( part );
1022 emit documentChangedState( url, Clean );
1023
1024 if ( iface )
1025 {
1026 iface->setCursorPositionReal( line, col );
1027 }
1028 }
1029}
1030
1031void PartController::revertFiles( const KURL::List & list )
1032{
1033 KURL::List::ConstIterator it = list.begin();
1034 while ( it != list.end() )
1035 {
1036 reloadFile( *it );
1037 ++it;
1038 }
1039}
1040
1041void PartController::revertAllFiles()
1042{
1043 revertFiles( openURLs() );
1044}
1045
1046void PartController::slotCloseWindow()
1047{
1048 closePart( activePart() );
1049}
1050
1051KURL::List PartController::modifiedDocuments()
1052{
1053 KURL::List modFiles;
1054
1055 TQPtrListIterator<KParts::Part> it( *parts() );
1056 while( it.current() )
1057 {
1058 KParts::ReadWritePart *rw_part = dynamic_cast<KParts::ReadWritePart*>(it.current());
1059 if ( rw_part && rw_part->isModified() )
1060 {
1061 modFiles << rw_part->url();
1062 }
1063 ++it;
1064 }
1065 return modFiles;
1066}
1067
1068void PartController::slotSave()
1069{
1070 kdDebug(9000) << k_funcinfo << endl;
1071
1072 if ( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( activePart() ) )
1073 {
1074 saveFile( part->url() );
1075 }
1076}
1077
1078void PartController::slotReload()
1079{
1080 kdDebug(9000) << k_funcinfo << endl;
1081
1082 if ( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( activePart() ) )
1083 {
1084 reloadFile( part->url() );
1085 }
1086}
1087
1088void PartController::slotSaveAllFiles()
1089{
1090 saveAllFiles();
1091}
1092
1093bool PartController::saveFile( const KURL & url, bool force )
1094{
1095 KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
1096 if ( !part ) return true;
1097
1098 switch( documentState( url ) )
1099 {
1100 case Clean:
1101 if ( !force )
1102 {
1103 return true;
1104 }
1105 kdDebug(9000) << "Forced save" << endl;
1106 break;
1107
1108 case Modified:
1109 kdDebug(9000) << "Normal save" << endl;
1110 break;
1111
1112 case Dirty:
1113 case DirtyAndModified:
1114 {
1115 int code = KMessageBox::warningYesNoCancel( TopLevel::getInstance()->main(),
1116 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() ),
1117 i18n("File Externally Modified"), i18n("Overwrite"), i18n("Do Not Overwrite") );
1118 if ( code == KMessageBox::Yes )
1119 {
1120 kdDebug(9000) << "Dirty save!!" << endl;
1121 }
1122 else if ( code == KMessageBox::No )
1123 {
1124 return true;
1125 }
1126 else
1127 {
1128 return false; // a 'false' return means to interrupt the process that caused the save
1129 }
1130 }
1131 break;
1132
1133 default:
1134 ;
1135 }
1136
1137 if ( part->save() )
1138 {
1139 _dirtyDocuments.remove( part );
1140 emit documentChangedState( url, Clean );
1141 emit savedFile( url );
1142 }
1143
1144 return true;
1145}
1146
1147bool PartController::saveAllFiles()
1148{
1149 return saveFiles( openURLs() );
1150}
1151
1152bool PartController::saveFiles( KURL::List const & filelist )
1153{
1154 KURL::List::ConstIterator it = filelist.begin();
1155 while ( it != filelist.end() )
1156 {
1157 if (saveFile( *it )==false)
1158 return false; //user cancelled
1159 ++it;
1160 }
1161 return true;
1162}
1163
1164bool PartController::querySaveFiles()
1165{
1166 return saveFilesDialog( KURL::List() );
1167}
1168
1169void PartController::clearModified( KURL::List const & filelist )
1170{
1171 KURL::List::ConstIterator it = filelist.begin();
1172 while ( it != filelist.end() )
1173 {
1174 KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( *it ) );
1175 if ( rw_part )
1176 {
1177 rw_part->setModified( false );
1178 }
1179 ++it;
1180 }
1181}
1182
1183bool PartController::saveFilesDialog( KURL::List const & ignoreList )
1184{
1185 KURL::List modList = modifiedDocuments();
1186
1187 if ( modList.count() > 0 && modList != ignoreList )
1188 {
1189 KSaveSelectDialog dlg( modList, ignoreList, TopLevel::getInstance()->main() );
1190 if ( dlg.exec() == TQDialog::Accepted )
1191 {
1192 saveFiles( dlg.filesToSave() );
1193 clearModified( dlg.filesNotToSave() );
1194 }
1195 else
1196 {
1197 return false;
1198 }
1199 }
1200 return true;
1201}
1202
1203bool PartController::closeFilesDialog( KURL::List const & ignoreList )
1204{
1205 if ( !saveFilesDialog( ignoreList ) ) return false;
1206
1207 TQPtrList<KParts::Part> partList( *parts() );
1208 TQPtrListIterator<KParts::Part> it( partList );
1209 while ( KParts::Part* part = it.current() )
1210 {
1211 KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part );
1212 if ( ro_part && !ignoreList.contains( ro_part->url() ) || !ro_part )
1213 {
1214 closePart( part );
1215 }
1216 ++it;
1217 }
1218 return true;
1219}
1220
1221bool PartController::closeFiles( const KURL::List & list )
1222{
1223 KURL::List::ConstIterator it = list.begin();
1224 while ( it != list.end() )
1225 {
1226 if ( !closePart( partForURL( *it ) ) )
1227 {
1228 return false;
1229 }
1230 ++it;
1231 }
1232 return true;
1233}
1234
1235bool PartController::closeFile( const KURL & url )
1236{
1237 return closePart( partForURL( url ) );
1238}
1239
1240bool PartController::closeAllFiles()
1241{
1242 return closeFilesDialog( KURL::List() );
1243}
1244
1245void PartController::slotCloseAllWindows()
1246{
1247 closeAllFiles();
1248}
1249
1250bool PartController::closeAllOthers( const KURL & url )
1251{
1252 KURL::List ignoreList;
1253 ignoreList.append( url );
1254
1255 return closeFilesDialog( ignoreList );
1256}
1257
1258
1259void PartController::slotCloseOtherWindows()
1260{
1261 if ( KParts::ReadOnlyPart * active = dynamic_cast<KParts::ReadOnlyPart*>( activePart() ) )
1262 {
1263 closeAllOthers( active->url() );
1264 }
1265}
1266
1267void PartController::slotOpenFile()
1268{
1269 TQString DefaultEncoding;
1270 if ( TQDomDocument * projectDom = API::getInstance()->projectDom() )
1271 {
1272 DefaultEncoding = DomUtil::readEntry( *projectDom, "/general/defaultencoding", TQString() );
1273 }
1274
1275 if ( DefaultEncoding.isEmpty() )
1276 {
1277 // have a peek at katepart's settings:
1278 TDEConfig * config = kapp->config();
1279 config->setGroup("Kate Document Defaults");
1280 DefaultEncoding = config->readEntry("Encoding", TQString() );
1281 }
1282
1283 KEncodingFileDialog::Result result = KEncodingFileDialog::getOpenURLsAndEncoding( DefaultEncoding, TQString(),
1284 TQString(), TopLevel::getInstance()->main(), TQString() );
1285
1286 for ( KURL::List::Iterator it = result.URLs.begin(); it != result.URLs.end(); ++it )
1287 {
1288 m_presetEncoding = result.encoding;
1289 editDocument( *it );
1290 }
1291}
1292
1293void PartController::slotOpenRecent( const KURL& url )
1294{
1295 editDocument( url );
1296 // stupid bugfix - don't allow an active item in the list
1297 m_openRecentAction->setCurrentItem( -1 );
1298}
1299
1300bool PartController::readyToClose()
1301{
1302 blockSignals( true );
1303
1304 closeAllFiles(); // this should never return false, as the files are already saved
1305
1306 return true;
1307}
1308
1309void PartController::slotActivePartChanged( KParts::Part *part )
1310{
1311 kdDebug(9000) << k_funcinfo << part << endl;
1312
1313 if ( !m_isJumping && !m_removingActivePart )
1314 {
1315 if ( KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>(m_currentActivePart) )
1316 {
1317 addHistoryEntry( ro_part );
1318 }
1319 }
1320
1321 m_currentActivePart = part;
1322 m_removingActivePart = false;
1323
1324 if (part) {
1325 KXMLGUIClient* client = dynamic_cast<KXMLGUIClient*>(part->widget());
1326 if (client) Core::setupShourtcutTips(client);
1327 }
1328
1329 updateMenuItems();
1330 TQTimer::singleShot( 100, this, TQ_SLOT(slotWaitForFactoryHack()) );
1331}
1332
1333void PartController::showPart( KParts::Part* part, const TQString& name, const TQString& shortDescription )
1334{
1335 if (!part->widget()) {
1337 return; // to avoid later crash
1338 }
1339
1340 TQPtrListIterator<KParts::Part> it(*parts());
1341 for ( ; it.current(); ++it)
1342 {
1343 if( it.current() == part ){
1344 // part already embedded
1345 activatePart( it.current() );
1346 return;
1347 }
1348 }
1349
1350 // embed the part
1351 TopLevel::getInstance()->embedPartView( part->widget(), name, shortDescription );
1352 addPart( part );
1353}
1354
1355void PartController::slotDocumentDirty( Kate::Document * d, bool isModified, unsigned char reason )
1356{
1357 kdDebug(9000) << k_funcinfo << endl;
1358
1359 // KTextEditor::Document * doc = reinterpret_cast<KTextEditor::Document*>( d ); // theoretically unsafe in MI scenario
1360 KTextEditor::Document * doc = 0;
1361
1362 TQPtrListIterator<KParts::Part> it( *parts() );
1363 while( it.current() )
1364 {
1365 if ( (void*)it.current() == (void*)d )
1366 {
1367 doc = dynamic_cast<KTextEditor::Document*>( it.current() );
1368 break;
1369 }
1370 ++it;
1371 }
1372
1373 // this is a bit strange, but in order to avoid weird crashes
1374 // down in KDirWatcher, we want to step off the call chain before
1375 // opening any messageboxes
1376 if ( doc )
1377 {
1378 ModificationData * p = new ModificationData;
1379 p->doc = doc;
1380 p->isModified = isModified;
1381 p->reason = reason;
1382 KDevJobTimer::singleShot( 0, this, TQ_SLOT(slotDocumentDirtyStepTwo(void*)), p );
1383 }
1384}
1385
1386void PartController::slotDocumentDirtyStepTwo( void * payload )
1387{
1388 if ( !payload ) return;
1389
1390 ModificationData * p = reinterpret_cast<ModificationData*>( payload );
1391 KTextEditor::Document * doc = p->doc;
1392
1393 // let's make sure the document is still loaded
1394 bool haveDocument = false;
1395 if( const TQPtrList<KParts::Part> * partlist = parts() )
1396 {
1397 TQPtrListIterator<KParts::Part> it( *partlist );
1398 while ( KParts::Part * part = it.current() )
1399 {
1400 if ( p->doc == dynamic_cast<KTextEditor::Document*>( part ) )
1401 {
1402 haveDocument = true;
1403 break;
1404 }
1405 ++it;
1406 }
1407 }
1408 if ( !haveDocument ) return;
1409
1410 bool isModified = p->isModified;
1411 unsigned char reason = p->reason;
1412 delete p;
1413
1414 KURL url = storedURLForPart( doc );
1415 if ( url.isEmpty() )
1416 {
1417 kdDebug(9000) << "Warning!! the stored url is empty. Bailing out!" << endl;
1418 }
1419
1420 if ( reason > 0 )
1421 {
1422 if ( !_dirtyDocuments.contains( doc ) )
1423 {
1424 _dirtyDocuments.append( doc );
1425 }
1426
1427 if ( reactToDirty( url, reason ) )
1428 {
1429 // file has been reloaded
1430 emit documentChangedState( url, Clean );
1431 _dirtyDocuments.remove( doc );
1432 }
1433 else
1434 {
1435 doEmitState( url );
1436 }
1437 }
1438 else
1439 {
1440 _dirtyDocuments.remove( doc );
1441 emit documentChangedState( url, Clean );
1442 }
1443
1444 kdDebug(9000) << doc->url().url() << endl;
1445 kdDebug(9000) << isModified << endl;
1446 kdDebug(9000) << reason << endl;
1447}
1448
1449bool PartController::isDirty( KURL const & url )
1450{
1451 return _dirtyDocuments.contains( static_cast<KTextEditor::Document*>( partForURL( url ) ) );
1452}
1453
1454bool PartController::reactToDirty( KURL const & url, unsigned char reason )
1455{
1456 TDEConfig *config = kapp->config();
1457 config->setGroup("Editor");
1458 TQString dirtyAction = config->readEntry( "DirtyAction" );
1459
1460 if ( dirtyAction == "nothing" ) return false;
1461
1462 bool isModified = true;
1463 if( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) ) )
1464 {
1465 isModified = part->isModified();
1466 }
1467 else
1468 {
1469 kdDebug(9000) << k_funcinfo << " Warning. Not a ReadWritePart." << endl;
1470 return false;
1471 }
1472
1473 if ( isModified )
1474 {
1475 KMessageBox::sorry( TopLevel::getInstance()->main(),
1476 i18n("Conflict: The file \"%1\" has changed on disk while being modified in memory.\n\n"
1477 "You should investigate before saving to make sure you are not losing data.").arg( url.path() ),
1478 i18n("Conflict") );
1479 return false;
1480 }
1481
1482 if ( reason == 3 ) // means the file was deleted
1483 {
1484 KMessageBox::sorry( TopLevel::getInstance()->main(),
1485 i18n("Warning: The file \"%1\" has been deleted on disk.\n\n"
1486 "If this was not your intention, make sure to save this file now.").arg( url.path() ),
1487 i18n("File Deleted") );
1488 return false;
1489 }
1490
1491 if ( dirtyAction == "alert" )
1492 {
1493 if ( KMessageBox::warningYesNo( TopLevel::getInstance()->main(),
1494 i18n("The file \"%1\" has changed on disk.\n\nDo you want to reload it?").arg( url.path() ),
1495 i18n("File Changed"), i18n("Reload"), i18n("Do Not Reload") ) == KMessageBox::No )
1496 {
1497 return false;
1498 }
1499 }
1500
1501 // here we either answered yes above or are in autoreload mode
1502 reloadFile( url );
1503
1504 return true;
1505}
1506
1507void PartController::slotNewDesignerStatus(const TQString &formName, int status)
1508{
1509 kdDebug(9000) << k_funcinfo << endl;
1510 kdDebug(9000) << " formName: " << formName << ", status: " << status << endl;
1511 emit documentChangedState( KURL::fromPathOrURL(formName), DocumentState(status) );
1512}
1513
1514void PartController::slotNewStatus( )
1515{
1516 kdDebug(9000) << k_funcinfo << endl;
1517
1518 TQObject * senderobj = const_cast<TQObject*>(sender());
1519 KTextEditor::View * view = dynamic_cast<KTextEditor::View*>( senderobj );
1520 if ( view )
1521 {
1522 doEmitState( view->document()->url() );
1523 }
1524}
1525
1526DocumentState PartController::documentState( KURL const & url )
1527{
1528 KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
1529 if ( !rw_part ) return Clean;
1530
1531 DocumentState state = Clean;
1532 if ( rw_part->isModified() )
1533 {
1534 state = Modified;
1535 }
1536
1537 if ( isDirty( url ) )
1538 {
1539 if ( state == Modified )
1540 {
1541 state = DirtyAndModified;
1542 }
1543 else
1544 {
1545 state = Dirty;
1546 }
1547 }
1548
1549 return state;
1550}
1551
1552void PartController::doEmitState( KURL const & url )
1553{
1554 emit documentChangedState( url, documentState( url ) );
1555}
1556
1557KURL::List PartController::openURLs( )
1558{
1559 KURL::List list;
1560 TQPtrListIterator<KParts::Part> it(*parts());
1561 for ( ; it.current(); ++it)
1562 {
1563 if ( KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(it.current()) )
1564 {
1565 list << ro_part->url();
1566 }
1567 }
1568 return list;
1569}
1570
1572
1573//BEGIN History methods
1574
1575PartController::HistoryEntry::HistoryEntry( const KURL & u, int l, int c)
1576 : url(u), line(l), col(c)
1577{
1578 id = abs( TQTime::currentTime().msecsTo( TQTime() ) ); // should provide a reasonably unique number
1579}
1580
1581void PartController::slotBack()
1582{
1583 HistoryEntry thatEntry = m_backHistory.front();
1584 m_backHistory.pop_front();
1585 m_backAction->setEnabled( !m_backHistory.isEmpty() );
1586
1587 HistoryEntry thisEntry = createHistoryEntry();
1588 if ( !thisEntry.url.isEmpty() )
1589 {
1590 m_forwardHistory.push_front( thisEntry );
1591 m_forwardAction->setEnabled( true );
1592 }
1593
1594 jumpTo( thatEntry );
1595}
1596
1597void PartController::slotForward()
1598{
1599 HistoryEntry thatEntry = m_forwardHistory.front();
1600 m_forwardHistory.pop_front();
1601 m_forwardAction->setEnabled( !m_forwardHistory.isEmpty() );
1602
1603 HistoryEntry thisEntry = createHistoryEntry();
1604 if ( !thisEntry.url.isEmpty() )
1605 {
1606 m_backHistory.push_front( thisEntry );
1607 m_backAction->setEnabled( true );
1608 }
1609
1610 jumpTo( thatEntry );
1611}
1612
1613void PartController::slotBackAboutToShow()
1614{
1615 TDEPopupMenu *popup = m_backAction->popupMenu();
1616 popup->clear();
1617
1618 if ( m_backHistory.isEmpty()) return;
1619
1620 int i = 0;
1621 TQValueList<HistoryEntry>::ConstIterator it = m_backHistory.begin();
1622 while( i < 10 && it != m_backHistory.end() )
1623 {
1624 popup->insertItem( (*it).url.fileName() + TQString(" (%1)").arg( (*it).line +1), (*it).id );
1625 ++i;
1626 ++it;
1627 }
1628}
1629
1630void PartController::slotForwardAboutToShow( )
1631{
1632 TDEPopupMenu * popup = m_forwardAction->popupMenu();
1633 popup->clear();
1634
1635 if ( m_forwardHistory.isEmpty() ) return;
1636
1637 int i = 0;
1638 TQValueList<HistoryEntry>::ConstIterator it = m_forwardHistory.begin();
1639 while( i < 10 && it != m_forwardHistory.end() )
1640 {
1641 popup->insertItem( (*it).url.fileName() + TQString(" (%1)").arg( (*it).line +1), (*it).id );
1642 ++i;
1643 ++it;
1644 }
1645}
1646
1647void PartController::slotBackPopupActivated( int id )
1648{
1649 TQValueList<HistoryEntry>::Iterator it = m_backHistory.begin();
1650 while( it != m_backHistory.end() )
1651 {
1652 if ( (*it).id == id )
1653 {
1654 HistoryEntry entry = *it;
1655 m_backHistory.erase( m_backHistory.begin(), ++it );
1656 m_backAction->setEnabled( !m_backHistory.isEmpty() );
1657
1658 HistoryEntry thisEntry = createHistoryEntry();
1659 if ( !thisEntry.url.isEmpty() )
1660 {
1661 m_forwardHistory.push_front( thisEntry );
1662 m_forwardAction->setEnabled( true );
1663 }
1664
1665 jumpTo( entry );
1666 return;
1667 }
1668 ++it;
1669 }
1670}
1671
1672void PartController::slotForwardPopupActivated( int id )
1673{
1674 TQValueList<HistoryEntry>::Iterator it = m_forwardHistory.begin();
1675 while( it != m_forwardHistory.end() )
1676 {
1677 if ( (*it).id == id )
1678 {
1679 HistoryEntry entry = *it;
1680 m_forwardHistory.erase( m_forwardHistory.begin(), ++it );
1681 m_forwardAction->setEnabled( !m_forwardHistory.isEmpty() );
1682
1683 HistoryEntry thisEntry = createHistoryEntry();
1684 if ( !thisEntry.url.isEmpty() )
1685 {
1686 m_backHistory.push_front( thisEntry );
1687 m_backAction->setEnabled( true );
1688 }
1689
1690 jumpTo( entry );
1691 return;
1692 }
1693 ++it;
1694 }
1695}
1696
1697void PartController::jumpTo( const HistoryEntry & entry )
1698{
1699 m_isJumping = true;
1700 editDocument( entry.url, entry.line, entry.col );
1701 m_isJumping = false;
1702}
1703
1704
1705PartController::HistoryEntry PartController::createHistoryEntry( KParts::ReadOnlyPart * ro_part ) {
1706 if( ro_part == 0 )
1707 ro_part = dynamic_cast<KParts::ReadOnlyPart*>( activePart() );
1708
1709 if ( !ro_part ) return HistoryEntry();
1710 KTextEditor::ViewCursorInterface * cursorIface = dynamic_cast<KTextEditor::ViewCursorInterface*>( ro_part->widget() );
1711 if ( !cursorIface ) return HistoryEntry();
1712
1713 uint line = 0;
1714 uint col = 0;
1715 cursorIface->cursorPositionReal( &line, &col );
1716
1717 return HistoryEntry( ro_part->url(), line, col );
1718}
1719
1720
1721// this should be called _before_ a jump is made
1722void PartController::addHistoryEntry( KParts::ReadOnlyPart * part )
1723{
1724 if ( m_isJumping ) return;
1725
1726 HistoryEntry thisEntry = createHistoryEntry( part );
1727 if ( !thisEntry.url.isEmpty() )
1728 {
1729 HistoryEntry lastEntry = m_backHistory.front();
1730 if ( (lastEntry.url.path() != thisEntry.url.path()) || (lastEntry.line != thisEntry.line) )
1731 {
1732 m_backHistory.push_front( thisEntry );
1733 m_backAction->setEnabled( true );
1734
1735 m_forwardHistory.clear();
1736 m_forwardAction->setEnabled( false );
1737 }
1738 else
1739 {
1740 kdDebug(9000) << "** avoiding to create duplicate history entry **" << endl;
1741 }
1742 }
1743}
1744
1745//END History methods
1746
1747void PartController::slotWaitForFactoryHack( )
1748{
1749 //kdDebug(9000) << k_funcinfo << endl;
1750
1751 if ( !activePart() ) return;
1752
1753 if ( dynamic_cast<KTextEditor::View*>( activePart()->widget() ) )
1754 {
1755 if ( !activePart()->factory() )
1756 {
1757 TQTimer::singleShot( 100, this, TQ_SLOT(slotWaitForFactoryHack()) );
1758 return;
1759 }
1760 else
1761 {
1762 EditorProxy::getInstance()->installPopup( activePart() );
1763 }
1764 }
1765
1766 if ( MultiBuffer *multiBuffer =
1767 dynamic_cast<MultiBuffer*>(
1768 EditorProxy::getInstance()->topWidgetForPart( activePart() ) )
1769 )
1770 {
1771 KURL url = dynamic_cast<KParts::ReadOnlyPart*>( activePart() )->url();
1772 multiBuffer->activePartChanged( url );
1773 // Really unfortunate, but the mainWindow relies upon this
1774 // to set the tab's icon
1775 emit documentChangedState( url, documentState( url ) );
1776 }
1777}
1778
1779KParts::ReadOnlyPart *PartController::qtDesignerPart()
1780{
1781 TQPtrListIterator<KParts::Part> it(*parts());
1782 for ( ; it.current(); ++it)
1783 {
1784 KInterfaceDesigner::Designer *des = dynamic_cast<KInterfaceDesigner::Designer*>(it.current());
1785 if (des && des->designerType() == KInterfaceDesigner::TQtDesigner)
1786 return des;
1787 }
1788 return 0;
1789}
1790
1791KTextEditor::Editor *PartController::openTextDocument( bool activate )
1792{
1793 KTextEditor::Editor *editorpart =
1794 createEditorPart( activate, false, KURL( i18n("unnamed") ) );
1795
1796 if ( editorpart )
1797 {
1798 if ( !m_presetEncoding.isNull() )
1799 {
1800 KParts::BrowserExtension * extension =
1801 KParts::BrowserExtension::childObject( editorpart );
1802 if ( extension )
1803 {
1804 KParts::URLArgs args;
1805 args.serviceType = TQString( "text/plain;" ) + m_presetEncoding;
1806 extension->setURLArgs(args);
1807 }
1808 m_presetEncoding = TQString();
1809 }
1810
1811 TQWidget * widget =
1812 EditorProxy::getInstance()->topWidgetForPart( editorpart );
1813
1814 addHistoryEntry();
1815 integratePart(editorpart, KURL(i18n("unnamed")), widget, true, true);
1816
1817 EditorProxy::getInstance()->setLineNumber(editorpart, 0, 0);
1818 }
1819
1820 return editorpart;
1821}
1822
1823void PartController::textChanged()
1824{
1825 if ( KTextEditor::Document * doc = dynamic_cast<KTextEditor::Document*>( activePart() ) )
1826 {
1827 if ( KTextEditor::ViewCursorInterface * vci = dynamic_cast<KTextEditor::ViewCursorInterface*>( doc->widget() ) )
1828 {
1829 m_gotoLastEditPosAction->setEnabled( true );
1830
1831 m_lastEditPos.url = doc->url();
1832 vci->cursorPosition( &m_lastEditPos.pos.first, &m_lastEditPos.pos.second );
1833 }
1834 }
1835}
1836
1837void PartController::gotoLastEditPos()
1838{
1839 editDocument( m_lastEditPos.url, m_lastEditPos.pos.first, m_lastEditPos.pos.second );
1840}
1841
1842void PartController::slotDocumentUrlChanged()
1843{
1844 TQObject *obj = const_cast<TQObject*>(sender());
1845 KTextEditor::Document *doc = dynamic_cast<KTextEditor::Document*>( obj );
1846 if (!doc)
1847 return;
1848
1849 MultiBuffer *multiBuffer = dynamic_cast<MultiBuffer*>(
1850 EditorProxy::getInstance()->findPartWidget( doc ));
1851 if (!multiBuffer)
1852 return;
1853
1854 multiBuffer->updateUrlForPart(doc, doc->url());
1855 updatePartURL(doc);
1856 emit partURLChanged(doc);
1857}
1858
1859#include "partcontroller.moc"
1860
Core::setupShourtcutTips
static void setupShourtcutTips(KXMLGUIClient *client=0)
Setup shourtcut tips.
Definition core.cpp:32
HTMLDocumentationPart
HTML documentation part.
Definition documentationpart.h:12
PartController
Part controler implementation.
Definition partcontroller.h:44
PartController::showPart
void showPart(KParts::Part *part, const TQString &name, const TQString &shortDescription)
Definition partcontroller.cpp:1333
TopLevel::getInstance
static KDevMainWindow * getInstance()
Get a pointer to the single KDevTopLevel object.
Definition toplevel.cpp:18

KDevelop Generic Shell

Skip menu "KDevelop Generic Shell"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

KDevelop Generic Shell

Skip menu "KDevelop Generic Shell"
  • buildtools
  •   lib
  •     base
  •     parsers
  •       autotools
  •       qmake
  •     widgets
  •   api
  • languages
  •   lib
  •     debugger
  •     designer_integration
  •     interfaces
  • lib
  •   catalog
  •   interfaces
  •     extensions
  •     external
  •     extras
  •   util
  •   widgets
  •     propeditor
  • parts
  •   documentation
  •     interfaces
  • src
  •   profileengine
  •     lib
Generated for KDevelop Generic Shell by doxygen 1.9.8
This website is maintained by Timothy Pearson.