Introducing StyledText in Notes.
svn: r10410
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| # | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2000-2006  Donald N. Allingham | ||||
| # Copyright (C) 2000-2007  Donald N. Allingham | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| @@ -25,7 +25,7 @@ | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| import logging | ||||
| log = logging.getLogger(".") | ||||
| _LOG = logging.getLogger(".DisplayModels.NoteModel") | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| @@ -40,20 +40,19 @@ import gtk | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| from _BaseModel import BaseModel | ||||
| import gen.lib | ||||
| from gen.lib import (Note, NoteType, MarkerType, StyledText) | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # PlaceModel | ||||
| # NoteModel | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class NoteModel(BaseModel): | ||||
|  | ||||
|     HANDLE_COL = 4 | ||||
|     _MARKER_COL = 6 | ||||
|  | ||||
|     def __init__(self,db,scol=0, order=gtk.SORT_ASCENDING,search=None, | ||||
|     """ | ||||
|     """ | ||||
|     def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None, | ||||
|                  skip=set(), sort_map=None): | ||||
|         """Setup initial values for instance variables.""" | ||||
|         self.gen_cursor = db.get_note_cursor | ||||
|         self.map = db.get_raw_note_data | ||||
|         self.fmap = [ | ||||
| @@ -63,7 +62,7 @@ class NoteModel(BaseModel): | ||||
|             self.column_marker, | ||||
|             self.column_handle, | ||||
|             self.column_marker_color | ||||
|             ] | ||||
|         ] | ||||
|         self.smap = [ | ||||
|             self.column_preview, | ||||
|             self.column_id, | ||||
| @@ -71,48 +70,57 @@ class NoteModel(BaseModel): | ||||
|             self.column_marker, | ||||
|             self.column_handle, | ||||
|             self.column_marker_color | ||||
|             ] | ||||
|         ] | ||||
|         self.marker_color_column = 5 | ||||
|         BaseModel.__init__(self, db, scol, order, | ||||
|                            search=search, skip=skip, sort_map=sort_map) | ||||
|         BaseModel.__init__(self, db, scol, order, search=search, | ||||
|                            skip=skip, sort_map=sort_map) | ||||
|  | ||||
|     def on_get_n_columns(self): | ||||
|         return len(self.fmap)+1 | ||||
|         """Return the column number of the Note tab.""" | ||||
|         return len(self.fmap) + 1 | ||||
|  | ||||
|     def column_handle(self,data): | ||||
|         return data[0] | ||||
|     def column_handle(self, data): | ||||
|         """Return the handle of the Note.""" | ||||
|         return data[Note.POS_HANDLE] | ||||
|  | ||||
|     def column_id(self,data): | ||||
|         return unicode(data[1]) | ||||
|     def column_id(self, data): | ||||
|         """Return the id of the Note.""" | ||||
|         return unicode(data[Note.POS_ID]) | ||||
|  | ||||
|     def column_type(self,data): | ||||
|         temp = gen.lib.NoteType() | ||||
|         temp.set(data[4]) | ||||
|     def column_type(self, data): | ||||
|         """Return the type of the Note in readable format.""" | ||||
|         temp = NoteType() | ||||
|         temp.set(data[Note.POS_TYPE]) | ||||
|         return unicode(str(temp)) | ||||
|  | ||||
|     def column_marker(self, data): | ||||
|         temp = gen.lib.MarkerType() | ||||
|         temp.set(data[6]) | ||||
|         """Return the marker type of the Note in readable format.""" | ||||
|         temp = MarkerType() | ||||
|         temp.set(data[Note.POS_MARKER]) | ||||
|         return unicode(str(temp)) | ||||
|      | ||||
|     def column_preview(self,data): | ||||
|     def column_preview(self, data): | ||||
|         """Return a shortend version of the Note's text.""" | ||||
|         #data is the encoding in the database, make it a unicode object | ||||
|         #for universal work | ||||
|         note = " ".join(unicode(data[2]).split()) | ||||
|         note = unicode(data[Note.POS_TEXT][StyledText.POS_TEXT]) | ||||
|         note = " ".join(note.split()) | ||||
|         if len(note) > 80: | ||||
|             return note[:80]+"..." | ||||
|             return note[:80] + "..." | ||||
|         else: | ||||
|             return note | ||||
|  | ||||
|     def column_marker_color(self, data): | ||||
|         """Return the color of the Note's marker type if exist.""" | ||||
|         try: | ||||
|             col = data[NoteModel._MARKER_COL][0] | ||||
|             if col == gen.lib.MarkerType.COMPLETE: | ||||
|             col = data[Note.POS_MARKER][MarkerType.POS_VALUE] | ||||
|             if col == MarkerType.COMPLETE: | ||||
|                 return self.complete_color | ||||
|             elif col == gen.lib.MarkerType.TODO_TYPE: | ||||
|             elif col == MarkerType.TODO_TYPE: | ||||
|                 return self.todo_color | ||||
|             elif col == gen.lib.MarkerType.CUSTOM: | ||||
|             elif col == MarkerType.CUSTOM: | ||||
|                 return self.custom_color | ||||
|             else: | ||||
|                 return None | ||||
|         except IndexError: | ||||
|             pass | ||||
|         return None | ||||
|             return None | ||||
|   | ||||
| @@ -28,7 +28,8 @@ | ||||
| from gettext import gettext as _ | ||||
|  | ||||
| import logging | ||||
| log = logging.getLogger(".") | ||||
| _LOG = logging.getLogger(".Editors.EditNote") | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GTK libraries | ||||
| @@ -41,19 +42,20 @@ import pango | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GRAMPS classes | ||||
| # GRAMPS modules | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| import const | ||||
| import Spell | ||||
| import Config | ||||
| import GrampsDisplay | ||||
| import MarkupText | ||||
| from const import GLADE_FILE | ||||
| from Spell import Spell | ||||
| from GrampsDisplay import url | ||||
| from Editors._StyledTextBuffer import (StyledTextBuffer, MATCH_START, | ||||
|                                        MATCH_END, MATCH_FLAVOR, MATCH_STRING) | ||||
| from Editors._EditPrimary import EditPrimary | ||||
| from DisplayTabs import GrampsTab, NoteBackRefList | ||||
| from GrampsWidgets import (MonitoredDataType, MonitoredCheckbox,  | ||||
|                            MonitoredEntry, PrivacyButton) | ||||
| import gen.lib | ||||
| from gen.lib import Note | ||||
| from QuestionDialog import ErrorDialog | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| @@ -61,17 +63,16 @@ from QuestionDialog import ErrorDialog | ||||
| # Constants | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| #USERCHARS = "-A-Za-z0-9" | ||||
| #PASSCHARS = "-A-Za-z0-9,?;.:/!%$^*&~\"#'" | ||||
| #HOSTCHARS = "-A-Za-z0-9" | ||||
| #PATHCHARS = "-A-Za-z0-9_$.+!*(),;:@&=?/~#%" | ||||
| ##SCHEME = "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:)" | ||||
| #SCHEME = "(file:/|https?:|ftps?:|webcal:)" | ||||
| #USER = "[" + USERCHARS + "]+(:[" + PASSCHARS + "]+)?" | ||||
| #URLPATH = "/[" + PATHCHARS + "]*[^]'.}>) \t\r\n,\\\"]" | ||||
| # | ||||
| #(GENERAL, HTTP, MAIL) = range(3) | ||||
| USERCHARS = "-A-Za-z0-9" | ||||
| PASSCHARS = "-A-Za-z0-9,?;.:/!%$^*&~\"#'" | ||||
| HOSTCHARS = "-A-Za-z0-9" | ||||
| PATHCHARS = "-A-Za-z0-9_$.+!*(),;:@&=?/~#%" | ||||
| #SCHEME = "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:)" | ||||
| SCHEME = "(file:/|https?:|ftps?:|webcal:)" | ||||
| USER = "[" + USERCHARS + "]+(:[" + PASSCHARS + "]+)?" | ||||
| URLPATH = "/[" + PATHCHARS + "]*[^]'.}>) \t\r\n,\\\"]" | ||||
|  | ||||
| (GENERAL, HTTP, MAIL) = range(3) | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| @@ -91,17 +92,17 @@ class NoteTab(GrampsTab): | ||||
|         the database, along with other state information. The GrampsTab | ||||
|         uses this to access the database and to pass to and created | ||||
|         child windows (such as edit dialogs). | ||||
|         @type dbstate: DbState | ||||
|         @type dbstate: L{DbState.DbState} | ||||
|         @param uistate: The UI state. Used primarily to pass to any created | ||||
|         subwindows. | ||||
|         @type uistate: DisplayState | ||||
|         @type uistate: L{DisplayState.DisplayState} | ||||
|         @param track: The window tracking mechanism used to manage windows. | ||||
|         This is only used to pass to generted child windows. | ||||
|         @type track: list | ||||
|         @param name: Notebook label name | ||||
|         @type name: str/unicode | ||||
|         @param widget: widget to be shown in the tab | ||||
|         @type widge: gtk widget | ||||
|         @type widget: gtk widget | ||||
|         """ | ||||
|         GrampsTab.__init__(self, dbstate, uistate, track, name) | ||||
|         eventbox = gtk.EventBox() | ||||
| @@ -131,11 +132,12 @@ class EditNote(EditPrimary): | ||||
|                  callertitle = None, extratype = None): | ||||
|         """Create an EditNote window. Associate a note with the window. | ||||
|          | ||||
|         @param callertitle: a text passed by calling object to add to title  | ||||
|         @param callertitle: Text passed by calling object to add to title  | ||||
|         @type callertitle: str | ||||
|         @param extratype: extra NoteType values to add to the default types  | ||||
|             They are removed from the ignorelist of NoteType. | ||||
|         @param extratype: Extra L{NoteType} values to add to the default types. | ||||
|         They are removed from the ignorelist of L{NoteType}. | ||||
|         @type extratype: list of int | ||||
|          | ||||
|         """ | ||||
|         self.callertitle = callertitle | ||||
|         self.extratype = extratype | ||||
| @@ -146,9 +148,10 @@ class EditNote(EditPrimary): | ||||
|     def empty_object(self): | ||||
|         """Return an empty Note object for comparison for changes. | ||||
|          | ||||
|         It is used by the base class (EditPrimary). | ||||
|         It is used by the base class L{EditPrimary}. | ||||
|          | ||||
|         """ | ||||
|         empty_note = gen.lib.Note(); | ||||
|         empty_note = Note(); | ||||
|         if self.extratype: | ||||
|             empty_note.set_type(self.extratype[0]) | ||||
|         return empty_note | ||||
| @@ -157,16 +160,16 @@ class EditNote(EditPrimary): | ||||
|         if self.obj.get_handle(): | ||||
|             if self.callertitle : | ||||
|                 title = _('Note: %(id)s - %(context)s') % { | ||||
|                                         'id'      : self.obj.get_gramps_id(), | ||||
|                                         'context' : self.callertitle | ||||
|                                         } | ||||
|                     'id'      : self.obj.get_gramps_id(), | ||||
|                     'context' : self.callertitle | ||||
|                 } | ||||
|             else : | ||||
|                 title = _('Note: %s') % self.obj.get_gramps_id() | ||||
|         else: | ||||
|             if self.callertitle : | ||||
|                 title = _('New Note - %(context)s') % { | ||||
|                                         'context' : self.callertitle | ||||
|                                         } | ||||
|                     'context' : self.callertitle | ||||
|                 } | ||||
|             else : | ||||
|                 title = _('New Note')   | ||||
|              | ||||
| @@ -179,11 +182,11 @@ class EditNote(EditPrimary): | ||||
|         """Local initialization function. | ||||
|          | ||||
|         Perform basic initialization, including setting up widgets | ||||
|         and the glade interface. It is called by the base class (EditPrimary), | ||||
|         and the glade interface. It is called by the base class L{EditPrimary}, | ||||
|         and overridden here. | ||||
|          | ||||
|         """ | ||||
|         self.top = glade.XML(const.GLADE_FILE, "edit_note", "gramps") | ||||
|         self.top = glade.XML(GLADE_FILE, "edit_note", "gramps") | ||||
|         win = self.top.get_widget("edit_note") | ||||
|         self.set_window(win, None, self.get_menu_title()) | ||||
|  | ||||
| @@ -242,7 +245,7 @@ class EditNote(EditPrimary): | ||||
|     def _connect_signals(self): | ||||
|         """Connects any signals that need to be connected. | ||||
|          | ||||
|         Called by the init routine of the base class (_EditPrimary). | ||||
|         Called by the init routine of the base class L{EditPrimary}. | ||||
|          | ||||
|         """ | ||||
|         self.define_ok_button(self.top.get_widget('ok'), self.save) | ||||
| @@ -250,122 +253,68 @@ class EditNote(EditPrimary): | ||||
|         self.define_help_button(self.top.get_widget('help'), '') | ||||
|      | ||||
|     def _create_tabbed_pages(self): | ||||
|         """ | ||||
|         Create the notebook tabs and inserts them into the main | ||||
|         window. | ||||
|         """ | ||||
|         """Create the notebook tabs and inserts them into the main window.""" | ||||
|         notebook = self.top.get_widget("note_notebook") | ||||
|  | ||||
|         self._add_tab(notebook, self.ntab) | ||||
|  | ||||
|         self.backref_tab = self._add_tab( | ||||
|             notebook, | ||||
|             NoteBackRefList(self.dbstate, self.uistate, self.track, | ||||
|                             self.dbstate.db.find_backlink_handles( | ||||
|                                                             self.obj.handle)) | ||||
|                                         ) | ||||
|         handles = self.dbstate.db.find_backlink_handles(self.obj.handle) | ||||
|         rlist = NoteBackRefList(self.dbstate, self.uistate, self.track, handles) | ||||
|         self.backref_tab = self._add_tab(notebook, rlist) | ||||
|          | ||||
|         self._setup_notebook_tabs( notebook) | ||||
|         self._setup_notebook_tabs(notebook) | ||||
|  | ||||
| # THIS IS THE MARKUP VERSION - enable for markup | ||||
| #    def build_interface(self): | ||||
| #        FORMAT_TOOLBAR = ''' | ||||
| #        <ui> | ||||
| #        <toolbar name="ToolBar"> | ||||
| #          <toolitem action="italic"/>   | ||||
| #          <toolitem action="bold"/>   | ||||
| #          <toolitem action="underline"/> | ||||
| #          <separator/> | ||||
| #          <toolitem action="font"/> | ||||
| #          <toolitem action="foreground"/> | ||||
| #          <toolitem action="background"/> | ||||
| #          <separator/> | ||||
| #          <toolitem action="clear"/> | ||||
| #        </toolbar> | ||||
| #        </ui> | ||||
| #        ''' | ||||
| # | ||||
| #        buffer_ = MarkupText.MarkupBuffer() | ||||
| #        buffer_.create_tag('hyperlink', | ||||
| #                          underline=pango.UNDERLINE_SINGLE, | ||||
| #                          foreground='blue') | ||||
| #        buffer_.match_add("(www|ftp)[" + HOSTCHARS + "]*\\.[" + HOSTCHARS + | ||||
| #                         ".]+" + "(:[0-9]+)?(" + URLPATH + ")?/?", HTTP) | ||||
| #        buffer_.match_add("(mailto:)?[a-z0-9][a-z0-9.-]*@[a-z0-9][a-z0-9-]*" | ||||
| #                         "(\\.[a-z0-9][a-z0-9-]*)+", MAIL) | ||||
| #        buffer_.match_add(SCHEME + "//(" + USER + "@)?[" + HOSTCHARS + ".]+" + | ||||
| #                             "(:[0-9]+)?(" + URLPATH + ")?/?", GENERAL) | ||||
| #        self.match = None | ||||
| #        self.last_match = None | ||||
| # | ||||
| #        self.text = self.top.get_widget('text') | ||||
| #        self.text.set_editable(not self.dbstate.db.readonly) | ||||
| #        self.text.set_buffer(buffer_) | ||||
| #        self.text.connect('key-press-event', | ||||
| #                          self.on_textview_key_press_event) | ||||
| #        self.text.connect('insert-at-cursor', | ||||
| #                          self.on_textview_insert_at_cursor) | ||||
| #        self.text.connect('delete-from-cursor', | ||||
| #                          self.on_textview_delete_from_cursor) | ||||
| #        self.text.connect('paste-clipboard', | ||||
| #                          self.on_textview_paste_clipboard) | ||||
| #        self.text.connect('motion-notify-event', | ||||
| #                          self.on_textview_motion_notify_event) | ||||
| #        self.text.connect('button-press-event', | ||||
| #                          self.on_textview_button_press_event) | ||||
| #        self.text.connect('populate-popup', | ||||
| #                          self.on_textview_populate_popup) | ||||
| #         | ||||
| #        # setup spell checking interface | ||||
| #        spellcheck = Spell.Spell(self.text) | ||||
| #        liststore = gtk.ListStore(gobject.TYPE_STRING) | ||||
| #        cell = gtk.CellRendererText() | ||||
| #        lang_selector = self.top.get_widget('spell') | ||||
| #        lang_selector.set_model(liststore) | ||||
| #        lang_selector.pack_start(cell, True) | ||||
| #        lang_selector.add_attribute(cell, 'text', 0) | ||||
| #        act_lang = spellcheck.get_active_language() | ||||
| #        idx = 0 | ||||
| #        for lang in spellcheck.get_all_languages(): | ||||
| #            lang_selector.append_text(lang) | ||||
| #            if lang == act_lang: | ||||
| #                act_idx = idx | ||||
| #            idx = idx + 1 | ||||
| #        lang_selector.set_active(act_idx) | ||||
| #        lang_selector.connect('changed', self.on_spell_change, spellcheck) | ||||
| #        #lang_selector.set_sensitive(Config.get(Config.SPELLCHECK)) | ||||
| # | ||||
| #        # create a formatting toolbar | ||||
| #        if not self.dbstate.db.readonly: | ||||
| #            uimanager = gtk.UIManager() | ||||
| #            uimanager.insert_action_group(buffer_.format_action_group, 0) | ||||
| #            uimanager.add_ui_from_string(FORMAT_TOOLBAR) | ||||
| #            uimanager.ensure_update() | ||||
| # | ||||
| #            toolbar = uimanager.get_widget('/ToolBar')       | ||||
| #            toolbar.set_style(gtk.TOOLBAR_ICONS) | ||||
| #            vbox = self.top.get_widget('container') | ||||
| #            vbox.pack_start(toolbar) | ||||
| #                 | ||||
| #        # setup initial values for textview and buffer_ | ||||
| #        if self.obj: | ||||
| #            self.empty = False | ||||
| #            self.flow_changed(self.obj.get_format()) | ||||
| #            buffer_.set_text(self.obj.get(markup=True)) | ||||
| #            log.debug("Initial Note: %s" % buffer_.get_text()) | ||||
| #        else: | ||||
| #            self.empty = True | ||||
|  | ||||
| # NON-MARKUP VERSION - Disable for markup | ||||
|     def build_interface(self): | ||||
|         buffer_ = gtk.TextBuffer() | ||||
|         FORMAT_TOOLBAR = ''' | ||||
|         <ui> | ||||
|         <toolbar name="ToolBar"> | ||||
|           <toolitem action="italic"/>   | ||||
|           <toolitem action="bold"/>   | ||||
|           <toolitem action="underline"/> | ||||
|           <separator/> | ||||
|           <toolitem action="font"/> | ||||
|           <toolitem action="foreground"/> | ||||
|           <toolitem action="background"/> | ||||
|           <separator/> | ||||
|           <toolitem action="clear"/> | ||||
|         </toolbar> | ||||
|         </ui> | ||||
|         ''' | ||||
|  | ||||
|         textbuffer = StyledTextBuffer() | ||||
|         textbuffer.create_tag('hyperlink', | ||||
|                               underline=pango.UNDERLINE_SINGLE, | ||||
|                               foreground='blue') | ||||
|         textbuffer.match_add("(www|ftp)[" + HOSTCHARS + "]*\\.[" + HOSTCHARS + | ||||
|                              ".]+" + "(:[0-9]+)?(" + URLPATH + ")?/?", HTTP) | ||||
|         textbuffer.match_add("(mailto:)?[a-z0-9][a-z0-9.-]*@[a-z0-9][a-z0-9-]*" | ||||
|                              "(\\.[a-z0-9][a-z0-9-]*)+", MAIL) | ||||
|         textbuffer.match_add(SCHEME + "//(" + USER + "@)?[" + HOSTCHARS + | ||||
|                              ".]+" + "(:[0-9]+)?(" + URLPATH + ")?/?", GENERAL) | ||||
|         self.match = None | ||||
|         self.last_match = None | ||||
|  | ||||
|         self.text = self.top.get_widget('text') | ||||
|         self.text.set_editable(not self.dbstate.db.readonly) | ||||
|         self.text.set_buffer(buffer_) | ||||
|         self.text.set_buffer(textbuffer) | ||||
|         self.text.connect('key-press-event', | ||||
|                           self.on_textview_key_press_event) | ||||
|         self.text.connect('insert-at-cursor', | ||||
|                           self.on_textview_insert_at_cursor) | ||||
|         self.text.connect('delete-from-cursor', | ||||
|                           self.on_textview_delete_from_cursor) | ||||
|         self.text.connect('paste-clipboard', | ||||
|                           self.on_textview_paste_clipboard) | ||||
|         self.text.connect('motion-notify-event', | ||||
|                           self.on_textview_motion_notify_event) | ||||
|         self.text.connect('button-press-event', | ||||
|                           self.on_textview_button_press_event) | ||||
|         self.text.connect('populate-popup', | ||||
|                           self.on_textview_populate_popup) | ||||
|          | ||||
|         # setup spell checking interface | ||||
|         spellcheck = Spell.Spell(self.text) | ||||
|         spellcheck = Spell(self.text) | ||||
|         liststore = gtk.ListStore(gobject.TYPE_STRING) | ||||
|         cell = gtk.CellRendererText() | ||||
|         lang_selector = self.top.get_widget('spell') | ||||
| @@ -382,14 +331,62 @@ class EditNote(EditPrimary): | ||||
|         lang_selector.set_active(act_idx) | ||||
|         lang_selector.connect('changed', self.on_spell_change, spellcheck) | ||||
|         #lang_selector.set_sensitive(Config.get(Config.SPELLCHECK)) | ||||
|  | ||||
|         # create a formatting toolbar | ||||
|         if not self.dbstate.db.readonly: | ||||
|             uimanager = gtk.UIManager() | ||||
|             uimanager.insert_action_group(textbuffer.format_action_group, 0) | ||||
|             uimanager.add_ui_from_string(FORMAT_TOOLBAR) | ||||
|             uimanager.ensure_update() | ||||
|  | ||||
|             toolbar = uimanager.get_widget('/ToolBar')       | ||||
|             toolbar.set_style(gtk.TOOLBAR_ICONS) | ||||
|             vbox = self.top.get_widget('container') | ||||
|             vbox.pack_start(toolbar) | ||||
|                  | ||||
|         # setup initial values for textview and buffer_ | ||||
|         # setup initial values for textview and textbuffer | ||||
|         if self.obj: | ||||
|             self.empty = False | ||||
|             self.flow_changed(self.obj.get_format()) | ||||
|             buffer_.set_text(self.obj.get()) | ||||
|             textbuffer.set_text(self.obj.get_styledtext()) | ||||
|             _LOG.debug("Initial Note: %s" % str(textbuffer.get_text())) | ||||
|         else: | ||||
|             self.empty = True | ||||
|  | ||||
| # NON-MARKUP VERSION - Disable for markup | ||||
|     #def build_interface(self): | ||||
|         #textbuffer = gtk.TextBuffer() | ||||
|  | ||||
|         #self.text = self.top.get_widget('text') | ||||
|         #self.text.set_editable(not self.dbstate.db.readonly) | ||||
|         #self.text.set_buffer(textbuffer) | ||||
|          | ||||
|         ## setup spell checking interface | ||||
|         #spellcheck = Spell(self.text) | ||||
|         #liststore = gtk.ListStore(gobject.TYPE_STRING) | ||||
|         #cell = gtk.CellRendererText() | ||||
|         #lang_selector = self.top.get_widget('spell') | ||||
|         #lang_selector.set_model(liststore) | ||||
|         #lang_selector.pack_start(cell, True) | ||||
|         #lang_selector.add_attribute(cell, 'text', 0) | ||||
|         #act_lang = spellcheck.get_active_language() | ||||
|         #idx = 0 | ||||
|         #for lang in spellcheck.get_all_languages(): | ||||
|             #lang_selector.append_text(lang) | ||||
|             #if lang == act_lang: | ||||
|                 #act_idx = idx | ||||
|             #idx = idx + 1 | ||||
|         #lang_selector.set_active(act_idx) | ||||
|         #lang_selector.connect('changed', self.on_spell_change, spellcheck) | ||||
|         ##lang_selector.set_sensitive(Config.get(Config.SPELLCHECK)) | ||||
|                  | ||||
|         ## setup initial values for textview and textbuffer | ||||
|         #if self.obj: | ||||
|             #self.empty = False | ||||
|             #self.flow_changed(self.obj.get_format()) | ||||
|             #textbuffer.set_text(self.obj.get()) | ||||
|         #else: | ||||
|             #self.empty = True | ||||
|              | ||||
|     def build_menu_names(self, person): | ||||
|         """ | ||||
| @@ -401,119 +398,116 @@ class EditNote(EditPrimary): | ||||
|     def _post_init(self): | ||||
|         self.text.grab_focus() | ||||
|  | ||||
| # enable for markup | ||||
| #    def on_textview_key_press_event(self, textview, event): | ||||
| #        """Handle shortcuts in the TextView.""" | ||||
| #        return textview.get_buffer().on_key_press_event(textview, event) | ||||
| #     | ||||
| #    def on_textview_insert_at_cursor(self, textview, string): | ||||
| #        log.debug("Textview insert '%s'" % string) | ||||
| #         | ||||
| #    def on_textview_delete_from_cursor(self, textview, type, count): | ||||
| #        log.debug("Textview delete type %d count %d" % (type, count)) | ||||
| #         | ||||
| #    def on_textview_paste_clipboard(self, textview): | ||||
| #        log.debug("Textview paste clipboard") | ||||
| #     | ||||
| #    def on_textview_motion_notify_event(self, textview, event): | ||||
| #        window = textview.get_window(gtk.TEXT_WINDOW_TEXT) | ||||
| #        x, y = textview.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, | ||||
| #                                                int(event.x), int(event.y)) | ||||
| #        iter = textview.get_iter_at_location(x, y) | ||||
| #        buffer_ = textview.get_buffer() | ||||
| #        self.match = buffer_.match_check(iter.get_offset()) | ||||
| #         | ||||
| #        if self.match != self.last_match: | ||||
| #            start, end = buffer_.get_bounds() | ||||
| #            buffer_.remove_tag_by_name('hyperlink', start, end) | ||||
| #            if self.match: | ||||
| #                start_offset = self.match[MarkupText.MATCH_START] | ||||
| #                end_offset = self.match[MarkupText.MATCH_END] | ||||
| #                 | ||||
| #                start = buffer_.get_iter_at_offset(start_offset) | ||||
| #                end = buffer_.get_iter_at_offset(end_offset) | ||||
| # | ||||
| #                buffer_.apply_tag_by_name('hyperlink', start, end) | ||||
| #                window.set_cursor(self.hand_cursor) | ||||
| #            else: | ||||
| #                window.set_cursor(self.regular_cursor) | ||||
| # | ||||
| #        self.last_match = self.match | ||||
| #         | ||||
| #        textview.window.get_pointer() | ||||
| #        return False | ||||
| # | ||||
| #    def on_textview_button_press_event(self, textview, event): | ||||
| #        if ((event.type == gtk.gdk.BUTTON_PRESS) and | ||||
| #            (event.button == 1) and | ||||
| #            (event.state and gtk.gdk.CONTROL_MASK) and | ||||
| #            (self.match)): | ||||
| #             | ||||
| #            flavor = self.match[MarkupText.MATCH_FLAVOR] | ||||
| #            url = self.match[MarkupText.MATCH_STRING] | ||||
| #            self.open_url_cb(None, url, flavor) | ||||
| #             | ||||
| #        return False | ||||
| #         | ||||
| #    def on_textview_populate_popup(self, textview, menu): | ||||
| #        """Insert extra menuitems according to matched pattern.""" | ||||
| #        if self.match: | ||||
| #            flavor = self.match[MarkupText.MATCH_FLAVOR] | ||||
| #            url = self.match[MarkupText.MATCH_STRING] | ||||
| #             | ||||
| #            if flavor == MAIL: | ||||
| #                open_menu = gtk.MenuItem(_('_Send Mail To...')) | ||||
| #                copy_menu = gtk.MenuItem(_('Copy _E-mail Address')) | ||||
| #            else: | ||||
| #                open_menu = gtk.MenuItem(_('_Open Link')) | ||||
| #                copy_menu = gtk.MenuItem(_('Copy _Link Address')) | ||||
| # | ||||
| #            copy_menu.connect('activate', self.copy_url_cb, url, flavor) | ||||
| #            copy_menu.show() | ||||
| #            menu.prepend(copy_menu) | ||||
| #             | ||||
| #            open_menu.connect('activate', self.open_url_cb, url, flavor) | ||||
| #            open_menu.show() | ||||
| #            menu.prepend(open_menu) | ||||
|     def on_textview_key_press_event(self, textview, event): | ||||
|         """Handle shortcuts in the TextView.""" | ||||
|         return textview.get_buffer().on_key_press_event(textview, event) | ||||
|      | ||||
|     def on_textview_insert_at_cursor(self, textview, string): | ||||
|         _LOG.debug("Textview insert '%s'" % string) | ||||
|          | ||||
|     def on_textview_delete_from_cursor(self, textview, type, count): | ||||
|         _LOG.debug("Textview delete type %d count %d" % (type, count)) | ||||
|          | ||||
|     def on_textview_paste_clipboard(self, textview): | ||||
|         _LOG.debug("Textview paste clipboard") | ||||
|      | ||||
|     def on_textview_motion_notify_event(self, textview, event): | ||||
|         window = textview.get_window(gtk.TEXT_WINDOW_TEXT) | ||||
|         x, y = textview.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, | ||||
|                                                 int(event.x), int(event.y)) | ||||
|         iter = textview.get_iter_at_location(x, y) | ||||
|         textbuffer = textview.get_buffer() | ||||
|         self.match = textbuffer.match_check(iter.get_offset()) | ||||
|          | ||||
|         if self.match != self.last_match: | ||||
|             start, end = textbuffer.get_bounds() | ||||
|             textbuffer.remove_tag_by_name('hyperlink', start, end) | ||||
|             if self.match: | ||||
|                 start_offset = self.match[MATCH_START] | ||||
|                 end_offset = self.match[MATCH_END] | ||||
|                  | ||||
|                 start = textbuffer.get_iter_at_offset(start_offset) | ||||
|                 end = textbuffer.get_iter_at_offset(end_offset) | ||||
|  | ||||
|                 textbuffer.apply_tag_by_name('hyperlink', start, end) | ||||
|                 window.set_cursor(self.hand_cursor) | ||||
|             else: | ||||
|                 window.set_cursor(self.regular_cursor) | ||||
|  | ||||
|         self.last_match = self.match | ||||
|          | ||||
|         textview.window.get_pointer() | ||||
|         return False | ||||
|  | ||||
|     def on_textview_button_press_event(self, textview, event): | ||||
|         if ((event.type == gtk.gdk.BUTTON_PRESS) and | ||||
|             (event.button == 1) and | ||||
|             (event.state and gtk.gdk.CONTROL_MASK) and | ||||
|             (self.match)): | ||||
|              | ||||
|             flavor = self.match[MATCH_FLAVOR] | ||||
|             url = self.match[MATCH_STRING] | ||||
|             self.open_url_cb(None, url, flavor) | ||||
|              | ||||
|         return False | ||||
|          | ||||
|     def on_textview_populate_popup(self, textview, menu): | ||||
|         """Insert extra menuitems according to matched pattern.""" | ||||
|         if self.match: | ||||
|             flavor = self.match[MATCH_FLAVOR] | ||||
|             url = self.match[MATCH_STRING] | ||||
|              | ||||
|             if flavor == MAIL: | ||||
|                 open_menu = gtk.MenuItem(_('_Send Mail To...')) | ||||
|                 copy_menu = gtk.MenuItem(_('Copy _E-mail Address')) | ||||
|             else: | ||||
|                 open_menu = gtk.MenuItem(_('_Open Link')) | ||||
|                 copy_menu = gtk.MenuItem(_('Copy _Link Address')) | ||||
|  | ||||
|             copy_menu.connect('activate', self.copy_url_cb, url, flavor) | ||||
|             copy_menu.show() | ||||
|             menu.prepend(copy_menu) | ||||
|              | ||||
|             open_menu.connect('activate', self.open_url_cb, url, flavor) | ||||
|             open_menu.show() | ||||
|             menu.prepend(open_menu) | ||||
|  | ||||
|     def on_spell_change(self, combobox, spell): | ||||
|         """Set spell checker language according to user selection.""" | ||||
|         lang = combobox.get_active_text() | ||||
|         spell.set_active_language(lang) | ||||
|  | ||||
| # enable for markup | ||||
| #    def open_url_cb(self, menuitem, url, flavor): | ||||
| #        if not url: | ||||
| #            return | ||||
| #         | ||||
| #        if flavor == HTTP: | ||||
| #            url = 'http:' + url | ||||
| #        elif flavor == MAIL: | ||||
| #            if not url.startswith('mailto:'): | ||||
| #                url = 'mailto:' + url | ||||
| #        elif flavor == GENERAL: | ||||
| #            pass | ||||
| #        else: | ||||
| #            return | ||||
| #         | ||||
| #        GrampsDisplay.url(url) | ||||
| #     | ||||
| #    def copy_url_cb(self, menuitem, url, flavor): | ||||
| #        """Copy url to both useful selections.""" | ||||
| #        clipboard = gtk.Clipboard(selection="CLIPBOARD") | ||||
| #        clipboard.set_text(url) | ||||
| #         | ||||
| #        clipboard = gtk.Clipboard(selection="PRIMARY") | ||||
| #        clipboard.set_text(url) | ||||
|     def open_url_cb(self, menuitem, url, flavor): | ||||
|         if not url: | ||||
|             return | ||||
|          | ||||
|         if flavor == HTTP: | ||||
|             url = 'http:' + url | ||||
|         elif flavor == MAIL: | ||||
|             if not url.startswith('mailto:'): | ||||
|                 url = 'mailto:' + url | ||||
|         elif flavor == GENERAL: | ||||
|             pass | ||||
|         else: | ||||
|             return | ||||
|          | ||||
|         url(url) | ||||
|      | ||||
|     def copy_url_cb(self, menuitem, url, flavor): | ||||
|         """Copy url to both useful selections.""" | ||||
|         clipboard = gtk.Clipboard(selection="CLIPBOARD") | ||||
|         clipboard.set_text(url) | ||||
|          | ||||
|         clipboard = gtk.Clipboard(selection="PRIMARY") | ||||
|         clipboard.set_text(url) | ||||
|      | ||||
|     def update_note(self): | ||||
|         """Update the Note object with current value.""" | ||||
|         if self.obj: | ||||
|             buffer_ = self.text.get_buffer() | ||||
|             (start, stop) = buffer_.get_bounds() | ||||
|             text = buffer_.get_text(start, stop) | ||||
|             self.obj.set(text) | ||||
|             log.debug(text) | ||||
|             textbuffer = self.text.get_buffer() | ||||
|             text = textbuffer.get_text() | ||||
|             self.obj.set_styledtext(text) | ||||
|             _LOG.debug(str(text)) | ||||
|  | ||||
|     def flow_changed(self, active): | ||||
|         if active: | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2000-2006  Donald N. Allingham | ||||
| # Copyright (C) 2007-2008  Zsolt Foldvari | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| @@ -20,28 +20,18 @@ | ||||
| 
 | ||||
