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