Пример #1
0
    def display(self, params):
        if 'skipPrevs' in self.install_config and self.install_config[
                'skipPrevs'] == True:
            self.delete()
            return ActionResult(False, {'goBack': True})
        if 'autopartition' in self.install_config and self.install_config[
                'autopartition'] == True:
            return ActionResult(True, None)
        if 'delete_partition' in self.install_config and self.install_config[
                'delete_partition'] == True:
            self.delete()
            self.install_config['delete_partition'] = False

        self.device_index = self.install_config['diskindex']

        self.disk_buttom_items = []
        self.disk_buttom_items.append(('<Next>', self.next))
        self.disk_buttom_items.append(('<Create New>', self.create_function))
        self.disk_buttom_items.append(('<Delete All>', self.delete_function))
        self.disk_buttom_items.append(('<Go Back>', self.go_back))

        self.text_items = []
        self.text_items.append(('Disk', 20))
        self.text_items.append(('Size', 5))
        self.text_items.append(('Type', 5))
        self.text_items.append(('Mountpoint', 20))
        self.table_space = 5

        title = 'Current partitions:\n'
        self.window.addstr(0, (self.win_width - len(title)) / 2, title)

        info = "Unpartitioned space: " + str(
            self.disk_size[self.device_index][1]) + " MB, Total size: " + str(
                int(self.devices[self.device_index].size) / 1048576) + " MB"

        self.text_pane = TextPane(self.text_starty,
                                  self.maxx,
                                  self.text_width,
                                  "EULA.txt",
                                  self.text_height,
                                  self.disk_buttom_items,
                                  partition=True,
                                  popupWindow=True,
                                  install_config=self.install_config,
                                  text_items=self.text_items,
                                  table_space=self.table_space,
                                  default_start=1,
                                  info=info,
                                  size_left=str(
                                      self.disk_size[self.device_index][1]))

        self.window.set_action_panel(self.text_pane)

        return self.window.do_action()
Пример #2
0
    def display(self):
        accept_decline_items = [('<Accept>', self.accept_function),
                                ('<Cancel>', self.exit_function)]

        self.window.addstr(0, (self.win_width - len(self.title)) // 2, self.title)
        self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width,
                                  self.eula_file_path, self.text_height, accept_decline_items)

        self.window.set_action_panel(self.text_pane)

        return self.window.do_action()
Пример #3
0
    def display(self, params):
        accept_decline_items = [('<Accept>', self.accept_function),
                                ('<Cancel>', self.exit_function)]

        title = 'VMWARE 2.0 LICENSE AGREEMENT'
        self.window.addstr(0, (self.win_width - len(title)) // 2, title)
        self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width,
                                  "EULA.txt", self.text_height,
                                  accept_decline_items)

        self.window.set_action_panel(self.text_pane)

        return self.window.do_action()
Пример #4
0
    def display(self, params):
        if 'skipPrevs' in self.install_config and self.install_config['skipPrevs'] == True:
            self.delete()
            return ActionResult(False, {'goBack':True})
        if 'autopartition' in self.install_config and self.install_config['autopartition'] == True:
            return ActionResult(True, None)
        if ('delete_partition' in self.install_config and
                self.install_config['delete_partition'] == True):
            self.delete()
            self.install_config['delete_partition'] = False

        self.device_index = self.install_config['diskindex']

        self.disk_buttom_items = []
        self.disk_buttom_items.append(('<Next>', self.next))
        self.disk_buttom_items.append(('<Create New>', self.create_function))
        self.disk_buttom_items.append(('<Delete All>', self.delete_function))
        self.disk_buttom_items.append(('<Go Back>', self.go_back))

        self.text_items = []
        self.text_items.append(('Disk', 20))
        self.text_items.append(('Size', 5))
        self.text_items.append(('Type', 5))
        self.text_items.append(('Mountpoint', 20))
        self.table_space = 5

        title = 'Current partitions:\n'
        self.window.addstr(0, (self.win_width - len(title)) // 2, title)

        info = ("Unpartitioned space: " +
                str(self.disk_size[self.device_index][1])+
                " MB, Total size: "+
                str(int(self.devices[self.device_index].size)/ 1048576) + " MB")

        self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width,
                                  "EULA.txt", self.text_height, self.disk_buttom_items,
                                  partition=True, popupWindow=True,
                                  install_config=self.install_config,
                                  text_items=self.text_items, table_space=self.table_space,
                                  default_start=1, info=info,
                                  size_left=str(self.disk_size[self.device_index][1]))

        self.window.set_action_panel(self.text_pane)

        return self.window.do_action()
