Beispiel #1
0
def visualize(content, target):
    parser = AstParser()
    ast = parser.parse(content, rule_name='ast')
    graph = Digraph(comment=argv[1])
    handle_node(ast, graph)
    handle_edges(ast.edges, graph)
    graph.render(target, view=False)
Beispiel #2
0
    def __init__(self, parent=None):
        QMainWindow.__init__(self)
        super(MyWindowClass, self).__init__(parent)
        self.installEventFilter(self)
        self.setupUi(self)

        # self.QSciEditor = tabEditor(parent=self)
        # self.QSciEditor.setIndentationGuides(True)

        model = QFileSystemModel()
        model.setRootPath(path_of_me)

        self.label.setText(os.path.basename(path_of_me))
        self.treeView.setModel(model)
        self.treeView.setRootIndex(model.index(path_of_me))
        self.treeView.hideColumn(1)
        self.treeView.hideColumn(2)
        self.treeView.hideColumn(3)
        self.treeView.setHeaderHidden(True)
        self.treeView.doubleClicked.connect(self.tabHandler)

        self.terminal = embterminal(self.pageLayout)
        # self.process = QtCore.QProcess(self)
        # self.terminal = QtWidgets.QWidget(self)
        # self.pageLayout.addWidget(self.terminal)
        # self.process.start('xterm', ['-into', str(self.terminal.winId())])

        # self.page.setLayout(layout)

        # self.infoBox.resize(531,0)
        # self.pageLayout
        self.tabWidget.tabCloseRequested.connect(self.delTab)
        self.shortcuts = []
        for key in self.keybindings:
            self.shortcuts.append(QShortcut(self))
            self.shortcuts[-1].setKey(key[0])
            self.shortcuts[-1].activated.connect(getattr(self, key[1]))

        self.treeOutline.title = "Outline View"  # Probably treeOutline will be a discrete class soon..
        self.treeOutline.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeOutline.customContextMenuRequested.connect(self.openMenu)
        self.treeOutline.clicked.connect(self.outline_clicked)
        self.cleanButton.clicked.connect(self.clean)
        # self.gridLayout.addWidget(self.QSciEditor)
        self.ast = AstParser()
        self.show()
    def __init__(self, handle):
        ShareableActivity.__init__(self, handle)

        self.old_eqs = []

        self.ml = MathLib()
        self.parser = AstParser(self.ml)

        # These will result in 'Ans <operator character>' being inserted
        self._chars_ans_diadic = [
            op[0] for op in self.parser.get_diadic_operators()
        ]
        try:
            self._chars_ans_diadic.remove('-')
        except:
            pass

        self.KEYMAP['multiply'] = self.ml.mul_sym
        self.KEYMAP['divide'] = self.ml.div_sym
        self.KEYMAP['equal'] = self.ml.equ_sym

        self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.select_reason = self.SELECT_SELECT
        self.buffer = u""
        self.showing_version = 0
        self.showing_error = False
        self.ans_inserted = False
        self.show_vars = False

        self.connect("key_press_event", self.keypress_cb)
        self.connect("destroy", self.cleanup_cb)
        self.color = sugar3.profile.get_color()

        self.layout = CalcLayout(self)
        self.label_entry = self.layout.label_entry
        self.text_entry = self.layout.text_entry
        self.last_eq_sig = None
        self.last_eqn_textview = None

        self.reset()
        self.layout.show_it()

        self.connect('joined', self._joined_cb)

        self.parser.log_debug_info()
Beispiel #4
0
    def __init__(self, handle):
        ShareableActivity.__init__(self, handle)

        self.old_eqs = []

        self.ml = MathLib()
        self.parser = AstParser(self.ml)

        # These will result in 'Ans <operator character>' being inserted
        self._chars_ans_diadic = [op[0]
                                  for op in self.parser.get_diadic_operators()]
        try:
            self._chars_ans_diadic.remove('-')
        except:
            pass

        self.KEYMAP['multiply'] = self.ml.mul_sym
        self.KEYMAP['divide'] = self.ml.div_sym
        self.KEYMAP['equal'] = self.ml.equ_sym

        self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.select_reason = self.SELECT_SELECT
        self.buffer = u""
        self.showing_version = 0
        self.showing_error = False
        self.ans_inserted = False
        self.show_vars = False

        self.connect("key_press_event", self.keypress_cb)
        self.connect("destroy", self.cleanup_cb)
        self.color = sugar3.profile.get_color()

        self.layout = CalcLayout(self)
        self.label_entry = self.layout.label_entry
        self.text_entry = self.layout.text_entry
        self.last_eq_sig = None
        self.last_eqn_textview = None

        self.reset()
        self.layout.show_it()

        self.connect('joined', self._joined_cb)

        self.parser.log_debug_info()
