Esempio n. 1
0
 def __init__(self, file):
     INIConfigFile.__init__(self, file)
     if os.name == 'nt':
         endofline = 'dos'
     else:
         endofline = 'unix'
     name = file.dir.basename if hasattr(file, 'dir') else file.parent(
     ).basename  # HACK zim.fs and zim.newfs compat
     self['Notebook'].define((
         ('version', String('.'.join(map(str, DATA_FORMAT_VERSION)))),
         ('name', String(name)),
         ('interwiki', String(None)),
         ('home', ConfigDefinitionByClass(Path('Home'))),
         ('icon',
          String(None)),  # XXX should be file, but resolves relative
         ('document_root',
          String(None)),  # XXX should be dir, but resolves relative
         ('short_links', Boolean(False)),
         ('shared', Boolean(True)),
         ('endofline', Choice(endofline, {'dos', 'unix'})),
         ('disable_trash', Boolean(False)),
         ('default_file_format', String('zim-wiki')),
         ('default_file_extension', String('.txt')),
         ('notebook_layout', String('files')),
     ))
Esempio n. 2
0
class SourceViewObjectType(InsertedObjectTypeExtension):

	name = 'code'

	label = _('Code Block') # T: menu item

	object_attr = {
		'lang': String(None),
		'linenumbers': Boolean(True),
	}

	def __init__(self, plugin, objmap):
		self._widgets = WeakSet()
		self.preferences = plugin.preferences
		InsertedObjectTypeExtension.__init__(self, plugin, objmap)
		self.connectto(self.preferences, 'changed', self.on_preferences_changed)

	def new_model_interactive(self, parent, notebook, page):
		lang = InsertCodeBlockDialog(parent).run()
		if lang is None:
			raise ValueError # dialog cancelled
		else:
			attrib = self.parse_attrib({'lang': lang})
			return SourceViewBuffer(attrib, '')

	def model_from_data(self, notebook, page, attrib, text):
		return SourceViewBuffer(attrib, text)

	def data_from_model(self, buffer):
		return buffer.get_object_data()

	def create_widget(self, buffer):
		widget = SourceViewWidget(buffer)
		widget.set_preferences(self.preferences)
		self._widgets.add(widget)
		return widget

	def on_preferences_changed(self, preferences):
		for widget in self._widgets:
			widget.set_preferences(preferences)

	def format_html(self, dumper, attrib, data):
		# to use highlight.js add the following to your template:
		#<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/default.min.css">
		#<script src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js"></script>
		#<script>hljs.initHighlightingOnLoad();</script>
		#Map GtkSourceView language ids match with Highlight.js language ids.
		#http://packages.ubuntu.com/precise/all/libGtkSource.0-common/filelist
		#http://highlightjs.readthedocs.io/en/latest/css-classes-reference.html
		sh_map = {'dosbatch': 'dos'}
		sh_lang = sh_map[attrib['lang']] if attrib['lang'] in sh_map else attrib['lang']
		# TODO: some template instruction to be able to use other highlighters as well?
		output = ['<pre><code class="%s">' % html_encode(sh_lang)] # for syntaxhigligther
		#class="brush: language;" works with SyntaxHighlighter 2.0.278, 3 & 4
		#output = ['<pre class="brush: %s;">' % html_encode(sh_lang)] # for syntaxhigligther

		output.append(html_encode(data))
		output.append('</code></pre>\n')

		return output
Esempio n. 3
0
 def __init__(self, file):
     INIConfigFile.__init__(self, file)
     if os.name == 'nt': endofline = 'dos'
     else: endofline = 'unix'
     self['Notebook'].define((
         ('version', String('.'.join(map(str, DATA_FORMAT_VERSION)))),
         ('name', String(file.dir.basename)),
         ('interwiki', String(None)),
         ('home', ConfigDefinitionByClass(Path('Home'))),
         ('icon',
          String(None)),  # XXX should be file, but resolves relative
         ('document_root',
          String(None)),  # XXX should be dir, but resolves relative
         ('shared', Boolean(True)),
         ('endofline', Choice(endofline, set(('dos', 'unix')))),
         ('disable_trash', Boolean(False)),
         ('profile', String(None)),
     ))
