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

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.8.13
This website is maintained by Timothy Pearson.