Beispiel #5
0
class Calculate(ShareableActivity):

    TYPE_FUNCTION = 1
    TYPE_OP_PRE = 2
    TYPE_OP_POST = 3
    TYPE_TEXT = 4

    SELECT_NONE = 0
    SELECT_SELECT = 1
    SELECT_TAB = 2

    KEYMAP = {
        'Return': lambda o: o.process(),
        'period': '.',
        'equal': '=',
        'plus': '+',
        'minus': '-',
        'asterisk': '*',
        'multiply': '×',
        'divide': '÷',
        'slash': '/',
        'BackSpace': lambda o: o.remove_character(-1),
        'Delete': lambda o: o.remove_character(1),
        'parenleft': '(',
        'parenright': ')',
        'exclam': '!',
        'ampersand': '&',
        'bar': '|',
        'asciicircum': '^',
        'less': '<',
        'greater': '>',
        'percent': '%',
        'comma': ',',
        'underscore': '_',
        'Left': lambda o: o.move_left(),
        'Right': lambda o: o.move_right(),
        'Up': lambda o: o.get_older(),
        'Down': lambda o: o.get_newer(),
        'colon': lambda o: o.label_entered(),
        'Home': lambda o: o.text_entry.set_position(0),
        'End': lambda o: o.text_entry.set_position(
            len(o.text_entry.get_text())),
        'Tab': lambda o: o.tab_complete(),
    }

    CTRL_KEYMAP = {
        'c': lambda o: o.text_copy(),
        'v': lambda o: o.text_paste(),
        'x': lambda o: o.text_cut(),
        'q': lambda o: o.close(),
        'a': lambda o: o.text_select_all(),
    }

    SHIFT_KEYMAP = {
        'Left': lambda o: o.expand_selection(-1),
        'Right': lambda o: o.expand_selection(1),
        'Home': lambda o: o.expand_selection(-1000),
        'End': lambda o: o.expand_selection(1000),
    }

    IDENTIFIER_CHARS = \
        "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ "

    def __init__(self, handle):
        ShareableActivity.__init__(self, handle)

        self.old_eqs = []

        self.ml = MathLib()
        self.parser = AstParser(self.ml)

        # These will result in 'Ans <operator character>' being inserted
        self._chars_ans_diadic = [op[0]
                                  for op in self.parser.get_diadic_operators()]
        if '-' in self._chars_ans_diadic:
            self._chars_ans_diadic.remove('-')

        self.KEYMAP['multiply'] = self.ml.mul_sym
        self.KEYMAP['divide'] = self.ml.div_sym
        self.KEYMAP['equal'] = self.ml.equ_sym

        self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.select_reason = self.SELECT_SELECT
        self.buffer = ""
        self.showing_version = 0
        self.showing_error = False
        self.ans_inserted = False
        self.show_vars = False

        self.connect("key_press_event", self.keypress_cb)
        self.connect("destroy", self.cleanup_cb)
        self.color = sugar3.profile.get_color()

        self.layout = CalcLayout(self)
        self.label_entry = self.layout.label_entry
        self.text_entry = self.layout.text_entry
        self.last_eq_sig = None
        self.last_eqn_textview = None

        self.reset()
        self.layout.show_it()

        self.connect('joined', self._joined_cb)

        self.parser.log_debug_info()

    def ignore_key_cb(self, widget, event):
        return True

    def cleanup_cb(self, arg):
        _logger.debug('Cleaning up...')

    def equation_pressed_cb(self, eqn):
        """Callback for when an equation box is clicked."""

        if isinstance(eqn.result, SVGImage):
            return True

        if len(eqn.label) > 0:
            text = eqn.label
        else:
            # don't insert plain text
            if type(eqn.result) in (bytes, str):
                text = ''
            else:
                text = self.parser.ml.format_number(eqn.result)
                text = text.rstrip('0').rstrip('.') if '.' in text else text

        self.button_pressed(self.TYPE_TEXT, text)
        return True

    def set_last_equation(self, eqn):
        """Set the 'last equation' TextView."""

        if self.last_eq_sig is not None:
            self.layout.last_eq.disconnect(self.last_eq_sig)
            self.last_eq_sig = None

        if not isinstance(eqn.result, ParserError):
            self.last_eq_sig = self.layout.last_eq.connect(
                'button-press-event',
                lambda a1, a2, e: self.equation_pressed_cb(e), eqn)

        self.layout.last_eq.set_buffer(eqn.create_lasteq_textbuf())

    def set_error_equation(self, eqn):
        """Set equation with error markers. Since set_last_equation implements
        this we can just forward the call."""
        self.set_last_equation(eqn)

    def clear_equations(self):
        """Clear the list of old equations."""
        self.old_eqs = []
        self.showing_version = 0

    def add_equation(self, eq, prepend=False, drawlasteq=False, tree=None):
        """
        Insert equation in the history list and set variable if assignment.
        Input:
            eq: the equation object
            prepend: if True, prepend to list, else append
            drawlasteq: if True, draw in 'last equation' textbox and queue the
            buffer to be added to the history next time an equation is added.
            tree: the parsed tree, this will be used to set the label variable
            so that the equation can be used symbolicaly.
            """
        if eq.equation is not None and len(eq.equation) > 0:
            if prepend:
                self.old_eqs.insert(0, eq)
            else:
                self.old_eqs.append(eq)

            self.showing_version = len(self.old_eqs)

        if self.last_eqn_textview is not None and drawlasteq:
            # Prepending here should be the opposite: prepend -> eqn on top.
            # We always own this equation
            self.layout.add_equation(self.last_eqn_textview, True,
                                     prepend=not prepend)
            self.last_eqn_textview = None

        if eq.label is not None and len(eq.label) > 0:
            w = self.create_var_textview(eq.label, eq.result)
            if w is not None:
                self.layout.add_variable(eq.label, w)

            if tree is None:
                tree = self.parser.parse(eq.equation)
            try:
                self.parser.set_var(eq.label, tree)
            except Exception as e:
                eq.result = ParseError(e.message, 0, "")
                self.set_error_equation(eq)
                return

        own = (eq.owner == self.get_owner_id())
        w = eq.create_history_object()
        w.connect('button-press-event', lambda w,
                  e: self.equation_pressed_cb(eq))
        if drawlasteq:
            self.set_last_equation(eq)

            # SVG images can't be plotted in last equation window
            if isinstance(eq.result, SVGImage):
                self.layout.add_equation(w, own, prepend=not prepend)
            else:
                self.last_eqn_textview = w
        else:
            self.layout.add_equation(w, own, prepend=not prepend)

    # FIXME: to be implemented
    def process_async(self, eqn):
        """Parse and process an equation asynchronously."""

    def process(self):
        """Parse the equation entered and show the result."""

        s = _s(self.text_entry.get_text())
        label = self.label_entry.get_text()
        _logger.debug('process(): parsing %r, label: %r', s, label)
        try:
            tree = self.parser.parse(s)
            res = self.parser.evaluate(tree)
        except ParserError as e:
            res = e
            self.showing_error = True

        if isinstance(res, str) and res.find('</svg>') > -1:
            res = SVGImage(data=res)

        _logger.debug('Result: %r', res)

        # Check whether assigning this label would cause recursion
        if not isinstance(res, ParserError) and len(label) > 0:
            lastpos = self.parser.get_var_used_ofs(label)
            if lastpos is not None:
                res = RuntimeError(
                    _('Can not assign label: will cause recursion'),
                    lastpos)

        # If parsing went ok, see if we have to replace the previous answer
        # to get a (more) exact result
        if self.ans_inserted and not isinstance(res, ParserError) \
                and not isinstance(res, SVGImage):
            ansvar = self.format_insert_ans()
            pos = s.find(ansvar)
            if len(ansvar) > 6 and pos != -1:
                s2 = s.replace(ansvar, 'LastEqn')
                _logger.debug(
                    'process(): replacing previous answer %r: %r', ansvar, s2)
                tree = self.parser.parse(s2)
                res = self.parser.evaluate(tree)

        if isinstance(res, ParserError):
            eqn = Equation(label, _n(s), res, self.color,
                           self.get_owner_id(), ml=self.ml)
            self.set_error_equation(eqn)
        else:
            eqn = Equation(label, _n(s), _n(str(res)), self.color,
                           self.get_owner_id(), ml=self.ml)
            self.add_equation(eqn, drawlasteq=True, tree=tree)
            self.send_message("add_eq", value=str(eqn))

            self.parser.set_var('Ans', eqn.result)

            # Setting LastEqn to the parse tree would certainly be faster,
            # however, it introduces recursion problems
            self.parser.set_var('LastEqn', eqn.result)

            self.showing_error = False
            self.ans_inserted = False
            self.text_entry.set_text('')
            self.label_entry.set_text('')

        return res is not None

    def create_var_textview(self, name, value):
        """Create a Gtk.TextView for a variable."""

        reserved = ["Ans", "LastEqn", "help"]
        if name in reserved:
            return None
        w = Gtk.TextView()
        w.modify_base(
            Gtk.StateType.NORMAL, Gdk.color_parse(self.color.get_fill_color()))
        w.modify_bg(
            Gtk.StateType.NORMAL,
            Gdk.color_parse(self.color.get_stroke_color()))
        w.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        w.set_border_window_size(Gtk.TextWindowType.LEFT, 4)
        w.set_border_window_size(Gtk.TextWindowType.RIGHT, 4)
        w.set_border_window_size(Gtk.TextWindowType.TOP, 4)
        w.set_border_window_size(Gtk.TextWindowType.BOTTOM, 4)
        w.connect('realize', _textview_realize_cb)
        buf = w.get_buffer()

        # TODO Fix for old Sugar 0.82 builds, red_float not available
        bright = (
            Gdk.color_parse(self.color.get_fill_color()).red_float +
            Gdk.color_parse(self.color.get_fill_color()).green_float +
            Gdk.color_parse(self.color.get_fill_color()).blue_float) / 3.0
        if bright < 0.5:
            col = Gdk.color_parse('white')
        else:
            col = Gdk.color_parse('black')

        tag = buf.create_tag(font=CalcLayout.FONT_SMALL_NARROW,
                             foreground=col)
        text = '%s:' % (name)
        buf.insert_with_tags(buf.get_end_iter(), text, tag)
        tag = buf.create_tag(font=CalcLayout.FONT_SMALL,
                             foreground=col)
        text = '%s' % (str(value))
        buf.insert_with_tags(buf.get_end_iter(), text, tag)

        return w

    def clear(self):
        self.text_entry.set_text('')
        self.text_entry.grab_focus()
        return True

    def reset(self):
        self.clear()
        return True