Esempio n. 4
0
	def __init__(self, parent):
		Dialog.__init__(self, parent, _('Insert Code Block')) # T: dialog title
		self.result = (None, None, None)
		self.uistate.define(id=String(None))
		self.uistate.define(lang=String(None))
		self.uistate.define(line_numbers=Boolean(True))

		grid = Gtk.Grid()
		grid.set_column_spacing(5)
		grid.set_row_spacing(5)

		label = Gtk.Label(_('Syntax') + ':') # T: input label
		grid.add(label)

		self.combobox = Gtk.ComboBox.new_with_model_and_entry(self.init_combobox_model())
		self.combobox.set_entry_text_column(0)
		entry = self.combobox.get_child()
		entry.set_activates_default(True)  # Pressing enter will activate the default button (here: ok-button)

		completion = Gtk.EntryCompletion()
		completion.set_model(self.init_autocomplete_model())
		completion.set_text_column(0)
		completion.set_minimum_key_length(0)
		entry.set_completion(completion)

		defaultlang = self.init_default_language()
		if defaultlang:
			entry.set_text(defaultlang)

		self.combobox.connect("changed", self.on_combobox_changed)

		grid.attach(self.combobox, 1, 0, 1, 1)

		label = Gtk.Label(_('Id') + ':') # T: input label for object ID
		grid.attach(label, 0, 1, 1, 1)
		self.entry = InputEntry()
		grid.attach(self.entry, 1, 1, 1, 1)

		self.checkbox = Gtk.CheckButton(_('Display line numbers')) # T: input checkbox
		self.checkbox.set_active(self.uistate['line_numbers'])
		grid.attach(self.checkbox, 1, 2, 1, 1)

		self.vbox.add(grid)

		# Set ok button as default.
		self.btn_ok = self.get_widget_for_response(response_id=Gtk.ResponseType.OK)
		self.btn_ok.set_can_default(True)
		self.btn_ok.grab_default()
		self.btn_ok.set_sensitive(defaultlang is not None)
Esempio n. 5
0
    def __init__(self, parent):
        Dialog.__init__(self, parent,
                        _('Insert Code Block'))  # T: dialog title
        self.result = (None, None)
        self.uistate.define(lang=String(None))
        self.uistate.define(line_numbers=Boolean(True))
        defaultlang = self.uistate['lang']

        menu = {}
        for l in sorted(LANGUAGES, key=lambda k: k.lower()):
            key = l[0].upper()
            if not key in menu:
                menu[key] = []
            menu[key].append(l)

        model = Gtk.TreeStore(str)
        defaultiter = None
        for key in sorted(menu):
            iter = model.append(None, [key])
            for lang in menu[key]:
                myiter = model.append(iter, [lang])
                if LANGUAGES[lang] == defaultlang:
                    defaultiter = myiter

        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        hbox.set_spacing(5)
        label = Gtk.Label(_('Syntax') + ':')  # T: input label
        hbox.add(label)

        combobox = Gtk.ComboBox.new_with_model(model)
        renderer_text = Gtk.CellRendererText()
        combobox.pack_start(renderer_text, True)
        combobox.add_attribute(renderer_text, "text", 0)
        if defaultiter is not None:
            combobox.set_active_iter(defaultiter)
        hbox.add(combobox)
        self.combobox = combobox
        self.vbox.add(hbox)
        self.checkbox = Gtk.CheckButton(
            _('Display line numbers'))  # T: input checkbox
        self.checkbox.set_active(self.uistate['line_numbers'])
        self.vbox.add(self.checkbox)
