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')), ))
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 __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)), ))
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, 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)
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)
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()
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()
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)