#
# Journal functions
#

    def write_file(self, file_path):
        """Write journal entries, Calculate Journal Version (cjv) 1.0"""

        _logger.info(_('Writing to journal (%s)'), file_path)

        f = open(file_path, 'w')
        f.write("cjv 1.0\n")

        sel = self.text_entry.get_selection_bounds()
        pos = self.text_entry.get_position()
        if len(sel) == 0:
            sel = (pos, pos)
            f.write("%s;%d;%d;%d\n" %
                    (self.text_entry.get_text(), pos, sel[0], sel[1]))

        # In reverse order
        for eq in self.old_eqs:
            f.write(str(eq))

        f.close()

    def read_file(self, file_path):
        """Read journal entries, version 1.0"""

        _logger.info('Reading from journal (%s)', file_path)

        f = open(file_path, 'r')
        str = f.readline().rstrip("\r\n")   # chomp
        k = str.split()
        if len(k) != 2:
            _logger.error('Unable to determine version')
            return False

        version = k[1]
        if len(version) > 1 and version[0:2] == "1.":
            _logger.info('Reading journal entry (version %s)', version)

            str = f.readline().rstrip("\r\n")
            k = str.split(';')
            if len(k) != 4:
                _logger.error('State line invalid (%s)', str)
                return False

            self.text_entry.set_text(k[0])
            self.text_entry.set_position(int(k[1]))
            if k[2] != k[3]:
                self.text_entry.select_region(int(k[2]), int(k[3]))

            self.clear_equations()
            for str in f:
                eq = Equation(eqnstr=str, ml=self.ml)
                self.add_equation(eq, prepend=False)

            return True
        else:
            _logger.error(
                'Unable to read journal entry, unknown version (%s)', version)
            return False

#
# User interaction functions
#

    def remove_character(self, dir):
        pos = self.text_entry.get_position()
        sel = self.text_entry.get_selection_bounds()
        if len(sel) == 0:
            if pos + dir <= len(self.text_entry.get_text()) and pos + dir >= 0:
                if dir < 0:
                    self.text_entry.delete_text(pos + dir, pos)
                    pos -= 1
                else:
                    self.text_entry.delete_text(pos, pos + dir)
                    pos += 1
        else:
            self.text_entry.delete_text(sel[0], sel[1])
        self.text_entry.grab_focus()
        self.text_entry.set_position(pos)

    def move_left(self):
        pos = self.text_entry.get_position()
        if pos > 0:
            pos -= 1
            self.text_entry.set_position(pos)
        self.text_entry.grab_focus()
        self.text_entry.set_position(pos)

    def move_right(self):
        pos = self.text_entry.get_position()
        if pos < len(self.text_entry.get_text()):
            pos += 1
            self.text_entry.set_position(pos)
        self.text_entry.grab_focus()
        self.text_entry.set_position(pos)

    def label_entered(self):
        if len(self.label_entry.get_text()) > 0:
            return
        pos = self.text_entry.get_position()
        str = self.text_entry.get_text()
        self.label_entry.set_text(str[:pos])
        self.text_entry.set_text(str[pos:])

    def tab_complete(self):

        # Get start of variable name
        str = self.text_entry.get_text()
        if len(str) == 0:
            return

        sel = self.text_entry.get_selection_bounds()
        if len(sel) == 0:
            end_ofs = self.text_entry.get_position()
        else:
            end_ofs = sel[0]
        start_ofs = end_ofs - 1
        while start_ofs > 0 and str[start_ofs - 1] in self.IDENTIFIER_CHARS:
            start_ofs -= 1
        if end_ofs - start_ofs <= 0:
            return False
        partial_name = str[start_ofs:end_ofs]
        _logger.debug('tab-completing %s...', partial_name)

        # Lookup matching variables
        vars = self.parser.get_names(start=partial_name)
        if len(vars) == 0:
            return False

        # Nothing selected, select first match
        if len(sel) == 0:
            name = vars[0]
            self.text_entry.set_text(str[:start_ofs] + name + str[end_ofs:])

        # Select next matching variable
        else:
            full_name = str[start_ofs:sel[1]]
            if full_name not in vars:
                name = vars[0]
            else:
                name = vars[(vars.index(full_name) + 1) % len(vars)]
            self.text_entry.set_text(str[:start_ofs] + name + str[sel[1]:])

        self.text_entry.set_position(start_ofs + len(name))
        self.text_entry.select_region(end_ofs, start_ofs + len(name))
        self.select_reason = self.SELECT_TAB
        return True

    # Selection related functions

    def expand_selection(self, dir):
        # logger.info('Expanding selection in dir %d', dir)
        sel = self.text_entry.get_selection_bounds()
        slen = len(self.text_entry.get_text())
        pos = self.text_entry.get_position()
        if len(sel) == 0:
            sel = (pos, pos)
        if dir < 0:
            # apparently no such thing as a cursor position during select
            newpos = max(0, sel[0] + dir)
            self.text_entry.set_position(newpos)
            self.text_entry.select_region(newpos, sel[1])
        elif dir > 0:
            newpos = min(sel[1] + dir, slen)
            self.text_entry.set_position(newpos)
            self.text_entry.select_region(sel[0], newpos)
        self.select_reason = self.SELECT_SELECT

    def text_copy(self):
        if self.layout.graph_selected is not None:
            self.clipboard.set_image(
                self.layout.graph_selected.get_child().get_pixbuf())
            self.layout.toggle_select_graph(self.layout.graph_selected)
        else:
            str = self.text_entry.get_text()
            sel = self.text_entry.get_selection_bounds()
            # _logger.info('text_copy, sel: %r, str: %s', sel, str)
            if len(sel) == 2:
                (start, end) = sel
                self.clipboard.set_text(str[start:end], -1)

    def text_select_all(self):
        end = self.text_entry.get_text_length()
        self.text_entry.select_region(0, end)

    def get_clipboard_text(self):
        text = self.clipboard.wait_for_text()
        if text is None:
            return ""
        else:
            return text

    def text_paste(self):
        self.button_pressed(self.TYPE_TEXT, self.get_clipboard_text())

    def text_cut(self):
        self.text_copy()
        self.remove_character(1)

    def keypress_cb(self, widget, event):
        if not self.text_entry.is_focus():
            return

        key = Gdk.keyval_name(event.keyval)
        if event.hardware_keycode == 219:
            if (event.get_state() & Gdk.ModifierType.SHIFT_MASK):
                key = 'divide'
            else:
                key = 'multiply'
        _logger.debug('Key: %s (%r, %r)', key,
                      event.keyval, event.hardware_keycode)

        if event.get_state() & Gdk.ModifierType.CONTROL_MASK:
            if key in self.CTRL_KEYMAP:
                f = self.CTRL_KEYMAP[key]
                return f(self)
        elif (event.get_state() & Gdk.ModifierType.SHIFT_MASK) and \
                key in self.SHIFT_KEYMAP:
            f = self.SHIFT_KEYMAP[key]
            return f(self)
        elif str(key) in self.IDENTIFIER_CHARS:
            self.button_pressed(self.TYPE_TEXT, key)
        elif key in self.KEYMAP:
            f = self.KEYMAP[key]
            if isinstance(f, str) or \
                    isinstance(f, str):
                self.button_pressed(self.TYPE_TEXT, f)
            else:
                return f(self)

        return True

    def get_older(self):
        self.showing_version = max(0, self.showing_version - 1)
        if self.showing_version == len(self.old_eqs) - 1:
            self.buffer = self.text_entry.get_text()
        if len(self.old_eqs) > 0:
            self.text_entry.set_text(
                self.old_eqs[self.showing_version].equation)

    def get_newer(self):
        self.showing_version = min(len(self.old_eqs), self.showing_version + 1)
        if self.showing_version == len(self.old_eqs):
            self.text_entry.set_text(self.buffer)
        else:
            self.text_entry.set_text(
                self.old_eqs[self.showing_version].equation)

    def add_text(self, input_str):
        self.button_pressed(self.TYPE_TEXT, input_str)

    # This function should be split up properly
    def button_pressed(self, str_type, input_str):
        sel = self.text_entry.get_selection_bounds()
        pos = self.text_entry.get_position()

        # If selection by tab completion just manipulate end
        if len(sel) == 2 and self.select_reason != self.SELECT_SELECT:
            pos = sel[1]
            sel = ()

        self.text_entry.grab_focus()
        if len(sel) == 2:
            (start, end) = sel
            text = self.text_entry.get_text()
        elif len(sel) != 0:
            _logger.error('button_pressed(): len(sel) != 0 or 2')
            return False

        if str_type == self.TYPE_FUNCTION:
            if len(sel) == 0:
                self.text_entry.insert_text(input_str + '()', pos)
                self.text_entry.set_position(pos + len(input_str) + 1)
            else:
                self.text_entry.set_text(
                    text[:start] + input_str + '(' + text[start:end] + ')' +
                    text[end:])
                self.text_entry.set_position(end + len(input_str) + 2)

        elif str_type == self.TYPE_OP_PRE:
            if len(sel) == 2:
                pos = start
            self.text_entry.insert_text(input_str, pos)
            self.text_entry.set_position(pos + len(input_str))

        elif str_type == self.TYPE_OP_POST:
            if len(sel) == 2:
                pos = end
            elif pos == 0:
                ans = self.format_insert_ans()
                input_str = ans + input_str
                self.ans_inserted = True
            self.text_entry.insert_text(input_str, pos)
            self.text_entry.set_position(pos + len(input_str))

        elif str_type == self.TYPE_TEXT:
            tlen = len(self.text_entry.get_text())
            if len(sel) == 2:
                tlen -= (end - start)

            if tlen == 0 and (input_str in self._chars_ans_diadic) and \
                    self.parser.get_var('Ans') is not None and \
                    type(self.parser.get_var('Ans')) is not str:
                ans = self.format_insert_ans()
                self.text_entry.set_text(ans + input_str)
                self.text_entry.set_position(len(ans) + len(input_str))
                self.ans_inserted = True
            elif len(sel) == 2:
                self.text_entry.set_text(text[:start] + input_str + text[end:])
                self.text_entry.set_position(
                    pos + start - end + len(input_str))
            else:
                self.text_entry.insert_text(input_str, pos)
                self.text_entry.set_position(pos + len(input_str))

        else:
            _logger.error(_('button_pressed(): invalid type'))

    def message_received(self, msg, **kwargs):
        _logger.debug('Message received: %s(%r)', msg, kwargs)

        value = kwargs.get('value', None)
        if msg == "add_eq":
            eq = Equation(eqnstr=str(value), ml=self.ml)
            self.add_equation(eq)
        elif msg == "req_sync":
            data = []
            for eq in self.old_eqs:
                data.append(str(eq))
            self.send_message("sync", value=data)
        elif msg == "sync":
            self.clear_equations()
            for eq_str in value:
                _logger.debug('receive_message: %s', str(eq_str))
                self.add_equation(Equation(eqnstr=str(eq_str)), ml=self.ml)

    def _joined_cb(self, gobj):
        _logger.debug('Requesting synchronization')
        self.send_message('req_sync')

    def format_insert_ans(self):
        ans = self.parser.get_var('Ans')
        if isinstance(ans, Rational):
            return str(ans)
        elif ans is not None:
            return self.ml.format_number(ans)
        else:
            return ''