Esempio n. 6
0
class SourceViewObject(CustomObjectClass):

    OBJECT_ATTR = {
        'type': String('code'),
        'lang': String(None),
        'linenumbers': Boolean(True),
    }

    def __init__(self, attrib, data, preferences):
        if data.endswith('\n'):
            data = data[:-1]
            # If we have trailing \n it looks like an extra empty line
            # in the buffer, so we default remove one
        CustomObjectClass.__init__(self, attrib, data)
        self.preferences = preferences
        self.buffer = None
        self._widgets = WeakSet()

    def get_widget(self):
        if not self.buffer:
            self.buffer = gtksourceview2.Buffer()
            self.buffer.set_text(self._data)
            self.buffer.connect('modified-changed', self.on_modified_changed)
            self.buffer.set_highlight_matching_brackets(True)
            self.buffer.set_modified(False)
            self._data = None

            try:
                if self._attrib['lang']:
                    self.buffer.set_language(
                        lm.get_language(self._attrib['lang']))
            except:
                logger.exception('Could not set language for sourceview: %s',
                                 lang)

        widget = SourceViewWidget(self, self.buffer)
        self._widgets.add(widget)

        widget.view.set_show_line_numbers(self._attrib['linenumbers'])
        widget.set_preferences(self.preferences)
        return widget

    def preferences_changed(self):
        for widget in self._widgets:
            widget.set_preferences(self.preferences)

    def on_modified_changed(self, buffer):
        # Sourceview changed, set change on oject, reset state of
        # sourceview buffer so we get a new signal with next change
        if buffer.get_modified():
            self.set_modified(True)
            buffer.set_modified(False)

    def get_data(self):
        '''Returns data as text.'''
        if self.buffer:
            bounds = self.buffer.get_bounds()
            text = self.buffer.get_text(bounds[0], bounds[1])
            text += '\n'  # Make sure we always have a trailing \n
            return text
        else:
            return self._data

    def dump(self, format, dumper, linker=None):
        if format == "html":
            if self._attrib['lang']:
                # class="brush: language;" works with SyntaxHighlighter 2.0.278
                # by Alex Gorbatchev <http://alexgorbatchev.com/SyntaxHighlighter/>
                # TODO: not all GtkSourceView language ids match with SyntaxHighlighter
                # language ids.
                # TODO: some template instruction to be able to use other highlighters as well?
                output = [
                    '<pre class="brush: %s;">\n' %
                    html_encode(self._attrib['lang'])
                ]
            else:
                output = ['<pre>\n']
            data = self.get_data()
            data = html_encode(
                data)  # XXX currently dumper gives encoded lines - NOK
            if self._attrib['linenumbers']:
                for i, l in enumerate(data.splitlines(1)):
                    output.append('%i&nbsp;' % (i + 1) + l)
            else:
                output.append(data)
            output.append('</pre>\n')
            return output
        return CustomObjectClass.dump(self, format, dumper, linker)

    def set_language(self, lang):
        '''Set language in SourceView.'''
        self._attrib['lang'] = lang
        self.set_modified(True)

        if self.buffer:
            if lang is None:
                self.buffer.set_language(None)
            else:
                self.buffer.set_language(lm.get_language(lang))

    def show_line_numbers(self, show):
        '''Toggles line numbers in SourceView.'''
        self._attrib['linenumbers'] = show
        self.set_modified(True)

        for widget in self._widgets:
            widget.view.set_show_line_numbers(show)
    def __init__(self, notebook, page=None, fullscreen=False, geometry=None):
        '''Constructor
		@param notebook: the L{Notebook} to show in this window
		@param page: a C{Path} object to open
		@param fullscreen: if C{True} the window is shown fullscreen,
		if C{None} the previous state is restored
		@param geometry: the window geometry as string in format
		"C{WxH+X+Y}", if C{None} the previous state is restored
		'''
        Window.__init__(self)
        self.notebook = notebook
        self.page = None  # will be set later by open_page
        self.navigation = NavigationModel(self)
        self.hideonclose = False

        self.preferences = ConfigManager.preferences['GtkInterface']
        self.preferences.define(
            toggle_on_ctrlspace=Boolean(False),
            remove_links_on_delete=Boolean(True),
            always_use_last_cursor_pos=Boolean(True),
        )
        self.preferences.connect('changed', self.do_preferences_changed)

        self.maximized = False
        self.isfullscreen = False
        self.connect_after('window-state-event',
                           self.__class__.on_window_state_event)

        # Hidden setting to force the gtk bell off. Otherwise it
        # can bell every time you reach the begin or end of the text
        # buffer. Especially specific gtk version on windows.
        # See bug lp:546920
        self.preferences.setdefault('gtk_bell', False)
        if not self.preferences['gtk_bell']:
            Gtk.rc_parse_string('gtk-error-bell = 0')

        self._block_toggle_panes = False
        self._sidepane_autoclose = False
        self._switch_focus_accelgroup = None

        # Catching this signal prevents the window to actually be destroyed
        # when the user tries to close it. The action for close should either
        # hide or destroy the window.
        def do_delete_event(*a):
            logger.debug('Action: close (delete-event)')
            self.close()
            return True  # Do not destroy - let close() handle it

        self.connect('delete-event', do_delete_event)

        # setup uistate
        self.uistate = notebook.state['MainWindow']
        self.uistate.setdefault('windowpos', None, check=value_is_coord)
        self.uistate.setdefault('windowsize', (600, 450), check=value_is_coord)
        self.uistate.setdefault('windowmaximized', False)
        self.uistate.setdefault('active_tabs', None, tuple)
        self.uistate.setdefault('show_toolbar', True)
        self.uistate.setdefault('show_statusbar', True)
        self.uistate.setdefault('readonly', False)

        self.history = History(notebook, notebook.state)

        # init uimanager
        self.uimanager = Gtk.UIManager()
        self.uimanager.add_ui_from_string('''
		<ui>
			<menubar name="menubar">
			</menubar>
			<toolbar name="toolbar">
			</toolbar>
		</ui>
		''')

        # setup menubar and toolbar
        self.add_accel_group(self.uimanager.get_accel_group())
        self.menubar = self.uimanager.get_widget('/menubar')
        self.toolbar = self.uimanager.get_widget('/toolbar')
        self.toolbar.connect('popup-context-menu', self.do_toolbar_popup)
        self.add_bar(self.menubar)
        self.add_bar(self.toolbar)

        self.pageview = PageView(self.notebook, self.navigation)
        self.connect_object('readonly-changed', PageView.set_readonly,
                            self.pageview)
        self.pageview.connect_after('textstyle-changed',
                                    self.on_textview_textstyle_changed)
        self.pageview.textview.connect_after('toggle-overwrite',
                                             self.on_textview_toggle_overwrite)
        self.pageview.textview.connect('link-enter', self.on_link_enter)
        self.pageview.textview.connect('link-leave', self.on_link_leave)

        self.add(self.pageview)

        # create statusbar
        self.statusbar = Gtk.Statusbar()
        self.statusbar.push(0, '<page>')
        self.add_bar(self.statusbar, start=False)
        self.statusbar.set_property('margin', 0)
        self.statusbar.set_property('spacing', 0)

        def statusbar_element(string, size):
            frame = Gtk.Frame()
            frame.set_shadow_type(Gtk.ShadowType.NONE)
            self.statusbar.pack_end(frame, False, True, 0)
            label = Gtk.Label(label=string)
            label.set_size_request(size, 10)
            label.set_alignment(0.1, 0.5)
            frame.add(label)
            return label

        # specify statusbar elements right-to-left
        self.statusbar_insert_label = statusbar_element('INS', 60)
        self.statusbar_style_label = statusbar_element('<style>', 110)

        # and build the widget for backlinks
        self.statusbar_backlinks_button = \
         BackLinksMenuButton(self.notebook, self.open_page, status_bar_style=True)
        frame = Gtk.Frame()
        frame.set_shadow_type(Gtk.ShadowType.NONE)
        self.statusbar.pack_end(Gtk.Separator(), False, True, 0)
        self.statusbar.pack_end(frame, False, True, 0)
        self.statusbar.pack_end(Gtk.Separator(), False, True, 0)
        frame.add(self.statusbar_backlinks_button)

        self.move_bottom_minimized_tabs_to_statusbar(self.statusbar)

        self.do_preferences_changed()

        self._geometry_set = False
        self._set_fullscreen = False
        if geometry:
            try:
                self.parse_geometry(geometry)
                self._geometry_set = True
            except:
                logger.exception('Parsing geometry string failed:')
        elif fullscreen:
            self._set_fullscreen = True

        # Init mouse settings
        self.preferences.setdefault('mouse_nav_button_back', 8)
        self.preferences.setdefault('mouse_nav_button_forw', 9)

        # Finish uimanager
        self._uiactions = UIActions(self, self.notebook, self.page,
                                    self.navigation)
        group = get_gtk_actiongroup(self._uiactions)
        self.uimanager.insert_action_group(group, 0)

        group = get_gtk_actiongroup(self.pageview)
        self.uimanager.insert_action_group(group, 0)

        group = get_gtk_actiongroup(self)
        # don't use mnemonics on macOS to allow alt-<letter> shortcuts
        global MENU_ACTIONS
        if sys.platform == "darwin":
            MENU_ACTIONS = tuple(
                (t[0], t[1], t[2].replace('_', '')) for t in MENU_ACTIONS)
        group.add_actions(MENU_ACTIONS)
        self.uimanager.insert_action_group(group, 0)

        group.get_action('open_page_back').set_sensitive(False)
        group.get_action('open_page_forward').set_sensitive(False)

        fname = 'menubar.xml'
        self.uimanager.add_ui_from_string(data_file(fname).read())
        self.pageview.emit(
            'ui-init')  # Needs to trigger after default menus are build

        # Do this last, else menu items show up in wrong place
        self._customtools = CustomToolManagerUI(self.uimanager, self.pageview)
        self._insertedobjects = InsertedObjectUI(self.uimanager, self.pageview)
        # XXX: would like to do this in PageView itself, but need access to uimanager

        # Setup notebook signals
        notebook.connect('page-info-changed', self.do_page_info_changed)

        def move_away(o, path):
            # Try several options to get awaay
            actions = [
                self.open_page_back, self.open_page_parent, self.open_page_home
            ]
            while (path == self.page or self.page.ischild(path)) and actions:
                action = actions.pop(0)
                action()

        notebook.connect('deleted-page', move_away)  # after action

        def follow(o, path, newpath):
            if path == self.page:
                self.open_page(newpath)
            elif self.page.ischild(path):
                newpath = newpath + self.page.relname(path)
                self.open_page(newpath)
            else:
                pass

        notebook.connect('moved-page', follow)  # after action

        # init page
        page = page or self.history.get_current()
        if page:
            page = notebook.get_page(page)
            self.open_page(page)
        else:
            self.open_page_home()

        self.pageview.grab_focus()
