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')), ))
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)
def __init__(self): assert self.name is not None assert self.label is not None self.object_attr = self.object_attr.copy() # Prevent referencing and modifying class attribute of parent class self.object_attr['type'] = String(self.name) # each inserted object has an id attribute usable as anchor for linking self.object_attr['id'] = String(None) for name in ('model_from_data', 'data_from_model', 'format'): orig = getattr(self, name) wrapper = getattr(self, '_' + name + '_wrapper') setattr(self, '_inner_' + name, orig) setattr(self, name, wrapper)
def check(self, value): # Only ascii chars allowed in these keys value = BaseString.check(self, value) if isinstance(value, unicode) \ and value.encode('utf-8') != value: raise ValueError, 'ASCII string required' return value
class BackwardImageGeneratorObjectType(ImageGeneratorObjectType): '''Base class for backward compatible image generator objects.''' object_attr = { 'src': String('_new_'), } scriptname = None imagefile_extension = None def model_from_data(self, notebook, page, attrib, data): generator = self.generator_klass(self.plugin, notebook, page) return BackwardImageGeneratorModel(notebook, page, generator, attrib, data, self.scriptname, self.imagefile_extension) def format(self, format, dumper, attrib, data): if data: logger.warn('Unexpected data in %s object: %r', attrib['type'], data) try: return ImageGeneratorObjectType.format(self, format, dumper, attrib, data) except ValueError: if attrib['type'].startswith('image+'): attrib = attrib.copy() attrib['type'] = attrib['type'][6:] return dumper.dump_img(IMAGE, attrib, None)
def do_response_ok(self): # Get dynamic tabs newpreferences = {} for form in list(self.forms.values()): for key, value in list(form.items()): section = form.preferences_sections[key] if not section in newpreferences: newpreferences[section] = {} newpreferences[section][key] = value # Set font - special case, consider it a HACK customfont = newpreferences['GtkInterface'].pop('use_custom_font') if customfont: font = self.fontbutton.get_font_name() else: font = None text_style = self.config.get_config_dict('<profile>/style.conf') text_style['TextView'].define(font=String(None)) text_style['TextView']['font'] = font # with self.preferences.block_signals('changed'): # note we do not block signal on section dicts for section in newpreferences: self.preferences[section].update(newpreferences[section]) self.preferences.emit('changed') # delayed emission return True
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
def check(self, value): # Only ascii chars allowed in these keys value = BaseString.check(self, value) if isinstance(value, unicode) \ and value.encode('utf-8') != value: raise ValueError, 'ASCII string required' return value
class TableViewObjectType(InsertedObjectTypeExtension): name = 'runnable' label = _('Runnable') object_attr = { 'program': String('grep'), 'arguments': String('-r "hello!"') } def __init__(self, plugin, objmap): # InsertSymbolDialog(self.plugi).run() self._widgets = WeakSet() self.preferences = plugin.preferences InsertedObjectTypeExtension.__init__(self, plugin, objmap) self.connectto(self.preferences, 'changed', self.on_preferences_changed) def data_from_model(self, buffer): return buffer.get_object_data() def model_from_data(self, notebook, page, attrib, data): return TableModel(attrib) def model_from_element(self, attrib, element): assert ElementTree.iselement(element) attrib = self.parse_attrib(attrib) return TableModel(attrib) def create_widget(self, model): widget = TableViewWidget(model) self._widgets.add(widget) return widget def on_preferences_changed(self, preferences): for widget in self._widgets: widget.set_preferences(preferences) def dump(self, builder): builder.start("start") builder.data("data") builder.end("end")
def __init__(self): assert self.name is not None assert self.label is not None self.object_attr['type'] = String(self.name) for name in ('model_from_data', 'data_from_model', 'format'): orig = getattr(self, name) wrapper = getattr(self, '_' + name + '_wrapper') setattr(self, '_inner_' + name, orig) setattr(self, name, wrapper)
def check(self, value): # Only ascii chars allowed in these keys value = BaseString.check(self, value) if isinstance(value, str): try: x = value.encode('ascii') except UnicodeEncodeError: raise ValueError('ASCII string required') else: pass return value
def mount_notebook(filepath): from zim.config import ConfigManager, String configdict = ConfigManager.get_config_dict('automount.conf') groups = sorted([k for k in list(configdict.keys()) if k.startswith('Path')]) for group in groups: path = group[4:].strip() # len('Path') = 4 dir = Dir(path) if is_relevant_mount_point(dir, filepath): configdict[group].define(mount=String(None)) handler = ApplicationMountPointHandler(dir, **configdict[group]) if handler(filepath): break
def mount_notebook(filepath): from zim.config import ConfigManager, String config = ConfigManager() # XXX should be passed in configdict = config.get_config_dict('automount.conf') groups = sorted([k for k in configdict.keys() if k.startswith('Path')]) for group in groups: path = group[4:].strip() # len('Path') = 4 dir = Dir(path) if filepath.path == dir.path or filepath.ischild(dir): configdict[group].define(mount=String(None)) handler = ApplicationMountPointHandler(dir, **configdict[group]) if handler(filepath): break
def __init__(self, ui): Dialog.__init__(self, ui, _('Insert Code Block')) # T: dialog title names = sorted(LANGUAGES, key=lambda k: k.lower()) self.add_form((('lang', 'choice', _('Syntax'), names), ) # T: input label ) # Set previous used language self.uistate.define(lang=String(None)) if 'lang' in self.uistate: for name, id in LANGUAGES.items(): if self.uistate['lang'] == id: try: self.form['lang'] = name except ValueError: pass break
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)
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)), ))
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 ' % (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)
def parse(self, text): '''Parses the config and cache and populates the list Format is:: [NotebookList] Default=uri1 1=uri1 2=uri2 [Notebook 1] name=Foo uri=uri1 Then followed by more "[Notebook]" sections that are cache data @param text: a string or a list of lines ''' # Format <= 0.60 was: # # [NotebookList] # Default=uri1 # uri1 # uri2 # # [Notebook] # name=Foo # uri=uri1 if isinstance(text, str): text = text.splitlines(True) assert text[0].strip() == '[NotebookList]' # Backward compatibility, make valid INI file: # - make redundant [Notebook] sections numbered # - prefix lines without a key with a number n = 0 l = 0 for i, line in enumerate(text): if line.strip() == '[Notebook]': n += 1 text[i] = '[Notebook %i]\n' % n elif line and not line.isspace() \ and not line.lstrip().startswith('[') \ and not line.lstrip().startswith('#') \ and not '=' in line: l += 1 text[i] = ('%i=' % l) + line ### config = INIConfigFile(VirtualFile(text)) mylist = config['NotebookList'] mylist.define(Default=String(None)) mylist.define((k, String(None)) for k in list(mylist._input.keys())) # XXX for key, uri in list(config['NotebookList'].items()): if key == 'Default': continue section = config['Notebook %s' % key] section.define( uri=String(None), name=String(None), icon=String(None), mtime=String(None), interwiki=String(None) ) if section['uri'] == uri: info = NotebookInfo(**section) else: info = NotebookInfo(uri) self.append(info) if 'Default' in config['NotebookList'] \ and config['NotebookList']['Default']: self.set_default(config['NotebookList']['Default'])
class TableViewObjectType(InsertedObjectType): name = 'table' label = _('Table') # T: menu item verb_icon = 'zim-insert-table' object_attr = { 'aligns': String(''), # i.e. String(left,right,center) 'wraps': String('') # i.e. String(0,1,0) } def __init__(self, plugin): InsertedObjectType.__init__(self, plugin) self._widgets = WeakSet() self.preferences = plugin.preferences self.connectto(self.preferences, 'changed', self.on_preferences_changed) def new_object_interactive(self, parent): definition = EditTableDialog(parent).run() if definition is None: raise ValueError # dialog cancelled ids, headers, wraps, aligns = definition attrib = self.parse_attrib({ 'aligns': ','.join(map(str, aligns)), 'wraps': ','.join(map(str, wraps)) }) rows = [''] * len(headers) data = ' | '.join(headers) + '\n' + ' | '.join(rows) + '\n' return attrib, data def model_from_data(self, attrib, data): rows = [line.split(' | ') for line in data.splitlines()] headers = rows.pop(0) if rows else [] if not headers: headers = ['Column1', 'Column2'] return TableModel(attrib, headers, rows) def model_from_element(self, attrib, element): assert ElementTree.iselement(element) attrib = self.parse_attrib(attrib) headers, rows = self._tabledom_to_list(element) return TableModel(attrib, headers, rows) def _tabledom_to_list(self, tabledata): ''' Extracts necessary data out of a xml-table into a list structure :param tabledata: XML - formated as a zim-tree table-object :return: tuple of header-list and list of row lists - ([h1,h2],[[r11,r12],[r21,r22]) ''' headers = [head.text for head in tabledata.findall('thead/th')] headers = list(map(CellFormatReplacer.zim_to_cell, headers)) rows = [] for trow in tabledata.findall('trow'): row = trow.findall('td') row = [ ElementTree.tostring(r, 'unicode').replace('<td>', '').replace( '</td>', '') for r in row ] row = list(map(CellFormatReplacer.zim_to_cell, row)) rows.append(row) return headers, rows def create_widget(self, model): widget = TableViewWidget(model) 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 dump(self, builder, model): headers, attrib, rows = model.get_object_data() builder.start(TABLE, dict(attrib)) builder.start(HEADROW) for header in headers: builder.append(HEADDATA, header) builder.end(HEADROW) for row in rows: builder.start(TABLEROW) for cell in row: builder.append(TABLEDATA, cell) builder.end(TABLEROW) builder.end(TABLE)
class TableViewObject(CustomObjectClass): '''data presenter of an inserted table within a page''' OBJECT_ATTR = { 'type': String('table'), 'aligns': String(''), # i.e. String(left,right,center) 'wraps': String('') # i.e. String(0,1,0) } def __init__(self, attrib, header, rows, preferences): ''' Creates a new object which can displayed within the page :param attrib: aligns, wraps :param header: titles of the table as list :param rows: body-rows of the table as list of lists :param preferences: optionally some preferences ''' _attrib = {} for k, v in attrib.iteritems(): if isinstance(v, list): v = ','.join(map(str, v)) _attrib[k] = v CustomObjectClass.__init__(self, _attrib, [header] + rows) self.attrib = {'type': OBJECT_TYPE} # just to be sure self._tableattrib = attrib self._header = header self._rows = rows self._widgets = WeakSet() self._liststore = None # shared model between widgets self.preferences = preferences # getters and setters for attributes def get_aligns(self): ''' get the list of align-attributes ''' return self._attrib['aligns'].split(',') def set_aligns(self, data): ''' Set list of align attributes for the current table. Each item belongs to a column.''' assert (isinstance(data, list)) self._attrib['aligns'] = ','.join(data) def get_wraps(self): ''' get the list of wrap-attributes ''' return map(int, self._attrib['wraps'].split(',')) def set_wraps(self, data): ''' Set list of wrap attributes for the current table. Each item belongs to a column.''' assert (isinstance(data, list)) self._attrib['wraps'] = ','.join(str(item) for item in data) def _get_liststore(self, reset=False): if reset or not self._liststore: cols = [str] * len(self._header) self._liststore = gtk.ListStore(*cols) for trow in self._rows: self._liststore.append(trow) self._liststore.connect('row-changed', self.on_modified_changed) return self._liststore def get_widget(self): ''' Creates a new table-widget which can displayed on the wiki-page ''' liststore = self._get_liststore() attrib = {'aligns': self.get_aligns(), 'wraps': self.get_wraps()} widget = TableViewWidget(self, liststore, self._header, attrib) self._widgets.add(widget) widget.set_preferences(self.preferences) return widget def preferences_changed(self): ''' Updates all created table-widgets, if preferences have changed ''' for widget in self._widgets: widget.set_preferences(self.preferences) def on_sort_column_changed(self, liststore): ''' Trigger after a column-header is clicked and therefore its sort order has changed ''' self.set_modified(True) def on_modified_changed(self, liststore, path, treeiter): ''' Trigger after a table cell content is changed by the user ''' self.set_modified(True) def get_data(self): '''Returns table-object into textual data, for saving it as text.''' headers = self._header attrs = { 'aligns': self._attrib['aligns'], 'wraps': self._attrib['wraps'] } if not self._liststore: rows = self._rows else: rows = [] for treerow in self._liststore: rows.append( map( lambda cell: CellFormatReplacer.cell_to_input( cell, True), treerow)) return headers, rows, attrs def change_model(self, new_model): ''' Replace liststore with new model and notify widgets to update their treeview. :param new_model: tuple of lists for ([id], [header], [warps], [aligns]) ''' # prepare results out of dialog-window id_mapping, headers, aligns, wraps = ({}, [], [], []) for i, model in enumerate(new_model): if model[0] != -1: id_mapping[i] = model[0] header = model[1] if model[1] else ' ' headers.append(header) aligns.append(model[3]) wraps.append(model[2]) # update data if self._liststore: liststore = self._get_liststore() self._rows = self._update_rows(liststore, id_mapping, len(headers)) liststore = self._get_liststore(reset=True) else: liststore = None self._rows = self._update_rows(self._rows, id_mapping, len(headers)) self.set_aligns(aligns) self.set_wraps(wraps) self.set_modified(True) # notify widgets for widget in self._widgets: assert liststore is not None, 'Huh?' attrib = {'aligns': self.get_aligns(), 'wraps': self.get_wraps()} widget.on_model_changed(liststore, headers, attrib) self.preferences_changed() # reset prefs on widgets def _update_rows(self, old_rows, id_mapping, nr_cols): ''' Old value of cells are used in the new table, but only if its column is not deleted ''' new_rows = [] for oldrow in old_rows: newrow = [' '] * nr_cols for v, k in id_mapping.iteritems(): newrow[v] = oldrow[k] new_rows.append(newrow) return new_rows def build_parsetree_of_table(self, builder, iter): logger.debug("Anchor with TableObject: %s", self) # inserts a newline before and after table-object bound = iter.copy() bound.backward_char() char_before_table = bound.get_slice(iter) need_newline_infront = char_before_table.decode( 'utf-8') != "\n".decode('utf-8') bound = iter.copy() bound.forward_char() iter2 = bound.copy() bound.forward_char() char_after_table = iter2.get_slice(bound) need_newline_behind = char_after_table.decode('utf-8') != "\n".decode( 'utf-8') # headers, rows, attrib = self.get_data() #~ print "Table data:", headers, rows, attrib if need_newline_infront: builder.data('\n') builder.start(TABLE, attrib) builder.start(HEADROW) for header in headers: builder.append(HEADDATA, header) builder.end(HEADROW) for row in rows: builder.start(TABLEROW) for cell in row: builder.append(TABLEDATA, cell) builder.end(TABLEROW) builder.end(TABLE) if need_newline_behind: builder.data('\n')
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 ' % (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)
class TableViewObjectType(InsertedObjectTypeExtension): name = 'table' label = _('Table') # T: menu item verb_icon = 'zim-insert-table' object_attr = { 'aligns': String(''), # i.e. String(left,right,center) 'wraps': String('') # i.e. String(0,1,0) } 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): definition = EditTableDialog(parent).run() if definition is None: raise ValueError # dialog cancelled ids, headers, wraps, aligns = definition attrib = self.parse_attrib({ 'aligns': ','.join(map(str, aligns)), 'wraps': ','.join(map(str, wraps)) }) rows = [''] * len(headers) return TableModel(attrib, headers, rows) def model_from_data(self, notebook, page, attrib, data): tree = WikiParser().parse(data) element = tree._etree.getroot().find( 'table') # XXX - should use token interface instead if element is not None: return self.model_from_element(element.attrib, element) else: return TableModel(attrib, [data.strip()], ['']) def model_from_element(self, attrib, element): assert ElementTree.iselement(element) attrib = self.parse_attrib(attrib) headers, rows = self._tabledom_to_list(element) return TableModel(attrib, headers, rows) def _tabledom_to_list(self, tabledata): ''' Extracts necessary data out of a xml-table into a list structure :param tabledata: XML - formated as a zim-tree table-object :return: tuple of header-list and list of row lists - ([h1,h2],[[r11,r12],[r21,r22]) ''' headers = [head.text for head in tabledata.findall('thead/th')] headers = list(map(CellFormatReplacer.zim_to_cell, headers)) rows = [] for trow in tabledata.findall('trow'): row = trow.findall('td') row = [ ElementTree.tostring(r, 'unicode').replace('<td>', '').replace( '</td>', '') for r in row ] row = list(map(CellFormatReplacer.zim_to_cell, row)) rows.append(row) return headers, rows def create_widget(self, model): widget = TableViewWidget(model) 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 dump(self, builder, model): headers, attrib, rows = model.get_object_data() def append(tag, text): builder.start(tag, {}) builder.data(text) builder.end(tag) builder.start(TABLE, dict(attrib)) builder.start(HEADROW) for header in headers: append(HEADDATA, header) builder.end(HEADROW) for row in rows: builder.start(TABLEROW, {}) for cell in row: append(TABLEDATA, cell) builder.end(TABLEROW) builder.end(TABLE)
def __init__(self, parent, store, preferences): title = _('Command Palette') Dialog.__init__(self, parent, title) self.uistate.define(last_entry=String(None)) self.action = None self.store = store self.entries = {item[0]: item[1] for item in self.store} # { label: action } # Configure completion for search field. completion = Gtk.EntryCompletion() completion.set_model(store) cell_shortcut = Gtk.CellRendererText() completion.pack_end(cell_shortcut, False) completion.add_attribute(cell_shortcut, 'text', 2) completion.set_text_column(0) completion.set_minimum_key_length(0) completion.connect("match-selected", self.on_match_selected) def match_anywhere(_completion, _entrystr, _iter, _data): """ Match any part. """ _modelstr = _completion.get_model()[_iter][0].lower() for part in _entrystr.split(): if part not in _modelstr: return False return True completion.set_match_func(match_anywhere, None) self.hbox = Gtk.HBox() # Add search field. self.txt_search = Gtk.SearchEntry(hexpand=True, margin=2) self.txt_search.set_activates_default( True) # Make ENTER key press trigger the OK button. self.txt_search.set_icon_from_icon_name( Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_FIND) self.txt_search.set_placeholder_text("Search commands") self.txt_search.set_completion(completion) last_entry = self.init_last_entry() if last_entry: self.txt_search.set_text(last_entry) self.txt_search.connect('search-changed', self.do_validate, parent) self.txt_search.connect("key-press-event", self.on_key_pressed, parent) # Add ok button. 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(last_entry is not None) # Configure dialog. self.set_modal(True) self.set_default_size(380, 100) self.hbox.pack_start(self.txt_search, True, True, 0) self.vbox.pack_start(self.hbox, True, True, 0) # Set focus to search field self.txt_search.grab_focus()