Beispiel #6
0
class MyWindowClass(QMainWindow, Ui_MainWindow):

    keybindings = [
        (Qt.CTRL + Qt.Key_S, "save"),
        (Qt.CTRL + Qt.Key_Q, "quit"),
        (Qt.CTRL + Qt.Key_R, "refresh_outline"),
        (Qt.CTRL + Qt.Key_Space, "autocomplete"),
        (Qt.Key_F1, "testplace"),
    ]

    tabs = []

    def __init__(self, parent=None):
        QMainWindow.__init__(self)
        super(MyWindowClass, self).__init__(parent)
        self.installEventFilter(self)
        self.setupUi(self)

        # self.QSciEditor = tabEditor(parent=self)
        # self.QSciEditor.setIndentationGuides(True)

        model = QFileSystemModel()
        model.setRootPath(path_of_me)

        self.label.setText(os.path.basename(path_of_me))
        self.treeView.setModel(model)
        self.treeView.setRootIndex(model.index(path_of_me))
        self.treeView.hideColumn(1)
        self.treeView.hideColumn(2)
        self.treeView.hideColumn(3)
        self.treeView.setHeaderHidden(True)
        self.treeView.doubleClicked.connect(self.tabHandler)

        self.terminal = embterminal(self.pageLayout)
        # self.process = QtCore.QProcess(self)
        # self.terminal = QtWidgets.QWidget(self)
        # self.pageLayout.addWidget(self.terminal)
        # self.process.start('xterm', ['-into', str(self.terminal.winId())])

        # self.page.setLayout(layout)

        # self.infoBox.resize(531,0)
        # self.pageLayout
        self.tabWidget.tabCloseRequested.connect(self.delTab)
        self.shortcuts = []
        for key in self.keybindings:
            self.shortcuts.append(QShortcut(self))
            self.shortcuts[-1].setKey(key[0])
            self.shortcuts[-1].activated.connect(getattr(self, key[1]))

        self.treeOutline.title = "Outline View"  # Probably treeOutline will be a discrete class soon..
        self.treeOutline.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeOutline.customContextMenuRequested.connect(self.openMenu)
        self.treeOutline.clicked.connect(self.outline_clicked)
        self.cleanButton.clicked.connect(self.clean)
        # self.gridLayout.addWidget(self.QSciEditor)
        self.ast = AstParser()
        self.show()

    def createTab(self, filename, content):
        newTab = tabEditor(parent=self.tabWidget,
                           filepath=filename,
                           content=content)
        self.tabs.append(newTab)

    def delTab(self, index):
        widget = self.tabWidget.widget(index)
        if widget is not None:
            widget.deleteLater()
        self.tabWidget.removeTab(index)
        self.tabs[index].deleteLater()
        del (self.tabs[index])

    def checkFileTabIsOpen(self, filename):
        returnText = "False"
        for i in range(0, len(self.tabs)):
            tabText = str(self.tabWidget.tabText(i)).replace("&", "")
            # print(tabText)
            # print(filename)
            if tabText == filename:
                # print("Found file")
                returnText = "True"
                return i, returnText
        return 0, returnText

    def tabHandler(self, signal):
        file_path = self.treeView.model().filePath(signal)
        filename = file_path.split("/")[-1]
        index, result = self.checkFileTabIsOpen(filename)
        if result == "False":
            try:
                if os.path.isfile(file_path):
                    with open(file_path, 'r+') as currentFile:
                        self.createTab(file_path, currentFile.read())
                        self.tabWidget.setCurrentIndex(len(self.tabs) - 1)
                        self.refresh_outline()
            except Exception as e:
                print(e)
        else:
            self.tabWidget.setCurrentIndex(index)

    def save(self):
        current_tab = self.tabs[self.tabWidget.currentIndex()]
        file = open(current_tab._filepath, "w")
        file.write(current_tab.QSciEditor.text())
        file.close()
        self.refresh_outline()
        debug(current_tab._filepath + " is saved...")

    def quit(self):
        debug("quitting...")
        app.quit()

    def openMenu(self, position):
        menu = QMenu()
        menu.addAction(self.tr("Go to"))
        menu.exec_(self.treeOutline.viewport().mapToGlobal(position))

    def test(self, signal):
        file_path = self.treeView.model().filePath(signal)
        self.self.tabs[self.tabWidget.currentIndex(
        )].QSciEditor.filename = file_path  # Soon will be used to save to that file.
        # try:
        if os.path.isfile(file_path):
            with open(file_path, 'r+') as currentFile:
                self.self.tabs[
                    self.tabWidget.currentIndex()].QSciEditor.setText(
                        currentFile.read())
                self.refresh_outline()

    def refresh_outline(self):
        kinds = []
        kinds.append(CursorKind.FUNCTION_DECL)
        kinds.append(CursorKind.VAR_DECL)
        kinds.append(CursorKind.DECL_REF_EXPR)
        kinds.append(CursorKind.COMPOUND_STMT)
        # kinds.append(CursorKind.STRUCT_DECL)
        kinds.append(CursorKind.FIELD_DECL)

        outline = self.ast.get_outline(
            self.tabs[self.tabWidget.currentIndex()]._filepath, kinds)
        self.treeOutline.setModel(outline)

        # except Exception as e:
        #     print(e)

    def outline_clicked(self, index):
        loc = (self.treeOutline.model().itemFromIndex(index).location)
        debug("Node Location: " + str(loc.line) + "," + str(loc.column))
        self.tabs[self.tabWidget.currentIndex()].QSciEditor.setCursorPosition(
            loc.line - 1, loc.column - 1)
        self.tabs[self.tabWidget.currentIndex()].QSciEditor.ensureLineVisible(
            loc.line - 1)
        self.tabs[
            self.tabWidget.currentIndex()].QSciEditor.ensureCursorVisible()
        self.tabs[self.tabWidget.currentIndex()].QSciEditor.setFocus()

    def clean(self, signal):
        debug("clean...")

    def autocomplete(self):
        self.tabs[
            self.tabWidget.currentIndex()].QSciEditor.autoCompleteFromAll()
        debug("autocompleted...")

    def testplace(self):
        debug("This is for testing purposes...")
