/***************************************************************************
 *   Copyright (C) 2005-2006 Nicolas Hadacek <hadacek@kde.org>             *
 *   Copyright (C) 2003 Alain Gibaud <alain.gibaud@free.fr>                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/
#include "project_manager.h"

#include <tqdragobject.h>
#include <tqpainter.h>
#include <tqstyle.h>
#include <tqtimer.h>
#include <tqheader.h>

#include <tdelocale.h>
#include <kiconloader.h>
#include <tdeaction.h>
#include <tdeapplication.h>

#include "project.h"
#include "project_wizard.h"
#include "project_editor.h"
#include "toplevel.h"
#include "editor_manager.h"
#include "object_view.h"
#include "devices/list/device_list.h"
#include "tools/list/compile_config.h"
#include "register_view.h"
#include "hex_editor.h"
#include "main_global.h"
#include "common/gui/purl_gui.h"
#include "device_gui.h"

//----------------------------------------------------------------------------
ProjectManager::View::View(TQWidget *parent)
  : ListView(parent, "project_manager"), _project(0), _modified(false)
{
  connect(this, TQ_SIGNAL(mouseButtonClicked(int, TQListViewItem *, const TQPoint &, int)),
          TQ_SLOT(clicked(int, TQListViewItem *, const TQPoint &, int)));
  connect(this, TQ_SIGNAL(contextMenuRequested(TQListViewItem *, const TQPoint &, int)),
          TQ_SLOT(contextMenu(TQListViewItem *, const TQPoint &, int)));
  connect(this, TQ_SIGNAL(itemRenamed(TQListViewItem *, int, const TQString &)),
          TQ_SLOT(renamed(TQListViewItem *, int, const TQString &)));
  connect(this, TQ_SIGNAL(moved()), TQ_SLOT(filesReordered()));

  header()->hide();
  setSorting(-1);
  addColumn(TQString(), 170);
  setFullWidth(true);
  setRootIsDecorated(false);
  setAcceptDrops(true);
  setDragEnabled(true);
  setDropVisualizer(true);
  TQTimer::singleShot(0, this, TQ_SLOT(init()));;
}

ProjectManager::View::~View()
{
  delete _project;
}

void ProjectManager::View::init()
{
  clear();
  _rootItem  = new RootItem(this);
  setCurrentItem(_rootItem);
  TDEListViewItem *item = new RegisterItem(headerItem(DeviceGroup));
  ensureItemVisible(item);
  _deviceItem = new DeviceItem(headerItem(DeviceGroup));
  ensureItemVisible(_deviceItem);
  _linkerScriptItem = 0;
  (void)headerItem(SourceGroup);
}

ProjectManager::HeaderItem *ProjectManager::View::findHeaderItem(Group group) const
{
  TQListViewItemIterator it(const_cast<View *>(this));
  for(; it.current(); ++it) {
    if ( it.current()->rtti()!=HeaderRtti ) continue;
    HeaderItem *hi = static_cast<HeaderItem *>(it.current());
    if ( hi->group()==group ) return hi;
  }
  return 0;
}

ProjectManager::HeaderItem *ProjectManager::View::headerItem(Group group)
{
  HeaderItem *item = findHeaderItem(group);
  if (item) return item;
  item = new HeaderItem(_rootItem, group);
  // reorder groups...
  HeaderItem *items[Nb_Groups];
  for (uint i=0; i<Nb_Groups; i++) {
    items[i] = findHeaderItem(Group(i));
    if (items[i]) _rootItem->takeItem(items[i]);
  }
  for (int i=Nb_Groups-1; i>=0; i--) {
    if ( items[i]==0 ) continue;
    _rootItem->insertItem(items[i]);
  }
  return item;
}

ProjectManager::FileItem *ProjectManager::View::findFileItem(const PURL::Url &url) const
{
  TQListViewItemIterator it(const_cast<View *>(this));
  for(; it.current(); ++it) {
    if ( it.current()->rtti()!=FileRtti ) continue;
    FileItem *fi = static_cast<FileItem *>(it.current());
    if ( fi->url()==url ) return fi;
  }
  return 0;
}

ProjectManager::FileItem *ProjectManager::View::findFileItem(PURL::FileType type) const
{
  TQListViewItemIterator it(const_cast<View *>(this));
  for(; it.current(); ++it) {
    if ( it.current()->rtti()!=FileRtti ) continue;
    FileItem *fi = static_cast<FileItem *>(it.current());
    if ( fi->ftype()==type ) return fi;
  }
  return 0;
}

TQListViewItem *ProjectManager::View::findItem(const TQString &tag) const
{
  TQListViewItemIterator it(const_cast<View *>(this));
  for(; it.current(); ++it) {
    switch (Rtti(it.current()->rtti())) {
      case RootRtti:
      case FileRtti:
      case LinkerScriptRtti:
      case DeviceRtti: continue;
      case HeaderRtti:
      case RegisterRtti: break;
    }
    if ( it.current()->text(0)==tag ) return it.current();
  }
  return 0;
}

void ProjectManager::View::select(const Editor *e)
{
  TQListViewItem *item = 0;
  if ( e->url().isEmpty() ) item = findItem(e->tag());
  else item = findFileItem(e->url());
  if (item) setSelected(item, true);
  else clearSelection();
}

void ProjectManager::View::contextMenu(TQListViewItem *item, const TQPoint &p, int)
{
  if ( item==0 ) return;

  PopupMenu pop;
  Group group = Nb_Groups;
  if ( item->rtti()==HeaderRtti) group = static_cast<const HeaderItem *>(item)->group();

  if ( item->rtti()==LinkerScriptRtti || group==LinkerScriptGroup ) {
    if ( _project==0 || Main::toolGroup().linkerScriptType()==PURL::Nb_FileTypes ) return;
    pop.insertTitle(i18n("Linker Script"));
    pop.insertItem("piklab_addfile", i18n("Set Custom..."));
    if ( !_project->customLinkerScript().isEmpty() ) pop.insertItem("edit-delete", i18n("Set Default"));
    switch( pop.exec(p) ) {
      case 1: {
        PURL::Url url = PURL::getOpenUrl(":custom_linker_script", PURL::filter(Main::toolGroup().linkerScriptType()), this, i18n("Select Linker Script"));
        if ( !url.isEmpty() ) _project->setCustomLinkerScript(url);
        break;
      }
      case 2: _project->setCustomLinkerScript(PURL::Url()); break;
    }
    _linkerScriptItem->init();
  } else if ( item->rtti()==RootRtti ) {
    RootItem *ri = static_cast<RootItem *>(item);
    if ( _project==0 ) {
      if ( ri->url().isEmpty() ) {
        pop.insertItem("piklab_createproject", i18n("New Project..."), &Main::toplevel(), TQ_SLOT(newProject()));
        pop.insertItem("piklab_openproject", i18n("Open Project..."), &Main::toplevel(), TQ_SLOT(openProject()));
        pop.exec(p);
      } else {
        pop.insertTitle(i18n("Standalone File"));
        pop.insertItem("configure", i18n("Configure..."));
        if ( pop.exec(p)==1 ) Main::toplevel().configure(ConfigCenter::Standalone);
      }
    } else {
      pop.insertTitle(i18n("Project"));
      pop.insertItem("configure", i18n("Options..."), &Main::toplevel(), TQ_SLOT(configureProject()));
      pop.insertItem("edit-find", i18n("Find Files..."),   &Main::toplevel(), TQ_SLOT(runKfind()));
      pop.insertSeparator();
      pop.insertItem("piklab_compile", i18n("Build Project"), &Main::toplevel(), TQ_SLOT(buildProject()));
      pop.insertItem("trashcan_empty", i18n("Clean Project"), &Main::toplevel(), TQ_SLOT(cleanBuild()));
      pop.insertSeparator();
      pop.insertItem("document-new", i18n("New Source File..."), &Main::toplevel(), TQ_SLOT(newSourceFile()));
      pop.insertItem("piklab_addfile", i18n("Add Source Files..."), this, TQ_SLOT(insertSourceFiles()));
      pop.insertItem("piklab_addfile", i18n("Add Object Files..."), this, TQ_SLOT(insertObjectFiles()));
      if ( Main::currentEditor() ) pop.insertItem("piklab_addcurrentfile", i18n("Add Current File"), this, TQ_SLOT(insertCurrentFile()));
      pop.exec(p);
    }
  } else if ( item->rtti()==FileRtti ) {
    if ( _project==0 ) return;
    FileItem *fi = static_cast<FileItem *>(item);
    if ( isExternalFile(fi->url()) ) return;
    pop.insertTitle(item->text(0));
    pop.insertItem("edit-delete", i18n("Remove From Project"));
    if ( pop.exec(p)==1 ) removeFile(fi->url());
  } else if ( item->rtti()==HeaderRtti ) {
    if ( _project==0 ) return;
    if ( group==LinkerObjectGroup ) {
      pop.insertTitle(i18n("Objects"));
      pop.insertItem("piklab_addfile", i18n("Add Object Files..."), this, TQ_SLOT(insertObjectFiles()));
      pop.exec(p);
    } else if ( group==SourceGroup || group==HeaderGroup ) {
      pop.insertTitle(i18n("Sources"));
      pop.insertItem("document-new", i18n("New File..."), &Main::toplevel(), TQ_SLOT(newSourceFile()));
      pop.insertItem("piklab_addfile", i18n("Add Source Files..."), this, TQ_SLOT(insertSourceFiles()));
      pop.exec(p);
    } else if ( group==DeviceGroup ) {
      pop.insertItem("document-new", i18n("Select Device..."), &Main::toplevel(), TQ_SLOT(showDeviceInfo()));
      pop.exec(p);
    }
  }
}

void ProjectManager::View::closeProject()
{
  if ( _project==0 && projectUrl().isEmpty() ) return;
  if (_project) {
    // save opened files
    PURL::UrlList files = Main::editorManager().files();
    PURL::UrlList::const_iterator it = files.begin();
    PURL::UrlList opened;
    for (; it!=files.end(); ++it) {
      if ( !isExternalFile(*it) ) opened += *it;
      Main::editorManager().closeEditor(*it);
    }
    _project->setOpenedFiles(opened);
    // save watched registers
    _project->setWatchedRegisters(Register::list().watched());
    TQString error;
    if ( !_project->save(error) )
      MessageBox::detailedSorry(i18n("Could not save project file \"%1\".").arg(_project->url().pretty()), error, Log::Show);
    delete _project;
    _project = 0;
  }
  _modified = false;
  init();
}

void ProjectManager::View::addExternalFiles()
{
  const TQMap<PURL::Url, FileOrigin> &ext = projectData().externals;
  TQMap<PURL::Url, FileOrigin>::const_iterator it;
  for (it=ext.begin(); it!=ext.end(); ++it) {
    if ( !it.key().exists() ) continue;
    addFile(it.key(), it.key().fileType(), it.data());
  }
}

void ProjectManager::View::setStandalone(const PURL::Url &url, PURL::FileType type)
{
  if ( projectUrl()==url ) return;
  closeProject();
  _rootItem->set(url, true);
  addFile(url, type, Intrinsic);
  _standaloneData[url].type = type;
  addExternalFiles();
}

PURL::Url ProjectManager::View::standaloneGenerator(const PURL::Url &url, PURL::FileType &type) const
{
  TQMap<PURL::Url, ProjectData>::const_iterator it;
  for (it=_standaloneData.begin(); it!=_standaloneData.end(); ++it) {
    if ( !it.data().externals.contains(url) ) continue;
    if ( !it.key().exists() ) continue;
    type = it.data().type;
    return it.key();
  }
  type = PURL::Nb_FileTypes;
  return url;
}

void ProjectManager::View::insertSourceFiles()
{
  Q_ASSERT(_project);
  PURL::UrlList list = PURL::getOpenUrls(":<sources>", PURL::sourceFilter(PURL::CompleteFilter), this, i18n("Select Source File"));
  if ( list.isEmpty() ) return;
  PURL::UrlList::const_iterator it;
  for (it=list.begin(); it!=list.end(); ++it) insertFile(*it);
}

void ProjectManager::View::insertObjectFiles()
{
  Q_ASSERT(_project);
  PURL::UrlList list = PURL::getOpenUrls(":<objects>", PURL::objectFilter(PURL::CompleteFilter), this, i18n("Select Object File"));
  if ( list.isEmpty() ) return;
  PURL::UrlList::const_iterator it;
  for (it=list.begin(); it!=list.end(); ++it) insertFile(*it);
}

void ProjectManager::View::insertFile(const PURL::Url &url)
{
  if ( !url.exists() ) {
    MessageBox::detailedSorry(i18n("Could not find file."), i18n("File: %1").arg(url.pretty()), Log::Show);
    return;
  }
  PURL::Url purl = url;
  MessageBox::Result copy = MessageBox::No;
  if ( !url.isInto(_project->directory()) ) {
    copy = MessageBox::questionYesNoCancel(i18n("File \"%1\" is not inside the project directory. Do you want to copy the file to your project directory?").arg(url.pretty()),
                                          i18n("Copy and Add"), i18n("Add only"));
    if ( copy==MessageBox::Cancel ) return;
    if ( copy==MessageBox::Yes ) purl = PURL::Url(_project->directory(), url.filename());
  }
  if ( _project->absoluteFiles().contains(purl) ) {
    MessageBox::detailedSorry(i18n("File is already in the project."), i18n("File: %1").arg(purl.pretty()), Log::Show);
    return;
  }
  if ( copy==MessageBox::Yes ) {
    Log::StringView sview;
    if ( !url.copyTo(purl, sview) ) {
      MessageBox::detailedSorry(i18n("Copying file to project directory failed."), i18n("File: %1\n").arg(url.pretty()) + sview.string(), Log::Show);
      return;
    }
  }
  _project->addFile(purl);
  addFile(purl, purl.fileType(), Intrinsic);
}

bool ProjectManager::View::openProject()
{
  PURL::Url url = PURL::getOpenUrl(":open_project", PURL::projectFilter(PURL::CompleteFilter), this, i18n("Select Project file"));
  return openProject(url);
}

void ProjectManager::View::addExternalFile(const PURL::Url &url, FileOrigin origin)
{
  Q_ASSERT( origin!=Intrinsic );
  addFile(url, url.fileType(), origin);
}

const ProjectManager::View::ProjectData &ProjectManager::View::projectData() const
{
  if ( _project==0 ) return _standaloneData[projectUrl()];
  return _projectData;
}

ProjectManager::View::ProjectData &ProjectManager::View::projectData()
{
  if ( _project==0 ) return _standaloneData[projectUrl()];
  return _projectData;
}

void ProjectManager::View::addFile(const PURL::Url &url, PURL::FileType type, FileOrigin origin)
{
  if ( contains(url) ) return;
  TQMap<PURL::Url, FileOrigin> &ext = projectData().externals;
  if ( type.data().group==PURL::LinkerScript && _linkerScriptItem ) {
    _linkerScriptItem->set(url);
    ext[url] = Included;
    return;
  }
  PURL::FileProperties properties = type.data().properties;
  Group grp = Nb_Groups;
  switch (origin) {
    case Intrinsic: grp = group(type); break;
    case Generated: grp = GeneratedGroup; break;
    case Included:  grp = IncludedGroup; break;
  }
  HeaderItem *hitem = headerItem(grp);
  TQListViewItem *item = new FileItem(hitem, type, url, origin!=Intrinsic);
  item->moveItem(hitem->lastChild());
  ensureItemVisible(item);
  if ( origin!=Intrinsic ) ext[url] = origin;
  if ( type==PURL::Hex && _project==0 ) {
    TQString extension = PURL::extension(PURL::AsmGPAsm);
    PURL::Url durl = PURL::Url::fromPathOrUrl("<" + (url.isEmpty() ? i18n("Disassembly") : url.appendExtension(extension).filename()) + ">");
    if ( findFileItem(durl)==0 ) {
      (void)new FileItem(headerItem(ViewGroup), PURL::Coff, durl, true);
      ext[durl] = Generated;
    }
  }
  if ( _project && origin==Intrinsic ) _modified = true;
}

void ProjectManager::View::removeExternalFiles()
{
  TQMap<PURL::Url, FileOrigin> &ext = projectData().externals;
  TQMap<PURL::Url, FileOrigin>::const_iterator it;
  for (it=ext.begin(); it!=ext.end(); ++it) {
    Main::editorManager().closeEditor(it.key());
    removeFile(it.key());
  }
  ext.clear();
  if (_linkerScriptItem) _linkerScriptItem->init();
}

void ProjectManager::View::removeFile(const PURL::Url &url)
{
  if ( _project && !isExternalFile(url) ) _project->removeFile(url);
  FileItem *item = findFileItem(url);
  if ( item==0 ) return;
  HeaderItem *group = static_cast<HeaderItem *>(item->parent());
  delete item;
  if ( group->childCount()==0 ) delete group;
  _modified = true;
  emit guiChanged();
}

void ProjectManager::View::clicked(int button, TQListViewItem *item, const TQPoint &, int)
{
  if ( item==0 ) return;
  if ( button!=TQt::LeftButton ) return;
  const Device::Data *data = Main::deviceData();
  Rtti rtti = Rtti(item->rtti());
  if ( data==0 && rtti!=DeviceRtti && rtti!=RootRtti ) {
    MessageBox::sorry(i18n("Cannot open without device specified."), Log::Show);
    return;
  }
  Editor *e = 0;
  ::PBusyCursor bc;
  switch (rtti) {
    case RootRtti:
      Main::toplevel().configureProject();
      break;
    case HeaderRtti: {
      if ( static_cast<HeaderItem *>(item)->group()!=DeviceGroup ) break;
      e = Main::editorManager().openEditor(EditorManager::DeviceEditor);
      break;
    }
    case RegisterRtti:
      e = Main::editorManager().openEditor(EditorManager::RegisterEditor);
      break;
    case DeviceRtti: return;
    case LinkerScriptRtti: {
      PURL::Url url = Main::projectManager().linkerScriptUrl();
      if ( url.isEmpty() ) break;
      e = Main::editorManager().findEditor(url);
      if ( e==0 ) {
        e = Main::editorManager().createEditor(url.fileType(), url);
        if ( !e->open(url) ) {
	  delete e;
	  break;
	}
        if ( e && isExternalFile(url) ) e->setReadOnly(true);
        Main::editorManager().addEditor(e);
      } else Main::editorManager().showEditor(e);
      break;
    }
    case FileRtti: {
      FileItem *fi = static_cast<FileItem *>(item);
      if ( !(fi->ftype().data().properties & PURL::Editable) ) break;
      e = Main::editorManager().findEditor(fi->url());
      if ( e==0 ) {
        if ( fi->ftype()==PURL::Coff && _project==0 && !fi->url().exists() ) {
          PURL::Url url = findFileItem(PURL::Hex)->url();
          if ( url.isEmpty() ) {
            HexEditor *he = ::tqt_cast<HexEditor *>(Main::currentEditor());
            if ( he==0 ) break;
            e = new DisassemblyEditor(*he, *data, this);
          } else e = new DisassemblyEditor(url, *data, this);
          addExternalFile(fi->url(), Generated);
        } else e = Main::editorManager().createEditor(fi->url().fileType(), fi->url());
        if ( e==0 ) break; 
        if ( !e->open(fi->url()) ) {
	  delete e;
	  break;
	}
        if ( isExternalFile(fi->url()) ) e->setReadOnly(true);
        Main::editorManager().addEditor(e);
      } else Main::editorManager().showEditor(e);
      break;
    }
  }
  cancelRenaming();
  emit guiChanged();
}

void ProjectManager::View::insertCurrentFile()
{
  insertFile(Main::editorManager().currentEditor()->url());
}

bool ProjectManager::View::editProject()
{
  ProjectEditor dialog(*_project, this);
  if ( dialog.exec()!=TQDialog::Accepted ) return false;
  _modified = true;
  if (_linkerScriptItem) _linkerScriptItem->init();
  return true;
}

bool ProjectManager::View::newProject()
{
  ProjectWizard wizard(this);
  if ( wizard.exec()==TQDialog::Rejected ) return false;
  closeProject();
  TQString error;
  if ( !wizard.project()->save(error) ) {
    MessageBox::detailedSorry(i18n("Failed to create new project file"), error, Log::Show);
    return false;
  }
  openProject(wizard.url());
  return true;
}

void ProjectManager::View::setProject(Project *project)
{
  closeProject();
  Main::editorManager().closeAllEditors();
  _project = project;
  _rootItem->set(project->url(), false);
  if ( project && Main::toolGroup().linkerScriptType()!=PURL::Nb_FileTypes ) {
    _linkerScriptItem = new LinkerScriptItem(headerItem(LinkerScriptGroup));
    ensureItemVisible(_linkerScriptItem);
  }
}

bool ProjectManager::View::openProject(const PURL::Url &url)
{
  if ( url.isEmpty() ) return false;
  bool reload = ( _project && _project->url()==url );
  if ( reload && !MessageBox::askContinue(i18n("Project already loaded. Reload?"), i18n("Reload")) ) return false;
  static_cast< TDERecentFilesAction *>(Main::action("project_open_recent"))->removeURL(url.kurl());
  Project *p = new Project(url);
  TQString error;
  if ( !p->load(error) ) {
    MessageBox::detailedSorry(i18n("Could not open project file."), error, Log::Show);
    delete p;
    return false;
  }
  setProject(p);
  PURL::UrlList files = _project->absoluteFiles();
  PURL::UrlList::const_iterator it;
  for(it=files.begin(); it!=files.end(); ++it) addFile(*it, (*it).fileType(), Intrinsic);
  _projectData.type = PURL::Project;
  _projectData.externals.clear();
  static_cast<TDERecentFilesAction *>(Main::action("project_open_recent"))->addURL(url.kurl());
  files = _project->openedFiles();
  for(it = files.begin(); it!=files.end(); ++it) Main::editorManager().openFile(*it);
  Register::list().init();
  TQValueList<Register::TypeData> watched = _project->watchedRegisters();
  TQValueList<Register::TypeData>::const_iterator wit;
  for (wit=watched.begin(); wit!=watched.end(); ++wit) Register::list().setWatched(*wit, true);
  return true;
}

bool ProjectManager::View::isExternalFile(const PURL::Url &url) const
{
  if ( projectUrl().isEmpty() ) return false;
  return projectData().externals.contains(url);
}

void ProjectManager::View::modified(const PURL::Url &url)
{
  FileItem *item = findFileItem(url);
  if ( item && !isExternalFile(url) ) _modified = true;
}

void ProjectManager::View::renamed(TQListViewItem *item, int, const TQString &text)
{
  Q_ASSERT ( item->rtti()==DeviceRtti );
  Main::toplevel().setDevice(text);
}

void ProjectManager::View::updateGUI()
{
  _deviceItem->updateText();
}

TQDragObject *ProjectManager::View::dragObject()
{
  if ( currentItem()==0 || currentItem()->rtti()!=FileRtti ) return 0;
  const FileItem *item = static_cast<const FileItem *>(currentItem());
  const HeaderItem *hitem = static_cast<const HeaderItem *>(item->parent());
  if ( hitem->group()!=SourceGroup ) return 0;
  TQStrList uris;
  uris.append(TQUriDrag::localFileToUri(item->url().filepath()));
  return new TQUriDrag(uris, viewport());
}

bool ProjectManager::View::acceptDrag(TQDropEvent* e) const
{
  if ( e->source()!=viewport() ) return false;
  const TQListViewItem *item = itemAt(e->pos());
  if ( item==0 || item->rtti()!=FileRtti ) return false;
  const HeaderItem *hitem = static_cast<const HeaderItem *>(item->parent());
  return ( hitem->group()==SourceGroup );
}

void ProjectManager::View::filesReordered()
{
  if ( _project==0 ) return;
  _project->clearFiles();
  TQListViewItem *item = headerItem(SourceGroup)->firstChild();
  for (;item; item=item->nextSibling())
    _project->addFile(static_cast<FileItem *>(item)->url());
}

TQString ProjectManager::View::tooltip(TQListViewItem *item, int) const
{
  switch (Rtti(item->rtti())) {
    case RootRtti:
    case FileRtti:
    case LinkerScriptRtti: return static_cast<const UrlItem *>(item)->url().filepath();
    case DeviceRtti:
    case RegisterRtti:
    case HeaderRtti: break;
  }
  return TQString();
}

PURL::Url ProjectManager::View::linkerScriptUrl() const
{
  TQListViewItemIterator it(const_cast<View *>(this));
  for(; it.current(); ++it) {
    if ( it.current()->rtti()!=LinkerScriptRtti ) continue;
    return static_cast<LinkerScriptItem *>(it.current())->url();
  }
  return PURL::Url();
}

bool ProjectManager::View::needsRecompile() const
{
  // ### this could be perfected...
  PURL::Url output = projectUrl().toFileType(PURL::Hex);
  TQDateTime outputLastModified;
  if ( !output.exists(&outputLastModified) ) return true;
  PURL::UrlList files;
  if ( Main::project() ) files = Main::project()->absoluteFiles();
  else files.append(projectUrl());
  PURL::UrlList::const_iterator it;
  for (it=files.begin(); it!=files.end(); ++it) {
    TQDateTime lastModified;
    if ( !(*it).exists(&lastModified) ) continue;
    if ( lastModified>outputLastModified ) return true;
  }
  return false;
}

PURL::Url ProjectManager::View::selectedUrl() const
{
  TQListViewItem *item = currentItem();
  if ( item==0 ) return PURL::Url();
  Rtti rtti = Rtti(item->rtti());
  if ( rtti!=FileRtti ) return PURL::Url();
  return static_cast<FileItem *>(item)->url();
}

#include "project_manager.moc"