| # $Id$ | ||||
| 
 | ||||
| "Handling formatted ('rich text') strings" | ||||
| "Text buffer subclassed from gtk.TextBuffer handling L{StyledText}." | ||||
| 
 | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # Python modules | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| from xml.sax import saxutils, xmlreader, ContentHandler | ||||
| from xml.sax import parseString, SAXParseException | ||||
| from gettext import gettext as _ | ||||
| import re | ||||
| try: | ||||
|     from cStringIO import StringIO | ||||
| except: | ||||
|     from StringIO import StringIO | ||||
| 
 | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # Set up logging | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| import logging | ||||
| log = logging.getLogger(".MarkupText") | ||||
| _LOG = logging.getLogger(".StyledTextBuffer") | ||||
| 
 | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| @@ -50,318 +40,49 @@ log = logging.getLogger(".MarkupText") | ||||
| #------------------------------------------------------------------------- | ||||
| import gtk | ||||
| from pango import WEIGHT_BOLD, STYLE_ITALIC, UNDERLINE_SINGLE | ||||
| import gobject | ||||
| 
 | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GRAMPS modules | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| from gen.lib import (StyledText, StyledTextTag, StyledTextTagType) | ||||
| 
 | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # Constants | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| ROOT_ELEMENT = 'gramps' | ||||