class Calculate(ShareableActivity):

    TYPE_FUNCTION = 1
    TYPE_OP_PRE = 2
    TYPE_OP_POST = 3
    TYPE_TEXT = 4

    SELECT_NONE = 0
    SELECT_SELECT = 1
    SELECT_TAB = 2

    KEYMAP = {
        'Return': lambda o: o.process(),
        'period': '.',
        'equal': '=',
        'plus': '+',
        'minus': '-',
        'asterisk': '*',
        'multiply': '×',
        'divide': '÷',
        'slash': '/',
        'BackSpace': lambda o: o.remove_character(-1),
        'Delete': lambda o: o.remove_character(1),
        'parenleft': '(',
        'parenright': ')',
        'exclam': '!',
        'ampersand': '&',
        'bar': '|',
        'asciicircum': '^',
        'less': '<',
        'greater': '>',
        'percent': '%',
        'comma': ',',
        'underscore': '_',
        'Left': lambda o: o.move_left(),
        'Right': lambda o: o.move_right(),
        'Up': lambda o: o.get_older(),
        'Down': lambda o: o.get_newer(),
        'colon': lambda o: o.label_entered(),
        'Home': lambda o: o.text_entry.set_position(0),
        'End':
        lambda o: o.text_entry.set_position(len(o.text_entry.get_text())),
        'Tab': lambda o: o.tab_complete(),
    }

    CTRL_KEYMAP = {
        'c': lambda o: o.text_copy(),
        'v': lambda o: o.text_paste(),
        'x': lambda o: o.text_cut(),
        'q': lambda o: o.close(),
        'a': lambda o: o.text_select_all(),
    }

    SHIFT_KEYMAP = {
        'Left': lambda o: o.expand_selection(-1),
        'Right': lambda o: o.expand_selection(1),
        'Home': lambda o: o.expand_selection(-1000),
        'End': lambda o: o.expand_selection(1000),
    }

    IDENTIFIER_CHARS = \
        u"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ "

    def __init__(self, handle):
        ShareableActivity.__init__(self, handle)

        self.old_eqs = []

        self.ml = MathLib()
        self.parser = AstParser(self.ml)

        # These will result in 'Ans <operator character>' being inserted
        self._chars_ans_diadic = [
            op[0] for op in self.parser.get_diadic_operators()
        ]
        try:
            self._chars_ans_diadic.remove('-')
        except:
            pass

        self.KEYMAP['multiply'] = self.ml.mul_sym
        self.KEYMAP['divide'] = self.ml.div_sym
        self.KEYMAP['equal'] = self.ml.equ_sym

        self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.select_reason = self.SELECT_SELECT
        self.buffer = u""
        self.showing_version = 0
        self.showing_error = False
        self.ans_inserted = False
        self.show_vars = False

        self.connect("key_press_event", self.keypress_cb)
        self.connect("destroy", self.cleanup_cb)
        self.color = sugar3.profile.get_color()

        self.layout = CalcLayout(self)
        self.label_entry = self.layout.label_entry
        self.text_entry = self.layout.text_entry
        self.last_eq_sig = None
        self.last_eqn_textview = None

        self.reset()
        self.layout.show_it()

        self.connect('joined', self._joined_cb)

        self.parser.log_debug_info()

    def ignore_key_cb(self, widget, event):
        return True

    def cleanup_cb(self, arg):
        _logger.debug('Cleaning up...')

    def equation_pressed_cb(self, eqn):
        """Callback for when an equation box is clicked"""

        if isinstance(eqn.result, SVGImage):
            return True

        if len(eqn.label) > 0:
            text = eqn.label
        else:
            # don't insert plain text
            if type(eqn.result) in (types.StringType, types.UnicodeType):
                text = ''
            else:
                text = self.parser.ml.format_number(eqn.result)
                text = text.rstrip('0').rstrip('.') if '.' in text else text

        self.button_pressed(self.TYPE_TEXT, text)
        return True

    def set_last_equation(self, eqn):
        """Set the 'last equation' TextView"""

        if self.last_eq_sig is not None:
            self.layout.last_eq.disconnect(self.last_eq_sig)
            self.last_eq_sig = None

        if not isinstance(eqn.result, ParserError):
            self.last_eq_sig = self.layout.last_eq.connect(
                'button-press-event',
                lambda a1, a2, e: self.equation_pressed_cb(e), eqn)

        self.layout.last_eq.set_buffer(eqn.create_lasteq_textbuf())

    def set_error_equation(self, eqn):
        """Set equation with error markers. Since set_last_equation implements
        this we can just forward the call."""
        self.set_last_equation(eqn)

    def clear_equations(self):
        """Clear the list of old equations."""
        self.old_eqs = []
        self.showing_version = 0

    def add_equation(self, eq, prepend=False, drawlasteq=False, tree=None):
        """
        Insert equation in the history list and set variable if assignment.
        Input:
            eq: the equation object
            prepend: if True, prepend to list, else append
            drawlasteq: if True, draw in 'last equation' textbox and queue the
            buffer to be added to the history next time an equation is added.
            tree: the parsed tree, this will be used to set the label variable
            so that the equation can be used symbolicaly.
            """
        if eq.equation is not None and len(eq.equation) > 0:
            if prepend:
                self.old_eqs.insert(0, eq)
            else:
                self.old_eqs.append(eq)

            self.showing_version = len(self.old_eqs)

        if self.last_eqn_textview is not None and drawlasteq:
            # Prepending here should be the opposite: prepend -> eqn on top.
            # We always own this equation
            self.layout.add_equation(self.last_eqn_textview,
                                     True,
                                     prepend=not prepend)
            self.last_eqn_textview = None

        if eq.label is not None and len(eq.label) > 0:
            w = self.create_var_textview(eq.label, eq.result)
            if w is not None:
                self.layout.add_variable(eq.label, w)

            if tree is None:
                tree = self.parser.parse(eq.equation)
            try:
                self.parser.set_var(eq.label, tree)
            except Exception, e:
                eq.result = ParseError(e.message, 0, "")
                self.set_error_equation(eq)
                return

        own = (eq.owner == self.get_owner_id())
        w = eq.create_history_object()
        w.connect('button-press-event',
                  lambda w, e: self.equation_pressed_cb(eq))
        if drawlasteq:
            self.set_last_equation(eq)

            # SVG images can't be plotted in last equation window
            if isinstance(eq.result, SVGImage):
                self.layout.add_equation(w, own, prepend=not prepend)
            else:
                self.last_eqn_textview = w
        else:
            self.layout.add_equation(w, own, prepend=not prepend)
Beispiel #8
0
class Calculate(ShareableActivity):

    TYPE_FUNCTION = 1
    TYPE_OP_PRE = 2
    TYPE_OP_POST = 3
    TYPE_TEXT = 4

    SELECT_NONE = 0
    SELECT_SELECT = 1
    SELECT_TAB = 2

    KEYMAP = {
        'Return': lambda o: o.process(),
        'period': '.',
        'equal': '=',
        'plus': '+',
        'minus': '-',
        'asterisk': '*',
        'multiply': '×',
        'divide': '÷',
        'slash': '/',
        'BackSpace': lambda o: o.remove_character(-1),
        'Delete': lambda o: o.remove_character(1),
        'parenleft': '(',
        'parenright': ')',
        'exclam': '!',
        'ampersand': '&',
        'bar': '|',
        'asciicircum': '^',
        'less': '<',
        'greater': '>',
        'percent': '%',
        'comma': ',',
        'underscore': '_',
        'Left': lambda o: o.move_left(),
        'Right': lambda o: o.move_right(),
        'Up': lambda o: o.get_older(),
        'Down': lambda o: o.get_newer(),
        'colon': lambda o: o.label_entered(),
        'Home': lambda o: o.text_entry.set_position(0),
        'End': lambda o: o.text_entry.set_position(
            len(o.text_entry.get_text())),
        'Tab': lambda o: o.tab_complete(),
    }

    CTRL_KEYMAP = {
        'c': lambda o: o.text_copy(),
        'v': lambda o: o.text_paste(),
        'x': lambda o: o.text_cut(),
        'q': lambda o: o.close(),
        'a': lambda o: o.text_select_all(),
    }

    SHIFT_KEYMAP = {
        'Left': lambda o: o.expand_selection(-1),
        'Right': lambda o: o.expand_selection(1),
        'Home': lambda o: o.expand_selection(-1000),
        'End': lambda o: o.expand_selection(1000),
    }

    IDENTIFIER_CHARS = \
        u"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ "

    def __init__(self, handle):
        ShareableActivity.__init__(self, handle)

        self.old_eqs = []

        self.ml = MathLib()
        self.parser = AstParser(self.ml)

        # These will result in 'Ans <operator character>' being inserted
        self._chars_ans_diadic = [op[0]
                                  for op in self.parser.get_diadic_operators()]
        try:
            self._chars_ans_diadic.remove('-')
        except:
            pass

        self.KEYMAP['multiply'] = self.ml.mul_sym
        self.KEYMAP['divide'] = self.ml.div_sym
        self.KEYMAP['equal'] = self.ml.equ_sym

        self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.select_reason = self.SELECT_SELECT
        self.buffer = u""
        self.showing_version = 0
        self.showing_error = False
        self.ans_inserted = False
        self.show_vars = False

        self.connect("key_press_event", self.keypress_cb)
        self.connect("destroy", self.cleanup_cb)
        self.color = sugar3.profile.get_color()

        self.layout = CalcLayout(self)
        self.label_entry = self.layout.label_entry
        self.text_entry = self.layout.text_entry
        self.last_eq_sig = None
        self.last_eqn_textview = None

        self.reset()
        self.layout.show_it()

        self.connect('joined', self._joined_cb)

        self.parser.log_debug_info()

    def ignore_key_cb(self, widget, event):
        return True

    def cleanup_cb(self, arg):
        _logger.debug('Cleaning up...')

    def equation_pressed_cb(self, eqn):
        """Callback for when an equation box is clicked"""

        if isinstance(eqn.result, SVGImage):
            return True

        if len(eqn.label) > 0:
            text = eqn.label
        else:
            # don't insert plain text
            if type(eqn.result) in (types.StringType, types.UnicodeType):
                text = ''
            else:
                text = self.parser.ml.format_number(eqn.result)
                text = text.rstrip('0').rstrip('.') if '.' in text else text

        self.button_pressed(self.TYPE_TEXT, text)
        return True

    def set_last_equation(self, eqn):
        """Set the 'last equation' TextView"""

        if self.last_eq_sig is not None:
            self.layout.last_eq.disconnect(self.last_eq_sig)
            self.last_eq_sig = None

        if not isinstance(eqn.result, ParserError):
            self.last_eq_sig = self.layout.last_eq.connect(
                'button-press-event',
                lambda a1, a2, e: self.equation_pressed_cb(e), eqn)

        self.layout.last_eq.set_buffer(eqn.create_lasteq_textbuf())

    def set_error_equation(self, eqn):
        """Set equation with error markers. Since set_last_equation implements
        this we can just forward the call."""
        self.set_last_equation(eqn)

    def clear_equations(self):
        """Clear the list of old equations."""
        self.old_eqs = []
        self.showing_version = 0

    def add_equation(self, eq, prepend=False, drawlasteq=False, tree=None):
        """
        Insert equation in the history list and set variable if assignment.
        Input:
            eq: the equation object
            prepend: if True, prepend to list, else append
            drawlasteq: if True, draw in 'last equation' textbox and queue the
            buffer to be added to the history next time an equation is added.
            tree: the parsed tree, this will be used to set the label variable
            so that the equation can be used symbolicaly.
            """
        if eq.equation is not None and len(eq.equation) > 0:
            if prepend:
                self.old_eqs.insert(0, eq)
            else:
                self.old_eqs.append(eq)

            self.showing_version = len(self.old_eqs)

        if self.last_eqn_textview is not None and drawlasteq:
            # Prepending here should be the opposite: prepend -> eqn on top.
            # We always own this equation
            self.layout.add_equation(self.last_eqn_textview, True,
                                     prepend=not prepend)
            self.last_eqn_textview = None

        if eq.label is not None and len(eq.label) > 0:
            w = self.create_var_textview(eq.label, eq.result)
            if w is not None:
                self.layout.add_variable(eq.label, w)

            if tree is None:
                tree = self.parser.parse(eq.equation)
            try:
                self.parser.set_var(eq.label, tree)
            except Exception, e:
                eq.result = ParseError(e.message, 0, "")
                self.set_error_equation(eq)
                return

        own = (eq.owner == self.get_owner_id())
        w = eq.create_history_object()
        w.connect('button-press-event', lambda w,
                  e: self.equation_pressed_cb(eq))
        if drawlasteq:
            self.set_last_equation(eq)

            # SVG images can't be plotted in last equation window
            if isinstance(eq.result, SVGImage):
                self.layout.add_equation(w, own, prepend=not prepend)
            else:
                self.last_eqn_textview = w
        else:
            self.layout.add_equation(w, own, prepend=not prepend)
