# Edit Extensions plugin for WikidPad # author: Alessandro Orsi # first release: 21 Jul 2009 # licence: GPL # ############# # Constants # ############# # Set this to NO if you don't want the Help page to be automatically created when EditExtensions is first loaded CREATE_HELP_PAGE = 'Yes' EDIT_EXTENSIONS_VERSION = '0.4' WIKIDPAD_PLUGIN = (("hooks", 1),("MenuFunctions",1),) from WikidPadStarter import VERSION_STRING WIKIDPAD_VERSION = VERSION_STRING[9:12] # ############ # Modules # ############ import wx import re import locale import StringIO import pickle # ################################################## # Initialize string memory buffer for EncloseSelection # ################################################## # Initialization list containing empty values init_values = [] init_values.append('') init_values.append('') # Create memory stream to store the text boxes values for the EncloseSelection method enclose_selection_buffer_w = StringIO.StringIO() # Pickle the list and store in the stream # (!) Pickle raises a 'LookupError: unknown encoding: string-escape', if using # the default protocol (0) pickle.dump(init_values, enclose_selection_buffer_w, 1) # Set up a readable stream enclose_selection_buffer_r = StringIO.StringIO(enclose_selection_buffer_w.getvalue()) # Unpickle the list (contains two values: the first for the first text # box, the second for the second text box test_values = pickle.load(enclose_selection_buffer_r) # ########### # Locale # ########### # set the locale to the system locale for sorting locale.setlocale(locale.LC_ALL, '') # ######################### # WX Classes and events # ######################### class UserInputDlg(wx.Dialog): """Following dialog used by 'Insert before/after lines' (2 text boxes), 'Enclose selection' (2 text boxes), and 'StripChars' (1 text box) """ # CLASS VARIABLES caller = '' two_text_boxes = False # Following two variables are for Insert before/after and StripChars # that do not use the memory stream and need not retain text boxes values # between calls first_text_box_value = '' second_text_box_value = '' def __init__(self, parent, title, label_first_text_box, label_second_text_box, x_size, y_size, two_text_boxes, caller, prev_text_boxes_values=None): """Parameters: parent(=wiki), dialogue_title, label_of_first_text_box, label_of_second_text_box, two_text_boxes, x_size_of_the_frame, y_size_of_the_frame, caller, previous_text_boxes_values two_text_boxes = True shows both text boxes, otherwise only one caller = method that's calling UserInputDlg, one of: enclose_selection | strip_chars | insert_bfaf previous_text_boxes_values (optional): used only by EncloseSelection to remember previos values in text boxes """ wx.Dialog.__init__(self, parent, -1, title, size=(x_size, y_size)) self.two_text_boxes = two_text_boxes self.caller = caller # === wx Controls === # Text boxes # First text box label self.label_first_text_box = wx.StaticText(self, label=label_first_text_box) # First text box input field self.input_field_first_text_box = wx.TextCtrl(self, size=wx.Size(195, -1)) if prev_text_boxes_values is not None: self.input_field_first_text_box.SetValue(prev_text_boxes_values[0]) if self.two_text_boxes: # Second text box label self.label_second_text_box = wx.StaticText(self, label=label_second_text_box) # Second text box input field self.input_field_second_text_box = wx.TextCtrl(self, size=wx.Size(195, -1)) if prev_text_boxes_values is not None: self.input_field_second_text_box.SetValue(prev_text_boxes_values[1]) # Ok/Cancel buttons button_size = wx.Button.GetDefaultSize() self.ok_button = wx.Button(self, wx.ID_OK, '', (0, 0), button_size ) self.cancel_button = wx.Button( self, wx.ID_CANCEL, '', (120, 0), button_size ) # LAYOUT # Colors: same as Wikidpad dialogs self.SetBackgroundColour('#E0DFE3') # === WX SIZERS === # == Horizontal sizer for buttons == self.horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL) # Add buttons to sizer self.horizontal_sizer.Add(self.ok_button, 0) self.horizontal_sizer.Add(self.cancel_button, 0, wx.LEFT, 15) # == Vertical sizer for labels, input boxes and buttons == self.vertical_sizer = wx.BoxSizer(wx.VERTICAL) # Add first text control label and input box to sizer self.vertical_sizer.Add(self.label_first_text_box, 0, wx.TOP | wx.BOTTOM | wx.LEFT, 5) self.vertical_sizer.Add(self.input_field_first_text_box, 0, wx.BOTTOM | wx.LEFT, 5) if self.two_text_boxes: # Add second text control label and input box to sizer self.vertical_sizer.Add(self.label_second_text_box, 0, wx.TOP | wx.BOTTOM | wx.LEFT, 5) self.vertical_sizer.Add(self.input_field_second_text_box, 0, wx.BOTTOM | wx.LEFT, 5) # Add horizontal sizer containing buttons to vertical sizer self.vertical_sizer.Add(self.horizontal_sizer, 0, wx.ALIGN_CENTER | wx.TOP, 10) self.SetSizer(self.vertical_sizer) # EVENTS self.ok_button.Bind(wx.EVT_BUTTON, self.on_ok) self.cancel_button.Bind(wx.EVT_BUTTON, self.on_cancel) # === Events handling functions === def on_ok(self, event): if self.caller == 'enclose_selection': text_box_values = [] # Store the values entered in the text boxes in a list text_box_values.append(self.input_field_first_text_box.GetValue()) text_box_values.append(self.input_field_second_text_box.GetValue()) # Check if both values are empty (user didn't enter any value) if text_box_values[0] == text_box_values[1] == '': self.EndModal(wx.ID_CANCEL) return # Reposition at the beginning of the stream, otherwise new values are # appended instead of overwritten enclose_selection_buffer_w.seek(0) # Pickle the list with the newly entered values and store them in the # memory stream pickle.dump(text_box_values, enclose_selection_buffer_w, 1) elif self.caller == 'insert_bfaf': self.first_text_box_value = self.input_field_first_text_box.GetValue() self.second_text_box_value = self.input_field_second_text_box.GetValue() # Check if both values are empty (user didn't enter any value) if self.first_text_box_value == self.second_text_box_value == '': self.EndModal(wx.ID_CANCEL) return elif self.caller == 'strip_chars': self.first_text_box_value = self.input_field_first_text_box.GetValue() # Check if the value is empty (user didn't enter any value) if self.first_text_box_value == '': self.EndModal(wx.ID_CANCEL) return self.EndModal(wx.ID_OK) return def on_cancel(self, event): self.EndModal(wx.ID_CANCEL) return # ##################### # Common functions # ##################### def get_selection(wiki): # Get selection SelectedText = wiki.getActiveEditor().GetSelectedText() # Check if selection is empty if SelectedText == '': warn_msg('Select some text first.') return None else: return SelectedText def warn_msg(msg_text): msg = wx.MessageDialog(None, msg_text, 'Edit Extensions', wx.OK) msg.ShowModal() return def delete_blank_lines(lines): """ This funcion is called by SortLines() and RemoveBlankLines() """ mod_lines = [] for id in range(len(lines)): if lines[id] <> '': mod_lines.append(lines[id]) return mod_lines # ################ # Wikidpad Hooks # ################ def openedWiki(wiki, wikiName, wikiConfig): """Creates the help page for EditExtensions plugin """ if CREATE_HELP_PAGE.lower() == 'yes': # Check if Help Page already exists wd = wiki.getWikiDocument() # Method isDefinedWikiPage() is called isDefinedWikiWord in Wikidpad version 1.9 if WIKIDPAD_VERSION == '1.9': help_page_exists = wd.isDefinedWikiWord('EditExtensionsHelp') else: help_page_exists = wd.isDefinedWikiPage('EditExtensionsHelp') if help_page_exists == True: # Help page already exists, let's check if it's a different version p = wiki.getWikiData().getPropertiesForWord('EditExtensionsHelp') # Get EditExtensions version number # attr contains: Help for version VERSION_NUMBER attr = p[0][1] strip_text = re.compile(ur'Help for version ') version = strip_text.sub(r'', attr) # If page already exists and is the same version, don't create a new one if version == EDIT_EXTENSIONS_VERSION: return page_header = '+ Edit Extensions Plugin Help Page\n' + 'Version: ' + EDIT_EXTENSIONS_VERSION + '\nAuthor: Alessandro Orsi' + '\nLicence: GPL\n' page_body = help_text() # <> markup is not supported in v 1.9 if WIKIDPAD_VERSION == '1.9': page_footer = '\n[EditExtensions:"Help for version ' + EDIT_EXTENSIONS_VERSION + '"]\n' else: page_footer = '\n<>' help_page_text = page_header + page_body + page_footer wiki.getWikiDocument().getWikiPageNoError('EditExtensionsHelp').replaceLiveText(help_page_text) wiki.saveCurrentWikiState() # Pop-up message to inform the user about the creation of the help page this_wiki = wiki.getWikiDocument().getWikiName() warn_msg('Help page EditExtensionsHelp created in ' + this_wiki + ' wiki.') def exit(wiki): """Closes the enclose_selection stringIO buffer when Wikidpad is closed. """ try: enclose_selection_buffer_w.close() enclose_selection_buffer_r.close() except IOError : print('EditExtensions: No memory buffer to close') # ############## # Menu items # ############## def describeMenuItems(wiki): """ Creates the EditExtension menu and associates every menu entry with the related function """ global nextNumber return ((uppercase, 'Edit Extensions|UPPERCASE\tAlt-Shift-U', 'Turns selected text to upper case'), (lowercase, 'Edit Extensions|lowercase\tAlt-U', 'Turns selected text to lower case'), (sentence_case, 'Edit Extensions|Sentence case\tAlt-S', 'Capitalize the first character of each line in the selected text'), (title_case, 'Edit Extensions|Title Case\tAlt-T', 'Capitalize the first character of each word in the selected text'), (swap_case, 'Edit Extensions|sWAP cASE\tAlt-P', 'Swap the case of each character in the selected text'), (cut_line, 'Edit Extensions|Cut line\tAlt-X', 'Cut current line'), (copy_line, 'Edit Extensions|Copy line\tAlt-C', 'Copy current line'), (duplicate_line, 'Edit Extensions|Duplicate line\tAlt-D', 'Duplicate current line'), (delete_line, 'Edit Extensions|Delete line\tAlt-Shift-D', 'Delete current line'), (swap_lines, 'Edit Extensions|Swap lines\tAlt-Shift-P', 'Swap the current line with the one above'), (insert_blank_lines, 'Edit Extensions|Insert blank lines\tAlt-Shift-I', 'Insert a blank line after each line in the selection'), (remove_blank_lines, 'Edit Extensions|Remove blank lines\tAlt-Shift-R', 'Remove blank lines from the selection'), (sort_lines, 'Edit Extensions|Sort lines\tAlt-O', 'Sort alphabetically the lines of the selected text'), (insert_before_after, 'Edit Extensions|Insert before/after lines\tAlt-I', 'Add chosen text before and/or after each line in selection'), (strip_first_chars, 'Edit Extensions|Strip from the beginning of line\tAlt-B', 'Delete n characters from the beginning of each line in the selected text'), (strip_last_chars, 'Edit Extensions|Strip from the end of line\tAlt-E', 'Delete n characters from the end of each line in the selected text'), (enclose_selection, 'Edit Extensions|Enclose selection\tAlt-N', 'Add chosen text before and/or after the selected text'), (fast_enclose_selection, 'Edit Extensions|Fast enclose selection\tAlt-Shift-N', 'Enclose the selected text with the last text used in \'Enclose selection\''), (trim_spaces, 'Edit Extensions|Trim spaces\tAlt-R', 'Remove leading and trailing spaces from each line in the selected text'), (compress_spaces, 'Edit Extensions|Compress spaces\tAlt-M', 'Remove multiple spaces between words in the selected text'), (show_hide_spaces, 'Edit Extensions|Show/Hide white spaces', 'Show or hide white spaces and tabs markers'), (show_hide_EOL, 'Edit Extensions|Show/Hide end of line marker', 'Show or hide a marker at the end of each line'), (word_count, 'Edit Extensions|Word count', 'Counts the number of words and characters in the selected text or the whole page '),) # ########### # Functions # ########### def uppercase(wiki, evt): """ Converts the selected text to upper case """ if get_selection(wiki) is not None: text = get_selection(wiki) text = text.upper() wiki.getActiveEditor().ReplaceSelection(text) def lowercase(wiki, evt): """ Converts the selected text to lower case """ if get_selection(wiki) is not None: text = get_selection(wiki) text = text.lower() wiki.getActiveEditor().ReplaceSelection(text) def sentence_case(wiki, evt): """ Capitalizes the first character of each line in the selected text """ if get_selection(wiki) is not None: text = get_selection(wiki) # Store lines in a list lines = text.split('\n') # Loop through each line for line in range(len(lines)): mod_line= '' first_alpha_chr = True # Loop through each character in a line for pos in range(len(lines[line])): # Check if it is an alphabetic character if lines[line][pos].isalpha(): # If it's the first alphabetical character, capitalize if first_alpha_chr: mod_line = mod_line + lines[line][pos].upper() first_alpha_chr = False # otherwise convert to lower case else: mod_line = mod_line + lines[line][pos].lower() else: # if char is not alphabhetic leave unchanged mod_line = mod_line + lines[line][pos] lines[line] = mod_line # Convert the list in a string modified_lines = '\n'.join(lines) wiki.getActiveEditor().ReplaceSelection(modified_lines) def title_case(wiki, evt): """ Capitalizes the first character of each word in the selected text """ if get_selection(wiki) is not None: text = get_selection(wiki) text = text.title() wiki.getActiveEditor().ReplaceSelection(text) def swap_case(wiki, evt): """ Swaps the case of each character in the selected text """ if get_selection(wiki) is not None: text = get_selection(wiki) text = text.swapcase() wiki.getActiveEditor().ReplaceSelection(text) def cut_line(wiki, evt): """ Cut the line at the caret position """ wiki.getActiveEditor().LineCut() def copy_line(wiki, evt): """ Copy the line at the caret position """ wiki.getActiveEditor().LineCopy() def duplicate_line(wiki, evt): """ Duplicate the line at the caret position """ wiki.getActiveEditor().LineDuplicate() def delete_line(wiki, evt): """ Delete the line at the caret position """ wiki.getActiveEditor().LineDelete() def swap_lines(wiki, evt): """ Swap the line at the caret position with the one above """ wiki.getActiveEditor().LineTranspose() def sort_lines(wiki, evt): """ Sort the selected lines """ if get_selection(wiki) is not None: text = get_selection(wiki) # Store lines in a list lines = text.split('\n') no_blank_lines = delete_blank_lines(lines) # Sort according to the system locale. Works in Windows, needs testing on other systems no_blank_lines.sort(cmp=locale.strcoll) sorted_lines = '\n'.join(no_blank_lines) wiki.getActiveEditor().ReplaceSelection(sorted_lines) def enclose_selection(wiki, evt): """ Shows a dialog where the user can enter one or two strings The strings are than added at the beginning and at the end of the selection respectively """ if get_selection(wiki) is not None: text = get_selection(wiki) # Get previous text boxes values from the memory stream (see beginning # of this script) # Set up a readable stream enclose_selection_buffer_r = StringIO.StringIO(enclose_selection_buffer_w.getvalue()) # Unpickle the list (contains two values: the first for the first text # box, the second for the second text box) old_values = pickle.load(enclose_selection_buffer_r) Dialog = UserInputDlg(wiki, 'Enclose selection', 'Insert before selection:', 'Insert after selection:', 215, 180, True, 'enclose_selection', old_values) Dialog.Centre() if Dialog.ShowModal() == wx.ID_OK: # Set up a readable stream enclose_selection_buffer_r = StringIO.StringIO(enclose_selection_buffer_w.getvalue()) # Get the new values values = pickle.load(enclose_selection_buffer_r) text = values[0] + text + values[1] wiki.getActiveEditor().ReplaceSelection(text) def fast_enclose_selection(wiki, evt): """Enclose selected text with last values entered in enclose_selection() dialogue. This function doesn't show any dialogue. """ if get_selection(wiki) is not None: text = get_selection(wiki) # Get current stored values from the memory stream # Set up a readable stream enclose_selection_buffer_r = StringIO.StringIO(enclose_selection_buffer_w.getvalue()) # Unpickle the list (contains two values: the first for the first text # box, the second for the second text box) last_entered_values = pickle.load(enclose_selection_buffer_r) if last_entered_values[0] == last_entered_values[1] == '': warn_msg("No stored text. Use \"Enclose selection\" once first.") return text = last_entered_values[0] + text + last_entered_values[1] wiki.getActiveEditor().ReplaceSelection(text) return def insert_before_after(wiki, evt): """ Shows a dialog where the user can enter one or two strings The strings are than added respectively at the beginning and at the end of each line in the selection """ if get_selection(wiki) is not None: text = get_selection(wiki) Dialog = UserInputDlg(wiki, 'Insert before/after lines', 'Insert before:', 'Insert after:', 215, 180, True, 'insert_bfaf') Dialog.Centre() if Dialog.ShowModal() == wx.ID_OK: prepend = Dialog.first_text_box_value append = Dialog.second_text_box_value # Store lines in a list lines = text.split('\n') for id in range(len(lines)): # Modify line only if it's not blank if lines[id] != '': lines[id] = prepend + lines[id] + append # Convert the list in a string modified_lines = '\n'.join(lines) wiki.getActiveEditor().ReplaceSelection(modified_lines) def strip_first_chars(wiki, evt): strip_chars(wiki, evt, 'Strip from beginning of line', True) def strip_last_chars(wiki, evt): strip_chars(wiki, evt, 'Strip from end of line', False) def strip_chars(wiki, evt, title, from_beginning): """ Shows a dialog where the user can enter a numeric value. An equal number of characters is the removed from the beginning or the end of each selected line. """ if get_selection(wiki) is not None: text = get_selection(wiki) Dialog = UserInputDlg(wiki, title, 'Number of characters to delete:', '', 215, 120, False, 'strip_chars') Dialog.Centre() if Dialog.ShowModal() == wx.ID_OK: strip_n_chars = Dialog.first_text_box_value # Check if the entered value is a number if not strip_n_chars.isdigit(): warn_msg('Only numbers allowed.') return strip_n_chars = int(strip_n_chars) # Store lines in a list lines = text.split('\n') for id in range(len(lines)): line_length = len(lines[id]) # Strip characters only when the number of characters to strip is less than # the total length of the line if strip_n_chars < line_length: actual_line = lines[id] if from_beginning == True: lines[id] = actual_line[strip_n_chars:line_length] else: lines[id] = actual_line[:-strip_n_chars] stripped_lines = '\n'.join(lines) wiki.getActiveEditor().ReplaceSelection(stripped_lines) def trim_spaces(wiki, env): """ Remove leading and trailing spaces from each line in the selection """ if get_selection(wiki) is not None: text = get_selection(wiki) # Store lines in a list lines = text.split('\n') for id in range(len(lines)): lines[id] = lines[id].strip() # Convert the list in a string modified_lines = '\n'.join(lines) wiki.getActiveEditor().ReplaceSelection(modified_lines) def compress_spaces(wiki, env): """ Leave only one space between words in the selection """ if get_selection(wiki) is not None: text = get_selection(wiki) text = re.sub(r'[ ]{2,}', ' ', text) wiki.getActiveEditor().ReplaceSelection(text) def show_hide_spaces(wiki, env): """ Shows or hide a marker for space characters. It's a toggle function. """ state = wiki.getActiveEditor().GetViewWhiteSpace() if state == 0: # 1: turn on markers wiki.getActiveEditor().SetViewWhiteSpace(1) # Show markers in red (#FF0000) wiki.getActiveEditor().SetWhitespaceForeground(1, '#FF0000') else: # 0: turn off markers wiki.getActiveEditor().SetViewWhiteSpace(0) def show_hide_EOL(wiki, env): """ Shows or hide end of line markers. It's a toggle function. """ state = wiki.getActiveEditor().GetViewEOL() if state == 0: # 1: turn on markers wiki.getActiveEditor().SetViewEOL(1) # Show markers in red (#FF0000) wiki.getActiveEditor().SetWhitespaceForeground(1, '#FF0000') else: # 0: turn off markers wiki.getActiveEditor().SetViewEOL(0) def insert_blank_lines(wiki, env): """ Insert a blank line after each line in the selected text """ if get_selection(wiki) is not None: text = get_selection(wiki) # Store lines in a list lines = text.split('\n') for id in range(len(lines)): # Add blank line if it's not a new line if lines[id] is not u'': lines[id] = lines[id] + '\n' # Convert the list in a string modified_lines = '\n'.join(lines) wiki.getActiveEditor().ReplaceSelection(modified_lines) def remove_blank_lines(wiki, env): """ Remove every blank line from the selected text """ if get_selection(wiki) is not None: text = get_selection(wiki) # Store lines in a list lines = text.split('\n') no_blank_lines = delete_blank_lines(lines) #Convert the list in a string modified_lines = '\n'.join(no_blank_lines) + '\n' wiki.getActiveEditor().ReplaceSelection(modified_lines) def word_count(wiki, env): """ Counts the number of words and characters in the whole page, if no text is selected, or in the selected text. Strips wiki markup. """ SelectedText = wiki.getActiveEditor().GetSelectedText() # If no text is selected, grab the whole page if SelectedText == '': # Save wiki state, or else the page on the screen might be different from the page retrieved by getContent() wiki.saveCurrentWikiState() current_page_name = wiki.getCurrentWikiWord() text = wiki.getWikiData().getContent(current_page_name) else: text = SelectedText # Remove wiki markup, leave only numbered and bulletted list. Whole text is in one long line # Match first title hack: strip slashes from page titles with subpages like in ++ This is/the/title/page title_slashes = re.compile(ur'^\++\s*.*\n', re.MULTILINE) match = title_slashes.match(text) if match: page_title = match.group() # Strip the title from the main text text = text.replace(page_title, '', 1) # Replace slashes with spaces in the page title page_title = page_title.replace('/', ' ') # Rejoin title to main text text = page_title + text headings = re.compile(ur'^\++\s*(.)', re.MULTILINE) text = headings.sub(r'\1', text) bold = re.compile(ur'\*(\S.*?\S)\*', re.DOTALL) text = bold.sub(r'\1', text) italics = re.compile(ur'\b_(\S.*?\S)_\b', re.DOTALL) text = italics.sub(r'\1', text) literals_and_tables = re.compile(ur'(^<>|^<> ) can be preceded or followed by any number of spaces literals_inline = re.compile(ur'<<\s*(\S.*\S)\s*>>', re.DOTALL) text = literals_inline.sub(r'\1', text) # Table cells: # REs to substitute '|', some matches have to be substituted with None, other with SPACE # # (% = one or more spaces, _ = one charcater) # # To substitute with None: # ^|%, %|$, ^|, |$, ^%|%, %|%$ # # To substitute with SPACE # %|_, _|_, %|%, _|% # None: table_cells_none = re.compile(ur'(^\|\s+)|(\s+\|$)|(^\|)|(\|$)|(^\s+\|\s+)|(\s+\|\s+$)', re.MULTILINE) text = table_cells_none.sub(r'', text) # SPACE: # | on the right side table_cells_spaces_right = re.compile(ur'\s+\|(\S)') text = table_cells_spaces_right.sub(r' \1', text) # | in the center surounded by spaces. This has to go first table_cells_spaces_ctr = re.compile(ur'\s+\|\s+') text = table_cells_spaces_ctr.sub(r' ', text) # | in the center surounded by characters table_cells_nospaces_ctr = re.compile(ur'(\S)\|(\S)') text = table_cells_nospaces_ctr.sub(r'\1 \2', text) # | on the left side table_cells_spaces_lft = re.compile(ur'(\S)\| +') text = table_cells_spaces_lft.sub(r'\1 ', text) # escaped characters substitutions has to come after 1. table cells and 2. literlas inline, otherwise: # 1. \|SPACE --> SPACE|SPACE --> None # 2. \<< ... >> --> << ...>> --> ... escaped = re.compile(ur' \\(\S)') text = escaped.sub(ur' \1', text) line_continuation = re.compile(ur'\s*\\\s*$', re.MULTILINE) text = line_continuation.sub(r'', text) attribs_and_inserts = re.compile(ur'^\[:{0,1}.*?:.*?\]\s*\n', re.MULTILINE) text = attribs_and_inserts.sub(r'', text) # this has to go after attributes and insertions, otherwise [:attrib:] --> :attrib: links = re.compile(ur'\[/*(\S.*?\S)\]') text = links.sub(r'\1', text) anchors = re.compile(ur'^anchor:.*\n', re.MULTILINE) text = anchors.sub(r'', text) # Counting starts here # Count characters with spaces (each tab counts as one character): # Strips all new lines nonewlines = re.compile(ur'\n') text_nonewlines = nonewlines.sub(r'', text) num_chars_spaces = len(text_nonewlines) # Count words: # Convert any whitespace character to space (\t, \n...) text = re.sub('\s', ' ', text) # Convert apostrophes to spaces so we'll --> we ll (two words) # Note: the difference before apostrophe and quotes (below) is that apostrophes # are preceded and followed by a non-space character apostrophes = re.compile(r'(\S)\'(\S)') text = apostrophes.sub(r'\1 \2', text) # Remove punctuation punctuation = re.compile(ur'[,|.|;|:|¿|?|¡|!|‘|’|«|»|\"|\'|\`]') text = punctuation.sub(r'', text) # Compress spaces text = re.sub(r'[ ]{2,}', ' ', text) # Trim trailing spaces to remove what was empty lines at the end of the text text = text.strip() text_for_words = text.split(' ') num_words = len(text_for_words) # Count characters without spaces: # Strips any whitespace character text_for_chars = re.sub('\s', '', text) num_chars_nospaces = len(text_for_chars) # Show Results popup results= 'Words: ' + `num_words` + '\n\nCharacters with spaces:' + `num_chars_spaces` + '\n\nCharacters without spaces:'+ `num_chars_nospaces` + '\n' msg = wx.MessageDialog(None, results, 'Word count', wx.OK) msg.ShowModal() def help_text(): """ Dummy function to improve code readability, return the help text. Called by openedWiki() hook """ help_text = u""" [:toc:] + Overview This plugin is inspired by some of the edit commands available in the Scintilla based text editor Notepad2[1]. It adds a new submenu called Edit Extensions to Wikipad's Plugins menu with the following items: * Uppercase * Lowercase * Sentence case * Title case * Swap case * Cut line * Copy line * Duplicate line * Delete line * Swap lines * Insert blank line * Remove blank lines * Sort lines * Insert before/after lines * Strip from the beginning of line * Strip from the end of line * Enclose selection * Fast enclose selection * Trim spaces * Compress spaces * Show/Hide white spaces * Show/Hide end of line marker * Word count All commands require some text to be selected first *except*: * Cut line, Copy line, Duplicate line, Delete line, Swap lines: they affect only the line where the caret is * Show/Hide white spaces, Show/Hide end of line marker: they affect the whole document * Word count: if nothing is selected it counts the whole page + Install and Upgrade Put the file EditExtensions.py into the 'user_extensions' folder of your Wikidpad installation (create the folder if it doesn't exist). Then restart Wikidpad. To upgrade, quit Wikidpad, delete the old EditExtensions.py and replace with the new one. To change any shortcut, edit the relevant parts of describeMenuItems() in EditExtensions.py A help page will be created in every wiki in which Edit Extensions is loaded. You can access the help page through the Wikidpad tree view: Views --> EditExtensions --> Help for version XX --> EditExtensionsHelp. Switch to Preview to read. A pop-up message will inform about the creation of the help page. _NOTE:_ If you don't want the help page to be created, set CREATE_WIKI_HELP = 'No' at the beginning of the file EditExtensions.py This version has been tested on a system running Windows XP SP2 with the following versions of Wikidpad: * Wikidpad 1.9 * Wikidpad 2.0rc01 * Wikidpad 2.1alpha01 + Brief explanation of each command ++ Commands that modify case *UPPERCASE:* all letters uppercase *lowercase:* all letters lowercase *Sentence case:* capitalizes the first letter of a sentence, if more than one line is selected, the first letter of each line will be capitalized *Title Case:* capitalizes the first letter of each word *sWAP cASE:* swaps the case of each letter ++ Commands that operates on lines or part of lines *Cut line, Copy line, Duplicate line, Delete line:* cut, copy, duplicate or delete the current line --the one with the caret on it, no matter if some text is selected or not *Swap lines: * swaps the current line with the one above *Sort lines:* sorts the selected lines in alphabetical order according to the locale used on the system. Note that if the text contains characters that do not belong to the range of letters used by the system locale, the sort won't work. *Insert before/after lines:* adds user defined text at the beginning and/or at the end of each line in the selected text *Strip from the beginning/end of line:* deletes the specified number of characters from the beginning or the end of each line in the selected text. If a line is shorter than the number of characters to delete, the line is skipped. ++ Commands that operates on selections of text *Insert blank line:* inserts a blank line after each line in the selected text *Remove blank lines:* removes all blank lines in the selected text *Enclose selection:* adds user defined text at the beginning and/or at the end of the selected text *Fast Enclose Selection:* encloses the selected text with the last values used in "Enclose selection" dialogue. This command does not show any dialogue. *Trim spaces:* deletes leading and trailing spaces of each line in the selection *Compress spaces:* replaces multiple spaces between words in the selected text with one space ++ Commands that affect the whole text *Show/hide white spaces, Show hide end-of-line markers:* this commands are usually found under the View menu and they give a visual reference for character that aren't normally displayed: an arrow for tabs, a dot for spaces and LF for the end of a line. ++ Word count Word count shows a popup windows with a summary of the number of characters with spaces, the number of characters without spaces, and the number of words contained in the selected text. If no text is selected, the computation is performed on the whole page. Before counting, standard Wikidpad markup is stripped from the text, in particular: headings, bold, italics, literals (\<<, \<<\pre, \>>), table markers (\<> \* _was Strip from the beginning of line_ \** _was Insert blank line_ + Contact and bug report Add to the Wkidpad Trac page[2] or post to the Wikidpad Yahoo! Group[3] + Changelog v 0.4 (29 May 2010) * Added: 'Fast enclose selection' command * Fixed: 'Sentence case' did not convert caps to lowercase * Fixed: 'Remove blank lines'. Now no new line is inserted after blank lines * Changed: rearranged some key bindings because of conflict with standard Wikidpad ones (see above) v 0.3 (30 Apr 2010) * Added: 'Insert blank line' and 'Remove blank lines' commands * Added: Word count * Added: a wiki page named EditExtensionsHelp is created when the plugin is first loaded * Fixed: 'Sort lines' sorted also every blank line present in the selection * Fixed: Trimming spaces around selection caused some commands like 'Strip from the beginning of line' to strip an incorrect number of characters on the first line * Fixed: 'Sentence case' now capitalizes the first alphabetical character of each line even when the line begins with no alphabetical characters * Changed: renamed 'Remove spaces' to 'Trim spaces' * Changed: rearranged some key bindings (see above) v 0.2 (3 Sep 09) * Added: Cut line, Copy line, Duplicate line, Delete line and Swap lines commands * Slightly reorganized the order of menu items v 0.1a (24 Jul 09) * Fixed: sort didn't work when lines contained non-ascii characters * Changed the case of some menu items to reflect the effect of the command v 0.1 (21 Jul 09) First release ---- [1]: http://www.flos-freeware.ch/notepad2.html [2]: http://trac.wikidpad2.webfactional.com/wiki/EditExtensions [3]: http://tech.groups.yahoo.com/group/wikidPad """ return(help_text) #EOF