Print envelopes online with just a web browser goEnvelope is a new web app for printing envelopes to your home or office printer. No software install required, print envelopes directly from your web browser.
Apphile app store search Apple iPhone, iPad & iPod Touch app store search engine. Find & download the newest apps, games, utilities, reference and iTunes movies & music.
DomainDriveway Domain Driveway is the best domain monetization solution available today. Stop parking your domains and start earning today!
Using Perl and Regular Expressions to Process Html Files - Part 2
In Part 1 we looked at what Perl and regular expressions are, and discussed how to use them to process ASCII files such as HTML files. In this part we'll develop a Perl script to process an HTML file.
Linux Disk Editor
Linux disk and file system editor script. Use with caution!
Edile Python Text Editor: A PyGTK text editor in one source code file
#!/usr/bin/env python
########################################################################
# 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.
#
# -----------------------------------------------------------------------------
#
# This is Edile: A PyGTK+ text editor.
# http://edile.googlecode.com
# Edile is a basic but useful text editor implemented in one source code file.
# It requires only PyGTK but if you have pygtksourceview2 installed it will use that.
#
# This file is based on part of the tutorial: Linux GUI Programming with GTK+ and Glade3
# http://www.micahcarrick.com/12-24-2007/gtk-glade-tutorial-part-1.html
########################################################################
# edit default configuration here
#=======================================================================
CONF_FONT = "inconsolata 10"
CONF_HIGHLIGHT_CURRENT_LINE = True
CONF_HIGHLIGHT_MATCHING_BRACKETS = True
CONF_OVERWRITE = False # insert/overwrite mode
CONF_SHOW_LINE_NUMBERS = True
CONF_AUTO_INDENT = True
CONF_INDENT_ON_TAB = True
CONF_SHOW_RIGHT_MARGIN = True
CONF_RIGHT_MARGIN_POSITION = 72
CONF_HIGHLIGHT_SYNTAX = True
CONF_SPACES_INSTEAD_OF_TABS = True
CONF_TAB_WIDTH = 4
CONF_INDENT_WIDTH = -1 # -1 means use same as tab width
CONF_MAX_UNDO_LEVELS = 50 # -1 means no limit
CONF_HIGHLIGHT_CHANGES_SINCE_SAVE = True
# color for highlighting changes since last save
CONF_CHANGE_HIGHLIGHT = "dark slate gray" # from /etc/X11/rgb.txt
# color for highlighting find/replace matches
CONF_FIND_HIGHLIGHT = "IndianRed4"
# gtksrcview color scheme
CONF_STYLE_SCHEME = ("oblivion","kate","tango","classic")
# 'note pad'. will open this file on startup instead of untitled. empty string means no default file.
CONF_DEFAULT_FILE = ""
# whether or not to load plugins. off by default.
# plugins provide a way for you to customise edile for your purpose or environment
# without changing the application's source code.
CONF_LOAD_PLUGINS = False
# whether to load plugins when running as the super user. 'no' by default.
CONF_LOAD_PLUGINS_AS_ROOT = False
# url of file to load plugins from. any scheme should work (e.g. file:///, http://)
# default is http://edile.googlecode.com/files/edile_plugins-0.1.8.py
# see that file for a description of the plugin interface.
# as of version 0.1.8 options can also be set from that file.
CONF_PLUGIN_LOCATION = "http://edile.googlecode.com/files/edile_plugins-0.1.7.py"
# sha 256 hash of the plugin file for authentication. empty string means no authentication.
CONF_PLUGIN_SHA256 = "eca99d7b7ae2042a742366d7b389857d7fa2ee496573d7dee4ebb5a885b53288"
# move whichever you want as default into the first position
CONF_WRAP = ('None','Character','Word')
CONF_SMART_HOME_END_TYPE = ('Before','Disabled','After','Always')
#=======================================================================
# end configuration
# stuff here you probably shouldn't edit
#=======================================================================
EDILE_VERSION = '0.2'
EDILE_URL = 'http://edile.googlecode.com'
EDILE_NAME = 'Edile'
EDILE_DESCRIPTION = 'a pygtk text editor in one file.'
#=======================================================================
# here begins the program edile
# TODO: command line options (verbosity, language, conf options)
# improve find code. handle everything internal to edile w/o relying on gtksrcview features eg. case sensitivity
# config option for encoding to use when saving file instead of always file's encoding
# generally firm everything up. condense and refactor code, exception handling, etc.
# UI definition. GTK builder XML string
U_I = '''
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKEdile Text Editor540660TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKFalseTrueTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK0GTK_POLICY_AUTOMATICGTK_POLICY_AUTOMATICGTK_SHADOW_ETCHED_INTrueTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK22GTK_WRAP_NONE1TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK2False2
'''
# for the search ui, because it was easier this way.
SEARCH_UI = '''
TrueTrueGTK_ORIENTATION_VERTICALGTK_ORIENTATION_VERTICALTrueTrueFindGTK_JUSTIFY_RIGHTTrueTrue●1TrueTrueReplaceGTK_JUSTIFY_RIGHTTrueTrue●11TrueTrueWhole wordTrue2TrueTrueCase sensitiveTrue3TrueFalse4TrueTrueTrueTrueCloseTrueTrueTrueFind Previous1TrueTrueTrueFind Next2TrueTrueTrueFind & Replace35
'''
import sys
import os
import imp,types,tempfile
import string
#import urlparse
import urllib
#import platform
import gio #this breaks windows
import gtk,gobject
import pango
import subprocess
import hashlib
from optparse import OptionParser
# prints about info from global vars to console
def print_about():
print "\n%s %s\n%s\n%s"%(EDILE_NAME,EDILE_VERSION,EDILE_URL,EDILE_DESCRIPTION)
print_about()
try:
import gtksourceview2
using_source_view = True
print "using gtksourceview"
except ImportError:
using_source_view = False
print "not using gtksourceview"
pass
# names for text wrapping types
CONF_WRAP_MAP = {'None':gtk.WRAP_NONE,'Character':gtk.WRAP_CHAR, 'Word':gtk.WRAP_WORD, 'Word Character':gtk.WRAP_WORD_CHAR}
if using_source_view:
# names for gtksourceview smart home end types
CONF_SMART_HOME_END_TYPE_MAP = {'Disabled':gtksourceview2.SMART_HOME_END_DISABLED,'Before':gtksourceview2.SMART_HOME_END_BEFORE,'After':gtksourceview2.SMART_HOME_END_AFTER,'Always':gtksourceview2.SMART_HOME_END_ALWAYS}
class Edile:
###
# on_* = action methods from ui
###
# Called when the user clicks the 'About' menu. We use gtk_show_about_dialog()
# which is a convenience function to show a GtkAboutDialog. This dialog will
# NOT be modal but will be on top of the main application window.
def on_about_menu_item_activate(self, menuitem, data=None):
# show standard gtk about window
def show_about():
if self.about_dialog:
self.about_dialog.present()
return
about_dialog = gtk.AboutDialog()
about_dialog.set_transient_for(self.window)
about_dialog.set_destroy_with_parent(True)
about_dialog.set_name(EDILE_NAME)
about_dialog.set_version(EDILE_VERSION)
about_dialog.set_copyright("")
about_dialog.set_website(EDILE_URL)
about_dialog.set_comments(EDILE_DESCRIPTION)
#about_dialog.set_authors(EDILE_AUTHORS)
about_dialog.set_logo_icon_name(gtk.STOCK_EDIT)
# callbacks for destroying the about dialog
def close(dialog, response, editor):
editor.about_dialog = None
dialog.destroy()
def delete_event(dialog, event, editor):
editor.about_dialog = None
return True
about_dialog.connect("response", close, self)
about_dialog.connect("delete-event", delete_event, self)
self.about_dialog = about_dialog
about_dialog.show()
# run functions
print_about()
show_about()
# view source. displays this file in a new instance.
def on_view_source_menu_item_activate(self, menuitem, data=None):
exe = __file__
exepath = os.path.realpath(exe)
self.spawn(exepath)
# tries to run file in a terminal
def on_execute_menu_item_activate(self,menuitem,data=None):
exe = self.filename
if exe == None:
self.error_message("Save file first!")
return
if os.path.exists(exe):
exepath = os.path.realpath(exe)
subprocess.Popen(["xterm", "-hold", "-e", "\"%s\""%(exepath)])
# When our window is destroyed, we want to break out of the GTK main loop.
# We do this by calling gtk_main_quit(). We could have also just specified
# gtk_main_quit as the handler in Glade!
def on_window_destroy(self, widget, data=None):
gtk.main_quit()
# When the window is requested to be closed, we need to check if they have
# unsaved work. We use this callback to prompt the user to save their work
# before they exit the application. From the "delete-event" signal, we can
# choose to effectively cancel the close based on the value we return.
def on_window_delete_event(self, widget, event, data=None):
if self.check_for_save():
self.on_save_menu_item_activate(None, None)
# if user cancelled save
if self.filename == None: return True
self.log('exiting')
return False # Propogate event
# Called when the user clicks the 'New' menu. We need to prompt for save if
# the file has been modified, and then delete the buffer and clear the
# modified flag.
def on_new_menu_item_activate(self, menuitem, data=None):
if self.check_for_save(): self.on_save_menu_item_activate(None, None)
# clear editor for a new file
buff = self.text_view.get_buffer()
buff.set_text("")
buff.set_modified(False)
self.filename = None
self.language = None
self.text_encoding = None
self.reset_default_status()
def on_new_window_menu_item_activate(self, menuitem, data=None):
self.spawn()
# Called when the user clicks the 'Open' menu. We need to prompt for save if
# thefile has been modified, allow the user to choose a file to open, and
# then call load_file() on that file.
def on_open_menu_item_activate(self, menuitem, data=None):
if self.check_for_save(): self.on_save_menu_item_activate(None, None)
filename = self.get_open_filename()
if filename: self.load_file(filename)
def on_open_in_new_window_menu_item_activate(self, menuitem, data=None):
filename = self.get_open_filename()
if filename: self.spawn(filename)
# Called when the user clicks the 'Save' menu. We need to allow the user to choose
# a file to save if it's an untitled document, and then call write_file() on that
# file.
def on_save_menu_item_activate(self, menuitem, data=None):
if self.filename == None:
filename = self.get_save_filename()
if filename: self.write_file(filename)
else: self.write_file(None)
# Called when the user clicks the 'Save As' menu. We need to allow the user
# to choose a file to save and then call write_file() on that file.
def on_save_as_menu_item_activate(self, menuitem, data=None):
filename = self.get_save_filename()
if filename: self.write_file(filename)
def on_close_menu_item_activate(self, menuitem, data=None):
if self.check_for_save():
self.on_save_menu_item_activate(None, None)
# if user cancelled save
if self.filename == None: return
self.text_view.set_sensitive(False)
buff = self.text_view.get_buffer()
buff.set_text("")
buff.set_modified(False)
self.text_view.set_sensitive(True)
self.filename = None
self.language = None
self.reset_default_status()
# Offer to save any changes, then reload original file from disk
def on_reload_menu_item_activate(self, menuitem, data=None):
old_filename = self.filename
if self.check_for_save():
self.on_save_as_menu_item_activate(menuitem, None)
# if user cancelled save
if self.filename == None: return
self.log('reloading %s'%(old_filename))
self.load_file(old_filename)
# Called when the user clicks the 'Quit' menu. We need to prompt for save if
# the file has been modified and then break out of the GTK+ main loop
def on_quit_menu_item_activate(self, menuitem, data=None):
if self.check_for_save():
self.on_save_menu_item_activate(None, None)
# if user cancelled save
if self.filename == None: return
self.log('exiting')
gtk.main_quit()
def on_undo_menu_item_activate(self, menuitem, data=None):
if using_source_view:
if self.text_view.get_buffer().can_undo():
self.text_view.get_buffer().undo()
def on_redo_menu_item_activate(self, menuitem, data=None):
if using_source_view:
if self.text_view.get_buffer().can_redo():
self.text_view.get_buffer().redo()
# Called when the user clicks the 'Cut' menu.
def on_cut_menu_item_activate(self, menuitem, data=None):
buff = self.text_view.get_buffer();
buff.cut_clipboard (gtk.clipboard_get(), True);
# Called when the user clicks the 'Copy' menu.
def on_copy_menu_item_activate(self, menuitem, data=None):
buff = self.text_view.get_buffer();
buff.copy_clipboard (gtk.clipboard_get());
# Called when the user clicks the 'Paste' menu.
def on_paste_menu_item_activate(self, menuitem, data=None):
buff = self.text_view.get_buffer();
buff.paste_clipboard (gtk.clipboard_get(), None, True);
def on_select_all_menu_item_activate(self, menuitem, data=None):
buff = self.text_view.get_buffer();
buff.select_range(buff.get_start_iter(),buff.get_end_iter())
# Called when the user clicks the 'Delete' menu.
def on_delete_menu_item_activate(self, menuitem, data=None):
buff = self.text_view.get_buffer();
gone = buff.get_text(buff.get_selection_bounds()[0],buff.get_selection_bounds()[1])
self.log('deleting %s'%(gone))
buff.delete_selection (False, True);
def on_find_prev_selected_menu_item_activate(self,menuitem,data=None):
buff = self.text_view.get_buffer()
selected = buff.get_text(buff.get_selection_bounds()[0],buff.get_selection_bounds()[1])
iter = self.get_find_iter(buffer=buff, backwards=True,limit_iter=None)
results = self.do_find_with_iter(iter=iter, search_for=selected,backwards=True,case_sensitive=False,whole_word=False)
try:
buff.select_range(results[0],results[1])
self.text_view.scroll_to_mark(mark=buff.get_insert(),within_margin=0.33,use_align=True,xalign=0.5,yalign=0.5)
except:
self.log("\'%s\' not found."%(selected))
def on_find_next_selected_menu_item_activate(self,menuitem,data=None):
buff = self.text_view.get_buffer()
selected = buff.get_text(buff.get_selection_bounds()[0],buff.get_selection_bounds()[1])
iter = self.get_find_iter(buffer=buff, backwards=False,limit_iter=None)
results = self.do_find_with_iter(iter=iter, search_for=selected,backwards=False,case_sensitive=True,whole_word=False)
try:
buff.select_range(results[0],results[1])
self.text_view.scroll_to_mark(mark=buff.get_insert(),within_margin=0.33,use_align=True,xalign=0.5,yalign=0.5)
except:
self.log("\'%s\' not found."%(selected))
def on_search_case_sensitive_checkbox_toggled(self, data=None):
#self.search_case_sensitive = togglebutton.get_active()
self.search_case_sensitive = not self.search_case_sensitive
def on_search_whole_word_checkbox_toggled(self, data=None):
#self.search_is_whole_word = togglebutton.get_active()
self.search_is_whole_word = not self.search_is_whole_word
def search_find_field_changed(self, data=None):
self.search_string = self.search_field.get_text()
def search_replace_field_changed(self, data=None):
self.replacement_string = self.replace_field.get_text()
def on_show_find_menu_item_activate(self,menuitem,data=None):
#TODO: could load search UI from xml string here
if self.search_window.get_property("visible"):
self.search_window.present()
return
buff = self.text_view.get_buffer()
if self.search_string == "":
if buff.get_selection_bounds():
self.search_string = buff.get_slice(buff.get_selection_bounds()[0],buff.get_selection_bounds()[1])
self.search_field.set_text(self.search_string)
self.replace_field.set_text(self.replacement_string)
self.search_field.grab_focus()
self.search_window.show_all()
def on_search_window_delete_event(self, widget=None, event=None, data=None):
self.search_window.hide_all()
return True
def on_find_next_menu_item_activate(self, sender, data=None):
buff = self.text_view.get_buffer();
if self.search_string == "":
if buff.get_selection_bounds():
self.search_string = buff.get_slice(buff.get_selection_bounds()[0],buff.get_selection_bounds()[1])
self.search_field.set_text(self.search_string)
else:
self.error_message("Enter the search string first.")
self.on_show_find_menu_item_activate(sender)
next_results = self.do_find(buffer=buff,iter=None,backwards=False,case_sensitive=self.search_case_sensitive,whole_word=self.search_is_whole_word)
#highlight next results
#offer to wrap
#find backwards
def on_find_previous_menu_item_activate(self, menuitem, data=None):
buff = self.text_view.get_buffer();
if self.search_string == "":
if buff.get_selection_bounds():
self.search_string = buff.get_slice(buff.get_selection_bounds()[0],buff.get_selection_bounds()[1])
self.search_field.set_text(self.search_string)
else:
self.error_message("Enter the search string first.")
self.on_show_find_menu_item_activate(menuitem)
prev_results = self.do_find(buffer=buff,iter=None,backwards=True,case_sensitive=self.search_case_sensitive,whole_word=self.search_is_whole_word)
#highlight prev results
#offer to wrap
def on_find_all_menu_item_activate(self, menuitem, data=None):
buff = self.text_view.get_buffer();
if self.search_string == "":
if buff.get_selection_bounds():
self.search_string = buff.get_slice(buff.get_selection_bounds()[0],buff.get_selection_bounds()[1])
self.search_field.set_text(self.search_string)
else:
self.error_message("Enter the search string first.")
self.on_show_find_menu_item_activate(menuitem)
return
self.do_find_all(buffer=buff)
def on_replace_and_find_menu_item_activate(self, menuitem, data=None):
if self.search_string == "":
self.error_message("Enter the search string first.")
return
if self.replacement_string == '':
self.error_message("Enter the replacement string first.")
self.on_show_find_menu_item_activate(menuitem)
return
buff = self.text_view.get_buffer()
replace_iter = self.get_find_iter(buffer=buff, backwards=buff.get_has_selection(),limit_iter=None) #LAST
next_results = self.do_find(buffer=buff,iter=replace_iter,backwards=False,case_sensitive=self.search_case_sensitive,whole_word=self.search_is_whole_word)
if next_results == None:
return
else:
result_iter = next_results[0]
#ask for confirm here
self.do_replace_next(buffer=buff,iter=result_iter,search_for=self.search_string,replace_with=self.replacement_string,limit=next_results[1])
def on_replace_all_menu_item_activate(self, menuitem, data=None):
if self.search_string == "":
self.error_message("Enter the search string first.")
return
if self.replacement_string == "":
self.error_message("Enter the replacement string first.")
return
buff = self.text_view.get_buffer()
self.do_replace_all(buff,self.search_string,self.replacement_string)
# Called when the user clicks an item from the 'Wrap' menu.
def on_wrap_none_menu_item_activate(self, menuitem, data=None):
self.wrapping = 'None'
self.text_view.set_wrap_mode(gtk.WRAP_NONE);
def on_wrap_char_menu_item_activate(self, menuitem, data=None):
self.wrapping = 'Character'
self.text_view.set_wrap_mode(gtk.WRAP_CHAR);
def on_wrap_word_menu_item_activate(self, menuitem, data=None):
self.wrapping = 'Word'
self.text_view.set_wrap_mode(gtk.WRAP_WORD);
def on_select_font_menu_item_activate(self,menuitem, data=None):
if not self.font_dialog:
window = gtk.FontSelectionDialog("Font Selection Dialog")
self.font_dialog = window
window.set_position(gtk.WIN_POS_MOUSE)
window.set_transient_for(self.window)
window.connect("destroy", self.font_dialog_destroyed)
window.ok_button.connect("clicked", self.font_selection_ok)
window.cancel_button.connect_object("clicked", lambda wid: wid.destroy(), self.font_dialog)
current_font = self.text_view.get_pango_context().get_font_description().to_string()
window.set_font_name(current_font)
window = self.font_dialog
if not (window.flags() & gtk.VISIBLE):
window.show()
else:
window.destroy()
self.font_dialog = None
def font_selection_ok(self, button):
self.font = self.font_dialog.get_font_name()
if self.window:
font_desc = pango.FontDescription(self.font)
if font_desc:
self.text_view.modify_font(font_desc)
self.font_dialog.destroy()
def font_dialog_destroyed(self, data=None):
self.font_dialog = None
#emitted when the modified flag of the buffer changes
def on_text_buffer_modified_changed(self, buff, data=None):
self.mark_window_status(self.window,buff.get_modified())
#emitted BEFORE the text is actually inserted into the buffer
def on_text_buffer_insert_text(self, buff, iter, text, length, data=None):
# for change highlighting. just save the edit location and length here.
# the actual highlight is applied in on_text_buffer_changed()
self.edit_loc = iter.get_offset()
self.edit_len = length
# remove any find highlighting we've done
if buff.get_tag_table().lookup("search_results"):
buff.remove_tag_by_name('search_results', buff.get_start_iter(), buff.get_end_iter())
#emitted AFTER text is inserted into the buffer
#here we retrieve the location and apply the highlight tag
def on_text_buffer_changed(self, buff,data=None):
# we set edit_loc to -1 on deletions
if self.edit_loc >=0:
start_iter = buff.get_iter_at_offset(self.edit_loc)
start_mark = buff.create_mark(None,start_iter)
end_iter = buff.get_iter_at_offset(self.edit_loc + self.edit_len)
end_mark = buff.create_mark(None,end_iter)
self.mark_range_as_changed(buff,start_mark,end_mark)
buff.delete_mark(start_mark)
buff.delete_mark(end_mark)
#set the edit location to -1 here so on_text_buffer_changed() doesn't
#try to set attributes on a deleted range
def on_text_buffer_delete_range(self,buff,start,end, data=None):
self.edit_loc = -1
#def on_text_buffer_mark_set(self, textbuffer, iter, textmark, data=None):
# if textmark.get_name() == 'insert':
# self.move_replace_cursor(textbuffer,iter)
# We call error_message() any time we want to display an error message to
# the user. It will both show an error dialog and log the error to the
# terminal window.
def error_message(self, message):
# log to terminal window
self.log(message)
# create an error message dialog and display modally to the user
dialog = gtk.MessageDialog(None,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message)
dialog.run()
dialog.destroy()
# This function will check to see if the text buffer has been
# modified and prompt the user to save if it has been modified.
def check_for_save (self):
ret = False
buff = self.text_view.get_buffer()
if buff.get_modified():
# we need to prompt for save
message = "Do you want to save the changes you have made?"
dialog = gtk.MessageDialog(self.window,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
message)
dialog.set_title("Save?")
if dialog.run() == gtk.RESPONSE_NO: ret = False
else: ret = True
dialog.destroy()
return ret
# We call get_open_filename() when we want to get a filename to open from the
# user. It will present the user with a file chooser dialog and return the
# filename or None.
def get_open_filename(self):
filename = None
chooser = gtk.FileChooserDialog("Open File...", self.window,
gtk.FILE_CHOOSER_ACTION_OPEN,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
response = chooser.run()
if response == gtk.RESPONSE_OK: filename = chooser.get_filename()
chooser.destroy()
return filename
# We call get_save_filename() when we want to get a filename to save from the
# user. It will present the user with a file chooser dialog and return the
# filename or None.
def get_save_filename(self):
filename = None
chooser = gtk.FileChooserDialog("Save File...", self.window,
gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK))
response = chooser.run()
if response == gtk.RESPONSE_OK: filename = chooser.get_filename()
#if response == gtk.RESPONSE_CANCEL: print "save cancelled"
chooser.destroy()
return filename
###
# do_* = controller methods called from on_* actions
###
# find
def get_find_iter(self,buffer=None, backwards=False,limit_iter=None):
find_iter = None
selection = buffer.get_selection_bounds()
if selection == ():
find_iter = buffer.get_iter_at_mark(buffer.get_insert())
if find_iter == None:
find_iter = buffer.get_bounds()[int(backwards)]
else:
find_iter = selection[int(not backwards)]
return find_iter
def do_find(self,buffer=None,iter=None,backwards=False,case_sensitive=False,whole_word=False):
if self.search_string == "":
# show the ui
self.on_show_find_menu_item_activate(buffer)
return
find_iter = iter
if find_iter == None:
find_iter = self.get_find_iter(buffer=buffer, backwards=backwards,limit_iter=None)
results = self.do_find_with_iter(find_iter, self.search_string, backwards=backwards, case_sensitive=case_sensitive,whole_word=whole_word)
if results == None:
#nothing else found. ask to wrap.
self.log( "\'%s\' not found"%(self.search_string))
if self.ask_for_wrap(backwards=backwards):
self.log("wrapping")
wrapped_iter = buffer.get_bounds()[int(backwards)]
results = self.do_find_with_iter(wrapped_iter, self.search_string, backwards=backwards, case_sensitive=case_sensitive,whole_word=whole_word)
if results:
ins, bound = results
buffer.select_range(ins,bound)
self.text_view.scroll_to_mark(mark=buffer.get_insert(),within_margin=0.33,use_align=True,xalign=0.5,yalign=0.5)
else:
# really not found. beep.
gtk.gdk.beep()
else:
#happily select and highlight
ins, bound = results
buffer.select_range(ins,bound)
self.text_view.scroll_to_mark(mark=buffer.get_insert(),within_margin=0.33,use_align=True,xalign=0.5,yalign=0.5)
return results
def do_find_with_iter(self, iter=None, search_for=None,backwards=False,case_sensitive=False,whole_word=False):
def next_match():
results = None
search_flags = 0
if using_source_view:
if not self.search_case_sensitive:
search_flags = gtksourceview2.SEARCH_CASE_INSENSITIVE
if backwards:
results = gtksourceview2.iter_backward_search(iter, search_for, flags=search_flags)#, limit=limit_iter)
else:
results = gtksourceview2.iter_forward_search(iter, search_for, flags=search_flags)#, limit=limit_iter)
else:
if backwards:
results = iter.backward_search(search_for, flags=search_flags)#, limit=limit_iter)
else:
results = iter.forward_search(search_for, flags=search_flags)#, limit=limit_iter)
return results
#test for whole word , case
def next_whole_word():
word_hit = None
whole_word_hit = False
while not whole_word_hit:
word_hit = next_match()
if word_hit:
if word_hit[0].starts_word() and word_hit[1].ends_word():
#hit = result
whole_word_hit = True
return word_hit
else:
if not backwards:
iter.forward_chars(len(search_for))
else:
iter.backward_chars(len(search_for))
#print iter.get_offset()
word_hit = next_match()
else:
return None
return word_hit
result = next_match()
# test substring result for conditions
if self.search_is_whole_word:
hit = next_whole_word()
else:
hit = result
return hit
def ask_for_wrap(self,backwards=False):
ret = False
direction = 'beginning'
if backwards:
direction = 'end'
message = "\'%s\' not found. Wrap and start from %s of document?"%(self.search_string,direction)
dialog = gtk.MessageDialog(self.window,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
message)
dialog.set_title("Wrap Search?")
if dialog.run() == gtk.RESPONSE_NO: ret = False
else: ret = True
dialog.destroy()
return ret
def ask_for_replace(self):
ret = False
message = "Replace?"
dialog = gtk.MessageDialog(self.window,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
message)
dialog.set_title("Replace?")
if dialog.run() == gtk.RESPONSE_NO: ret = False
else: ret = True
dialog.destroy()
return ret
def do_find_all(self,buffer=None,case_sensitive=False,whole_word=False):
start_iter = buffer.get_start_iter()
end_iter = buffer.get_end_iter()
begin_mark = buffer.create_mark(None,start_iter)
end_mark = buffer.create_mark(None,end_iter)
search_results = self.find_all_in_range(buffer, self.search_string, begin_mark,end_mark)
buffer.delete_mark(begin_mark)
buffer.delete_mark(end_mark)
if not buffer.get_tag_table().lookup("search_results"):
preview_tag = buffer.create_tag("search_results", background_set="True",background=CONF_FIND_HIGHLIGHT)
else:
buffer.remove_tag_by_name('search_results', start_iter, end_iter)
if search_results:
for range in search_results:
if self.search_is_whole_word:
if range[0].starts_word() and range[1].ends_word():
buffer.apply_tag_by_name("search_results",range[0],range[1])
else:
buffer.apply_tag_by_name("search_results",range[0],range[1])
def do_replace_next(self,buffer=None,iter=None,search_for='',replace_with='',limit=None):
buffer.remove_tag_by_name('search_results', buffer.get_start_iter(), buffer.get_end_iter())
replace_iter = iter
range = self.do_find(buffer=buffer,iter=replace_iter,backwards=False,case_sensitive=self.search_case_sensitive,whole_word=self.search_is_whole_word)
if range:
if not buffer.get_tag_table().lookup("search_results"):
preview_tag = buffer.create_tag("search_results", background_set="True",background=CONF_FIND_HIGHLIGHT)
#apply tag
buffer.apply_tag_by_name("search_results",range[0],range[1])
if not self.ask_for_replace():
self.log("replace declined")
return
replace_begin = buffer.create_mark(None,range[0])
replace_end = buffer.create_mark(None,range[1])
replace_list = [(replace_begin,replace_end)]
replaced_range = self.replace_text(buffer,replace_with,replace_list)
self.text_view.scroll_to_mark(replace_end,0)
buffer.delete_mark(replace_begin)
buffer.delete_mark(replace_end)
def do_replace_all(self,buffer,search_for,replace_with):
#search_results = self.search_for_text(buffer,search_for,buffer.get_start_iter(),buffer.get_end_iter())
replace_begin = buffer.create_mark(None,buffer.get_start_iter())
replace_end = buffer.create_mark(None,buffer.get_end_iter())
search_results = self.find_all_in_range(buffer,search_for,replace_begin,replace_end)
result_marks = []
for iter_pair in search_results:
this_mark_pair = []
for iter in iter_pair:
this_mark = buffer.create_mark(None,iter)
this_mark_pair.append(this_mark)
self.edit_loc = iter_pair[0]#wft
result_marks.append(this_mark_pair)
self.replace_text(buffer,replace_with,result_marks)
for that_mark_pair in result_marks:
for that_mark in that_mark_pair:
buffer.delete_mark(that_mark)
buffer.delete_mark(replace_begin)
buffer.delete_mark(replace_end)
def find_all_in_range(self, buff, search_string, start_mark, end_mark):
start_iter = buff.get_iter_at_mark(start_mark)
end_iter = buff.get_iter_at_mark(end_mark)
search_results = []
next_result = self.do_find_with_iter(iter=start_iter,search_for=self.search_string,backwards=False,case_sensitive=self.search_case_sensitive,whole_word=self.search_is_whole_word)
while next_result: