Example #1
0
class TRContourSelect(QtGui.QVBoxLayout):
    # - Split/Break contour
    def __init__(self):
        super(TRContourSelect, self).__init__()

        # -- Init
        self.table_dict = {0: {0: None}}  # Empty table
        self.layer_names = []  # Empty layer list
        #self.table_columns = 'N,Shape,Contour,X,Y,Type,Relative'.split(',')
        self.table_columns = 'N,Sh,Cn,X,Y,Type,Rel'.split(',')

        # -- Widgets
        self.lay_head = QtGui.QGridLayout()

        self.edt_glyphName = QtGui.QLineEdit()
        self.cmb_layer = QtGui.QComboBox()

        self.btn_refresh = QtGui.QPushButton('&Refresh')
        self.btn_apply = QtGui.QPushButton('&Apply')
        self.btn_apply.setEnabled(False)

        # -- Build Layout
        self.lay_head.addWidget(QtGui.QLabel('G:'), 0, 0, 1, 1)
        self.lay_head.addWidget(self.edt_glyphName, 0, 1, 1, 5)
        self.lay_head.addWidget(self.btn_refresh, 0, 6, 1, 2)
        self.lay_head.addWidget(QtGui.QLabel('L:'), 1, 0, 1, 1)
        self.lay_head.addWidget(self.cmb_layer, 1, 1, 1, 5)
        self.lay_head.addWidget(self.btn_apply, 1, 6, 1, 2)
        self.addLayout(self.lay_head)

        # -- Node List Table
        self.tab_nodes = TRTableView(self.table_dict)

        self.addWidget(self.tab_nodes)
        #self.refresh() # Build Table

        self.btn_refresh.clicked.connect(lambda: self.refresh())
        self.cmb_layer.currentIndexChanged.connect(lambda: self.changeLayer())

        # -- Table Styling
        self.tab_nodes.horizontalHeader().setStretchLastSection(False)
        self.tab_nodes.setSortingEnabled(True)
        self.tab_nodes.horizontalHeader().sortIndicatorChanged.connect(
            lambda: self.tab_nodes.resizeColumnsToContents())
        self.tab_nodes.verticalHeader().hide()
        self.tab_nodes.resizeColumnsToContents()
        self.tab_nodes.selectionModel().selectionChanged.connect(
            self.selectionChanged)
        self.tab_nodes.itemChanged.connect(self.valueChanged)

    def refresh(self, layer=None):
        # - Init
        self.glyph = eGlyph()
        self.edt_glyphName.setText(eGlyph().name)

        self.table_dict = {}
        node_count = 0

        # - Populate layers
        if layer is None:
            self.layer_names = [
                item.name for item in self.glyph.layers()
                if '#' not in item.name
            ]
            self.cmb_layer.clear()
            self.cmb_layer.addItems(self.layer_names)
            self.cmb_layer.setCurrentIndex(
                self.layer_names.index(self.glyph.activeLayer().name))

        # - Populate table
        try:  # Dirty Quick Fix - Solve later
            for sID, shape in enumerate(self.glyph.shapes(layer)):
                for cID, contour in enumerate(shape.contours):
                    for nID, node in enumerate(contour.nodes()):

                        table_values = [
                            node_count, sID, cID,
                            round(node.x, 2),
                            round(node.y, 2), node.type,
                            round(eNode(node).distanceToPrev(), 2)
                        ]

                        self.table_dict[node_count] = OrderedDict(
                            zip(self.table_columns, table_values))
                        node_count += 1

            self.tab_nodes.setTable(self.table_dict, (False, False))
            self.tab_nodes.resizeColumnsToContents()

        except AttributeError:
            pass

    def doCheck(self):
        if self.glyph.fg.id != fl6.CurrentGlyph(
        ).id and self.glyph.fl.name != fl6.CurrentGlyph().name:
            print '\nERRO:\tGlyph mismatch:\n\tCurrent active glyph: %s\n\tOutline panel glyph: %s' % (
                fl6.CurrentGlyph(), self.glyph.fg)
            print 'WARN:\tNo action taken! Forcing refresh!'
            self.refresh()
            return 0
        return 1

    def changeLayer(self):
        if self.doCheck():
            self.refresh(self.cmb_layer.currentText)

    def selectionChanged(self):
        if self.doCheck():
            if self.cmb_layer.currentText == self.glyph.activeLayer().name:
                # - Prepare
                self.glyph.fl.unselectAllNodes()

                # - Process
                for cel_coords in self.tab_nodes.selectionModel(
                ).selectedIndexes:
                    #print self.tab_nodes.item(cel_coords.row(), cel_coords.column()).text()
                    selected_nid = int(
                        self.tab_nodes.item(cel_coords.row(), 0).text())
                    self.glyph.nodes(self.cmb_layer.currentText
                                     )[selected_nid].selected = True

                # - Finish
                self.glyph.updateObject(self.glyph.fl, verbose=False)

    def valueChanged(self, item):
        if self.doCheck():
            #print item.text(), item.row()
            try:  # Dirty Quick Fix - Solve later
                # - Init
                x_col, y_col = self.table_columns.index(
                    'X'), self.table_columns.index('Y')
                active_nid = int(self.tab_nodes.item(item.row(), 0).text())

                # - Process
                if item.column() == x_col or item.column() == y_col:
                    new_x = float(
                        self.tab_nodes.item(item.row(), x_col).text())
                    new_y = float(
                        self.tab_nodes.item(item.row(), y_col).text())

                    active_node = eNode(
                        self.glyph.nodes(
                            self.cmb_layer.currentText)[active_nid])
                    active_node.reloc(new_x, new_y)

                    # -- Finish
                    self.glyph.update()
                    self.glyph.updateObject(self.glyph.fl, verbose=False)

            except AttributeError:
                pass
Example #2
0
class TRGlyphInfo(QtGui.QVBoxLayout):
    # - Split/Break contour
    def __init__(self):
        super(TRGlyphInfo, self).__init__()

        # -- Init
        self.table_dict = {0: {0: None}}  # Empty table

        # -- Widgets
        self.lay_head = QtGui.QGridLayout()

        self.edt_glyphName = QtGui.QLineEdit()
        self.edt_glyphsSeq = QtGui.QLineEdit()
        self.edt_glyphName.setToolTip('Current Glyph Name.')
        self.edt_glyphsSeq.setToolTip(
            'Manual entry for Glyph names to populate stats info. Separated by SPACE'
        )

        self.cmb_query = QtGui.QComboBox()
        self.cmb_charset = QtGui.QComboBox()
        self.cmb_query.setToolTip('Select query type.')
        self.cmb_charset.setToolTip('Select character set to compare with.')

        # --- Add queries
        self.query_list = [
            '(BBox) Bounding Box Width', '(BBox) Bounding Box Height',
            '(Metrics) Advance Width', '(Metrics) Left Side-bearing',
            '(Metrics) Right Side-bearing'
        ]

        self.cmb_query.addItems(self.query_list)

        self.btn_refresh = QtGui.QPushButton('&Refresh')
        self.btn_populate = QtGui.QPushButton('&Populate')
        self.btn_get = QtGui.QPushButton('&Window')
        self.btn_probe = QtGui.QPushButton('Glyph')
        self.btn_units = QtGui.QPushButton('Percent')

        self.btn_refresh.setToolTip('Refresh active glyph and table.')
        self.btn_populate.setToolTip(
            'Populate character set selector from current font.')
        self.btn_get.setToolTip('Get current string from active Glyph Window.')
        self.btn_probe.setToolTip(
            'Toggle between Row (Glyph) or Column (Layer) based comparison.')
        self.btn_units.setToolTip(
            'Toggle the results beeing shown as (Units) or (Percent).')

        self.btn_probe.setCheckable(True)
        self.btn_units.setCheckable(True)
        self.btn_probe.setChecked(False)
        self.btn_units.setChecked(False)

        # !!! Disable for now
        self.cmb_charset.setEnabled(False)
        self.btn_populate.setEnabled(False)

        # -- Build Layout
        self.lay_head.addWidget(QtGui.QLabel('G:'), 0, 0, 1, 1)
        self.lay_head.addWidget(self.edt_glyphName, 0, 1, 1, 5)
        self.lay_head.addWidget(self.btn_refresh, 0, 6, 1, 2)
        #self.lay_head.addWidget(QtGui.QLabel('C:'),	1,0,1,1)
        #self.lay_head.addWidget(self.cmb_charset,	1,1,1,5)
        #self.lay_head.addWidget(self.btn_populate,	1,6,1,2)
        self.lay_head.addWidget(QtGui.QLabel('C:'), 2, 0, 1, 1)
        self.lay_head.addWidget(self.edt_glyphsSeq, 2, 1, 1, 5)
        self.lay_head.addWidget(self.btn_get, 2, 6, 1, 2)
        self.lay_head.addWidget(QtGui.QLabel('Q:'), 3, 0, 1, 1)
        self.lay_head.addWidget(self.cmb_query, 3, 1, 1, 5)
        self.lay_head.addWidget(self.btn_probe, 3, 6, 1, 2)
        self.addLayout(self.lay_head)

        # -- Table
        self.tab_stats = TRTableView(self.table_dict)
        #self.refresh()

        # -- Note/Descriotion
        self.addWidget(self.tab_stats)
        self.addWidget(self.btn_units)

        # -- Addons
        self.btn_refresh.clicked.connect(self.refresh)
        self.btn_populate.clicked.connect(self.populate)
        self.btn_get.clicked.connect(self.get_string)
        self.btn_probe.clicked.connect(self.toggle_query)
        self.btn_units.clicked.connect(self.toggle_units)
        self.cmb_query.currentIndexChanged.connect(self.refresh)

        # -- Table Styling
        self.tab_stats.horizontalHeader().setStretchLastSection(False)
        self.tab_stats.resizeColumnsToContents()
        self.tab_stats.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        self.tab_stats.selectionModel().selectionChanged.connect(
            self.change_selection)

    def populate(self):
        font = pFont()
        self.glyphNames = font.getGlyphNamesDict()
        self.cmb_charset.addItems(sorted(self.glyphNames.keys()))

    def get_string(self):
        workspace = pWorkspace()
        glyphsSeq = ' '.join(
            sorted(
                list(
                    set([
                        glyph.name for glyph in workspace.getTextBlockGlyphs()
                    ]))))
        self.edt_glyphsSeq.setText(glyphsSeq)

    def refresh(self, layer=None):
        # - Init
        font = pFont()
        glyph = eGlyph()
        pLayers = (
            True, True, False, False
        )  # !!! Quickfix: Stats crashes FL after mode switch + refresh
        wLayers = glyph._prepareLayers(pLayers)
        self.table_data, self.table_proc = {}, {}

        current_glyph_name = eGlyph().name
        self.edt_glyphName.setText(current_glyph_name)

        # - Populate table
        process_glyph_names = [current_glyph_name
                               ] + self.edt_glyphsSeq.text.split(' ') if len(
                                   self.edt_glyphsSeq.text) else [
                                       current_glyph_name
                                   ]

        for glyph_name in process_glyph_names:
            wGlyph = font.glyph(glyph_name)
            self.table_data[glyph_name] = {
                layer: self.process_query(wGlyph, layer,
                                          self.cmb_query.currentText)
                for layer in wLayers
            }

        self.tab_stats.setTable(self.table_data)
        self.tab_stats.resizeColumnsToContents()

    def change_selection(self):
        # - Helper for avoiding ZeroDivision error
        def noZero(value):
            return value if value != 0 else 1

        # - Init
        base_index = self.tab_stats.selectionModel().selectedIndexes[0]
        base_name = self.tab_stats.verticalHeaderItem(base_index.row()).text()
        base_layer = self.tab_stats.horizontalHeaderItem(
            base_index.column()).text()

        for glyph_name, glyph_layers in self.table_data.iteritems():
            if not self.btn_probe.isChecked():
                if self.btn_units.isChecked():
                    self.table_proc[glyph_name] = {
                        layer_name: -round(
                            self.table_data[base_name][layer_name] -
                            layer_value, 2)
                        for layer_name, layer_value in
                        glyph_layers.iteritems()
                    }
                else:
                    self.table_proc[glyph_name] = {
                        layer_name: '%s %%' % round(
                            ratfrac(
                                noZero(layer_value),
                                noZero(
                                    self.table_data[base_name][layer_name])),
                            2)
                        for layer_name, layer_value in
                        glyph_layers.iteritems()
                    }
            else:
                if self.btn_units.isChecked():
                    self.table_proc[glyph_name] = {
                        layer_name: -round(
                            self.table_data[glyph_name][base_layer] -
                            layer_value, 2)
                        for layer_name, layer_value in
                        glyph_layers.iteritems()
                    }
                else:
                    self.table_proc[glyph_name] = {
                        layer_name: '%s %%' % round(
                            ratfrac(
                                noZero(layer_value),
                                noZero(
                                    self.table_data[glyph_name][base_layer])),
                            2)
                        for layer_name, layer_value in
                        glyph_layers.iteritems()
                    }

        self.tab_stats.setTable(self.table_proc)
        self.tab_stats.resizeColumnsToContents()

    def toggle_query(self):
        if self.btn_probe.isChecked():
            self.btn_probe.setText('Layer')
            self.tab_stats.setSelectionBehavior(
                QtGui.QAbstractItemView.SelectColumns)
        else:
            self.btn_probe.setText('Glyph')
            self.tab_stats.setSelectionBehavior(
                QtGui.QAbstractItemView.SelectRows)

    def toggle_units(self):
        if self.btn_units.isChecked():
            self.btn_units.setText('Units')
        else:
            self.btn_units.setText('Percent')

    def process_query(self, glyph, layer, query):
        if 'bbox' in query.lower() and 'width' in query.lower():
            return glyph.getBounds(layer).width()
        if 'bbox' in query.lower() and 'height' in query.lower():
            return glyph.getBounds(layer).height()
        if 'metrics' in query.lower() and 'advance' in query.lower():
            return glyph.getAdvance(layer)
        if 'metrics' in query.lower() and 'left' in query.lower():
            return glyph.getLSB(layer)
        if 'metrics' in query.lower() and 'right' in query.lower():
            return glyph.getRSB(layer)
Example #3
0
class TRSmartCorner(QtGui.QVBoxLayout):
	# - Split/Break contour 
	def __init__(self, parentWidget):
		super(TRSmartCorner, self).__init__()
		self.upper_widget = parentWidget

		# -- Init
		self.active_font = pFont()
		self.builder = None
		self.font_masters = self.active_font.masters()
		self.empty_preset = lambda row: OrderedDict([(row, OrderedDict([('Preset', 'Preset %s' %row)] + [(master, '0') for master in self.font_masters]))])
		self.table_dict = self.empty_preset(0)
		self.last_preset = 0

		# -- Widgets
		self.lay_head = QtGui.QGridLayout()

		self.edt_glyphName = QtGui.QLineEdit()
		self.edt_glyphName.setPlaceholderText('Glyph name')

		# -- Buttons
		self.btn_getBuilder = QtGui.QPushButton('Set &Builder')
		self.btn_findBuilder = QtGui.QPushButton('&From Font')
		self.btn_addPreset = QtGui.QPushButton('Add')
		self.btn_delPreset = QtGui.QPushButton('Remove')
		self.btn_resetPreset = QtGui.QPushButton('Reset')
		self.btn_loadPreset = QtGui.QPushButton('&Load Presets')
		self.btn_savePreset = QtGui.QPushButton('&Save Presets')
		self.btn_apply_smartCorner = QtGui.QPushButton('&Apply Smart Corner')
		self.btn_remove_smartCorner = QtGui.QPushButton('R&emove Smart Corner')
		self.btn_remove_presetCorner = QtGui.QPushButton('&Find and Remove')

		self.btn_apply_smartCorner.setToolTip('Apply Smart Corner preset on SELECTED nodes.')
		self.btn_remove_smartCorner.setToolTip('Remove Smart Corner on SELECTED nodes.')
		self.btn_remove_presetCorner.setToolTip('Find and remove all Smart Corners that equal the currently selected preset.')


		self.btn_apply_round = QtGui.QPushButton('&Round')
		self.btn_apply_mitre = QtGui.QPushButton('&Mitre')
		self.btn_apply_overlap = QtGui.QPushButton('&Overlap')
		self.btn_apply_trap = QtGui.QPushButton('&Trap')
		self.btn_rebuild = QtGui.QPushButton('Rebuild corner')

		self.btn_getBuilder.setMinimumWidth(70)
		self.btn_findBuilder.setMinimumWidth(70)
		self.btn_apply_round.setMinimumWidth(70)
		self.btn_apply_mitre.setMinimumWidth(70)
		self.btn_apply_overlap.setMinimumWidth(70)
		self.btn_apply_trap.setMinimumWidth(70)
		self.btn_rebuild.setMinimumWidth(70)

		self.btn_addPreset.setMinimumWidth(70)
		self.btn_delPreset.setMinimumWidth(70)
		self.btn_loadPreset.setMinimumWidth(140)
		self.btn_savePreset.setMinimumWidth(140)
		self.btn_apply_smartCorner.setMinimumWidth(140)
		self.btn_remove_smartCorner.setMinimumWidth(140)
		self.btn_remove_presetCorner.setMinimumWidth(140)

		self.btn_getBuilder.setCheckable(True)
		self.btn_getBuilder.setChecked(False)
		self.btn_findBuilder.setEnabled(False)
		self.btn_apply_round.setEnabled(False)

		self.btn_getBuilder.clicked.connect(lambda: self.getBuilder())
		self.btn_addPreset.clicked.connect(lambda: self.preset_modify(False))
		self.btn_delPreset.clicked.connect(lambda: self.preset_modify(True))
		self.btn_resetPreset.clicked.connect(lambda: self.preset_reset())
		self.btn_loadPreset.clicked.connect(lambda: self.preset_load())
		self.btn_savePreset.clicked.connect(lambda: self.preset_save())

		self.btn_apply_smartCorner.clicked.connect(lambda: self.apply_SmartCorner(False))
		self.btn_remove_smartCorner.clicked.connect(lambda: self.apply_SmartCorner(True))
		self.btn_remove_presetCorner.clicked.connect(lambda: self.remove_SmartCorner())
		
		#self.btn_apply_round.clicked.connect(lambda: self.apply_round())
		self.btn_apply_mitre.clicked.connect(lambda: self.apply_mitre(False))
		self.btn_apply_overlap.clicked.connect(lambda: self.apply_mitre(True))
		self.btn_apply_trap.clicked.connect(lambda: self.apply_trap())
		self.btn_rebuild.clicked.connect(lambda: self.rebuild())

		# -- Preset Table
		self.tab_presets = TRTableView(None)
		self.preset_reset()			

		# -- Build Layout
		self.lay_head.addWidget(QtGui.QLabel('Value Presets:'), 0,0,1,8)
		self.lay_head.addWidget(self.btn_loadPreset,			1,0,1,4)
		self.lay_head.addWidget(self.btn_savePreset,			1,4,1,4)
		self.lay_head.addWidget(self.btn_addPreset,				2,0,1,2)
		self.lay_head.addWidget(self.btn_delPreset,				2,2,1,2)
		self.lay_head.addWidget(self.btn_resetPreset,			2,4,1,4)
		self.lay_head.addWidget(self.tab_presets,				3,0,5,8)

		self.lay_head.addWidget(QtGui.QLabel('Corner Actions:'),10, 0, 1, 8)
		self.lay_head.addWidget(self.btn_apply_round,			11, 0, 1, 2)
		self.lay_head.addWidget(self.btn_apply_mitre,			11, 2, 1, 2)
		self.lay_head.addWidget(self.btn_apply_overlap,			11, 4, 1, 2)
		self.lay_head.addWidget(self.btn_apply_trap,			11, 6, 1, 2)
		self.lay_head.addWidget(self.btn_rebuild,				12, 0, 1, 8)

		self.lay_head.addWidget(QtGui.QLabel('Smart Corner:'),	13,0,1,8)
		self.lay_head.addWidget(QtGui.QLabel('Builder: '),		14,0,1,1)
		self.lay_head.addWidget(self.edt_glyphName,				14,1,1,3)
		self.lay_head.addWidget(self.btn_getBuilder,			14,4,1,2)
		self.lay_head.addWidget(self.btn_findBuilder,			14,6,1,2)
		self.lay_head.addWidget(self.btn_remove_smartCorner,	15,0,1,4)
		self.lay_head.addWidget(self.btn_remove_presetCorner,	15,4,1,4)
		self.lay_head.addWidget(self.btn_apply_smartCorner,		16,0,1,8)

		self.addLayout(self.lay_head)

	# - Presets management ------------------------------------------------
	def preset_reset(self):
		self.builder = None
		self.active_font = pFont()
		self.font_masters = self.active_font.masters()
		
		self.table_dict = self.empty_preset(0)
		self.tab_presets.clear()
		self.tab_presets.setTable(self.table_dict, sortData=(False, False))
		self.tab_presets.horizontalHeader().setStretchLastSection(False)
		self.tab_presets.verticalHeader().hide()
		#self.tab_presets.resizeColumnsToContents()

	def preset_modify(self, delete=False):
		table_rawList = self.tab_presets.getTable(raw=True)
		
		if delete:
			for selection in self.tab_presets.selectionModel().selectedIndexes:
				table_rawList.pop(selection.row())
				print selection.row()

		new_entry = OrderedDict()
		
		for key, data in table_rawList:
			new_entry[key] = OrderedDict(data)

		if not delete: new_entry[len(table_rawList)] = self.empty_preset(len(table_rawList)).items()[0][1]
		self.tab_presets.setTable(new_entry, sortData=(False, False))

	def preset_load(self):
		fontPath = os.path.split(self.active_font.fg.path)[0]
		fname = QtGui.QFileDialog.getOpenFileName(self.upper_widget, 'Load presets from file', fontPath, 'TypeRig JSON (*.json)')
		
		if fname != None:
			with open(fname, 'r') as importFile:
				imported_data = json.load(importFile)
			
			# - Convert Data
			new_data = OrderedDict()
			for key, data in imported_data:
				new_data[key] = OrderedDict(data)

			self.tab_presets.setTable(new_data, sortData=(False, False))
			print 'LOAD:\t Font:%s; Presets loaded from: %s.' %(self.active_font.name, fname)

	def preset_save(self):
		fontPath = os.path.split(self.active_font.fg.path)[0]
		fname = QtGui.QFileDialog.getSaveFileName(self.upper_widget, 'Save presets to file', fontPath, 'TypeRig JSON (*.json)')
		
		if fname != None:
			with open(fname, 'w') as exportFile:
				json.dump(self.tab_presets.getTable(raw=True), exportFile)

			print 'SAVE:\t Font:%s; Presets saved to: %s.' %(self.active_font.name, fname)

	def getPreset(self):
		table_raw = self.tab_presets.getTable(raw=True)
		'''
		try:
			active_preset_index = self.tab_presets.selectionModel().selectedIndexes[0].row()
		except IndexError:
			active_preset_index = None
		'''
		active_preset_index = self.tab_presets.selectionModel().selectedIndexes[0].row()

		if active_preset_index is None: 
			active_preset_index = self.last_preset
		else:
			self.last_preset = active_preset_index

		return dict(table_raw[active_preset_index][1][1:])

	# - Basic Corner ------------------------------------------------
	def apply_mitre(self, doKnot=False):
		# - Init
		process_glyphs = getProcessGlyphs(pMode)
		active_preset = self.getPreset()	

		# - Process
		if len(process_glyphs):
			for glyph in process_glyphs:
				if glyph is not None:
					wLayers = glyph._prepareLayers(pLayers)
		
					for layer in reversed(wLayers):
						if layer in active_preset.keys():
							selection = glyph.selectedNodes(layer, filterOn=True, extend=eNode, deep=True)
							
							for node in reversed(selection):
								if not doKnot:
									node.cornerMitre(float(active_preset[layer]))
								else:
									node.cornerMitre(-float(active_preset[layer]), True)
					
					action = 'Mitre Corner' if not doKnot else 'Overlap Corner'
					glyph.update()
					glyph.updateObject(glyph.fl, '%s @ %s.' %(action, '; '.join(active_preset.keys())))

	def apply_trap(self):
		# - Init
		process_glyphs = getProcessGlyphs(pMode)
		active_preset = self.getPreset()	

		# - Process
		if len(process_glyphs):
			for glyph in process_glyphs:
				if glyph is not None:
					wLayers = glyph._prepareLayers(pLayers)
		
					for layer in reversed(wLayers):
						if layer in active_preset.keys():
							selection = glyph.selectedNodes(layer, filterOn=True, extend=eNode, deep=True)
							
							for node in reversed(selection):
								preset_values = tuple([float(item.strip()) for item in active_preset[layer].split(',')])
								node.cornerTrapInc(*preset_values)
					
					glyph.update()
					glyph.updateObject(glyph.fl, '%s @ %s.' %('Ink Trap', '; '.join(active_preset.keys())))

	def rebuild(self):
		# - Init
		process_glyphs = getProcessGlyphs(pMode)

		# - Process
		if len(process_glyphs):
			for glyph in process_glyphs:
				if glyph is not None:
					wLayers = glyph._prepareLayers(pLayers)
		
					for layer in wLayers:
						selection = glyph.selectedNodes(layer, filterOn=True, extend=eNode, deep=True)
						
						if len(selection) > 1:
							node_first = selection[0]
							node_last = selection[-1]
							
							line_in = node_first.getPrevLine() if node_first.getPrevOn(False) not in selection else node_first.getNextLine()
							line_out = node_last.getNextLine() if node_last.getNextOn(False) not in selection else node_last.getPrevLine()

							crossing = line_in & line_out

							node_first.smartReloc(*crossing)
							node_first.parent.removeNodesBetween(node_first.fl, node_last.getNextOn())
			
					glyph.update()
					glyph.updateObject(glyph.fl, 'Rebuild corner: %s nodes reduced; At layers: %s' %(len(selection), '; '.join(wLayers)))

	# - Smart Corner ------------------------------------------------
	def getBuilder(self):
		if self.btn_getBuilder.isChecked():
			if len(self.edt_glyphName.text):
				builder_glyph = self.active_font.glyph(self.edt_glyphName.text)
			else:
				builder_glyph = eGlyph()
				self.edt_glyphName.setText(builder_glyph.name)

			if builder_glyph is not None:
				temp_builder = builder_glyph.getBuilders()

				if len(temp_builder.keys()) and filter_name in temp_builder.keys():
					self.builder = temp_builder[filter_name][0]
					self.btn_getBuilder.setText('Release')
		else:
			self.builder = None
			self.edt_glyphName.clear()
			self.btn_getBuilder.setText('Set Builder')	

	def process_setFilter(self, glyph, shape, layer, builder, suffix='.old'):
		new_container = fl6.flShape()
		new_container.shapeData.name = shape.shapeData.name
		if len(shape.shapeData.name): shape.shapeData.name += suffix
		
		#!!!! TODO: transformation copy and delete

		new_container.include(shape, glyph.layer(layer))
		new_container.shapeBuilder = builder.clone()
		new_container.update()
		
		glyph.layer(layer).addShape(new_container)
		#glyph.layer(layer).update()

	def process_smartCorner(self, glyph, preset):
		wLayers = glyph._prepareLayers(pLayers)	
		nodes_at_shapes = {}	

		# - Build selection
		for work_layer in wLayers:
			if work_layer in preset.keys():
				# - Init
				selection_deep = [(item[0], item[2]) for item in glyph.selectedAtShapes(layer=work_layer, index=False, deep=True)]
				selection_shallow = [(item[0], item[2]) for item in glyph.selectedAtShapes(layer=work_layer, index=False, deep=False)]
				selection = selection_deep + selection_shallow
								
				# - Build note to shape reference
				nodes_at_shapes[work_layer] = [(shape, [node for shape, node in list(nodes)]) for shape, nodes in groupby(selection, key=itemgetter(0))]

		# - Process glyph
		for work_layer in wLayers:
			if work_layer in nodes_at_shapes.keys():

				# - Build filter if not present
				for work_shape, node_list in nodes_at_shapes[work_layer]:
					if len(glyph.containers(work_layer)):
						for container in glyph.containers(work_layer):
							if work_shape not in container.includesList: 
								self.process_setFilter(glyph, work_shape, work_layer, self.builder)
					else:
						self.process_setFilter(glyph, work_shape, work_layer, self.builder)


				# - Process the nodes
				process_nodes = [pNode(node) for shape, node_list in nodes_at_shapes[work_layer] for node in node_list]
				
				for work_node in process_nodes:
					angle_value = preset[work_layer]
					
					if 'DEL' not in angle_value.upper():
						work_node.setSmartAngle(float(angle_value))
					else:
						work_node.delSmartAngle()

		#glyph.update()
		#glyph.updateObject(glyph.fl, 'DONE:\t Glyph: %s; Filter: Smart corner; Parameters: %s' %(glyph.name, preset))

	def apply_SmartCorner(self, remove=False):
		# NOTE: apply and remove here apply only to soelected nodes.
		if self.builder is not None:
			# - Init
			process_glyphs = getProcessGlyphs(pMode)
			active_preset = self.getPreset()

			if remove: # Build a special preset that deletes
				active_preset = {key:'DEL' for key in active_preset.keys()} 
		
			# - Process
			if len(process_glyphs):
				for work_glyph in process_glyphs:
					if work_glyph is not None: 
						self.process_smartCorner(work_glyph, active_preset)
						
				self.update_glyphs(process_glyphs, True)
				print 'DONE:\t Filter: Smart Corner; Glyphs: %s' %'; '.join([g.name for g in process_glyphs])

		else:
			print 'ERROR:\t Please specify a Glyph with suitable Shape Builder (Smart corner) first!'

	def remove_SmartCorner(self):
		# Finds active preset in glyphs smart corners and removes them
		# - Init
		process_glyphs = getProcessGlyphs(pMode)
		active_preset = self.getPreset()

		# - Process
		if len(process_glyphs):
			for work_glyph in process_glyphs:
				if work_glyph is not None:
					# - Init
					wLayers = work_glyph._prepareLayers(pLayers)	
					smart_corners, target_corners = [], []
					
					# - Get all smart nodes/corners
					for layer in wLayers:
						for builder in work_glyph.getBuilders(layer)[filter_name]:
							smart_corners += builder.getSmartNodes()

						if len(smart_corners):
							for node in smart_corners:
								wNode = eNode(node)

								if wNode.getSmartAngleRadius() == float(active_preset[layer]):
									wNode.delSmartAngle()

			self.update_glyphs(process_glyphs, True)
			print 'DONE:\t Filter: Remove Smart Corner; Glyphs: %s' %'; '.join([g.name for g in process_glyphs])

	def update_glyphs(self, glyphs, complete=False):
		for glyph in glyphs:
			glyph.update()
			
			if not complete: # Partial update - contour only
				for contour in glyph.contours():
					contour.changed()
			else: # Full update - with undo snapshot
				glyph.updateObject(glyph.fl, verbose=False)