Beispiel #9
0
class Calculate(ShareableActivity):

    TYPE_FUNCTION = 1
    TYPE_OP_PRE = 2
    TYPE_OP_POST = 3
    TYPE_TEXT = 4

    SELECT_NONE = 0
    SELECT_SELECT = 1
    SELECT_TAB = 2

    KEYMAP = {
        'Return': lambda o: o.process(),
        'period': '.',
        'equal': '=',
        'plus': '+',
        'minus': '-',
        'asterisk': '*',
        'multiply': '×',
        'divide': '÷',
        'slash': '/',
        'BackSpace': lambda o: o.remove_character(-1),
        'Delete': lambda o: o.remove_character(1),
        'parenleft': '(',
        'parenright': ')',
        'exclam': '!',
        'ampersand': '&',
        'bar': '|',
        'asciicircum': '^',
        'less': '<',
        'greater': '>',
        'percent': '%',
        'comma': ',',
        'underscore': '_',
        'Left': lambda o: o.move_left(),
        'Right': lambda o: o.move_right(),
        'Up': lambda o: o.get_older(),
        'Down': lambda o: o.get_newer(),
        'colon': lambda o: o.label_entered(),
        'Home': lambda o: o.text_entry.set_position(0),
        'End':
        lambda o: o.text_entry.set_position(len(o.text_entry.get_text())),
        'Tab': lambda o: o.tab_complete(),
    }

    CTRL_KEYMAP = {
        'c': lambda o: o.text_copy(),
        'v': lambda o: o.text_paste(),
        'x': lambda o: o.text_cut(),
        'q': lambda o: o.close(),
        'a': lambda o: o.text_select_all(),
    }

    SHIFT_KEYMAP = {
        'Left': lambda o: o.expand_selection(-1),
        'Right': lambda o: o.expand_selection(1),
        'Home': lambda o: o.expand_selection(-1000),
        'End': lambda o: o.expand_selection(1000),
    }

    IDENTIFIER_CHARS = \
        u"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ "

    def __init__(self, handle):
        ShareableActivity.__init__(self, handle)

        self.old_eqs = []

        self.ml = MathLib()
        self.parser = AstParser(self.ml)

        # These will result in 'Ans <operator character>' being inserted
        self._chars_ans_diadic = [
            op[0] for op in self.parser.get_diadic_operators()
        ]
        try:
            self._chars_ans_diadic.remove('-')
        except:
            pass

        self.KEYMAP['multiply'] = self.ml.mul_sym
        self.KEYMAP['divide'] = self.ml.div_sym
        self.KEYMAP['equal'] = self.ml.equ_sym

        self.clipboard = gtk.Clipboard()
        self.select_reason = self.SELECT_SELECT
        self.buffer = u""
        self.showing_version = 0
        self.showing_error = False
        self.ans_inserted = False
        self.show_vars = False

        self.connect("key_press_event", self.keypress_cb)
        self.connect("destroy", self.cleanup_cb)
        self.color = sugar.profile.get_color()

        self.layout = CalcLayout(self)
        self.label_entry = self.layout.label_entry
        self.text_entry = self.layout.text_entry
        self.last_eq_sig = None
        self.last_eqn_textview = None

        self.reset()
        self.layout.show_it()

        self.connect('joined', self._joined_cb)

        self.parser.log_debug_info()

    def ignore_key_cb(self, widget, event):
        return True

    def cleanup_cb(self, arg):
        _logger.debug('Cleaning up...')

    def equation_pressed_cb(self, eqn):
        """Callback for when an equation box is clicked"""

        if isinstance(eqn.result, SVGImage):
            return True

        if len(eqn.label) > 0:
            text = eqn.label
        else:
            # don't insert plain text
            if type(eqn.result) in (types.StringType, types.UnicodeType):
                text = ''
            else:
                text = self.parser.ml.format_number(eqn.result)

        self.button_pressed(self.TYPE_TEXT, text)
        return True

    def set_last_equation(self, eqn):
        """Set the 'last equation' TextView"""

        if self.last_eq_sig is not None:
            self.layout.last_eq.disconnect(self.last_eq_sig)
            self.last_eq_sig = None

        if not isinstance(eqn.result, ParserError):
            self.last_eq_sig = self.layout.last_eq.connect(
                'button-press-event',
                lambda a1, a2, e: self.equation_pressed_cb(e), eqn)

        self.layout.last_eq.set_buffer(eqn.create_lasteq_textbuf())

    def set_error_equation(self, eqn):
        """Set equation with error markers. Since set_last_equation implements
        this we can just forward the call."""
        self.set_last_equation(eqn)

    def clear_equations(self):
        """Clear the list of old equations."""
        self.old_eqs = []
        self.showing_version = 0

    def add_equation(self, eq, prepend=False, drawlasteq=False, tree=None):
        """
        Insert equation in the history list and set variable if assignment.
        Input:
            eq: the equation object
            prepend: if True, prepend to list, else append
            drawlasteq: if True, draw in 'last equation' textbox and queue the
            buffer to be added to the history next time an equation is added.
            tree: the parsed tree, this will be used to set the label variable
            so that the equation can be used symbolicaly.
            """
        if eq.equation is not None and len(eq.equation) > 0:
            if prepend:
                self.old_eqs.insert(0, eq)
            else:
                self.old_eqs.append(eq)

            self.showing_version = len(self.old_eqs)

        if self.last_eqn_textview is not None and drawlasteq:
            # Prepending here should be the opposite: prepend -> eqn on top.
            # We always own this equation
            self.layout.add_equation(self.last_eqn_textview,
                                     True,
                                     prepend=not prepend)
            self.last_eqn_textview = None

        own = (eq.owner == self.get_owner_id())
        w = eq.create_history_object()
        w.connect('button-press-event',
                  lambda w, e: self.equation_pressed_cb(eq))
        if drawlasteq:
            self.set_last_equation(eq)

            # SVG images can't be plotted in last equation window
            if isinstance(eq.result, SVGImage):
                self.layout.add_equation(w, own, prepend=not prepend)
            else:
                self.last_eqn_textview = w
        else:
            self.layout.add_equation(w, own, prepend=not prepend)

        if eq.label is not None and len(eq.label) > 0:
            w = self.create_var_textview(eq.label, eq.result)
            if w is not None:
                self.layout.add_variable(eq.label, w)

            if tree is None:
                tree = self.parser.parse(eq.equation)
            self.parser.set_var(eq.label, tree)

    # FIXME: to be implemented
    def process_async(self, eqn):
        """Parse and process an equation asynchronously."""

    def process(self):
        """Parse the equation entered and show the result"""

        s = unicode(self.text_entry.get_text())
        label = unicode(self.label_entry.get_text())
        _logger.debug('process(): parsing %r, label: %r', s, label)
        try:
            tree = self.parser.parse(s)
            res = self.parser.evaluate(tree)
        except ParserError, e:
            res = e
            self.showing_error = True

        if isinstance(res, str) and res.find('</svg>') > -1:
            res = SVGImage(data=res)

        _logger.debug('Result: %r', res)

        # Check whether assigning this label would cause recursion
        if not isinstance(res, ParserError) and len(label) > 0:
            lastpos = self.parser.get_var_used_ofs(label)
            if lastpos is not None:
                res = RuntimeError(
                    _('Can not assign label: will cause recursion'), lastpos)

# If parsing went ok, see if we have to replace the previous answer
# to get a (more) exact result
        if self.ans_inserted and not isinstance(res, ParserError) \
                and not isinstance(res, SVGImage):
            ansvar = self.format_insert_ans()
            pos = s.find(ansvar)
            if len(ansvar) > 6 and pos != -1:
                s2 = s.replace(ansvar, 'LastEqn')
                _logger.debug('process(): replacing previous answer %r: %r',
                              ansvar, s2)
                tree = self.parser.parse(s2)
                res = self.parser.evaluate(tree)

        eqn = Equation(label,
                       s,
                       res,
                       self.color,
                       self.get_owner_id(),
                       ml=self.ml)

        if isinstance(res, ParserError):
            self.set_error_equation(eqn)
        else:
            self.add_equation(eqn, drawlasteq=True, tree=tree)
            self.send_message("add_eq", value=str(eqn))

            self.parser.set_var('Ans', eqn.result)

            # Setting LastEqn to the parse tree would certainly be faster,
            # however, it introduces recursion problems
            self.parser.set_var('LastEqn', eqn.result)

            self.showing_error = False
            self.ans_inserted = False
            self.text_entry.set_text(u'')
            self.label_entry.set_text(u'')

        return res is not None
