Codebase list sugar-pippy-activity / lintian-fixes/main texteditor.py
lintian-fixes/main

Tree @lintian-fixes/main (Download .tar.gz)

texteditor.py @lintian-fixes/mainraw · history · blame

# Copyright (C) 2015, Batchu Venkat Vishal
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA  02110-1301 USA

import logging

from gi.repository import Gtk

'''
The texteditor module provides a text editor widget
which can be included in any activity and then multiple
users can collaborate and edit together in the editor.
'''


class CollabTextEditor(Gtk.TextView):
    '''
    A CollabTextEditor widget is a adjustable text editor which
    can be placed on an activity screen.

    The `changed` signal is usually emitted when the text in the
    editor is changed by a user.
    The `message` signal is usually emitted when another user makes
    changes in the text editor, so they are reflected in your editor.

    The widget can be embedded in a window which can be displayed.
    Example usage:
        editorinstance = CollabTextEditor(self)
        scrolled_window.add(editorinstance)
        scrolled_window.show()

    '''

    def __init__(self, activity, editor_id, collab):
        Gtk.TextView.__init__(self)
        self.set_editable(True)
        self.set_cursor_visible(True)
        self.set_wrap_mode(Gtk.WrapMode.WORD)
        self.textbuffer = self.get_buffer()
        self._collaberizer = TextBufferCollaberizer(
            self.textbuffer, editor_id, collab)
        self.textbuffer.set_text("")
        self.show()


class TextBufferCollaberizer(object):

    def __init__(self, textbuffer, editor_id, collab):
        self._id = editor_id
        self._buffer = textbuffer
        self._callbacks_status = True
        self.has_initialized = False

        self._collab = collab
        self._collab.connect('message', self.__message_cb)
        self._collab.connect('joined', self.__joined_cb)

        self._buffer.connect('insert-text', self.__text_buffer_inserted_cb)
        self._buffer.connect('delete-range', self.__text_buffer_deleted_cb)
        self._buffer.set_text('')

        if not self._collab._leader:
            # We must be joining an activity and just made the buffer
            self._collab.post(dict(
                action='init_request',
                res_id=self._id
            ))

    '''
    The message callback is called whenever another user edits
    something in the text editor and the changes are reflected
    in the editor or when a new buddy joins and we send them the
    latest version of the text buffer.

    Args:
        buddy : another user who sent the message
        message : updates send over from other users
    '''

    def __message_cb(self, collab, buddy, message):
        action = message.get('action')
        if str(message.get('res_id')) != self._id:
            return

        if action == 'init_response' or action == 'sync_editors':
            self.has_initialized = True
            self._callbacks_status = False
            self._buffer.set_text(message.get('current_content'))
            self._callbacks_status = True
        if action == 'entry_inserted':
            start_iter = self._buffer.get_iter_at_line_offset(
                message.get('start_iter_line'),
                message.get('start_iter_offset'))
            self._callbacks_status = False
            self._buffer.insert(start_iter, message.get('new_text'))
            self._callbacks_status = True
        if action == 'entry_deleted':
            start_iter = self._buffer.get_iter_at_line_offset(
                message.get('start_iter_line'),
                message.get('start_iter_offset'))
            end_iter = self._buffer.get_iter_at_line_offset(
                message.get('end_iter_line'),
                message.get('end_iter_offset'))
            self._callbacks_status = False
            self._buffer.delete(start_iter, end_iter)
            self._callbacks_status = True
        if action == 'init_request':
            text = self._buffer.get_text(
                self._buffer.get_start_iter(),
                self._buffer.get_end_iter(),
                True)
            self._collab.post(dict(
                action='init_response',
                res_id=self._id,
                current_content=text
            ))

    def __joined_cb(self, sender):
        if self._collab._leader:
            return
        self._collab.post(dict(
            action='init_request',
            res_id=self._id
        ))

    '''
    This will send a message to all your buddies to set their editors to
    sync with the text specified as an argument.

    Args:
        text : Text to be set in all the editors
    '''
    def __set_text_synced(self, text):
        if self._callbacks_status is False:
            return
        if self.has_initialized is False:
            self.has_initialized = True
        self._callbacks_status = False
        self._buffer.set_text(text)
        self._callbacks_status = True
        self._collab.post(dict(action='sync_editors',
                               res_id=self._id,
                               current_content=text))

    '''
    The text buffer inserted callback is called whenever text is
    inserted in the editor, so that other users get updated with
    these changes.

    Args:
        textbuffer (:class:`Gtk.TextBuffer`): text storage widget
        start (:class:`Gtk.Iterator`): a pointer to the start position
    '''

    def __text_buffer_inserted_cb(self, textbuffer, start, text, length):
        if self._callbacks_status is False:
            return
        if self.has_initialized is False:
            self.has_initialized = True
        logging.debug('Text inserted is %s' % (text))
        logging.debug('Text has been updated, %s' % (textbuffer.get_text(
            textbuffer.get_start_iter(), textbuffer.get_end_iter(), True)))
        self._collab.post(dict(action='entry_inserted',
                               res_id=self._id,
                               start_iter_offset=start.get_line_offset(),
                               start_iter_line=start.get_line(),
                               new_text=text))

    '''
    The text buffer deleted callback is called whenever any text is
    removed in the editor, so that other users get updated with
    these changes.

    Args:
        textbuffer (:class:`Gtk.TextBuffer`): text storage widget
        start (:class:`Gtk.Iterator`): a pointer to the start position
        end (:class:`Gtk.Iterator`): a pointer to the end position
    '''

    def __text_buffer_deleted_cb(self, textbuffer, start, end):
        if self._callbacks_status is False:
            return
        if self.has_initialized is False:
            self.has_initialized = True
        logging.debug('Text deleted is %s' %
                      (textbuffer.get_text(start, end, True)))
        logging.debug('Text has been updated, %s' % (textbuffer.get_text(
            textbuffer.get_start_iter(), textbuffer.get_end_iter(), True)))
        self._collab.post(dict(action='entry_deleted',
                               res_id=self._id,
                               start_iter_offset=start.get_line_offset(),
                               start_iter_line=start.get_line(),
                               end_iter_offset=end.get_line_offset(),
                               end_iter_line=end.get_line()))