| ROOT_START_TAG = '<' + ROOT_ELEMENT + '>' | ||||
| ROOT_END_TAG = '</' + ROOT_ELEMENT + '>' | ||||
| LEN_ROOT_START_TAG = len(ROOT_START_TAG) | ||||
| LEN_ROOT_END_TAG = len(ROOT_END_TAG) | ||||
| 
 | ||||
| (MATCH_START, | ||||
|  MATCH_END, | ||||
|  MATCH_FLAVOR, | ||||
|  MATCH_STRING,) = range(4) | ||||
| 
 | ||||
| def is_gramps_markup(text): | ||||
|     return (text[:LEN_ROOT_START_TAG] == ROOT_START_TAG and | ||||
|             text[-LEN_ROOT_END_TAG:] == ROOT_END_TAG) | ||||
| 
 | ||||
| def clear_root_tags(text): | ||||
|     return text[LEN_ROOT_START_TAG:len(text)-LEN_ROOT_END_TAG] | ||||
|      | ||||
| class MarkupParser(ContentHandler): | ||||
|     """A simple ContentHandler class to parse Gramps markup'ed text. | ||||
|      | ||||
|     Use it with xml.sax.parse() or xml.sax.parseString(). A root tag is | ||||
|     required. Parsing result can be obtained via the public attributes of | ||||
|     the class:     | ||||
|     @attr content: clean text | ||||
|     @attr type: str | ||||
|     @attr elements: list of markup elements  | ||||
|     @attr type: list[tuple((start, end), name, attrs),] | ||||
|      | ||||
|     """ | ||||
|     def startDocument(self): | ||||
|         self._open_document = False | ||||
|         self._open_elements = [] | ||||
|         self.elements = [] | ||||
|         self.content = "" | ||||
|          | ||||
|     def endDocument(self): | ||||
|         self._open_document = False | ||||
|         if len(self._open_elements): | ||||
|             raise SAXParseException('Unclosed tags') | ||||
| 
 | ||||
|     def startElement(self, name, attrs): | ||||
|         if not self._open_document: | ||||
|             if name == ROOT_ELEMENT: | ||||
|                 self._open_document = True | ||||
|             else: | ||||
|                 raise SAXParseException('Root element missing') | ||||
|         else: | ||||
|             self._open_elements.append({'name': name, | ||||
|                                         'attrs': attrs.copy(), | ||||
|                                         'start': len(self.content), | ||||
|                                     }) | ||||
| 
 | ||||
|     def endElement(self, name): | ||||
|         # skip root element | ||||
|         if name == ROOT_ELEMENT: | ||||
|             return | ||||
|          | ||||
|         for e in self._open_elements: | ||||
|             if e['name'] == name: | ||||
|                 self.elements.append(((e['start'], len(self.content)), | ||||
|                                        e['name'], e['attrs'])) | ||||
| 
 | ||||
|                 self._open_elements.remove(e) | ||||
|                 return | ||||
| 
 | ||||
|     def characters (self, chunk): | ||||
|         self.content += chunk | ||||
| 
 | ||||
| class MarkupWriter: | ||||
|     """Generate XML markup text for Notes. | ||||
|      | ||||
|     Provides additional feature of accounting opened tags and closing them | ||||
|     properly in case of partially overlapping elements. | ||||
|      | ||||
|     """ | ||||
|     (EVENT_START, | ||||
|      EVENT_END) = range(2) | ||||
| 
 | ||||
|     def __init__(self, encoding='utf-8'): | ||||
|         self._output = StringIO() | ||||
|         self._encoding = encoding | ||||
|         self._writer = saxutils.XMLGenerator(self._output, self._encoding) | ||||
|          | ||||
|         self._attrs = xmlreader.AttributesImpl({}) | ||||
|          | ||||
|         self._open_elements = [] | ||||
|         self.content = '' | ||||
| 
 | ||||
|     # Private | ||||
| 
 | ||||
|     def _elements_to_events(self, elements): | ||||
|         """Create an event list for XML writer. | ||||
|          | ||||
|         @param elements: list of XML elements with start/end indices and attrs | ||||
|         @param type: [((start, end), xml_element_name, attrs),] | ||||
|         @return: eventdict | ||||
|         @rtype: {index: [(xml_element_name, attrs, event_type, pair_index),]} | ||||
|          index: place of the event | ||||
|          xml_element_name: element to apply | ||||
|          attrs: attributes of the tag (xml.sax.xmlreader.AttrubutesImpl) | ||||
|          event_type: START or END event | ||||
|          pair_index: index of the pair event, used for sorting | ||||
|          | ||||
|         """ | ||||
|         eventdict = {} | ||||
|         for (start, end), name, attrs in elements: | ||||
|             # append START events | ||||
|             if eventdict.has_key(start): | ||||
|                 eventdict[start].append((name, attrs, self.EVENT_START, end)) | ||||
|             else: | ||||
|                 eventdict[start] = [(name, attrs, self.EVENT_START, end)] | ||||
|             # END events have to prepended to avoid creating empty elements | ||||
|             if eventdict.has_key(end): | ||||
|                 eventdict[end].insert(0, (name, attrs, self.EVENT_END, start)) | ||||
|             else: | ||||
|                 eventdict[end] = [(name, attrs, self.EVENT_END, start)] | ||||
| 
 | ||||
|         # first round optimization | ||||
|         active_tags = {} | ||||
|         active_idx = {} | ||||
|          | ||||
|         indices = eventdict.keys() | ||||
|         indices.sort() | ||||
|         for index in indices: | ||||
|             # separate the events by tag names | ||||
|             tagdict = {} | ||||
|             for event in eventdict[index]: | ||||
|                 # we care only about tags having attributes | ||||
|                 if event[1].getLength(): | ||||
|                     if tagdict.has_key(event[0]): | ||||
|                         tagdict[event[0]].append(event) | ||||
|                     else: | ||||
|                         tagdict[event[0]] = [event] | ||||
|              | ||||
|             # let's handle each tag | ||||
|             for tag_name in tagdict.keys(): | ||||
|                  | ||||
|                 # first we close the tag if it's already open | ||||
|                 if active_tags.has_key(tag_name): | ||||
|                     tmp_attrs = xmlreader.AttributesImpl({}) | ||||
|                     tmp_attrs._attrs.update(active_tags[tag_name]) | ||||
|                     eventdict[index].insert(0, (name, tmp_attrs, | ||||
|                                                 self.EVENT_END, | ||||
|                                                 active_idx[tag_name])) | ||||
|                     # go back where the tag was opened and update the pair_idx, | ||||
|                     # i.e. with the current index. | ||||
|                     # FIXME this is ugly | ||||
|                     for event in eventdict[active_idx[tag_name]]: | ||||
|                         if (event[0] == tag_name and | ||||
|                             event[2] == self.EVENT_START): | ||||
|                             new_event = (event[0], event[1], event[2], index) | ||||
|                             eventdict[active_idx[tag_name]].remove(event) | ||||
|                             eventdict[active_idx[tag_name]].append(new_event) | ||||
|                 else: | ||||
|                     active_tags[tag_name] = xmlreader.AttributesImpl({}) | ||||
| 
 | ||||
|                 # update  | ||||
|                 for event in tagdict[tag_name]: | ||||
|                     # remove this event, we will insert new ones instead | ||||
|                     eventdict[index].remove(event) | ||||
|                      | ||||
|                     # update the active attribute object for the tag | ||||
|                     (name, attrs, type, pair_idx) = event | ||||
|                     if type == self.EVENT_START: | ||||
|                         active_tags[name]._attrs.update(attrs) | ||||
|                     elif type == self.EVENT_END: | ||||
|                         for attr_name in attrs.getNames(): | ||||
|                             try: | ||||
|                                 del active_tags[name]._attrs[attr_name] | ||||
|                             except: | ||||
|                                 pass # error | ||||
|                     else: | ||||
|                         pass # error | ||||
| 
 | ||||
|                 # if the tag's attr list is empty after the updates | ||||
|                 # delete the tag completely from the list of active tags | ||||
|                 if not active_tags[name].getLength(): | ||||
|                     del active_tags[name] | ||||
|                     ##del active_idx[name] | ||||
|                      | ||||
|                 # re-open all tags with updated attrs | ||||
|                 if active_tags.has_key(tag_name): | ||||
|                     tmp_attrs = xmlreader.AttributesImpl({}) | ||||
|                     tmp_attrs._attrs.update(active_tags[tag_name]) | ||||
|                     eventdict[index].append((tag_name, tmp_attrs, | ||||
|                                              self.EVENT_START, 0)) | ||||
|                     # also save the index of tag opening | ||||
|                     active_idx[tag_name] = index | ||||
| 
 | ||||
|         # sort events at the same index | ||||
|         indices = eventdict.keys() | ||||
|         for idx in indices: | ||||
|             if len(eventdict[idx]) > 1: | ||||
|                 eventdict[idx].sort(self._sort_events) | ||||
| 
 | ||||
|         return eventdict | ||||
| 
 | ||||
|     def _sort_events(self, event_a, event_b): | ||||
|         """Sort events that are at the same index. | ||||
|          | ||||
|         Sorting with the following rules: | ||||
|         1. END event goes always before START event; | ||||
|         2. from two START events the one goes first, which has it's own END | ||||
|         event later; | ||||
|         3. from two END events the one goes first, which has it's own START | ||||
|         event later. | ||||
|          | ||||
|         """ | ||||
|         tag_a, attr_a, type_a, pair_a = event_a | ||||
|         tag_b, attr_b, type_b, pair_b = event_b | ||||
|          | ||||
|         if (type_a + type_b) == (self.EVENT_START + self.EVENT_END): | ||||
|             return type_b - type_a | ||||
|         else: | ||||
|             return pair_b - pair_a | ||||
| 
 | ||||
|     def _startElement(self, name, attrs=None): | ||||
|         """Insert start tag.""" | ||||
|         if not attrs: | ||||
|             attrs = self._attrs | ||||
|         self._writer.startElement(name, attrs) | ||||
|         self._open_elements.append((name, attrs)) | ||||
|          | ||||
|     def _endElement(self, name): | ||||
|         """Insert end tag.""" | ||||
|         if not len(self._open_elements): | ||||
|             log.debug("Trying to close element '%s' when non is open" % name) | ||||
|             return | ||||
|          | ||||
|         tmp_list = [] | ||||
|         elem = '' | ||||
|          | ||||
|         # close all open elements until we reach to the requested one | ||||
|         while elem != name: | ||||
|             try: | ||||
|                 elem, attrs = self._open_elements.pop() | ||||
|                 self._writer.endElement(elem) | ||||
|                 if elem != name: | ||||
|                     tmp_list.append((elem, attrs)) | ||||
|             except: | ||||
|                 # we need to do something smart here... | ||||
|                 log.debug("Trying to close non open element '%s'" % name) | ||||
|                 break | ||||
|          | ||||
|         # open all other elements again | ||||
|         while True: | ||||
|             try: | ||||
|                 elem, attrs = tmp_list.pop() | ||||
|                 self._startElement(elem, attrs) | ||||
|             except: | ||||
|                 break | ||||
| 
 | ||||
|     # Public | ||||
| 
 | ||||
|     def generate(self, text, elements): | ||||
|         # reset output and start root element | ||||
|         self._output.truncate(0) | ||||
|         self._writer.startElement(ROOT_ELEMENT, self._attrs) | ||||
|          | ||||
|         # split the elements to events | ||||
|         events = self._elements_to_events(elements) | ||||
|          | ||||
|         # feed the events into the xml generator | ||||
|         last_pos = 0 | ||||
|         indices = events.keys() | ||||
|         indices.sort() | ||||
|         for index in indices: | ||||
|             self._writer.characters(text[last_pos:index]) | ||||
|             for name, attrs, event_type, p in events[index]: | ||||
|                 if event_type == self.EVENT_START: | ||||
|                     self._startElement(name, attrs) | ||||
|                 elif event_type == self.EVENT_END: | ||||
|                     self._endElement(name) | ||||
|             last_pos = index | ||||
|         self._writer.characters(text[last_pos:]) | ||||
|          | ||||
|         # close root element and end doc | ||||
|         self._writer.endElement(ROOT_ELEMENT) | ||||
|         self._writer.endDocument() | ||||
|          | ||||
|         # copy result | ||||
|         self.content = self._output.getvalue() | ||||
|         log.debug("Gramps XML: %s" % self.content) | ||||
|          | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GtkSpellState class | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class GtkSpellState: | ||||
|     """A simple state machine kinda thingy. | ||||
|      | ||||
|     Try tracking gtk.Spell activities on a buffer and reapply formatting | ||||
|     Trying to track gtk.Spell activities on a buffer and re-apply formatting | ||||
|     after gtk.Spell replaces a misspelled word. | ||||
|      | ||||
|          | ||||
|     """ | ||||
|     (STATE_NONE, | ||||
|      STATE_CLICKED, | ||||
|      STATE_DELETED, | ||||
|      STATE_INSERTING) = range(4) | ||||
| 
 | ||||
|     def __init__(self, buffer): | ||||
|         if not isinstance(buffer, gtk.TextBuffer): | ||||
|     def __init__(self, textbuffer): | ||||
|         if not isinstance(textbuffer, gtk.TextBuffer): | ||||
|             raise TypeError("Init parameter must be instance of gtk.TextBuffer") | ||||
|              | ||||
|         buffer.connect('mark-set', self.on_buffer_mark_set) | ||||
|         buffer.connect('delete-range', self.on_buffer_delete_range) | ||||
|         buffer.connect('insert-text', self.on_buffer_insert_text) | ||||
|         buffer.connect_after('insert-text', self.after_buffer_insert_text) | ||||
|         textbuffer.connect('mark-set', self.on_buffer_mark_set) | ||||
|         textbuffer.connect('delete-range', self.on_buffer_delete_range) | ||||
|         textbuffer.connect('insert-text', self.on_buffer_insert_text) | ||||
|         textbuffer.connect_after('insert-text', self.after_buffer_insert_text) | ||||
|          | ||||
|         self.reset_state() | ||||
|          | ||||
| @@ -371,36 +92,37 @@ class GtkSpellState: | ||||
|         self.end = 0 | ||||
|         self.tags = None | ||||
|          | ||||
|     def on_buffer_mark_set(self, buffer, iter, mark): | ||||
|     def on_buffer_mark_set(self, textbuffer, iter, mark): | ||||
|         mark_name = mark.get_name() | ||||
|         if  mark_name == 'gtkspell-click': | ||||
|             self.state = self.STATE_CLICKED | ||||
|             self.start, self.end = self.get_word_extents_from_mark(buffer, mark) | ||||
|             log.debug("SpellState got start %d end %d" % (self.start, self.end)) | ||||
|             self.start, self.end = self.get_word_extents_from_mark(textbuffer, | ||||
|                                                                    mark) | ||||
|             _LOG.debug("SpellState got start %d end %d" % (self.start, self.end)) | ||||
|         elif mark_name == 'insert': | ||||
|             self.reset_state() | ||||
| 
 | ||||
|     def on_buffer_delete_range(self, buffer, start, end): | ||||
|     def on_buffer_delete_range(self, textbuffer, start, end): | ||||
|         if ((self.state == self.STATE_CLICKED) and | ||||
|             (start.get_offset() == self.start) and | ||||
|             (end.get_offset() == self.end)): | ||||
|             self.state = self.STATE_DELETED | ||||
|             self.tags = start.get_tags() | ||||
|      | ||||
|     def on_buffer_insert_text(self, buffer, iter, text, length): | ||||
|     def on_buffer_insert_text(self, textbuffer, iter, text, length): | ||||
|         if self.state == self.STATE_DELETED and iter.get_offset() == self.start: | ||||
|             self.state = self.STATE_INSERTING | ||||
| 
 | ||||
|     def after_buffer_insert_text(self, buffer, iter, text, length): | ||||
|     def after_buffer_insert_text(self, textbuffer, iter, text, length): | ||||
|         if self.state == self.STATE_INSERTING: | ||||
|             mark = buffer.get_mark('gtkspell-insert-start') | ||||
|             insert_start = buffer.get_iter_at_mark(mark) | ||||
|             mark = textbuffer.get_mark('gtkspell-insert-start') | ||||
|             insert_start = textbuffer.get_iter_at_mark(mark) | ||||
|             for tag in self.tags: | ||||
|                 buffer.apply_tag(tag, insert_start, iter) | ||||
|                 textbuffer.apply_tag(tag, insert_start, iter) | ||||
|          | ||||
|         self.reset_state() | ||||
| 
 | ||||
