view python/apps/ide/ide.py @ 63:32078200cdd6

Several move action
author windel
date Sun, 07 Oct 2012 17:04:10 +0200
parents python/ide/ide/ide.py@fd7d5069734e
children 4a27c28c7d0f
line wrap: on
line source

from PyQt4.QtCore import *
from PyQt4.QtGui import *
# ide components:
from .codeeditor import CodeEdit
from .astviewer import AstViewer
import base64
from project import Project
import os.path

lcfospng = base64.decodestring(b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEhMKBk7B678AAAA/SURBVFjD\n7dbBCQAgDATBi9h/y7EFA4Kf2QLCwH1S6XQu6sqoujublc8BAAAAAAAAAAB8B+zXT6YJAAAAAKYd\nWSgFQNUyijIAAAAASUVORK5CYII=\n')

class BuildOutput(QTextEdit):
   """ Build output component """
   def __init__(self, parent=None):
      super(BuildOutput, self).__init__(parent)
      self.setCurrentFont(QFont('Courier'))
      self.setReadOnly(True)
      self.append('Build output will appear here!')

class BuildErrors(QListView):
   sigErrorSelected = pyqtSignal(object)
   def __init__(self, parent=None):
      super(BuildErrors, self).__init__(parent)
      model = QStandardItemModel()
      self.setModel(model)
      self.clicked.connect(self.itemSelected)
   def setErrorList(self, errorlist):
      model = QStandardItemModel()
      for e in errorlist:
         row, col, msg = e
         item = QStandardItem(str(msg))
         item.setData(e)
         model.appendRow(item)
      self.setModel(model)
   def itemSelected(self, index):
      if not index.isValid():
         return
      model = self.model()
      item = model.itemFromIndex(index)
      err = item.data()
      self.sigErrorSelected.emit(err)

class ProjectView(QWidget):
   sigLoadFile = pyqtSignal(str)
   def __init__(self, parent=None):
      super(ProjectView, self).__init__(parent)
      self.treeview = QTreeView(self)
      self.treeview.setContextMenuPolicy(Qt.CustomContextMenu)
      l = QVBoxLayout(self)
      l.addWidget(self.treeview)
      pm = QPixmap()
      pm.loadFromData(lcfospng)
      self.projectIcon = QIcon(pm)
      # Connect signals:
      self.treeview.activated.connect(self.activate)
      self.treeview.customContextMenuRequested.connect(self.contextMenu)
   def setProject(self, project):
      self.project = project
      model = QStandardItemModel()
      root = model.invisibleRootItem()
      pitem = QStandardItem(self.projectIcon, project.name)
      pitem.setEditable(False)
      pitem.setData(project)
      root.appendRow(pitem)
      for f in self.project.files:
         fitem = QStandardItem(f)
         pitem.appendRow(fitem)
         fitem.setEditable(False)
         fitem.setData(f)
      self.treeview.setModel(model)
      self.treeview.expandAll()
   def contextMenu(self, pos):
      idx = self.treeview.indexAt(pos)
      if not idx.isValid():
         return
      item = self.treeview.model().itemFromIndex(idx)
   def activate(self, index):
      if not index.isValid():
         return
      model = self.treeview.model()
      item = model.itemFromIndex(index)
      fn = item.data()
      if type(fn) is str:
         self.sigLoadFile.emit(fn)

class AboutDialog(QDialog):
   def __init__(self, parent=None):
      super(AboutDialog, self).__init__(parent)
      self.setWindowTitle('About')
      l = QVBoxLayout(self)
      txt = QTextEdit(self)
      txt.setReadOnly(True)
      aboutText = """<h1>lcfOS IDE</h1>
      <p>An all-in-one IDE for OS development.</p>
      <p>https://www.assembla.com/spaces/lcfOS/wiki</p>
      <p>Author: Windel Bouwman</p>
      """
      txt.append(aboutText)
      l.addWidget(txt)
      but = QPushButton('OK')
      but.setDefault(True)
      but.clicked.connect(self.close)
      l.addWidget(but)

class ProjectOptions(QDialog):
   pass
   # TODO: project options in here

class Ide(QMainWindow):
  def __init__(self, parent=None):
    super(Ide, self).__init__(parent)
    self.setWindowTitle('LCFOS IDE')
    icon = QPixmap()
    icon.loadFromData(lcfospng)
    self.setWindowIcon(QIcon(icon))

    # Create menus:
    self.fileMenu = self.menuBar().addMenu('File')
    self.viewMenu = self.menuBar().addMenu('View')
    self.projectMenu = self.menuBar().addMenu('Project')
    self.helpMenu = self.menuBar().addMenu('Help')

    # Create mdi area:
    self.mdiArea = QMdiArea()
    self.setCentralWidget(self.mdiArea)

    # Create components:
    self.buildOutput = BuildOutput()
    self.addComponent('Build output', self.buildOutput)

    self.astViewer = AstViewer()
    self.addComponent('AST viewer', self.astViewer)
    self.astViewer.sigNodeSelected.connect(self.nodeSelected)

    self.builderrors = BuildErrors()
    self.addComponent('Build errors', self.builderrors)
    self.builderrors.sigErrorSelected.connect(self.errorSelected)

    self.projectview = ProjectView()
    self.addComponent('Project', self.projectview)
    self.projectview.sigLoadFile.connect(self.loadFile)

    # About dialog:
    self.aboutDialog = AboutDialog()
    self.aboutDialog.setWindowIcon(QIcon(icon))
    # Create actions:
    self.buildAction = QAction('Build!', self)
    self.buildAction.setShortcut(QKeySequence('F7'))
    self.projectMenu.addAction(self.buildAction)
    self.buildAction.triggered.connect(self.buildFile)
    self.openProjectAction = QAction("Open project", self)
    self.openProjectAction.triggered.connect(self.openProject)
    self.projectMenu.addAction(self.openProjectAction)
    self.helpAction  = QAction('Help', self)
    self.helpAction.setShortcut(QKeySequence('F1'))
    self.helpMenu.addAction(self.helpAction)
    self.aboutAction = QAction('About', self)
    self.helpMenu.addAction(self.aboutAction)
    self.aboutAction.triggered.connect(self.aboutDialog.open)

    self.newFileAction = QAction("New File", self)
    self.fileMenu.addAction(self.newFileAction)
    self.newFileAction.triggered.connect(self.newFile)
    self.saveFileAction = QAction("Save File", self)
    self.fileMenu.addAction(self.saveFileAction)
    self.saveFileAction.triggered.connect(self.saveFile)
    self.closeFileAction = QAction("Close File", self)
    self.fileMenu.addAction(self.closeFileAction)
    self.closeFileAction.triggered.connect(self.closeFile)

    cascadeAction = QAction("Cascade windows", self)
    cascadeAction.triggered.connect(self.mdiArea.cascadeSubWindows)
    self.viewMenu.addAction(cascadeAction)
    tileAction = QAction('Tile windows', self)
    tileAction.triggered.connect(self.mdiArea.tileSubWindows)
    self.viewMenu.addAction(tileAction)

    # Load settings:
    self.settings = QSettings('windelsoft', 'lcfoside')
    self.loadSettings()

  def addComponent(self, name, widget):
     dw = QDockWidget(name)
     dw.setWidget(widget)
     dw.setObjectName(name)
     self.addDockWidget(Qt.RightDockWidgetArea, dw)
     self.viewMenu.addAction(dw.toggleViewAction())

  # File handling:
  def newFile(self):
     ce = CodeEdit()
     w = self.mdiArea.addSubWindow(ce)
     ce.show()

  def saveFile(self):
     ac = self.activeMdiChild()
     if ac:
        ac.saveFile()

  def saveAll(self):
     pass

  def openFile(self):
     # TODO
     pass

  def closeFile(self):
     ac = self.activeMdiChild()
     if ac:
        self.mdiArea.removeSubWindow(ac)
  
  def loadFile(self, filename):
     # Find existing mdi widget:
     wid = self.findMdiChild(filename)
     if wid:
        self.mdiArea.setActiveSubWindow(wid.parent())
        return wid

     # Create a new one:
     ce = CodeEdit()
     source = self.project.loadProjectFile(filename)
     ce.setSource(source)
     self.mdiArea.addSubWindow(ce)
     ce.show()
     return ce

  # MDI:
  def activeMdiChild(self):
     aw = self.mdiArea.activeSubWindow()
     if aw:
        return aw.widget()
     else:
        return None

  def findMdiChild(self, filename):
     for window in self.mdiArea.subWindowList():
        wid = window.widget()
        if wid.filename == filename:
           return wid
     return None
  
  def allChildren(self):
     c = []
     for window in self.mdiArea.subWindowList():
        wid = window.widget()
        c.append(wid)
     return c

  # Settings:
  def loadSettings(self):
     if self.settings.contains('mainwindowstate'):
        self.restoreState(self.settings.value('mainwindowstate'))
     if self.settings.contains('mainwindowgeometry'):
        self.restoreGeometry(self.settings.value('mainwindowgeometry'))
     if self.settings.contains('openedproject'):
        projectfile = self.settings.value('openedproject')
        self.loadProject(projectfile)
  
  def closeEvent(self, ev):
     self.settings.setValue('mainwindowstate', self.saveState())
     self.settings.setValue('mainwindowgeometry', self.saveGeometry())
     if self.project:
        self.settings.setValue('openedproject', self.project.filename)
        # TODO: ask for save of opened files
     ev.accept()

  # Error handling:
  def nodeSelected(self, node):
      ce = self.activeMdiChild()
      if not ce:
         return
      if node.location:
         row, col = node.location
         ce.highlightErrorLocation( row, col )
      else:
         ce.clearErrors()

  def errorSelected(self, err):
     row, col, msg = err
     ce = self.activeMdiChild()
     if not ce:
        return
     ce.highlightErrorLocation(row, col)

  # Project loading:
  def loadProject(self, filename):
     self.project = Project(filename)
     self.projectview.setProject(self.project)

  def openProject(self):
     filename = QFileDialog.getOpenFileName(self, \
       "Choose project file", "", "lcfos Project files (*.lcp)")
     if filename:
        self.loadProject(filename)

  # Build recepy:
  def buildFile(self):
     """ Build project """
     self.saveAll()
     self.buildOutput.clear()
     self.buildOutput.append(str(self.compiler))
     mods = self.compiler.compileProject(self.project)

     self.builderrors.setErrorList(self.compiler.errorlist)
     self.astViewer.setAst(mods[0])
     for err in self.compiler.errorlist:
        self.buildOutput.append(str(err))
     self.buildOutput.append("Done!")