Esempio n. 8
0
    def __init__(self, notebook, page=None, fullscreen=False, geometry=None):
        '''Constructor
		@param notebook: the L{Notebook} to show in this window
		@param page: a C{Path} object to open
		@param fullscreen: if C{True} the window is shown fullscreen,
		if C{None} the previous state is restored
		@param geometry: the window geometry as string in format
		"C{WxH+X+Y}", if C{None} the previous state is restored
		'''
        Window.__init__(self)
        self.notebook = notebook
        self.page = None  # will be set later by open_page
        self.navigation = NavigationModel(self)
        self.hideonclose = False

        self.preferences = ConfigManager.preferences['GtkInterface']
        self.preferences.define(
            toggle_on_ctrlspace=Boolean(False),
            always_use_last_cursor_pos=Boolean(True),
        )
        self.preferences.connect('changed', self.do_preferences_changed)

        self.maximized = False
        self.isfullscreen = False
        self._init_fullscreen_headerbar()
        self.connect_after('window-state-event',
                           self.__class__.on_window_state_event)

        # Hidden setting to force the gtk bell off. Otherwise it
        # can bell every time you reach the begin or end of the text
        # buffer. Especially specific gtk version on windows.
        # See bug lp:546920
        self.preferences.setdefault('gtk_bell', False)
        if not self.preferences['gtk_bell']:
            Gtk.rc_parse_string('gtk-error-bell = 0')

        self._block_toggle_panes = False
        self._sidepane_autoclose = False
        self._switch_focus_accelgroup = None

        # Catching this signal prevents the window to actually be destroyed
        # when the user tries to close it. The action for close should either
        # hide or destroy the window.
        def do_delete_event(*a):
            logger.debug('Action: close (delete-event)')
            self.close()
            return True  # Do not destroy - let close() handle it

        self.connect('delete-event', do_delete_event)

        # setup uistate
        self.uistate = notebook.state['MainWindow']
        self.uistate.setdefault('windowpos', None, check=value_is_coord)
        self.uistate.setdefault('windowsize', (600, 450), check=value_is_coord)
        self.uistate.setdefault('windowmaximized', False)
        self.uistate.setdefault('active_tabs', None, tuple)
        self.uistate.setdefault('readonly', False)

        self.history = History(notebook, notebook.state)

        # init uimanager
        self.uimanager = Gtk.UIManager()
        self.uimanager.add_ui_from_string('''
		<ui>
			<menubar name="menubar">
			</menubar>
		</ui>
		''')

        # setup menubar
        self.add_accel_group(self.uimanager.get_accel_group())
        self.menubar = self.uimanager.get_widget('/menubar')
        self.add_bar(self.menubar, position=TOP)

        self.pageview = NotebookView(self.notebook, self.navigation)
        self.connect_object('readonly-changed', NotebookView.set_readonly,
                            self.pageview)

        self.add(self.pageview)

        self.do_preferences_changed()

        self._geometry_set = False
        self._set_fullscreen = False
        if geometry:
            try:
                self.parse_geometry(geometry)
                self._geometry_set = True
            except:
                logger.exception('Parsing geometry string failed:')
        elif fullscreen:
            self._set_fullscreen = True

        # Init mouse settings
        self.preferences.setdefault('mouse_nav_button_back', 8)
        self.preferences.setdefault('mouse_nav_button_forw', 9)

        # Finish uimanager
        self._uiactions = UIActions(self, self.notebook, self.page,
                                    self.navigation)
        self.__zim_extension_objects__.append(
            self._uiactions)  # HACK to make actions discoverable
        group = get_gtk_actiongroup(self._uiactions)
        self.uimanager.insert_action_group(group, 0)

        group = get_gtk_actiongroup(self.pageview)
        self.uimanager.insert_action_group(group, 0)

        group = get_gtk_actiongroup(self)
        group.add_actions(MENU_ACTIONS)
        self.uimanager.insert_action_group(group, 0)

        self.open_page_back.set_sensitive(False)
        self.open_page_forward.set_sensitive(False)

        fname = 'menubar.xml'
        self.uimanager.add_ui_from_string(data_file(fname).read())

        # header Bar
        self._headerbar = Gtk.HeaderBar()
        self._headerbar.set_show_close_button(True)
        self.set_titlebar(self._headerbar)

        self._populate_headerbars()

        # Do this last, else menu items show up in wrong place
        self._customtools = CustomToolManagerUI(self.uimanager, self.pageview)
        self._insertedobjects = InsertedObjectUI(self.uimanager, self.pageview)
        # XXX: would like to do this in PageView itself, but need access to uimanager

        # Setup notebook signals
        notebook.connect('page-info-changed', self.do_page_info_changed)

        def move_away(o, path):
            # Try several options to get awaay
            actions = [
                self.open_page_back, self.open_page_parent, self.open_page_home
            ]
            while (path == self.page or self.page.ischild(path)) and actions:
                action = actions.pop(0)
                action()

        notebook.connect('deleted-page', move_away)  # after action

        def follow(o, path, newpath):
            if path == self.page:
                self.open_page(newpath)
            elif self.page.ischild(path):
                newpath = newpath + self.page.relname(path)
                self.open_page(newpath)
            else:
                pass

        notebook.connect('moved-page', follow)  # after action

        # init page
        page = page or self.history.get_current()
        if page:
            page = notebook.get_page(page)
            self.open_page(page)
        else:
            self.open_page_home()

        PluginManager.register_new_extendable(self.pageview)
        initialize_actiongroup(self, 'win')

        self.pageview.grab_focus()
