# Copyright (c) 2011-2015 Rusty Wagner
#
# 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.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
import os
from PySide.QtCore import *
from PySide.QtGui import *
from PythonConsole import *
ViewTypes = []
from RunWindow import *
class HistoryEntry:
def __init__(self, type, data):
self.type = type
self.data = data
class ViewFrame(QWidget):
statusUpdated = Signal(QWidget)
viewChanged = Signal(QWidget)
closeRequest = Signal(QWidget)
def __init__(self, type, data, filename, viewList):
super(ViewFrame, self).__init__(None)
self.navigation = {}
self.back = []
self.forward = []
self.undo_buffer = []
self.redo_buffer = []
self.undo_location = None
self.python_console = None
self.terminal = None
self.allow_title_change = False
self.splitter = QSplitter(Qt.Vertical, self)
self.main_area = QWidget()
size_policy = self.main_area.sizePolicy()
size_policy.setVerticalStretch(3)
self.main_area.setSizePolicy(size_policy)
self.splitter.addWidget(self.main_area)
parent_layout = QVBoxLayout()
parent_layout.setContentsMargins(0, 0, 0, 0)
parent_layout.setSpacing(0)
parent_layout.addWidget(self.splitter)
self.setLayout(parent_layout)
self.data = data
self.exe = data
self.view = type(data, filename, self, self.main_area)
self.filename = filename
self.new_filename = False
self.custom_tab_name = None
self.available = viewList
self.splittable = True
self.cache = {type : self.view}
self.status = {self.view : ""}
if hasattr(self.view, "statusUpdated"):
self.status[self.view] = self.view.status
self.view.statusUpdated.connect(self.viewStatusUpdated)
self.layout = QVBoxLayout()
self.layout.addWidget(self.view)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.main_area.setLayout(self.layout)
self.grabGesture(Qt.SwipeGesture)
def createView(self, type):
view = type(self.data, self.filename, self, self.main_area)
view.setVisible(False)
self.status[view] = ""
if hasattr(view, "statusUpdated"):
self.status[view] = view.status
view.statusUpdated.connect(self.viewStatusUpdated)
self.cache[type] = view
self.layout.addWidget(view)
return view
def setViewType(self, type):
self.view.setVisible(False)
if self.cache.has_key(type):
view = self.cache[type]
else:
view = self.createView(type)
self.view = view
self.view.setVisible(True)
self.viewChanged.emit(self)
self.statusUpdated.emit(self)
self.view.setFocus(Qt.OtherFocusReason)
def getTabName(self):
if self.custom_tab_name:
return self.custom_tab_name
if self.filename == "":
return "Untitled" + " (" + self.view.__class__.getShortViewName() + ")"
return os.path.basename(self.filename) + " (" + self.view.__class__.getShortViewName() + ")"
def setTabName(self, name):
if not self.allow_title_change:
return
self.custom_tab_name = name
self.statusUpdated.emit(self)
def getShortFileName(self):
if self.filename == "":
return "Untitled"
return os.path.basename(self.filename)
def isUntitled(self):
return self.filename == ""
def isNewFileName(self):
return self.new_filename
def getStatus(self):
return self.status[self.view]
def viewStatusUpdated(self, view):
self.status[view] = view.status
if self.view == view:
self.statusUpdated.emit(self)
def register_navigate(self, name, view, func):
self.navigation[name] = [view.__class__, func]
def navigate(self, name, ofs):
entry = self.get_history_entry()
# If view is already open, navigate now
if name in self.navigation:
if not self.navigation[name][1](ofs):
return False
self.setViewType(self.navigation[name][0])
self.back += [entry]
self.forward = []
self.view.setFocus(Qt.OtherFocusReason)
return True
# Look for a valid view type that handles this navigation
for type in self.available:
if type in self.cache:
continue
if hasattr(type, "handlesNavigationType"):
if type.handlesNavigationType(name):
view = self.createView(type)
if not self.navigation[name][1](ofs):
self.view.setFocus(Qt.OtherFocusReason)
return False
self.setViewType(type)
self.back += [entry]
self.forward = []
self.view.setFocus(Qt.OtherFocusReason)
return True
return False
def get_history_entry(self):
if hasattr(self.view, "get_history_entry"):
data = self.view.get_history_entry()
else:
data = None
return HistoryEntry(self.view.__class__, data)
def add_history_entry(self):
entry = self.get_history_entry()
self.back += [entry]
self.forward = []
def go_back(self):
if len(self.back) > 0:
entry = self.back.pop()
self.forward += [self.get_history_entry()]
self.setViewType(entry.type)
if entry.data != None:
self.view.navigate_to_history_entry(entry.data)
def go_forward(self):
if len(self.forward) > 0:
entry = self.forward.pop()
self.back += [self.get_history_entry()]
self.setViewType(entry.type)
if entry.data != None:
self.view.navigate_to_history_entry(entry.data)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape:
self.go_back()
elif event.key() == Qt.Key_Back:
self.go_back()
elif event.key() == Qt.Key_Forward:
self.go_forward()
else:
super(ViewFrame, self).keyPressEvent(event)
def event(self, event):
if event.type() == QEvent.Gesture:
gesture = event.gesture(Qt.SwipeGesture)
if (gesture != None) and (gesture.state() == Qt.GestureFinished):
if gesture.horizontalDirection() == QSwipeGesture.Left:
self.go_back()
return True
elif gesture.horizontalDirection() == QSwipeGesture.Right:
self.go_forward()
return True
return super(ViewFrame, self).event(event)
def save(self, filename):
try:
self.data.save(filename)
except IOError as (errno, msg):
QMessageBox.critical(self, "Error", "Unable to save: " + msg)
return False
self.notify_save(filename)
return True
def notify_save(self, filename):
self.filename = filename
self.new_filename = True
for view in self.cache.values():
if hasattr(view, "notify_save"):
view.notify_save()
def is_modified(self):
return self.data.is_modified()
def begin_undo(self):
self.undo_location = self.get_history_entry()
def commit_undo(self):
self.data.commit_undo(self.undo_location, self.get_history_entry())
def undo(self):
# Ensure any pending undo actions are accounted for
self.commit_undo()
entry = self.data.undo()
if entry:
self.setViewType(entry.type)
if entry.data != None:
self.view.navigate_to_history_entry(entry.data)
def redo(self):
# Ensure any pending undo actions are accounted for
self.commit_undo()
entry = self.data.redo()
if entry:
self.setViewType(entry.type)
if entry.data != None:
self.view.navigate_to_history_entry(entry.data)
def toggle_python_console(self):
if self.python_console:
if self.python_console.isVisible():
self.python_console.hide()
self.view.setFocus(Qt.OtherFocusReason)
else:
self.python_console.show()
self.python_console.input.setFocus(Qt.OtherFocusReason)
else:
self.python_console = PythonConsole(self)
self.splitter.addWidget(self.python_console)
self.python_console.input.setFocus(Qt.OtherFocusReason)
def run_in_terminal(self, cmd):
if self.terminal:
if not self.terminal.isVisible():
self.terminal.show()
self.terminal.run()
self.terminal.commandLine.setFocus(Qt.OtherFocusReason)
else:
self.terminal.run()
self.terminal.term.setFocus(Qt.OtherFocusReason)
else:
self.terminal = RunWindow(self, self, cmd)
self.splitter.addWidget(self.terminal)
self.terminal.commandLine.setFocus(Qt.OtherFocusReason)
def terminal_process_exit(self):
if self.terminal:
# Terminal process exited, set focus to view
self.view.setFocus(Qt.OtherFocusReason)
def terminal_closed(self):
self.terminal.hide()
self.view.setFocus(Qt.OtherFocusReason)
def closing(self):
if self.python_console:
self.python_console.stop()
if self.terminal:
self.terminal.closeRequest()
def force_close(self):
self.closeRequest.emit(self)
def font_changed(self):
for view in self.cache.values():
if hasattr(view, "fontChanged"):
view.fontChanged()