diff python/apps/ide.py @ 64:4a27c28c7d0f

File movage
author windel
date Sun, 07 Oct 2012 17:13:47 +0200
parents python/apps/runide.py@32078200cdd6
children 361f7d13adea
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/python/apps/ide.py	Sun Oct 07 17:13:47 2012 +0200
@@ -0,0 +1,322 @@
+import sys
+
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+# ide components:
+from .codeeditor import CodeEdit
+from .astviewer import AstViewer
+import base64
+import os.path
+
+# Compiler imports:
+from project import Project
+from compiler.compiler import Compiler
+
+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!")
+
+
+if __name__ == '__main__':
+   app = QApplication(sys.argv)
+   ide = Ide()
+   ide.compiler = Compiler()
+   ide.show()
+   app.exec_()
+