Esempio n. 9
0
class SourceViewObject(CustomObjectClass):

	OBJECT_ATTR = {
		'type': String('code'),
		'lang': String(None),
		'linenumbers': Boolean(True),
	}

	def __init__(self, attrib, data, preferences):
		if data.endswith('\n'):
			data = data[:-1]
			# If we have trailing \n it looks like an extra empty line
			# in the buffer, so we default remove one
		CustomObjectClass.__init__(self, attrib, data)
		self.preferences = preferences
		self.buffer = None
		self._widgets = WeakSet()

	def get_widget(self):
		if not self.buffer:
			self.buffer = gtksourceview2.Buffer()
			self.buffer.set_text(self._data)
			self.buffer.connect('modified-changed', self.on_modified_changed)
			self.buffer.set_highlight_matching_brackets(True)
			self.buffer.set_modified(False)
			self._data = None

			try:
				if self._attrib['lang']:
					self.buffer.set_language(lm.get_language(self._attrib['lang']))
			except:
				logger.exception('Could not set language for sourceview: %s', lang)

		widget = SourceViewWidget(self, self.buffer)
		self._widgets.add(widget)

		widget.view.set_show_line_numbers(self._attrib['linenumbers'])
		widget.set_preferences(self.preferences)
		return widget

	def preferences_changed(self):
		for widget in self._widgets:
			widget.set_preferences(self.preferences)

	def on_modified_changed(self, buffer):
		# Sourceview changed, set change on oject, reset state of
		# sourceview buffer so we get a new signal with next change
		if buffer.get_modified():
			self.set_modified(True)
			buffer.set_modified(False)

	def get_data(self):
		'''Returns data as text.'''
		if self.buffer:
			bounds = self.buffer.get_bounds()
			text = self.buffer.get_text(bounds[0], bounds[1])
			text += '\n' # Make sure we always have a trailing \n
			return text
		else:
			return self._data

	def dump(self, format, dumper, linker=None):
		if format == "html":
			if self._attrib['lang']:
				''' to use highlight.js add the following to your template:
				<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/default.min.css">
				<script src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js"></script>
				<script>hljs.initHighlightingOnLoad();</script>
				Map GtkSourceView language ids match with Highlight.js language ids.
				http://packages.ubuntu.com/precise/all/libgtksourceview2.0-common/filelist
				http://highlightjs.readthedocs.io/en/latest/css-classes-reference.html
                '''
				sh_map = {'dosbatch': 'dos'}
				sh_lang = sh_map[self._attrib['lang']] if self._attrib['lang'] in sh_map else self._attrib['lang']
				# TODO: some template instruction to be able to use other highlighters as well?
				output = ['<pre><code class="%s">' % html_encode(sh_lang)] # for syntaxhigligther
				'''' class="brush: language;" works with SyntaxHighlighter 2.0.278, 3 & 4
				output = ['<pre class="brush: %s;">' % html_encode(sh_lang)] # for syntaxhigligther
				'''
			else:
				output = ['<pre>\n']
			data = self.get_data()
			data = html_encode(data) # XXX currently dumper gives encoded lines - NOK
			#if self._attrib['linenumbers']:
			#	for i, l in enumerate(data.splitlines(1)):
			#		output.append('%i&nbsp;' % (i+1) + l)
			#else:
			output.append(data) # ignoring numbering for html - syntaxhighlighter takes care of that
			if self._attrib['lang']:
				output.append('</code></pre>\n')
			else:
				output.append('</pre>\n')
			return output
		return CustomObjectClass.dump(self, format, dumper, linker)

	def set_language(self, lang):
		'''Set language in SourceView.'''
		self._attrib['lang'] = lang
		self.set_modified(True)

		if self.buffer:
			if lang is None:
				self.buffer.set_language(None)
			else:
				self.buffer.set_language(lm.get_language(lang))

	def show_line_numbers(self, show):
		'''Toggles line numbers in SourceView.'''
		self._attrib['linenumbers'] = show
		self.set_modified(True)

		for widget in self._widgets:
			widget.view.set_show_line_numbers(show)