|     def get_word_extents_from_mark(self, buffer, mark): | ||||
|     def get_word_extents_from_mark(self, textbuffer, mark): | ||||
|         """Get the word extents as gtk.Spell does. | ||||
|          | ||||
|         Used to get the beginning of the word, in which user right clicked. | ||||
| @@ -408,7 +130,7 @@ class GtkSpellState: | ||||
|         misspelled words. | ||||
|          | ||||
|         """ | ||||
|         start = buffer.get_iter_at_mark(mark) | ||||
|         start = textbuffer.get_iter_at_mark(mark) | ||||
|         if not start.starts_word(): | ||||
|             #start.backward_word_start() | ||||
|             self.backward_word_start(start) | ||||
| @@ -455,14 +177,27 @@ class GtkSpellState: | ||||
|          | ||||
|         return True | ||||
|      | ||||
| class MarkupBuffer(gtk.TextBuffer): | ||||
|     """An extended TextBuffer with Gramps XML markup string interface. | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # StyledTextBuffer class | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class StyledTextBuffer(gtk.TextBuffer): | ||||
|     """An extended TextBuffer for handling StyledText strings. | ||||
|      | ||||
|     It implements MarkupParser and MarkupWriter on the input/output interfaces. | ||||
|     Also translates Gramps XML markup language to gtk.TextTag's and vice versa. | ||||
|     StyledTextBuffer is an interface between GRAMPS' L{StyledText} format | ||||
|     and gtk.TextBuffer. To get/set the text use the L{get_text} and  | ||||
|     L{set_text} methods. | ||||
|      | ||||
|     It provides an action group (L{format_action_group}) for GUIs. | ||||
|      | ||||
|     StyledTextBuffer has a regexp pattern matching mechanism too. To add a | ||||
|     regexp pattern to match in the text use the L{match_add} method. To check | ||||
|     if there's a match at a certain position in the text use the L{match_check} | ||||
|     method. | ||||
|      | ||||
|     """ | ||||
|     __gtype_name__ = 'MarkupBuffer' | ||||
|     __gtype_name__ = 'StyledTextBuffer' | ||||
|      | ||||
|     formats = ('italic', 'bold', 'underline', | ||||
|                'font', 'foreground', 'background',) | ||||
| @@ -470,9 +205,6 @@ class MarkupBuffer(gtk.TextBuffer): | ||||
|     def __init__(self): | ||||
|         gtk.TextBuffer.__init__(self) | ||||
| 
 | ||||
|         self.parser = MarkupParser() | ||||
|         self.writer = MarkupWriter() | ||||
|          | ||||
|         # Create fix tags. | ||||
|         # Other tags (e.g. color) have to be created on the fly | ||||
|         self.create_tag('bold', weight=WEIGHT_BOLD) | ||||
| @@ -520,6 +252,8 @@ class MarkupBuffer(gtk.TextBuffer): | ||||
|         self.bold = False | ||||
|         self.underline = False | ||||
|         self.font = None | ||||
|         # TODO could we separate font name and size? | ||||
|         ##self.size = None | ||||
|         self.foreground = None | ||||
|         self.background = None | ||||
|          | ||||
| @@ -545,15 +279,15 @@ class MarkupBuffer(gtk.TextBuffer): | ||||
|          | ||||
|     # Virtual methods | ||||
| 
 | ||||
|     def on_insert_text(self, buffer, iter, text, length): | ||||
|         log.debug("Will insert at %d length %d" % (iter.get_offset(), length)) | ||||
|     def on_insert_text(self, textbuffer, iter, text, length): | ||||
|         _LOG.debug("Will insert at %d length %d" % (iter.get_offset(), length)) | ||||
|          | ||||
|         # let's remember where we started inserting | ||||
|         self.move_mark(self.mark_insert, iter) | ||||
| 
 | ||||
|     def after_insert_text(self, buffer, iter, text, length): | ||||
|     def after_insert_text(self, textbuffer, iter, text, length): | ||||
|         """Format inserted text.""" | ||||
|         log.debug("Have inserted at %d length %d (%s)" % | ||||
|         _LOG.debug("Have inserted at %d length %d (%s)" % | ||||
|                   (iter.get_offset(), length, text)) | ||||
|                    | ||||
|         if not length: | ||||
| @@ -572,8 +306,8 @@ class MarkupBuffer(gtk.TextBuffer): | ||||
|                 self.apply_tag(self._find_tag_by_name(format, value), | ||||
|                                insert_start, iter) | ||||
|      | ||||
|     def after_delete_range(self, buffer, start, end): | ||||
|         log.debug("Deleted from %d till %d" % | ||||
|     def after_delete_range(self, textbuffer, start, end): | ||||
|         _LOG.debug("Deleted from %d till %d" % | ||||
|                   (start.get_offset(), end.get_offset())) | ||||
|          | ||||
|         # move 'insert' marker to have the format attributes updated | ||||
| @@ -592,16 +326,15 @@ class MarkupBuffer(gtk.TextBuffer): | ||||
|                     match = iter.next() | ||||
|                     self.matches.append((match.start(), match.end(), | ||||
|                                          flavor, match.group())) | ||||
|                     log.debug("Matches: %d, %d: %s [%d]" % | ||||
|                     _LOG.debug("Matches: %d, %d: %s [%d]" % | ||||
|                               (match.start(), match.end(), | ||||
|                                match.group(), flavor)) | ||||
|                 except StopIteration: | ||||
|                     break | ||||
|              | ||||
| 
 | ||||
|     def do_mark_set(self, iter, mark): | ||||
|         """Update format attributes each time the cursor moves.""" | ||||
|         log.debug("Setting mark %s at %d" % | ||||
|         _LOG.debug("Setting mark %s at %d" % | ||||
|                   (mark.get_name(), iter.get_offset())) | ||||
|          | ||||
|         if mark.get_name() != 'insert': | ||||
| @@ -629,72 +362,32 @@ class MarkupBuffer(gtk.TextBuffer): | ||||
| 
 | ||||
|     # Private | ||||
|      | ||||
|     def _xmltag_to_texttag(self, name, attrs): | ||||
|         """Convert XML tag to gtk.TextTag. | ||||
|                  | ||||
|         Return only the name of the TextTag. | ||||
|     def _tagname_to_tagtype(self, name): | ||||
|         """Convert gtk.TextTag names to StyledTextTagType values.""" | ||||
|         tag2type = { | ||||
|             'bold': StyledTextTagType.BOLD, | ||||
|             'italic': StyledTextTagType.ITALIC, | ||||
|             'underline': StyledTextTagType.UNDERLINE, | ||||
|             'foreground': StyledTextTagType.FONTCOLOR, | ||||
|             'background': StyledTextTagType.HIGHLIGHT, | ||||
|             'font': StyledTextTagType.FONTFACE, | ||||
|         } | ||||
|          | ||||
|         @param name: name of the XML tag | ||||
|         @param type: string | ||||
|         @param attrs: attributes of the XML tag | ||||
|         @param type: xmlreader.AttributesImpl | ||||
|         @return: property of gtk.TextTag, value of property | ||||
|         @rtype: [(string, string), ] | ||||
|          | ||||
|         """ | ||||
|         if name == 'b': | ||||
|             return [('bold', None)] | ||||
|         elif name == 'i': | ||||
|             return [('italic', None)] | ||||
|         elif name == 'u': | ||||
|             return [('underline', None)] | ||||
|         elif name == 'font': | ||||
|             ret = [] | ||||
|             attr_names = attrs.getNames() | ||||
|             if 'color' in attr_names: | ||||
|                 ret.append(('foreground', attrs.getValue('color'))) | ||||
|             if 'highlight' in attr_names: | ||||
|                 ret.append(('background', attrs.getValue('highlight'))) | ||||
|             if ('face' in attr_names) and ('size' in attr_names): | ||||
|                 ret.append(('font', '%s %s' % (attrs.getValue('face'), | ||||
|                                                attrs.getValue('size')))) | ||||
|             if len(ret): | ||||
|                 return ret | ||||
|             else: | ||||
|                 return [(None, None)] | ||||
|         else: | ||||
|             return [(None, None)] | ||||
|          | ||||
|     def _texttag_to_xmltag(self, name): | ||||
|         """Convert gtk.TextTag to XML tag. | ||||
|          | ||||
|         @param name: name of the gtk.TextTag | ||||
|         @param type: string | ||||
|         @return: XML tag name, attribute | ||||
|         @rtype: string, xmlreader.AttributesImpl | ||||
|          | ||||
|         """ | ||||
|         attrs = xmlreader.AttributesImpl({}) | ||||
|         if name == 'bold': | ||||
|             return 'b', attrs | ||||
|         elif name == 'italic': | ||||
|             return 'i', attrs | ||||
|         elif name == 'underline': | ||||
|             return 'u', attrs | ||||
|         elif name.startswith('foreground'): | ||||
|             attrs._attrs['color'] = name.split()[1] | ||||
|             return 'font', attrs | ||||
|         elif name.startswith('background'): | ||||
|             attrs._attrs['highlight'] = name.split()[1] | ||||
|             return 'font', attrs | ||||
|         elif name.startswith('font'): | ||||
|             name = name.replace('font ', '') | ||||
|             attrs._attrs['face'] = name.rsplit(' ', 1)[0] | ||||
|             attrs._attrs['size'] = name.rsplit(' ', 1)[1] | ||||
|             return 'font', attrs | ||||
|         else: | ||||
|             return None, None | ||||
|         return StyledTextTagType(tag2type[name]) | ||||
|      | ||||
|     def _tagtype_to_tagname(self, tagtype): | ||||
|         """Convert StyledTextTagType values to gtk.TextTag names.""" | ||||
|         type2tag = { | ||||
|             StyledTextTagType.BOLD: 'bold', | ||||
|             StyledTextTagType.ITALIC: 'italic', | ||||
|             StyledTextTagType.UNDERLINE: 'underline', | ||||
|             StyledTextTagType.FONTCOLOR: 'foreground', | ||||
|             StyledTextTagType.HIGHLIGHT: 'background', | ||||
|             StyledTextTagType.FONTFACE: 'font', | ||||
|         } | ||||
|          | ||||
|         return type2tag[tagtype] | ||||
|      | ||||
|     ##def get_tag_value_at_insert(self, name): | ||||
|         ##"""Get the value of the given tag at the insertion point.""" | ||||
|         ##tags = self.get_iter_at_mark(self._insert).get_tags() | ||||
| @@ -835,12 +528,7 @@ class MarkupBuffer(gtk.TextBuffer): | ||||
|         setattr(self, action.get_name(), action.get_active()) | ||||
| 
 | ||||
|     def on_action_activate(self, action): | ||||
|         """Apply a format. | ||||
|          | ||||
|         Other tags for the same format have to be removed from the range | ||||
|         first otherwise XML would get messy. | ||||
|          | ||||
|         """ | ||||
|         """Apply a format.""" | ||||
|         format = action.get_name() | ||||
|          | ||||
|         if format == 'foreground': | ||||
| @@ -870,11 +558,11 @@ class MarkupBuffer(gtk.TextBuffer): | ||||
|             value = font_selection.fontsel.get_font_name() | ||||
|             font_selection.destroy() | ||||
|         else: | ||||
|             log.debug("unknown format: '%s'" % format) | ||||
|             _LOG.debug("unknown format: '%s'" % format) | ||||
|             return | ||||
| 
 | ||||
|         if response == gtk.RESPONSE_OK: | ||||
|             log.debug("applying format '%s' with value '%s'" % (format, value)) | ||||
|             _LOG.debug("applying format '%s' with value '%s'" % (format, value)) | ||||
|              | ||||
|             tag = self._find_tag_by_name(format, value) | ||||
|             self.remove_format_from_selection(format) | ||||
| @@ -905,64 +593,51 @@ class MarkupBuffer(gtk.TextBuffer): | ||||
|          | ||||
|     # Public API | ||||
| 
 | ||||
|     def set_text(self, xmltext): | ||||
|     def set_text(self, r_text): | ||||
|         """Set the content of the buffer with markup tags.""" | ||||
|         try: | ||||
|             parseString(xmltext.encode('utf-8'), self.parser) | ||||
|             text = self.parser.content | ||||
|         except: | ||||
|             # if parse fails remove all tags and use clear text instead | ||||
|             text = re.sub(r'(<.*?>)', '', xmltext) | ||||
|             text = saxutils.unescape(text) | ||||
|          | ||||
|         gtk.TextBuffer.set_text(self, text) | ||||
| 
 | ||||
|         for element in self.parser.elements: | ||||
|             (start, end), xmltag_name, attrs = element | ||||
| 
 | ||||
|             #texttag_name, value = self._xmltag_to_texttag(xmltag_name, attrs) | ||||
|             tags = self._xmltag_to_texttag(xmltag_name, attrs) | ||||
| 
 | ||||
|             for texttag_name, value in tags: | ||||
|                 if texttag_name is not None: | ||||
|         gtk.TextBuffer.set_text(self, str(r_text)) | ||||
|      | ||||
|         r_tags = r_text.get_tags() | ||||
|         for r_tag in r_tags: | ||||
|             tagname = self._tagtype_to_tagname(int(r_tag.name)) | ||||
|             g_tag = self._find_tag_by_name(tagname, r_tag.value) | ||||
|             if g_tag is not None: | ||||
|                 for (start, end) in r_tag.ranges: | ||||
|                     start_iter = self.get_iter_at_offset(start) | ||||
|                     end_iter = self.get_iter_at_offset(end) | ||||
|                     tag = self._find_tag_by_name(texttag_name, value) | ||||
|                     if tag is not None: | ||||
|                         self.apply_tag(tag, start_iter, end_iter) | ||||
| 
 | ||||
|                     self.apply_tag(g_tag, start_iter, end_iter) | ||||
|                      | ||||
|     def get_text(self, start=None, end=None, include_hidden_chars=True): | ||||
|         """Return the buffer text with xml markup tags. | ||||
|          | ||||
|         If no markup was applied returns clean text | ||||
|         (i.e. without even root tags). | ||||
|          | ||||
|         """ | ||||
|         # get the clear text from the buffer | ||||
|         """Return the buffer text.""" | ||||
|         if not start: | ||||
|             start = self.get_start_iter() | ||||
|         if not end: | ||||
|             end = self.get_end_iter() | ||||
|         txt = unicode(gtk.TextBuffer.get_text(self, start, end)) | ||||
| 
 | ||||
|         txt = gtk.TextBuffer.get_text(self, start, end, include_hidden_chars) | ||||
|         txt = unicode(txt) | ||||
|          | ||||
|         # extract tags out of the buffer | ||||
|         texttag = self.get_tag_from_range() | ||||
|         g_tags = self.get_tag_from_range() | ||||
|         r_tags = [] | ||||
|          | ||||
|         if len(texttag): | ||||
|             # convert the texttags to xml elements | ||||
|             xml_elements = [] | ||||
|             for texttag_name, indices in texttag.items(): | ||||
|                 xml_tag_name, attrs = self._texttag_to_xmltag(texttag_name) | ||||
|                 if xml_tag_name is not None: | ||||
|                     for start_idx, end_idx in indices: | ||||
|                         xml_elements.append(((start_idx, end_idx+1), | ||||
|                                              xml_tag_name, attrs)) | ||||
|         for g_tagname, g_ranges in g_tags.items(): | ||||
|             name_value = g_tagname.split(' ', 1) | ||||
| 
 | ||||
|             # feed the elements into the xml writer | ||||
|             self.writer.generate(txt, xml_elements) | ||||
|             txt = self.writer.content | ||||
|             if len(name_value) == 1: | ||||
|                 name = name_value[0] | ||||
|                 r_value = None | ||||
|             else: | ||||
|                 (name, r_value) = name_value | ||||
| 
 | ||||
|             if name in self.formats: | ||||
|                 r_tagtype = self._tagname_to_tagtype(name) | ||||
|                 r_ranges = [(start, end+1) for (start, end) in g_ranges] | ||||
|                 r_tag = StyledTextTag(r_tagtype, r_value, r_ranges) | ||||
|                                        | ||||
|                 r_tags.append(r_tag) | ||||
|          | ||||
|         return txt | ||||
|         return StyledText(txt, r_tags) | ||||
| 
 | ||||
|     def match_add(self, pattern, flavor): | ||||
|         """Add a pattern to look for in the text.""" | ||||
| @@ -972,11 +647,7 @@ class MarkupBuffer(gtk.TextBuffer): | ||||
|     def match_check(self, pos): | ||||
|         """Check if pos falls into any of the matched patterns.""" | ||||
|         for match in self.matches: | ||||
|             if pos >= match[0] and pos <= match[1]: | ||||
|             if pos >= match[MATCH_START] and pos <= match[MATCH_END]: | ||||
|                 return match | ||||
| 
 | ||||
|         return None | ||||
| 
 | ||||
|          | ||||
| if gtk.pygtk_version < (2,8,0): | ||||
|     gobject.type_register(MarkupBuffer) | ||||
| @@ -39,7 +39,7 @@ from gettext import gettext as _ | ||||
| from bsddb import dbshelve, db | ||||
| import logging | ||||
|  | ||||
| log = logging.getLogger(".GrampsDb") | ||||
| _LOG = logging.getLogger(".GrampsDb") | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| @@ -56,7 +56,7 @@ from gen.db.cursor import GrampsCursor | ||||
| import Errors | ||||
|  | ||||
| _MINVERSION = 9 | ||||
| _DBVERSION = 13 | ||||
| _DBVERSION = 14 | ||||
|  | ||||
| IDTRANS     = "person_id" | ||||
| FIDTRANS    = "family_id" | ||||
| @@ -1485,7 +1485,7 @@ class GrampsDBDir(GrampsDbBase, UpdateCallback): | ||||
|             # under certain circumstances during a database reload, | ||||
|             # data_map can be none. If so, then don't report an error | ||||
|             if data_map: | ||||
|                 log.error("Failed to get from handle", exc_info=True) | ||||
|                 _LOG.error("Failed to get from handle", exc_info=True) | ||||
|         if data: | ||||
|             newobj = InstanceType(class_type) | ||||
|             newobj.unserialize(data) | ||||
| @@ -1665,13 +1665,42 @@ class GrampsDBDir(GrampsDbBase, UpdateCallback): | ||||
|     def gramps_upgrade(self, callback=None): | ||||
|         UpdateCallback.__init__(self, callback) | ||||
|          | ||||
| #        version = self.metadata.get('version', default=_MINVERSION) | ||||
|         version = self.metadata.get('version', default=_MINVERSION) | ||||
|  | ||||
|         t = time.time() | ||||
| #        if version < 13: | ||||
| #            self.gramps_upgrade_13() | ||||
|  | ||||
|         if version < 14: | ||||
|             self.gramps_upgrade_14() | ||||
|  | ||||
|         print "Upgrade time:", int(time.time()-t), "seconds" | ||||
|  | ||||
|     def gramps_upgrade_14(self): | ||||
|         """Upgrade database from version 13 to 14.""" | ||||
|         # This upgrade modifies notes | ||||
|         length = len(self.note_map) | ||||
|         self.set_total(length) | ||||
|          | ||||
|         # replace clear text with StyledText in Notes | ||||
|         for handle in self.note_map.keys(): | ||||
|             note = self.note_map[handle] | ||||
|              | ||||
|             (junk_handle, gramps_id, text, format, note_type, | ||||
|              change, marker, private) = note | ||||
|              | ||||
|             styled_text = (text, []) | ||||
|              | ||||
|             new_note = (handle, gramps_id, styled_text, format, note_type, | ||||
|                         change, marker, private) | ||||
|              | ||||
|             the_txn = self.env.txn_begin() | ||||
|             self.note_map.put(str(handle), new_note, txn=the_txn) | ||||
|             the_txn.commit() | ||||
|             self.update() | ||||
|  | ||||
|         # Bump up database version. Separate transaction to save metadata. | ||||
|         the_txn = self.env.txn_begin() | ||||
|         self.metadata.put('version', 14, txn=the_txn) | ||||
|         the_txn.commit() | ||||
|  | ||||
| class BdbTransaction(Transaction): | ||||
|     def __init__(self, msg, db, batch=False, no_magic=False): | ||||
|   | ||||
| @@ -69,3 +69,8 @@ from gen.lib.srcmediatype import SourceMediaType | ||||
| from gen.lib.eventroletype import EventRoleType | ||||
| from gen.lib.markertype import MarkerType | ||||
| from gen.lib.notetype import NoteType | ||||
| from gen.lib.styledtexttagtype import StyledTextTagType | ||||
|  | ||||
| # Text | ||||
| from gen.lib.styledtexttag import StyledTextTag | ||||
| from gen.lib.styledtext import StyledText | ||||
|   | ||||
| @@ -31,19 +31,26 @@ Base type for all gramps types. | ||||
| #------------------------------------------------------------------------ | ||||
| from gettext import gettext as _ | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # _init_map function | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| def _init_map(data, key_col, data_col, blacklist=None): | ||||
|     """ | ||||
|     Initialize the map, building a new map from the specified columns. | ||||
|     """ | ||||
|     """Initialize the map, building a new map from the specified columns.""" | ||||
|     if blacklist: | ||||
|         new_data = dict([ (item[key_col], item[data_col])  | ||||
|                         for item in data  | ||||
|                         if not item[0] in blacklist ]) | ||||
|         new_data = dict([(item[key_col], item[data_col])  | ||||
|                         for item in data if not item[0] in blacklist]) | ||||
|     else: | ||||
|         new_data = dict([ (item[key_col], item[data_col])  | ||||
|                         for item in data ]) | ||||
|         new_data = dict([(item[key_col], item[data_col]) for item in data]) | ||||
|          | ||||
|     return new_data | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GrampsTypeMeta class | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class GrampsTypeMeta(type): | ||||
|     """Metaclass for L{GrampsType}. | ||||
|      | ||||
| @@ -55,15 +62,30 @@ class GrampsTypeMeta(type): | ||||
|         type.__init__(mcs, name, bases, namespace) | ||||
|         mcs.__class_init__(namespace) | ||||
|          | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GrampsType class | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class GrampsType(object): | ||||
|     """Base class for all Gramps object types. | ||||
|          | ||||
|     _DATAMAP is a 3-tuple like (index, localized_string, english_string) | ||||
|     _BLACKLIST is a list of indices to ignore (obsolete/retired entries) | ||||
|       (gramps policy is never to delete type values,  | ||||
|        or reuse the name (TOKEN) of any specific type value) | ||||
|     @cvar _DATAMAP: 3-tuple like (index, localized_string, english_string). | ||||
|     @type _DATAMAP: list | ||||
|     @cvar _BLACKLIST: List of indices to ignore (obsolete/retired entries). | ||||
|     (gramps policy is never to delete type values, or reuse the name (TOKEN) | ||||
|     of any specific type value) | ||||
|     @type _BLACKLIST: list | ||||
|     @cvar POS_<x>: Position of <x> attribute in the serialized format of | ||||
|     an instance. | ||||
|     @type POS_<x>: int | ||||
|  | ||||
|     @attention: The POS_<x> class variables reflect the serialized object, they | ||||
|     have to be updated in case the data structure or the L{serialize} method | ||||
|     changes! | ||||
|  | ||||
|     """ | ||||
|     (POS_VALUE, POS_STRING) = range(2) | ||||
|      | ||||
|     _CUSTOM = 0 | ||||
|     _DEFAULT = 0 | ||||
| @@ -89,8 +111,8 @@ class GrampsType(object): | ||||
|         self.set(value) | ||||
|  | ||||
|     def __set_tuple(self, value): | ||||
|         v,s = self._DEFAULT,u'' | ||||
|         if len(value) > 0: | ||||
|         v, s = self._DEFAULT, u'' | ||||
|         if value: | ||||
|             v = value[0] | ||||
|             if len(value) > 1: | ||||
|                 s = value[1] | ||||
| @@ -148,15 +170,11 @@ class GrampsType(object): | ||||
|             return self._I2EMAP[self.val] | ||||
|  | ||||
|     def serialize(self): | ||||
|         """ | ||||
|         Convert the object to a serialized tuple of data. | ||||
|         """ | ||||
|         """Convert the object to a serialized tuple of data. """ | ||||
|         return (self.val, self.string) | ||||
|  | ||||
|     def unserialize(self, data): | ||||
|         """ | ||||
|         Convert a serialized tuple of data to an object. | ||||
|         """ | ||||
|         """Convert a serialized tuple of data to an object.""" | ||||
|         self.val, self.string = data | ||||
|  | ||||
|     def __str__(self): | ||||
| @@ -172,16 +190,12 @@ class GrampsType(object): | ||||
|         return self._I2SMAP | ||||
|  | ||||
|     def get_standard_names(self): | ||||
|         """ | ||||
|         Return the list of localized names for all standard types. | ||||
|         """ | ||||
|         """Return the list of localized names for all standard types.""" | ||||
|         return [s for (i, s) in self._I2SMAP.items() | ||||
|                 if (i != self._CUSTOM) and s.strip()] | ||||
|  | ||||
|     def get_standard_xml(self): | ||||
|         """ | ||||
|         Return the list of XML (english) names for all standard types. | ||||
|         """ | ||||
|         """Return the list of XML (english) names for all standard types.""" | ||||
|         return [s for (i, s) in self._I2EMAP.items() | ||||
|                 if (i != self._CUSTOM) and s.strip()] | ||||
|  | ||||
|   | ||||
| @@ -32,6 +32,7 @@ Note class for GRAMPS. | ||||
| from gen.lib.primaryobj import BasicPrimaryObject | ||||
| from gen.lib.notetype import NoteType | ||||
| from gen.lib.markertype import MarkerType | ||||
| from gen.lib.styledtext import StyledText | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| @@ -39,118 +40,163 @@ from gen.lib.markertype import MarkerType | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class Note(BasicPrimaryObject): | ||||
|     """ | ||||
|     Introduction | ||||
|     ============ | ||||
|     The Note class defines a text note. The note may be preformatted | ||||
|     or 'flowed', which indicates that it text string is considered | ||||
|     to be in paragraphs, separated by newlines. | ||||
|     """ | ||||
|     """Define a text note. | ||||
|      | ||||
|     FLOWED    = 0 | ||||
|     FORMATTED = 1 | ||||
|     Starting from GRAMPS 3.1 Note object stores the text in L{StyledText} | ||||
|     instance, thus it can have text formatting information. | ||||
|  | ||||
|     def __init__(self, text = ""): | ||||
|         """ | ||||
|         Create a new Note object, initializing from the passed string. | ||||
|         """ | ||||
|     To get and set only the clear text of the note use the L{get} and L{set} | ||||
|     methods. | ||||
|      | ||||
|     To get and set the formatted version of the Note's text use the | ||||
|     L{get_styledtext} and L{set_styledtext} methods. | ||||
|      | ||||
|     The note may be 'preformatted' or 'flowed', which indicates that the | ||||
|     text string is considered to be in paragraphs, separated by newlines. | ||||
|      | ||||
|     @cvar POS_<x>: Position of <x> attribute in the serialized format of | ||||
|     an instance. | ||||
|     @type POS_<x>: int | ||||
|  | ||||
|     @attention: The POS_<x> class variables reflect the serialized object, they | ||||
|     have to be updated in case the data structure or the L{serialize} method | ||||
|     changes! | ||||
|      | ||||
|     """ | ||||
|     (FLOWED, FORMATTED) = range(2) | ||||
|      | ||||
|     (POS_HANDLE, | ||||
|      POS_ID, | ||||
|      POS_TEXT, | ||||
|      POS_FORMAT, | ||||
|      POS_TYPE, | ||||
|      POS_CHANGE, | ||||
|      POS_MARKER, | ||||
|      POS_PRIVATE,) = range(8) | ||||
|  | ||||
|     def __init__(self, text=""): | ||||
|         """Create a new Note object, initializing from the passed string.""" | ||||
|         BasicPrimaryObject.__init__(self) | ||||
|         self.text = text | ||||
|         self.text = StyledText(text) | ||||
|         self.format = Note.FLOWED | ||||
|         self.type = NoteType() | ||||
|  | ||||
|     def serialize(self): | ||||
|         """Convert the object to a serialized tuple of data. | ||||
|          | ||||
|         @returns: The serialized format of the instance. | ||||
|         @rtype: tuple | ||||
|          | ||||
|         """ | ||||
|         Convert the object to a serialized tuple of data. | ||||
|         """ | ||||
|         return (self.handle, self.gramps_id, self.text, self.format, | ||||
|         return (self.handle, self.gramps_id, self.text.serialize(), self.format, | ||||
|                 self.type.serialize(), self.change, self.marker.serialize(), | ||||
|                 self.private) | ||||
|  | ||||
|     def unserialize(self, data): | ||||
|         """Convert a serialized tuple of data to an object. | ||||
|          | ||||
|         @param data: The serialized format of a Note. | ||||
|         @type: data: tuple | ||||
|          | ||||
|         """ | ||||
|         Convert a serialized tuple of data to an object. | ||||
|         """ | ||||
|         (self.handle, self.gramps_id, self.text, self.format, | ||||
|         (self.handle, self.gramps_id, the_text, self.format, | ||||
|          the_type, self.change, the_marker, self.private) = data | ||||
|  | ||||
|         self.text = StyledText() | ||||
|         self.text.unserialize(the_text) | ||||
|         self.marker = MarkerType() | ||||
|         self.marker.unserialize(the_marker) | ||||
|         self.type = NoteType() | ||||
|         self.type.unserialize(the_type) | ||||
|  | ||||
|     def get_text_data_list(self): | ||||
|         """ | ||||
|         Return the list of all textual attributes of the object. | ||||
|         """Return the list of all textual attributes of the object. | ||||
|  | ||||
|         @return: Returns the list of all textual attributes of the object. | ||||
|         @returns: The list of all textual attributes of the object. | ||||
|         @rtype: list | ||||
|          | ||||
|         """ | ||||
|         return [self.text] | ||||
|         return [str(self.text)] | ||||
|  | ||||
|     def set(self, text): | ||||
|         """ | ||||
|         Set the text associated with the note to the passed string. | ||||
|         """Set the text associated with the note to the passed string. | ||||
|  | ||||
|         @param text: Text string defining the note contents. | ||||
|         @param text: The I{clear} text defining the note contents. | ||||
|         @type text: str | ||||
|          | ||||
|         """ | ||||
|         self.text = text | ||||
|         self.text = StyledText(text) | ||||
|  | ||||
|     def get(self): | ||||
|         """ | ||||
|         Return the text string associated with the note. | ||||
|         """Return the text string associated with the note. | ||||
|  | ||||
|         @returns: Returns the text string defining the note contents. | ||||
|         @returns: The I{clear} text of the note contents. | ||||
|         @rtype: str | ||||
|          | ||||
|         """ | ||||
|         text = self.text | ||||
|         return text | ||||
|         return str(self.text) | ||||
|  | ||||
|     def append(self, text): | ||||
|     def set_styledtext(self, text): | ||||
|         """Set the text associated with the note to the passed string. | ||||
|  | ||||
|         @param text: The I{formatted} text defining the note contents. | ||||
|         @type text: L{StyledText} | ||||
|          | ||||
|         """ | ||||
|         Append the specified text to the text associated with the note. | ||||
|         self.text = text | ||||
|          | ||||
|     def get_styledtext(self): | ||||
|         """Return the text string associated with the note. | ||||
|  | ||||
|         @returns: The I{formatted} text of the note contents. | ||||
|         @rtype: L{StyledText} | ||||
|          | ||||
|         """ | ||||
|         return self.text | ||||
|      | ||||
|     def append(self, text): | ||||
|         """Append the specified text to the text associated with the note. | ||||
|  | ||||
|         @param text: Text string to be appended to the note. | ||||
|         @type text: str | ||||
|         @type text: str or L{StyledText} | ||||
|          | ||||
|         """ | ||||
|         self.text = self.text + text | ||||
|  | ||||
|     def set_format(self, format): | ||||
|         """ | ||||
|         Set the format of the note to the passed value.  | ||||
|         """Set the format of the note to the passed value.  | ||||
|          | ||||
|         The value can either indicate Flowed or Preformatted. | ||||
|  | ||||
|         @param: format: The value can either indicate Flowed or Preformatted. | ||||
|         @type format: int | ||||
|          | ||||
|         """ | ||||
|         self.format = format | ||||
|  | ||||
|     def get_format(self): | ||||
|         """ | ||||
|         Return the format of the note.  | ||||
|         """Return the format of the note.  | ||||
|          | ||||
|         The value can either indicate Flowed or Preformatted. | ||||
|  | ||||
|         @returns: 0 indicates Flowed, 1 indicates Preformated | ||||
|         @rtype: int | ||||
|  | ||||
|         """ | ||||
|         return self.format | ||||
|  | ||||
|     def set_type(self, the_type): | ||||
|         """ | ||||
|         Set descriptive type of the Note. | ||||
|         """Set descriptive type of the Note. | ||||
|          | ||||
|         @param the_type: descriptive type of the Note | ||||
|         @type the_type: str | ||||
|  | ||||
|         """ | ||||
|         self.type.set(the_type) | ||||
|  | ||||
|     def get_type(self): | ||||
|         """ | ||||
|         Get descriptive type of the Note. | ||||
|         """Get descriptive type of the Note. | ||||
|          | ||||
|         @returns: the descriptive type of the Note | ||||
|         @rtype: str | ||||
|  | ||||
|         """ | ||||
|         return self.type | ||||
|   | ||||
							
								
								
									
										162
									
								
								src/gen/lib/styledtext.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/gen/lib/styledtext.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| # | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2008  Zsolt Foldvari | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program 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 General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program; if not, write to the Free Software | ||||
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
| # | ||||
|  | ||||
| # $Id$ | ||||
|  | ||||
| "Handling formatted ('rich text') strings" | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GRAMPS modules | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| from gen.lib.styledtexttag import StyledTextTag | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # StyledText class | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class StyledText(object): | ||||
|     """Helper class to enable character based text formatting. | ||||
|      | ||||
|      | ||||
|     @ivar string: The clear text part. | ||||
|     @type string: str | ||||
|     @ivar tags: Text tags holding formatting information for the string. | ||||
|     @type tags: list of L{StyledTextTag} | ||||
|  | ||||
|     @cvar POS_TEXT: Position of I{string} attribute in the serialized format of | ||||
|     an instance. | ||||
|     @type POS_TEXT: int | ||||
|     @cvar POS_TAGS: Position of I{tags} attribute in the serialized format of | ||||
|     an instance. | ||||
|     @type POS_TAGS: int | ||||
|  | ||||
|     @attention: The POS_<x> class variables reflect the serialized object, they | ||||
|     have to be updated in case the data structure or the L{serialize} method | ||||
|     changes! | ||||
|        | ||||
|     """ | ||||
|     ##StyledText provides interface  | ||||
|     ##Provide interface for: | ||||
|     ##- tag manipulation for editor access: | ||||
|       ##. get_tags | ||||
|       ##. set_tags | ||||
|     ##- explicit formatting for reports; at the moment: | ||||
|       ##. start_bold() - end_bold() | ||||
|       ##. start_superscript() - end_superscript() | ||||
|  | ||||
|     (POS_TEXT, POS_TAGS) = range(2) | ||||
|      | ||||
|     def __init__(self, text="", tags=None): | ||||
|         """Setup initial instance variable values.""" | ||||
|         self._string = text | ||||
|         # TODO we might want to make simple sanity check first | ||||
|         if tags: | ||||
|             self._tags = tags | ||||
|         else: | ||||
|             self._tags = [] | ||||
|  | ||||
|     # special methods | ||||
|      | ||||
|     def __str__(self): return self._string.__str__() | ||||
|     def __repr__(self): return self._string.__repr__() | ||||
|  | ||||
|     def __add__(self, other): | ||||
|         if isinstance(other, StyledText): | ||||
|             # FIXME merging tags missing | ||||
|             return self.__class__("".join([self._string, other.string])) | ||||
|         elif isinstance(other, basestring): | ||||
|             # in this case tags remain the same, only text becomes longer | ||||
|             return self.__class__("".join([self._string, other])) | ||||
|         else: | ||||
|             return self.__class__("".join([self._string, str(other)])) | ||||
|  | ||||
|     # string methods in alphabetical order: | ||||
|  | ||||
|     def join(self, seq): | ||||
|         # FIXME handling tags missing | ||||
|         return self.__class__(self._string.join(seq)) | ||||
|      | ||||
|     def replace(self, old, new, maxsplit=-1): | ||||
|         # FIXME handling tags missing | ||||
|         return self.__class__(self._string.replace(old, new, maxsplit)) | ||||
|      | ||||
|     def split(self, sep=None, maxsplit=-1): | ||||
|         # FIXME handling tags missing | ||||
|         string_list = self._string.split(sep, maxsplit) | ||||
|         return [self.__class__(string) for string in string_list] | ||||
|  | ||||
|     # other public methods | ||||
|      | ||||
|     def serialize(self): | ||||
|         """Convert the object to a serialized tuple of data. | ||||
|          | ||||
|         @returns: Serialized format of the instance. | ||||
|         @returntype: tuple | ||||
|          | ||||
|         """ | ||||
|         if self._tags: | ||||
|             the_tags = [tag.serialize() for tag in self._tags] | ||||
|         else: | ||||
|             the_tags = [] | ||||
|              | ||||
|         return (self._string, the_tags) | ||||
|      | ||||
|     def unserialize(self, data): | ||||
|         """Convert a serialized tuple of data to an object. | ||||
|          | ||||
|         @param data: Serialized format of instance variables. | ||||
|         @type data: tuple | ||||
|          | ||||
|         """ | ||||
|         (self._string, the_tags) = data | ||||
|          | ||||
|         # I really wonder why this doesn't work... it does for all other types | ||||
|         #self._tags = [StyledTextTag().unserialize(tag) for tag in the_tags] | ||||
|         for tag in the_tags: | ||||
|             gtt = StyledTextTag() | ||||
|             gtt.unserialize(tag) | ||||
|             self._tags.append(gtt) | ||||
|      | ||||
|     def get_tags(self): | ||||
|         """Return the list of formatting tags. | ||||
|          | ||||
|         @returns: The formatting tags applied on the text. | ||||
|         @returntype: list of 0 or more L{StyledTextTag} instances. | ||||
|          | ||||
|         """ | ||||
|         return self._tags | ||||
|      | ||||
|     ##def set_tags(self, tags): | ||||
|         ##"""Set all the formatting tags at once. | ||||
|          | ||||
|         ##@param tags: The formatting tags to be applied on the text. | ||||
|         ##@type tags: list of 0 or more StyledTextTag instances. | ||||
|          | ||||
|         ##""" | ||||
|         ### TODO we might want to make simple sanity check first | ||||
|         ##self._tags = tags | ||||
|          | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     GT = StyledText("asbcde") | ||||
|     print GT | ||||
							
								
								
									
										73
									
								
								src/gen/lib/styledtexttag.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/gen/lib/styledtexttag.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| # | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2008  Zsolt Foldvari | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program 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 General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program; if not, write to the Free Software | ||||
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
| # | ||||
|  | ||||
| # $Id$ | ||||
|  | ||||
| "Provide formatting tag definition for StyledText." | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GRAMPS modules | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| from gen.lib.styledtexttagtype import StyledTextTagType | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # StyledTextTag class | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class StyledTextTag(): | ||||
|     """Hold formatting information for StyledText. | ||||
|      | ||||
|     @ivar name: Type or name of the tag instance. E.g. bold, etc. | ||||
|     @type name: L{gen.lib.StyledTextTagType} instace | ||||
|     @ivar value: Value of the tag. E.g. color hex string for font color, etc. | ||||
|     @type value: str or None | ||||
|     @ivar ranges: Pointer pairs into the string, where the tag applies. | ||||
|     @type ranges: list of (int(start), int(end)) tuples. | ||||
|      | ||||
|     """ | ||||
|     def __init__(self, name=None, value=None, ranges=None): | ||||
|         """Setup initial instance variable values.""" | ||||
|         self.name = StyledTextTagType(name) | ||||
|         self.value = value | ||||
|         self.ranges = ranges | ||||
|  | ||||
|     def serialize(self): | ||||
|         """Convert the object to a serialized tuple of data. | ||||
|         | ||||
|         @returns: Serialized format of the instance. | ||||
|         @returntype: tuple | ||||
|          | ||||
|         """ | ||||
|         return (self.name.serialize(), self.value, self.ranges) | ||||
|      | ||||
|     def unserialize(self, data): | ||||
|         """Convert a serialized tuple of data to an object. | ||||
|         | ||||
|         @param data: Serialized format of instance variables. | ||||
|         @type data: tuple | ||||
|          | ||||
|         """ | ||||
|         (the_name, self.value, self.ranges) = data | ||||
|          | ||||
|         self.name = StyledTextTagType() | ||||
|         self.name.unserialize(the_name) | ||||
							
								
								
									
										73
									
								
								src/gen/lib/styledtexttagtype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/gen/lib/styledtexttagtype.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| # | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2008  Zsolt Foldvari | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program 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 General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program; if not, write to the Free Software | ||||
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
| # | ||||
|  | ||||
| # $Id$ | ||||
|  | ||||
| "Define text formatting tag types." | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Python modules | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| from gettext import gettext as _ | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GRAMPS modules | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| from gen.lib.grampstype import GrampsType | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # StyledTextTagType class | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class StyledTextTagType(GrampsType): | ||||
|     """Text formatting tag type definition. | ||||
|      | ||||
|     Here we only define new class variables. For details see L{GrampsType}. | ||||
|      | ||||
|     """ | ||||
|     NONE_ = -1 | ||||
|     BOLD = 0 | ||||
|     ITALIC = 1 | ||||
|     UNDERLINE = 2 | ||||
|     FONTFACE = 3 | ||||
|     FONTCOLOR = 4 | ||||
|     HIGHLIGHT = 5 | ||||
|     SUPERSCRIPT = 6 | ||||
|      | ||||
|     _CUSTOM = NONE_ | ||||
|     _DEFAULT = NONE_ | ||||
|      | ||||
|     _DATAMAP = [ | ||||
|         (BOLD, _("Bold"), "bold"), | ||||
|         (ITALIC, _("Italic"), "italic"), | ||||
|         (UNDERLINE, _("Underline"), "underline"), | ||||
|         (FONTFACE, _("Fontface"), "fontface"), | ||||
|         (FONTCOLOR, _("Fontcolor"), "fontcolor"), | ||||
|         (HIGHLIGHT, _("Highlight"), "highlight"), | ||||
|         (SUPERSCRIPT, _("Superscript"), "superscript"), | ||||
|     ] | ||||
|      | ||||
|     def __init__(self, value=None): | ||||
|         GrampsType.__init__(self, value) | ||||
		Reference in New Issue
	
	Block a user