Пример #5
0
class PartitionISO(object):
    def __init__(self, maxy, maxx, install_config):
        self.maxx = maxx
        self.maxy = maxy
        self.win_width = maxx - 4
        self.win_height = maxy - 4
        self.install_config = install_config
        self.path_checker = []

        self.win_starty = (self.maxy - self.win_height) // 2
        self.win_startx = (self.maxx - self.win_width) // 2

        self.text_starty = self.win_starty + 4
        self.text_height = self.win_height - 6
        self.text_width = self.win_width - 6
        self.install_config['partitionsnumber'] = 0
        self.devices = Device.refresh_devices_bytes()
        self.has_slash = False
        self.has_remain = False
        self.has_empty = False

        self.disk_size = []
        for index, device in enumerate(self.devices):
            self.disk_size.append((device.path, int(device.size) / 1048576))

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx,
                             'Welcome to the Photon installer', False, can_go_next=False)
        Device.refresh_devices()

    def display(self, params):
        if 'skipPrevs' in self.install_config and self.install_config['skipPrevs'] == True:
            self.delete()
            return ActionResult(False, {'goBack':True})
        if 'autopartition' in self.install_config and self.install_config['autopartition'] == True:
            return ActionResult(True, None)
        if ('delete_partition' in self.install_config and
                self.install_config['delete_partition'] == True):
            self.delete()
            self.install_config['delete_partition'] = False

        self.device_index = self.install_config['diskindex']

        self.disk_buttom_items = []
        self.disk_buttom_items.append(('<Next>', self.next))
        self.disk_buttom_items.append(('<Create New>', self.create_function))
        self.disk_buttom_items.append(('<Delete All>', self.delete_function))
        self.disk_buttom_items.append(('<Go Back>', self.go_back))

        self.text_items = []
        self.text_items.append(('Disk', 20))
        self.text_items.append(('Size', 5))
        self.text_items.append(('Type', 5))
        self.text_items.append(('Mountpoint', 20))
        self.table_space = 5

        title = 'Current partitions:\n'
        self.window.addstr(0, (self.win_width - len(title)) // 2, title)

        info = ("Unpartitioned space: " +
                str(self.disk_size[self.device_index][1])+
                " MB, Total size: "+
                str(int(self.devices[self.device_index].size)/ 1048576) + " MB")

        self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width,
                                  "EULA.txt", self.text_height, self.disk_buttom_items,
                                  partition=True, popupWindow=True,
                                  install_config=self.install_config,
                                  text_items=self.text_items, table_space=self.table_space,
                                  default_start=1, info=info,
                                  size_left=str(self.disk_size[self.device_index][1]))

        self.window.set_action_panel(self.text_pane)

        return self.window.do_action()

    def validate_partition(self, pstr):
        if not pstr:
            return ActionResult(False, None)
        sizedata = pstr[0]
        mtdata = pstr[2]
        typedata = pstr[1]
        devicedata = self.devices[self.device_index].path

        #no empty fields unless swap
        if (typedata == 'swap' and
                (len(mtdata) != 0 or len(typedata) == 0 or len(devicedata) == 0)):
            return False, "invalid swap data "

        if (typedata != 'swap' and
                (len(sizedata) == 0 or
                 len(mtdata) == 0 or
                 len(typedata) == 0 or
                 len(devicedata) == 0)):
            if not self.has_empty and mtdata and typedata and devicedata:
                self.has_empty = True
            else:
                return False, "Input cannot be empty"

        if typedata != 'swap' and typedata != 'ext3' and typedata != 'ext4':
            return False, "Invalid type"

        if len(mtdata) != 0 and mtdata[0] != '/':
            return False, "Invalid path"

        if mtdata in self.path_checker:
            return False, "Path already existed"
        #validate disk: must be one of the existing disks
        i = self.device_index

        #valid size: must not exceed memory limit
        curr_size = self.disk_size[i][1]
        if len(sizedata) != 0:
            try:
                int(sizedata)
            except ValueError:
                return False, "invalid device size"

            if int(curr_size) - int(sizedata) < 0:
                return False, "invalid device size"
            #if valid, update the size and return true
            new_size = (self.disk_size[i][0], int(curr_size)- int(sizedata))
            self.disk_size[i] = new_size

        if mtdata == "/":
            self.has_slash = True

        self.path_checker.append(mtdata)
        return True, None

    def create_function(self):
        self.window.hide_window()

        self.install_config['partition_disk'] = self.devices[self.device_index].path
        self.partition_items = []
        self.partition_items.append(('Size in MB: ' +
                                     str(self.disk_size[self.device_index][1]) +
                                     ' available'))
        self.partition_items.append(('Type: (ext3, ext4, swap)'))
        self.partition_items.append(('Mountpoint:'))
        self.create_window = ReadMulText(
            self.maxy, self.maxx, 0,
            self.install_config,
            str(self.install_config['partitionsnumber']) + 'partition_info',
            self.partition_items,
            None,
            None,
            None,
            self.validate_partition,   #validation function of the input
            None,
            True,
            )
        result = self.create_window.do_action()
        if result.success:
            self.install_config['partitionsnumber'] = self.install_config['partitionsnumber'] + 1

        #parse the input in install config
        return self.display(False)

    def delete_function(self):
        self.delete()
        return self.display(False)

    def go_back(self):
        self.delete()
        self.window.hide_window()
        self.text_pane.hide()
        return ActionResult(False, {'goBack':True})

    def next(self):
        if self.install_config['partitionsnumber'] == 0:
            window_height = 9
            window_width = 40
            window_starty = (self.maxy-window_height) // 2 + 5
            confirm_window = ConfirmWindow(window_height, window_width, self.maxy,
                                           self.maxx, window_starty,
                                           'Partition information cannot be empty',
                                           info=True)
            confirm_window.do_action()
            return self.display(False)
        #must have /
        if not self.has_slash:
            window_height = 9
            window_width = 40
            window_starty = (self.maxy - window_height) // 2 + 5
            confirm_window = ConfirmWindow(window_height, window_width, self.maxy,
                                           self.maxx, window_starty, 'Missing /',
                                           info=True)
            confirm_window.do_action()
            return self.display(False)

        self.window.hide_window()
        self.text_pane.hide()
        return ActionResult(True, {'goNext':True})

    def delete(self):
        for i in range(int(self.install_config['partitionsnumber'])):
            self.install_config[str(i)+'partition_info'+str(0)] = ''
            self.install_config[str(i)+'partition_info'+str(1)] = ''
            self.install_config[str(i)+'partition_info'+str(2)] = ''
            self.install_config[str(i)+'partition_info'+str(3)] = ''
        del self.disk_size[:]
        for index, device in enumerate(self.devices):
            self.disk_size.append((device.path, int(device.size) / 1048576))
        del self.path_checker[:]
        self.has_slash = False
        self.has_remain = False
        self.has_empty = False
        self.install_config['partitionsnumber'] = 0
Пример #6
0
class PartitionISO(object):
    def __init__(self, maxy, maxx, install_config):
        self.maxx = maxx
        self.maxy = maxy
        self.win_width = maxx - 4
        self.win_height = maxy - 4
        self.install_config = install_config
        self.path_checker = []

        self.win_starty = (self.maxy - self.win_height) // 2
        self.win_startx = (self.maxx - self.win_width) // 2

        self.text_starty = self.win_starty + 4
        self.text_height = self.win_height - 6
        self.text_width = self.win_width - 6
        self.install_config['partitionsnumber'] = 0
        self.devices = Device.refresh_devices_bytes()
        self.has_slash = False
        self.has_remain = False
        self.has_empty = False

        self.disk_size = []
        for index, device in enumerate(self.devices):
            self.disk_size.append((device.path, int(device.size) / 1048576))

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx,
                             'Welcome to the Photon installer', False, can_go_next=False)
        Device.refresh_devices()

    def display(self):
        if 'autopartition' in self.install_config and self.install_config['autopartition'] == True:
            return ActionResult(True, None)
        if ('delete_partition' in self.install_config and
                self.install_config['delete_partition'] == True):
            self.delete()
            self.install_config['delete_partition'] = False

        self.device_index = self.install_config['diskindex']

        self.disk_buttom_items = []
        self.disk_buttom_items.append(('<Next>', self.next))
        self.disk_buttom_items.append(('<Create New>', self.create_function))
        self.disk_buttom_items.append(('<Delete All>', self.delete_function))
        self.disk_buttom_items.append(('<Go Back>', self.go_back))

        self.text_items = []
        self.text_items.append(('Disk', 20))
        self.text_items.append(('Size', 5))
        self.text_items.append(('Type', 5))
        self.text_items.append(('Mountpoint', 20))
        self.table_space = 5

        title = 'Current partitions:\n'
        self.window.addstr(0, (self.win_width - len(title)) // 2, title)

        info = ("Unpartitioned space: " +
                str(self.disk_size[self.device_index][1])+
                " MB, Total size: "+
                str(int(self.devices[self.device_index].size)/ 1048576) + " MB")

        self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width,
                                  "EULA.txt", self.text_height, self.disk_buttom_items,
                                  partition=True, popupWindow=True,
                                  install_config=self.install_config,
                                  text_items=self.text_items, table_space=self.table_space,
                                  default_start=1, info=info,
                                  size_left=str(self.disk_size[self.device_index][1]))

        self.window.set_action_panel(self.text_pane)

        return self.window.do_action()

    def validate_partition(self, pstr):
        if not pstr:
            return ActionResult(False, None)
        sizedata = pstr[0]
        mtdata = pstr[2]
        typedata = pstr[1]
        devicedata = self.devices[self.device_index].path

        #no empty fields unless swap
        if (typedata == 'swap' and
                (len(mtdata) != 0 or len(typedata) == 0 or len(devicedata) == 0)):
            return False, "invalid swap data "

        if (typedata != 'swap' and
                (len(sizedata) == 0 or
                 len(mtdata) == 0 or
                 len(typedata) == 0 or
                 len(devicedata) == 0)):
            if not self.has_empty and mtdata and typedata and devicedata:
                self.has_empty = True
            else:
                return False, "Input cannot be empty"

        if typedata != 'swap' and typedata != 'ext3' and typedata != 'ext4':
            return False, "Invalid type"

        if len(mtdata) != 0 and mtdata[0] != '/':
            return False, "Invalid path"

        if mtdata in self.path_checker:
            return False, "Path already existed"
        #validate disk: must be one of the existing disks
        i = self.device_index

        #valid size: must not exceed memory limit
        curr_size = self.disk_size[i][1]
        if len(sizedata) != 0:
            try:
                int(sizedata)
            except ValueError:
                return False, "invalid device size"

            if int(curr_size) - int(sizedata) < 0:
                return False, "invalid device size"
            #if valid, update the size and return true
            new_size = (self.disk_size[i][0], int(curr_size)- int(sizedata))
            self.disk_size[i] = new_size

        if mtdata == "/":
            self.has_slash = True

        self.path_checker.append(mtdata)
        return True, None

    def create_function(self):
        self.window.hide_window()

        self.install_config['partition_disk'] = self.devices[self.device_index].path
        self.partition_items = []
        self.partition_items.append(('Size in MB: ' +
                                     str(self.disk_size[self.device_index][1]) +
                                     ' available'))
        self.partition_items.append(('Type: (ext3, ext4, swap)'))
        self.partition_items.append(('Mountpoint:'))
        self.create_window = ReadMulText(
            self.maxy, self.maxx, 0,
            self.install_config,
            str(self.install_config['partitionsnumber']) + 'partition_info',
            self.partition_items,
            None,
            None,
            None,
            self.validate_partition,   #validation function of the input
            None,
            True,
            )
        result = self.create_window.do_action()
        if result.success:
            self.install_config['partitionsnumber'] = self.install_config['partitionsnumber'] + 1

        #parse the input in install config
        return self.display()

    def delete_function(self):
        self.delete()
        return self.display()

    def go_back(self):
        self.delete()
        self.window.hide_window()
        self.text_pane.hide()
        return ActionResult(False, {'goBack':True})

    def next(self):
        if self.install_config['partitionsnumber'] == 0:
            window_height = 9
            window_width = 40
            window_starty = (self.maxy-window_height) // 2 + 5
            confirm_window = ConfirmWindow(window_height, window_width, self.maxy,
                                           self.maxx, window_starty,
                                           'Partition information cannot be empty',
                                           info=True)
            confirm_window.do_action()
            return self.display()
        #must have /
        if not self.has_slash:
            window_height = 9
            window_width = 40
            window_starty = (self.maxy - window_height) // 2 + 5
            confirm_window = ConfirmWindow(window_height, window_width, self.maxy,
                                           self.maxx, window_starty, 'Missing /',
                                           info=True)
            confirm_window.do_action()
            return self.display()

        self.window.hide_window()
        self.text_pane.hide()

        partitions = []
        for i in range(int(self.install_config['partitionsnumber'])):
            if len(self.install_config[str(i)+'partition_info'+str(0)]) == 0:
                sizedata = 0
            else:
                sizedata = int(self.install_config[str(i) + 'partition_info' + str(0)])
            mtdata = self.install_config[str(i) + 'partition_info' + str(2)]
            typedata = self.install_config[str(i) + 'partition_info'+str(1)]

            partitions = partitions + [{"mountpoint": mtdata,
                                        "size": sizedata,
                                        "filesystem": typedata},]
        self.install_config['partitions'] = partitions

        return ActionResult(True, {'goNext':True})

    def delete(self):
        for i in range(int(self.install_config['partitionsnumber'])):
            self.install_config[str(i)+'partition_info'+str(0)] = ''
            self.install_config[str(i)+'partition_info'+str(1)] = ''
            self.install_config[str(i)+'partition_info'+str(2)] = ''
            self.install_config[str(i)+'partition_info'+str(3)] = ''
        del self.disk_size[:]
        for index, device in enumerate(self.devices):
            self.disk_size.append((device.path, int(device.size) / 1048576))
        del self.path_checker[:]
        self.has_slash = False
        self.has_remain = False
        self.has_empty = False
        self.install_config['partitionsnumber'] = 0