class Calculate(ShareableActivity):

    TYPE_FUNCTION = 1
    TYPE_OP_PRE = 2
    TYPE_OP_POST = 3
    TYPE_TEXT = 4
    

    SELECT_NONE = 0
    SELECT_SELECT = 1
    SELECT_TAB = 2

    KEYMAP = {
        'Return': lambda o: o.process(),
        'period': '.',
        'equal': '=',
        'plus': '+',
        'minus': '-',
        'asterisk': '*',
        'multiply': '×',
        'divide': '÷',
        'slash': '/',
        'BackSpace': lambda o: o.remove_character(-1),
        'Delete': lambda o: o.remove_character(1),
        'parenleft': '(',
        'parenright': ')',
        'exclam': '!',
        'ampersand': '&',
        'bar': '|',
        'asciicircum': '^',
        'less': '<',
        'greater': '>',
        'percent': '%',
        'comma': ',',
        'underscore': '_',
        'Left': lambda o: o.move_left(),
        'Right': lambda o: o.move_right(),
        'Up': lambda o: o.get_older(),
        'Down': lambda o: o.get_newer(),
        'colon': lambda o: o.label_entered(),
        'Home': lambda o: o.text_entry.set_position(0),
        'End': lambda o: o.text_entry.set_position(len(o.text_entry.get_text())),
        'Tab': lambda o: o.tab_complete(),
    }

    CTRL_KEYMAP = {
        'c': lambda o: o.text_copy(),
        'v': lambda o: o.text_paste(),
        'x': lambda o: o.text_cut(),
        'q': lambda o: o.close(),
    }

    SHIFT_KEYMAP = {
        'Left': lambda o: o.expand_selection(-1),
        'Right': lambda o: o.expand_selection(1),
        'Home': lambda o: o.expand_selection(-1000),
        'End': lambda o: o.expand_selection(1000),
    }

    IDENTIFIER_CHARS = u"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ "

    def __init__(self, handle):
        ShareableActivity.__init__(self, handle)

        self.old_eqs = []
        
        self.ml = MathLib()
        self.parser = AstParser(self.ml)

        # These will result in 'Ans <operator character>' being inserted
        self._chars_ans_diadic = [op[0] for op in self.parser.get_diadic_operators()]
        try:
            self._chars_ans_diadic.remove('-')
        except:
            pass

        self.KEYMAP['multiply'] = self.ml.mul_sym
        self.KEYMAP['divide'] = self.ml.div_sym
        self.KEYMAP['equal'] = self.ml.equ_sym

        self.clipboard = gtk.Clipboard()
        self.select_reason = self.SELECT_SELECT
        self.buffer = u""
        self.showing_version = 0
        self.showing_error = False
        self.ans_inserted = False
        self.show_vars = False

        self.connect("key_press_event", self.keypress_cb)
        self.connect("destroy", self.cleanup_cb)
        self.color = sugar.profile.get_color()

        self.layout = CalcLayout(self)
        self.label_entry = self.layout.label_entry
        self.text_entry = self.layout.text_entry
        self.last_eq_sig = None
        self.last_eqn_textview = None

        self.reset()
        self.layout.show_it()

        self.connect('joined', self._joined_cb)

        self.parser.log_debug_info()

    def ignore_key_cb(self, widget, event):
        return True

    def cleanup_cb(self, arg):
        _logger.debug('Cleaning up...')

    def equation_pressed_cb(self, eqn):
        """Callback for when an equation box is clicked"""

        if isinstance(eqn.result, SVGImage):
            return True

        if len(eqn.label) > 0:
            text = eqn.label
        else:
            # don't insert plain text
            if type(eqn.result) in (types.StringType, types.UnicodeType):
                text = ''
            else:
                text = self.parser.ml.format_number(eqn.result)

        self.button_pressed(self.TYPE_TEXT, text)
        return True

    def set_last_equation(self, eqn):
        """Set the 'last equation' TextView"""

        if self.last_eq_sig is not None:
            self.layout.last_eq.disconnect(self.last_eq_sig)
            self.last_eq_sig = None

        if not isinstance(eqn.result, ParserError):
            self.last_eq_sig = self.layout.last_eq.connect(
                    'button-press-event',
                    lambda a1, a2, e: self.equation_pressed_cb(e), eqn)

        self.layout.last_eq.set_buffer(eqn.create_lasteq_textbuf())

    def set_error_equation(self, eqn):
        """Set equation with error markers. Since set_last_equation implements
        this we can just forward the call."""
        self.set_last_equation(eqn)

    def clear_equations(self):
        """Clear the list of old equations."""
        self.old_eqs = []
        self.showing_version = 0

    def add_equation(self, eq, prepend=False, drawlasteq=False, tree=None):
        """
        Insert equation in the history list and set variable if assignment.
        Input:
            eq: the equation object
            prepend: if True, prepend to list, else append
            drawlasteq: if True, draw in 'last equation' textbox and queue the
            buffer to be added to the history next time an equation is added.
            tree: the parsed tree, this will be used to set the label variable
            so that the equation can be used symbolicaly.
            """
        if eq.equation is not None and len(eq.equation) > 0:
            if prepend:
                self.old_eqs.insert(0, eq)
            else:
                self.old_eqs.append(eq)

            self.showing_version = len(self.old_eqs)

        if self.last_eqn_textview is not None and drawlasteq:
            # Prepending here should be the opposite: prepend -> eqn on top.
            # We always own this equation
            self.layout.add_equation(self.last_eqn_textview, True,
                prepend=not prepend)
            self.last_eqn_textview = None

        own = (eq.owner == self.get_owner_id())
        w = eq.create_history_object()
        w.connect('button-press-event', lambda w, e: self.equation_pressed_cb(eq))
        if drawlasteq:
            self.set_last_equation(eq)

            # SVG images can't be plotted in last equation window
            if isinstance(eq.result, SVGImage):
                self.layout.add_equation(w, own, prepend=not prepend)
            else:
                self.last_eqn_textview = w
        else:
            self.layout.add_equation(w, own, prepend=not prepend)

        if eq.label is not None and len(eq.label) > 0:
            w = self.create_var_textview(eq.label, eq.result)
            if w is not None:
                self.layout.add_variable(eq.label, w)

            if tree is None:
                tree = self.parser.parse(eq.equation)
            self.parser.set_var(eq.label, tree)

    # FIXME: to be implemented
    def process_async(self, eqn):
        """Parse and process an equation asynchronously."""

    def process(self):
        """Parse the equation entered and show the result"""

        s = unicode(self.text_entry.get_text())
        label = unicode(self.label_entry.get_text())
        _logger.debug('process(): parsing %r, label: %r', s, label)
        try:
            tree = self.parser.parse(s)
            res = self.parser.evaluate(tree)
        except ParserError, e:
            res = e
            self.showing_error = True

        if type(res) == types.StringType and res.find('</svg>') > -1:
            res = SVGImage(data=res)

        _logger.debug('Result: %r', res)

        # Check whether assigning this label would cause recursion
        if not isinstance(res, ParserError) and len(label) > 0:
            lastpos = self.parser.get_var_used_ofs(label)
            if lastpos is not None:
                res = RuntimeError(_('Can not assign label: will cause recursion'),
                        lastpos)

# If parsing went ok, see if we have to replace the previous answer
# to get a (more) exact result
        if self.ans_inserted and not isinstance(res, ParserError) \
                and not isinstance(res, SVGImage):
            ansvar = self.format_insert_ans()
            pos = s.find(ansvar)
            if len(ansvar) > 6 and pos != -1:
                s2 = s.replace(ansvar, 'LastEqn')
                _logger.debug('process(): replacing previous answer %r: %r', ansvar, s2)
                tree = self.parser.parse(s2)
                res = self.parser.evaluate(tree)

        eqn = Equation(label, s, res, self.color, self.get_owner_id(), ml=self.ml)

        if isinstance(res, ParserError):
            self.set_error_equation(eqn)
        else:
            self.add_equation(eqn, drawlasteq=True, tree=tree)
            self.send_message("add_eq", value=str(eqn))

            self.parser.set_var('Ans', eqn.result)

            # Setting LastEqn to the parse tree would certainly be faster,
            # however, it introduces recursion problems
            self.parser.set_var('LastEqn', eqn.result)

            self.showing_error = False
            self.ans_inserted = False
            self.text_entry.set_text(u'')
            self.label_entry.set_text(u'')

        return res is not None