Пример #7
0
    def __init__(self, fileName=None, logName=None, parent=None):
        super(MainWin, self).__init__(parent)

        #self.setWindowIcon(QIcon(':/images/logo.png'))
        self.setToolButtonStyle(Qt.ToolButtonFollowStyle)
        self.setupFileActions()
        self.setupEditActions()
        self.setupTextActions()
        self.setupRunActions()
        self.initializeSettings()
        self.populateRunSettings()  # FIXME put in initializeSettings()?
        
        settingsMenu = QMenu('Settings', self)
        self.menuBar().addMenu(settingsMenu)
        settingsMenu.addAction('Configure...', self.configure)

        helpMenu = QMenu("Help", self)
        self.menuBar().addMenu(helpMenu)
        helpMenu.addAction("About", self.about)
        helpMenu.addAction("About &Qt", QApplication.instance().aboutQt)
 
        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Vertical)
        self.textPane = TextPane()
        self.logPane = LogPane()
        self.logBox = QGroupBox()
        self.logBox.setFlat(True)
        vbox = QVBoxLayout()
        vbox.addWidget(self.logPane)
        self.logBox.setLayout(vbox)
        self.splitter.addWidget(self.textPane)
        self.splitter.addWidget(QLabel()) # spacer
        self.splitter.addWidget(self.logBox)
        self.setCentralWidget(self.splitter)

        self.loadSrc(fileName)
        self.loadLog(logName)
        #if logName and (-1 == self.comboLogFile.findText(logName)):
            #self.comboLogFile.addItem(logName)

        self.logPane.setFocus()
        self.fontChanged(self.textPane.font())
        self.textPane.document().modificationChanged.connect(self.actionSave.setEnabled)
        self.textPane.document().modificationChanged.connect(self.setWindowModified)
        self.textPane.document().undoAvailable.connect(self.actionUndo.setEnabled)
        self.textPane.document().redoAvailable.connect( self.actionRedo.setEnabled)
        self.setWindowModified(self.textPane.document().isModified())
        self.actionSave.setEnabled(self.textPane.document().isModified())
        self.actionUndo.setEnabled(self.textPane.document().isUndoAvailable())
        self.actionRedo.setEnabled(self.textPane.document().isRedoAvailable())
        self.actionUndo.triggered.connect(self.textPane.undo)
        self.actionRedo.triggered.connect(self.textPane.redo)
        self.actionCut.setEnabled(False)
        self.actionCopy.setEnabled(False)
        self.actionCut.triggered.connect(self.textPane.cut)
        self.actionCopy.triggered.connect(self.textPane.copy)
        self.actionPaste.triggered.connect(self.textPane.paste)
        self.textPane.copyAvailable.connect(self.actionCut.setEnabled)
        self.textPane.copyAvailable.connect(self.actionCopy.setEnabled)
        QApplication.clipboard().dataChanged.connect(self.clipboardDataChanged)
        
        self.actionRun.triggered.connect(self.scannoCheck)       
        self.logPane.lineMatchChanged.connect(self.logLineMatchChanged)
Пример #8
0
class MainWin(QMainWindow):
    def __init__(self, fileName=None, logName=None, parent=None):
        super(MainWin, self).__init__(parent)

        #self.setWindowIcon(QIcon(':/images/logo.png'))
        self.setToolButtonStyle(Qt.ToolButtonFollowStyle)
        self.setupFileActions()
        self.setupEditActions()
        self.setupTextActions()
        self.setupRunActions()
        self.initializeSettings()
        self.populateRunSettings()  # FIXME put in initializeSettings()?
        
        settingsMenu = QMenu('Settings', self)
        self.menuBar().addMenu(settingsMenu)
        settingsMenu.addAction('Configure...', self.configure)

        helpMenu = QMenu("Help", self)
        self.menuBar().addMenu(helpMenu)
        helpMenu.addAction("About", self.about)
        helpMenu.addAction("About &Qt", QApplication.instance().aboutQt)
 
        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Vertical)
        self.textPane = TextPane()
        self.logPane = LogPane()
        self.logBox = QGroupBox()
        self.logBox.setFlat(True)
        vbox = QVBoxLayout()
        vbox.addWidget(self.logPane)
        self.logBox.setLayout(vbox)
        self.splitter.addWidget(self.textPane)
        self.splitter.addWidget(QLabel()) # spacer
        self.splitter.addWidget(self.logBox)
        self.setCentralWidget(self.splitter)

        self.loadSrc(fileName)
        self.loadLog(logName)
        #if logName and (-1 == self.comboLogFile.findText(logName)):
            #self.comboLogFile.addItem(logName)

        self.logPane.setFocus()
        self.fontChanged(self.textPane.font())
        self.textPane.document().modificationChanged.connect(self.actionSave.setEnabled)
        self.textPane.document().modificationChanged.connect(self.setWindowModified)
        self.textPane.document().undoAvailable.connect(self.actionUndo.setEnabled)
        self.textPane.document().redoAvailable.connect( self.actionRedo.setEnabled)
        self.setWindowModified(self.textPane.document().isModified())
        self.actionSave.setEnabled(self.textPane.document().isModified())
        self.actionUndo.setEnabled(self.textPane.document().isUndoAvailable())
        self.actionRedo.setEnabled(self.textPane.document().isRedoAvailable())
        self.actionUndo.triggered.connect(self.textPane.undo)
        self.actionRedo.triggered.connect(self.textPane.redo)
        self.actionCut.setEnabled(False)
        self.actionCopy.setEnabled(False)
        self.actionCut.triggered.connect(self.textPane.cut)
        self.actionCopy.triggered.connect(self.textPane.copy)
        self.actionPaste.triggered.connect(self.textPane.paste)
        self.textPane.copyAvailable.connect(self.actionCut.setEnabled)
        self.textPane.copyAvailable.connect(self.actionCopy.setEnabled)
        QApplication.clipboard().dataChanged.connect(self.clipboardDataChanged)
        
        self.actionRun.triggered.connect(self.scannoCheck)       
        self.logPane.lineMatchChanged.connect(self.logLineMatchChanged)

    def closeEvent(self, e):
        if self.maybeSave():
            e.accept()
        else:
            e.ignore()

    def setupFileActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("File Actions")
        self.addToolBar(tb)

        menu = QMenu("&File", self)
        self.menuBar().addMenu(menu)

        self.actionNew = QAction("&New", self, priority=QAction.LowPriority,
                shortcut=QKeySequence.New, triggered=self.fileNew)
        tb.addAction(self.actionNew)
        menu.addAction(self.actionNew)

        self.actionOpen = QAction("&Open...", self, shortcut=QKeySequence.Open,
                triggered=self.fileOpen)
        tb.addAction(self.actionOpen)
        menu.addAction(self.actionOpen)
        menu.addSeparator()

        self.actionSave = QAction("&Save", self, shortcut=QKeySequence.Save,
                triggered=self.fileSave, enabled=False)
        tb.addAction(self.actionSave)
        menu.addAction(self.actionSave)

        self.actionSaveAs = QAction("Save &As...", self, priority=QAction.LowPriority,
                shortcut=Qt.CTRL + Qt.SHIFT + Qt.Key_S, triggered=self.fileSaveAs)
        menu.addAction(self.actionSaveAs)
        menu.addSeparator()
 
        self.actionQuit = QAction("&Quit", self, shortcut=QKeySequence.Quit, triggered=self.close)
        menu.addAction(self.actionQuit)

    def setupEditActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("Edit Actions")
        self.addToolBar(tb)

        menu = QMenu("&Edit", self)
        self.menuBar().addMenu(menu)

        self.actionUndo = QAction("&Undo", self, shortcut=QKeySequence.Undo)
        tb.addAction(self.actionUndo)
        menu.addAction(self.actionUndo)

        self.actionRedo = QAction("&Redo", self, priority=QAction.LowPriority, shortcut=QKeySequence.Redo)
        tb.addAction(self.actionRedo)
        menu.addAction(self.actionRedo)
        menu.addSeparator()

        self.actionCut = QAction("Cu&t", self, priority=QAction.LowPriority, shortcut=QKeySequence.Cut)
        tb.addAction(self.actionCut)
        menu.addAction(self.actionCut)

        self.actionCopy = QAction("&Copy", self, priority=QAction.LowPriority, shortcut=QKeySequence.Copy)
        tb.addAction(self.actionCopy)
        menu.addAction(self.actionCopy)

        self.actionPaste = QAction("&Paste", self, priority=QAction.LowPriority,
                shortcut=QKeySequence.Paste, enabled=(len(QApplication.clipboard().text()) != 0))
        tb.addAction(self.actionPaste)
        menu.addAction(self.actionPaste)

    def setupTextActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("Format Actions")
        self.addToolBar(tb)

        tb = QToolBar(self)
        tb.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
        tb.setWindowTitle("Format Actions")
        self.addToolBarBreak(Qt.TopToolBarArea)
        self.addToolBar(tb)

        self.comboFont = QFontComboBox(tb)
        tb.addWidget(self.comboFont)
        self.comboFont.activated[str].connect(self.textFamily)

        self.comboSize = QComboBox(tb)
        self.comboSize.setObjectName("comboSize")
        tb.addWidget(self.comboSize)
        self.comboSize.setEditable(True)

        db = QFontDatabase()
        for size in db.standardSizes():
            self.comboSize.addItem('{}'.format(size))

        self.comboSize.activated[str].connect(self.textSize)
        self.comboSize.setCurrentIndex(self.comboSize.findText('{}'.format(QApplication.font().pointSize())))
        
    def setupRunActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("Run Actions")
        self.addToolBar(tb)

        menu = QMenu("Run", self)
        self.menuBar().addMenu(menu)

        self.actionRun = QAction(
                "&Run", self, shortcut=Qt.CTRL + Qt.Key_R)
        tb.addAction(self.actionRun)
        menu.addAction(self.actionRun)

        self.comboScannoFile = QComboBox(tb)
        self.comboScannoFile.setObjectName("comboScannoFile")
        tb.addWidget(self.comboScannoFile)
        self.comboScannoFile.setEditable(True)
        
        self.comboLogFile= QComboBox(tb)
        self.comboLogFile.setObjectName("comboLogFile")
        self.comboLogFile.setEditable(True)
        tb.addWidget(self.comboLogFile)
    
    def populateRunSettings(self):
        for f in self.scannoFiles():
            self.comboScannoFile.addItem(f)
        if self.defaultScannoFile:
            idx = self.comboScannoFile.findText(self.defaultScannoFile)
            self.comboScannoFile.setCurrentIndex(idx)

        self.comboLogFile.addItem('plog.txt')

    def loadSrc(self, src):
        if src:
            if not self.textPane.load(src):
                return False
        self.setCurrentFileName(src)
        return True

    def loadLog(self, log):
        if log:
            if not self.logPane.load(log):
                return False
            self.comboLogFile.clear()
            self.comboLogFile.addItem(log)
        else:
            self.logPane.clear()
            self.logPane.setEnabled(False)
            self.logBox.setTitle(self.tr('No log file loaded.'))
        return True
        
    def maybeSave(self):
        if not self.textPane.document().isModified():
            return True

        ret = QMessageBox.warning(self, 'GuiScannos',
                'The document has been modified.\n'
                'Do you want to save your changes?',
                QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)

        if ret == QMessageBox.Save:
            return self.fileSave()

        if ret == QMessageBox.Cancel:
            return False

        return True

    def setCurrentFileName(self, fileName=''):
        self.fileName = fileName
        self.textPane.document().setModified(False)

        if not fileName:
            shownName = 'untitled.txt'
            self.actionRun.setEnabled(False)
        else:
            shownName = QFileInfo(fileName).fileName()
            self.actionRun.setEnabled(True)

        self.setWindowTitle(self.tr('{}[*] - {}'.format(shownName, 'GUI Scannos')))
        self.setWindowModified(False)

    def fileNew(self):
        if self.maybeSave():
            self.textPane.clear()
            self.loadLog(None)  # clears logPane, logBox title, etc
            self.setCurrentFileName()

    def fileOpen(self):
        fn, _ = QFileDialog.getOpenFileName(self, 'Open File...', None, 'Text Files (*.txt);;All Files (*)')
        if fn:
            self.loadSrc(fn)
            self.loadLog(None)  # clears logPane, logBox title, etc

    def fileSave(self):
        if not self.fileName:
            return self.fileSaveAs()

        return self.textpane.save(self.fileName)

    def fileSaveAs(self):
        fn, _ = QFileDialog.getSaveFileName(self, "Save as...", None, "text files (*.txt);;All Files (*)")

        if not fn:
            return False

        self.setCurrentFileName(fn)
        return self.fileSave()

    def logLineMatchChanged(self):
        linenum = self.logPane.srcLineNum()
        col = self.logPane.srcColNum()
        s = self.logPane.srcScanno()
        self.textPane.setSelection(linenum, col, len(s))
    
    def textFamily(self, family):
        """Set font family for text and log panes."""
        
        self.textPane.setFontFamily(family)
        self.logPane.setFontFamily(family)

    def textSize(self, pointSize):
        """Set font size for text and log panes."""
        
        self.textPane.setFontPointSize(pointSize)
        self.logPane.setFontPointSize(pointSize)

    def clipboardDataChanged(self):
        self.actionPaste.setEnabled(len(QApplication.clipboard().text()) != 0)

    def about(self):
        QMessageBox.about(self, 'About', 'GUI for ppscannos.')

    def fontChanged(self, font):
        self.comboFont.setCurrentIndex(self.comboFont.findText(QFontInfo(font).family()))
        self.comboSize.setCurrentIndex(self.comboSize.findText('{}'.format(font.pointSize())))
        
    def scannoCheck(self):
        """Run ppscannos."""
        
        scannodir = os.path.dirname(self.ppscannos)
        cmd = sys.executable
        assert(cmd)
        scannoFile = self.comboScannoFile.currentText()
        if not scannoFile:
            scannoFile = self.defaultScannoFile
        scannoFile = scannodir + '/' + scannoFile
        src = self.fileName
        log = self.comboLogFile.currentText()
        if not log:
            log = './plog.txt'
        subprocess.call([cmd, self.ppscannos, '-s' + scannoFile, '-o' + log, '-i' + src])
        self.loadLog(log)
        self.logPane.setEnabled(True)
        
    def configure(self):
        """Configure application settings by way of a dialog."""
        
        dlg = ConfigDialog()
        if dlg.exec():
            self.setPPScannos(dlg.lineEditPPScannos.text())
            self.setDefaultScannoFile(dlg.comboScannoFiles.currentText())
            settings = QSettings(QApplication.organizationName(), QApplication.applicationName())
            settings.setValue('ppscannos', self.ppscannos)
            settings.setValue('defaultScannoFile', self.defaultScannoFile)

    def setPPScannos(self, s):
        self.ppscannos = s
        self.actionRun.setEnabled(self.ppscannos and os.path.exists(self.ppscannos))
        
    def scannoFiles(self):
        """Return list of .rc filenames (without path) that are in ppscannos directory."""
        
        if not self.ppscannos:
            return []
        return getRCFilesForDir(os.path.dirname(self.ppscannos))
        
    def setDefaultScannoFile(self, s):
        self.defaultScannoFile = s
        valid = False
        if self.defaultScannoFile and self.ppscannos and os.path.exists(self.ppscannos):
            if os.path.exists(os.path.dirname(self.ppscannos) + '/' + self.defaultScannoFile):
                valid = True
        self.actionRun.setEnabled(valid)
        
    def initializeSettings(self):
        """Load persistent config settings."""
        
        settings = QSettings()
        s = settings.value('ppscannos', type=str)
        if not s:
            # try the default
            s = os.path.expanduser('~') + '/ppscannos1/ppscannos1.py'
            #s = os.environ['HOME'] + '/ppscannos1/ppscannos1.py'
        self.setPPScannos(s)
        
        s = settings.value('defaultScannoFile', type=str)
        if (not s) and self.ppscannos:
            # try the default
            lst = getRCFilesForDir(os.path.dirname(self.ppscannos))
            if len(lst):
                # prefer 'regex.rc'; otherwise use the first one
                s = lst[0]
                for f in lst:
                    if f == 'regex.rc':
                        s = f
                        break
        self.setDefaultScannoFile(s)