Created
May 13, 2009 10:20
-
-
Save yurenju/110962 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: iso-8859-1 -*- | |
import pygtk | |
pygtk.require('2.0') | |
import sys, os, errno | |
import gtk | |
import pango | |
RESPONSE_FORWARD = 0 | |
RESPONSE_BACKWARD = 1 | |
book_closed_xpm = [ | |
"16 16 6 1", | |
" c None s None", | |
". c black", | |
"X c red", | |
"o c yellow", | |
"O c #808080", | |
"# c white", | |
" ", | |
" .. ", | |
" ..XX. ", | |
" ..XXXXX. ", | |
" ..XXXXXXXX. ", | |
".ooXXXXXXXXX. ", | |
"..ooXXXXXXXXX. ", | |
".X.ooXXXXXXXXX. ", | |
".XX.ooXXXXXX.. ", | |
" .XX.ooXXX..#O ", | |
" .XX.oo..##OO. ", | |
" .XX..##OO.. ", | |
" .X.#OO.. ", | |
" ..O.. ", | |
" .. ", | |
" "] | |
def hsv_to_rgb(h, s, v): | |
if s == 0.0: | |
return (v, v, v) | |
else: | |
hue = h * 6.0 | |
saturation = s | |
value = v | |
if hue >= 6.0: | |
hue = 0.0 | |
f = hue - int(hue) | |
p = value * (1.0 - saturation) | |
q = value * (1.0 - saturation * f) | |
t = value * (1.0 - saturation * (1.0 - f)) | |
ihue = int(hue) | |
if ihue == 0: | |
return(value, t, p) | |
elif ihue == 1: | |
return(q, value, p) | |
elif ihue == 2: | |
return(p, value, t) | |
elif ihue == 3: | |
return(p, q, value) | |
elif ihue == 4: | |
return(t, p, value) | |
elif ihue == 5: | |
return(value, p, q) | |
def hue_to_color(hue): | |
if hue > 1.0: | |
raise ValueError | |
h, s, v = hsv_to_rgb (hue, 1.0, 1.0) | |
return (h*65535, s*65535, v*65535) | |
class FileSel(gtk.FileSelection): | |
def __init__(self): | |
gtk.FileSelection.__init__(self) | |
self.result = False | |
def ok_cb(self, button): | |
self.hide() | |
if self.ok_func(self.get_filename()): | |
self.destroy() | |
self.result = True | |
else: | |
self.show() | |
def run(self, parent, title, start_file, func): | |
if start_file: | |
self.set_filename(start_file) | |
self.ok_func = func | |
self.ok_button.connect("clicked", self.ok_cb) | |
self.cancel_button.connect("clicked", lambda x: self.destroy()) | |
self.connect("destroy", lambda x: gtk.main_quit()) | |
self.set_modal(True) | |
self.show() | |
gtk.main() | |
return self.result | |
class Buffer(gtk.TextBuffer): | |
N_COLORS = 16 | |
PANGO_SCALE = 1024 | |
def __init__(self): | |
gtk.TextBuffer.__init__(self) | |
tt = self.get_tag_table() | |
self.refcount = 0 | |
self.filename = None | |
self.untitled_serial = -1 | |
self.color_tags = [] | |
self.color_cycle_timeout_id = 0 | |
self.start_hue = 0.0 | |
for i in range(Buffer.N_COLORS): | |
tag = self.create_tag() | |
self.color_tags.append(tag) | |
#self.invisible_tag = self.create_tag(None, invisible=True) | |
self.not_editable_tag = self.create_tag(editable=False, | |
foreground="purple") | |
self.found_text_tag = self.create_tag(foreground="red") | |
tabs = pango.TabArray(4, True) | |
tabs.set_tab(0, pango.TAB_LEFT, 10) | |
tabs.set_tab(1, pango.TAB_LEFT, 30) | |
tabs.set_tab(2, pango.TAB_LEFT, 60) | |
tabs.set_tab(3, pango.TAB_LEFT, 120) | |
self.custom_tabs_tag = self.create_tag(tabs=tabs, foreground="green") | |
TestText.buffers.push(self) | |
def pretty_name(self): | |
if self.filename: | |
return os.path.basename(self.filename) | |
else: | |
if self.untitled_serial == -1: | |
self.untitled_serial = TestText.untitled_serial | |
TestText.untitled_serial += 1 | |
if self.untitled_serial == 1: | |
return "Untitled" | |
else: | |
return "Untitled #%d" % self.untitled_serial | |
def filename_set(self): | |
for view in TestText.views: | |
if view.text_view.get_buffer() == self: | |
view.set_view_title() | |
def search(self, str, view, forward): | |
# remove tag from whole buffer | |
start, end = self.get_bounds() | |
self.remove_tag(self.found_text_tag, start, end) | |
iter = self.get_iter_at_mark(self.get_insert()) | |
i = 0 | |
if str: | |
if forward: | |
while 1: | |
res = iter.forward_search(str, gtk.TEXT_SEARCH_TEXT_ONLY) | |
if not res: | |
break | |
match_start, match_end = res | |
i += 1 | |
self.apply_tag(self.found_text_tag, match_start, match_end) | |
iter = match_end | |
else: | |
while 1: | |
res = iter.backward_search(str, gtk.TEXT_SEARCH_TEXT_ONLY) | |
if not res: | |
break | |
match_start, match_end = res | |
i += 1 | |
self.apply_tag(self.found_text_tag, match_start, match_end) | |
iter = match_start | |
dialog = gtk.MessageDialog(view, | |
gtk.DIALOG_DESTROY_WITH_PARENT, | |
gtk.MESSAGE_INFO, | |
gtk.BUTTONS_OK, | |
"%d strings found and marked in red" % i) | |
dialog.connect("response", lambda x,y: dialog.destroy()) | |
dialog.show() | |
def search_forward(self, str, view): | |
self.search(str, view, True) | |
def search_backward(self, str, view): | |
self.search(str, view, False) | |
def ref(self): | |
self.refcount += 1 | |
def unref(self): | |
self.refcount -= 1 | |
if self.refcount == 0: | |
self.set_colors(False) | |
TestText.buffers.remove(self) | |
del self | |
def color_cycle_timeout(self): | |
self.cycle_colors() | |
return True | |
def set_colors(self, enabled): | |
hue = 0.0 | |
if (enabled and self.color_cycle_timeout_id == 0): | |
self.color_cycle_timeout_id = gtk.timeout_add( | |
200, self.color_cycle_timeout) | |
elif (not enabled and self.color_cycle_timeout_id != 0): | |
gtk.timeout_remove(self.color_cycle_timeout_id) | |
self.color_cycle_timeout_id = 0 | |
for tag in self.color_tags: | |
if enabled: | |
color = apply(TestText.colormap.alloc_color, | |
hue_to_color(hue)) | |
tag.set_property("foreground_gdk", color) | |
else: | |
tag.set_property("foreground_set", False) | |
hue += 1.0 / Buffer.N_COLORS | |
def cycle_colors(self): | |
hue = self.start_hue | |
for tag in self.color_tags: | |
color = apply(TestText.colormap.alloc_color, | |
hue_to_color (hue)) | |
tag.set_property("foreground_gdk", color) | |
hue += 1.0 / Buffer.N_COLORS | |
if hue > 1.0: | |
hue = 0.0 | |
self.start_hue += 1.0 / Buffer.N_COLORS | |
if self.start_hue > 1.0: | |
self.start_hue = 0.0 | |
def tag_event_handler(self, tag, widget, event, iter): | |
char_index = iter.get_offset() | |
tag_name = tag.get_property("name") | |
if event.type == gtk.gdk.MOTION_NOTIFY: | |
print "Motion event at char %d tag `%s'\n" % (char_index, tag_name) | |
elif event.type == gtk.gdk.BUTTON_PRESS: | |
print "Button press at char %d tag `%s'\n" % (char_index, tag_name) | |
elif event.type == gtk.gdk._2BUTTON_PRESS: | |
print "Double click at char %d tag `%s'\n" % (char_index, tag_name) | |
elif event.type == gtk.gdk._3BUTTON_PRESS: | |
print "Triple click at char %d tag `%s'\n" % (char_index, tag_name) | |
elif event.type == gtk.gdk.BUTTON_RELEASE: | |
print "Button release at char %d tag `%s'\n" % (char_index, tag_name) | |
elif (event.type == gtk.gdk.KEY_PRESS or | |
event.type == gtk.gdk.KEY_RELEASE): | |
print "Key event at char %d tag `%s'\n" % (char_index, tag_name) | |
return False | |
def init_tags(self): | |
colormap = TestText.colormap | |
color = colormap.alloc_color(0, 0, 0xffff) | |
tag = self.create_tag("fg_blue", | |
foreground_gdk=color, | |
background='yellow', | |
size_points=24.0) | |
tag.connect("event", self.tag_event_handler) | |
color = colormap.alloc_color(0xffff, 0, 0) | |
tag = self.create_tag("fg_red", | |
rise= -4*Buffer.PANGO_SCALE, | |
foreground_gdk=color) | |
tag.connect("event", self.tag_event_handler) | |
color = colormap.alloc_color(0, 0xffff, 0) | |
tag = self.create_tag("bg_green", | |
background_gdk=color, | |
size_points=10.0) | |
tag.connect("event", self.tag_event_handler) | |
tag = self.create_tag("strikethrough", | |
strikethrough=True) | |
tag.connect("event", self.tag_event_handler) | |
tag = self.create_tag("underline", | |
underline=pango.UNDERLINE_SINGLE) | |
tag.connect("event", self.tag_event_handler) | |
tag = self.create_tag("centered", | |
justification=gtk.JUSTIFY_CENTER) | |
tag = self.create_tag("rtl_quote", | |
wrap_mode=gtk.WRAP_WORD, | |
direction=gtk.TEXT_DIR_RTL, | |
indent=30, | |
left_margin=20, | |
right_margin=20) | |
tag = self.create_tag("negative_indent", | |
indent=-25) | |
def fill_example_buffer(self): | |
tagtable = self.get_tag_table() | |
if not tagtable.lookup("fg_blue"): | |
self.init_tags() | |
iter = self.get_iter_at_offset(0) | |
anchor = self.create_child_anchor(iter) | |
self.set_data("anchor", anchor) | |
pixbuf = gtk.gdk.pixbuf_new_from_xpm_data(book_closed_xpm) | |
#pixbuf = gtk.gdk.pixbuf_new_from_file('book_closed.xpm') | |
for i in range(100): | |
iter = self.get_iter_at_offset(0) | |
self.insert_pixbuf(iter, pixbuf) | |
str = "%d Hello World! blah blah blah blah blah blah blah blah blah blah blah blah\nwoo woo woo woo woo woo woo woo woo woo woo woo woo woo woo\n" % i | |
self.insert(iter, str) | |
iter = self.get_iter_at_line_offset(0, 5) | |
self.insert(iter, | |
"(Hello World!)\nfoo foo Hello this is some text we are using to text word wrap. It has punctuation! gee; blah - hmm, great.\nnew line with a significant quantity of text on it. This line really does contain some text. More text! More text! More text!\n" | |
"German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew(שלום) Hebrew punctuation(\xd6\xbfש\xd6\xbb\xd6\xbc\xd6\xbb\xd6\xbfל\xd6\xbcו\xd6\xbc\xd6\xbb\xd6\xbb\xd6\xbfם\xd6\xbc\xd6\xbb\xd6\xbf) Japanese (日本語) Thai (สวัสดีครับ) Thai wrong spelling (คำต่อไปนื่สะกดผิด พัั้ัั่งโกะ)\n") | |
temp_mark = self.create_mark("tmp_mark", iter, True); | |
iter = self.get_iter_at_line_offset(0, 6) | |
iter2 = self.get_iter_at_line_offset(0, 13) | |
self.apply_tag_by_name("fg_blue", iter, iter2) | |
iter = self.get_iter_at_line_offset(1, 10) | |
iter2 = self.get_iter_at_line_offset(1, 16) | |
self.apply_tag_by_name("underline", iter, iter2) | |
iter = self.get_iter_at_line_offset(1, 14) | |
iter2 = self.get_iter_at_line_offset(1, 24) | |
self.apply_tag_by_name("strikethrough", iter, iter2) | |
iter = self.get_iter_at_line_offset(0, 9) | |
iter2 = self.get_iter_at_line_offset(0, 16) | |
self.apply_tag_by_name("bg_green", iter, iter2) | |
iter = self.get_iter_at_line_offset(4, 2) | |
iter2 = self.get_iter_at_line_offset(4, 10) | |
self.apply_tag_by_name("bg_green", iter, iter2) | |
iter = self.get_iter_at_line_offset(4, 8) | |
iter2 = self.get_iter_at_line_offset(4, 15) | |
self.apply_tag_by_name("fg_red", iter, iter2) | |
iter = self.get_iter_at_mark(temp_mark) | |
self.insert(iter, "Centered text!\n") | |
iter2 = self.get_iter_at_mark(temp_mark) | |
self.apply_tag_by_name("centered", iter2, iter) | |
self.move_mark(temp_mark, iter) | |
self.insert(iter, "Word wrapped, Right-to-left Quote\n") | |
self.insert(iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n") | |
iter2 = self.get_iter_at_mark(temp_mark) | |
self.apply_tag_by_name("rtl_quote", iter2, iter) | |
self.insert_with_tags(iter, | |
"Paragraph with negative indentation. blah blah blah blah blah. The quick brown fox jumped over the lazy dog.\n", | |
self.get_tag_table().lookup("negative_indent")) | |
print "%d lines %d chars\n" % (self.get_line_count(), | |
self.get_char_count()) | |
# Move cursor to start | |
iter = self.get_iter_at_offset(0) | |
self.place_cursor(iter) | |
self.set_modified(False) | |
def fill_file_buffer(self, filename): | |
try: | |
f = open(filename, "r") | |
except IOError, (errnum, errmsg): | |
err = "Cannot open file '%s': %s" % (filename, errmsg) | |
view = TestText.active_window_stack.get() | |
dialog = gtk.MessageDialog(view, gtk.DIALOG_MODAL, | |
gtk.MESSAGE_INFO, | |
gtk.BUTTONS_OK, err); | |
result = dialog.run() | |
dialog.destroy() | |
return False | |
iter = self.get_iter_at_offset(0) | |
buf = f.read() | |
f.close() | |
self.set_text(buf) | |
self.set_modified(False) | |
return True | |
def save_buffer(self): | |
result = False | |
have_backup = False | |
if not self.filename: | |
return False | |
bak_filename = self.filename + "~" | |
try: | |
os.rename(self.filename, bak_filename) | |
except (OSError, IOError), (errnum, errmsg): | |
if errnum != errno.ENOENT: | |
err = "Cannot back up '%s' to '%s': %s" % (self.filename, | |
bak_filename, | |
errmsg) | |
view = TestText.active_window_stack.get() | |
dialog = gtk.MessageDialog(view, gtk.DIALOG_MODAL, | |
gtk.MESSAGE_INFO, | |
gtk.BUTTONS_OK, err); | |
dialog.run() | |
dialog.destroy() | |
return False | |
have_backup = True | |
start, end = self.get_bounds() | |
chars = self.get_slice(start, end, False) | |
try: | |
file = open(self.filename, "w") | |
file.write(chars) | |
file.close() | |
result = True | |
self.set_modified(False) | |
except IOError, (errnum, errmsg): | |
err = "Error writing to '%s': %s" % (self.filename, errmsg) | |
view = TestText.active_window_stack.get() | |
dialog = gtk.MessageDialog(view, gtk.DIALOG_MODAL, | |
gtk.MESSAGE_INFO, | |
gtk.BUTTONS_OK, err); | |
dialog.run() | |
dialog.destroy() | |
if not result and have_backup: | |
try: | |
os.rename(bak_filename, self.filename) | |
except OSError, (errnum, errmsg): | |
err = "Can't restore backup file '%s' to '%s': %s\nBackup left as '%s'" % ( | |
self.filename, bak_filename, errmsg, bak_filename) | |
view = TestText.active_window_stack.get() | |
dialog = gtk.MessageDialog(view, gtk.DIALOG_MODAL, | |
gtk.MESSAGE_INFO, | |
gtk.BUTTONS_OK, err); | |
dialog.run() | |
dialog.destroy() | |
return result | |
def save_as_ok_func(self, filename): | |
old_filename = self.filename | |
if (not self.filename or filename != self.filename): | |
if os.path.exists(filename): | |
err = "Ovewrite existing file '%s'?" % filename | |
view = TestText.active_window_stack.get() | |
dialog = gtk.MessageDialog(view, gtk.DIALOG_MODAL, | |
gtk.MESSAGE_QUESTION, | |
gtk.BUTTONS_YES_NO, err); | |
result = dialog.run() | |
dialog.destroy() | |
if result != gtk.RESPONSE_YES: | |
return False | |
self.filename = filename | |
if self.save_buffer(): | |
self.filename_set() | |
return True | |
else: | |
self.filename = old_filename | |
return False | |
def save_as_buffer(self): | |
return FileSel().run(self, "Save File", None, self.save_as_ok_func) | |
def check_buffer_saved(self): | |
if self.get_modified(): | |
pretty_name = self.pretty_name() | |
msg = "Save changes to '%s'?" % pretty_name | |
view = TestText.active_window_stack.get() | |
dialog = gtk.MessageDialog(view, gtk.DIALOG_MODAL, | |
gtk.MESSAGE_QUESTION, | |
gtk.BUTTONS_YES_NO, msg); | |
dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) | |
result = dialog.run() | |
dialog.destroy() | |
if result == gtk.RESPONSE_YES: | |
if self.filename: | |
return self.save_buffer() | |
return self.save_as_buffer() | |
elif result == gtk.RESPONSE_NO: | |
return True | |
else: | |
return False | |
else: | |
return True | |
class View(gtk.Window): | |
def __init__(self, buffer=None): | |
menu_items = [ | |
( "/_File", None, None, 0, "<Branch>" ), | |
( "/File/_New", "<control>N", self.do_new, 0, None ), | |
( "/File/New _View", None, self.do_new_view, 0, None ), | |
( "/File/_Open", "<control>O", self.do_open, 0, None ), | |
( "/File/_Save", "<control>S", self.do_save, 0, None ), | |
( "/File/Save _As...", None, self.do_save_as, 0, None ), | |
( "/File/sep1", None, None, 0, "<Separator>" ), | |
( "/File/_Close", "<control>W" , self.do_close, 0, None ), | |
( "/File/E_xit", "<control>Q" , self.do_exit, 0, None ), | |
( "/_Edit", None, None, 0, "<Branch>" ), | |
( "/Edit/Find...", None, self.do_search, 0, None ), | |
( "/_Settings", None, None, 0, "<Branch>" ), | |
( "/Settings/Wrap _Off", None, self.do_wrap_changed, gtk.WRAP_NONE, "<RadioItem>" ), | |
( "/Settings/Wrap _Words", None, self.do_wrap_changed, gtk.WRAP_WORD, "/Settings/Wrap Off" ), | |
( "/Settings/Wrap _Chars", None, self.do_wrap_changed, gtk.WRAP_CHAR, "/Settings/Wrap Off" ), | |
( "/Settings/sep1", None, None, 0, "<Separator>" ), | |
( "/Settings/Editable", None, self.do_editable_changed, True, "<RadioItem>" ), | |
( "/Settings/Not editable", None, self.do_editable_changed, False, "/Settings/Editable" ), | |
( "/Settings/sep1", None, None, 0, "<Separator>" ), | |
( "/Settings/Cursor visible", None, self.do_cursor_visible_changed, True, "<RadioItem>" ), | |
( "/Settings/Cursor not visible", None, self.do_cursor_visible_changed, False, "/Settings/Cursor visible" ), | |
( "/Settings/sep1", None, None, 0, "<Separator>" ), | |
( "/Settings/Left-to-Right", None, self.do_direction_changed, gtk.TEXT_DIR_LTR, "<RadioItem>" ), | |
( "/Settings/Right-to-Left", None, self.do_direction_changed, gtk.TEXT_DIR_RTL, "/Settings/Left-to-Right" ), | |
( "/Settings/sep1", None, None, 0, "<Separator>" ), | |
( "/Settings/Sane spacing", None, self.do_spacing_changed, False, "<RadioItem>" ), | |
( "/Settings/Funky spacing", None, self.do_spacing_changed, True, "/Settings/Sane spacing" ), | |
( "/Settings/sep1", None, None, 0, "<Separator>" ), | |
( "/Settings/Don't cycle color tags", None, self.do_color_cycle_changed, False, "<RadioItem>" ), | |
( "/Settings/Cycle colors", None, self.do_color_cycle_changed, True, "/Settings/Don't cycle color tags" ), | |
( "/_Attributes", None, None, 0, "<Branch>" ), | |
( "/Attributes/Editable", None, self.do_apply_editable, True, None ), | |
( "/Attributes/Not editable", None, self.do_apply_editable, False, None ), | |
( "/Attributes/Invisible", None, self.do_apply_invisible, False, None ), | |
( "/Attributes/Visible", None, self.do_apply_invisible, True, None ), | |
( "/Attributes/Custom tabs", None, self.do_apply_tabs, False, None ), | |
( "/Attributes/Default tabs", None, self.do_apply_tabs, True, None ), | |
( "/Attributes/Color cycles", None, self.do_apply_colors, True, None ), | |
( "/Attributes/No colors", None, self.do_apply_colors, False, None ), | |
( "/Attributes/Remove all tags", None, self.do_remove_tags, 0, None ), | |
( "/Attributes/Properties", None, self.do_properties, 0, None ), | |
( "/_Test", None, None, 0, "<Branch>" ), | |
( "/Test/_Example", None, self.do_example, 0, None ), | |
( "/Test/_Insert and scroll", None, self.do_insert_and_scroll, 0, None ), | |
( "/Test/_Add movable children", None, self.do_add_children, 0, None ), | |
( "/Test/A_dd focusable children", None, self.do_add_focus_children, 0, None ), | |
] | |
if not buffer: | |
buffer = Buffer() | |
gtk.Window.__init__(self) | |
TestText.views.push(self) | |
buffer.ref() | |
if not TestText.colormap: | |
TestText.colormap = self.get_colormap() | |
self.connect("delete_event", self.delete_event_cb) | |
self.accel_group = gtk.AccelGroup() | |
self.item_factory = gtk.ItemFactory(gtk.MenuBar, "<main>", | |
self.accel_group) | |
self.item_factory.set_data("view", self) | |
self.item_factory.create_items(menu_items) | |
self.add_accel_group(self.accel_group) | |
vbox = gtk.VBox(False, 0) | |
self.add(vbox) | |
vbox.pack_start(self.item_factory.get_widget("<main>"), | |
False, False, 0) | |
sw = gtk.ScrolledWindow() | |
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) | |
self.text_view = gtk.TextView(buffer) | |
self.text_view.set_wrap_mode(gtk.WRAP_WORD) | |
# Make sure border width works, no real reason to do this other | |
# than testing | |
self.text_view.set_border_width(10) | |
# Draw tab stops in the top and bottom windows. | |
self.text_view.set_border_window_size(gtk.TEXT_WINDOW_TOP, 15) | |
self.text_view.set_border_window_size(gtk.TEXT_WINDOW_BOTTOM, 15) | |
self.text_view.connect("expose_event", self.tab_stops_expose) | |
self.bhid = buffer.connect("mark_set", self.cursor_set_callback) | |
# Draw line numbers in the side windows; we should really be | |
# more scientific about what width we set them to. | |
self.text_view.set_border_window_size(gtk.TEXT_WINDOW_RIGHT, 30) | |
self.text_view.set_border_window_size(gtk.TEXT_WINDOW_LEFT, 30) | |
self.text_view.connect("expose_event", self.line_numbers_expose) | |
vbox.pack_start(sw, True, True, 0) | |
sw.add(self.text_view) | |
self.set_default_size(500, 500) | |
self.text_view.grab_focus() | |
self.set_view_title() | |
self.init_menus() | |
self.add_example_widgets() | |
self.show_all() | |
def delete_event_cb(self, window, event, data=None): | |
TestText.active_window_stack.push(self) | |
self.check_close_view() | |
TestText.active_window_stack.pop() | |
return True | |
# | |
# Menu callbacks | |
# | |
def get_empty_view(self): | |
buffer = self.text_view.get_buffer() | |
if (not buffer.filename and not buffer.get_modified()): | |
return self | |
else: | |
return View(Buffer()) | |
def view_from_widget(widget): | |
if isinstance(widget, gtk.MenuItem): | |
item_factory = gtk.item_factory_from_widget(widget) | |
return item_factory.get_data("view") | |
else: | |
app = widget.get_toplevel() | |
return app.get_data("view") | |
def do_new(self, callback_action, widget): | |
View() | |
def do_new_view(self, callback_action, widget): | |
View(self.text_view.get_buffer()) | |
def open_ok_func(self, filename): | |
new_view = self.get_empty_view() | |
buffer = new_view.text_view.get_buffer() | |
if not buffer.fill_file_buffer(filename): | |
if new_view != self: | |
new_view.close_view() | |
return False | |
else: | |
buffer.filename = filename | |
buffer.filename_set() | |
return True; | |
def do_open(self, callback_action, widget): | |
FileSel().run(self, "Open File", None, self.open_ok_func) | |
def do_save_as(self, callback_action, widget): | |
TestText.active_window_stack.push(self) | |
self.text_view.get_buffer().save_as_buffer() | |
TestText.active_window_stack.pop() | |
def do_save(self, callback_action, widget): | |
TestText.active_window_stack.push(self) | |
buffer = self.text_view.get_buffer() | |
if not buffer.filename: | |
self.do_save_as(callback_data, callback_action) | |
else: | |
buffer.save_buffer() | |
TestText.active_window_stack.pop() | |
def do_close(self, callback_action, widget): | |
TestText.active_window_stack.push(self) | |
self.check_close_view() | |
TestText.active_window_stack.pop() | |
def do_exit(self, callback_action, widget): | |
TestText.active_window_stack.push(self) | |
for tmp in TestText.buffers: | |
if not tmp.check_buffer_saved(): | |
return | |
gtk.main_quit() | |
TestText.active_window_stack.pop() | |
def do_example(self, callback_action, widget): | |
new_view = self.get_empty_view() | |
buffer = new_view.text_view.get_buffer() | |
buffer.fill_example_buffer() | |
new_view.add_example_widgets() | |
def do_insert_and_scroll(self, callback_action, widget): | |
buffer = self.text_view.get_buffer() | |
start, end = buffer.get_bounds() | |
mark = buffer.create_mark(None, end, False) | |
buffer.insert(end, | |
"Hello this is multiple lines of text\n" | |
"Line 1\n" "Line 2\n" | |
"Line 3\n" "Line 4\n" | |
"Line 5\n") | |
self.text_view.scroll_to_mark(mark, 0, True, 0.0, 1.0) | |
buffer.delete_mark(mark) | |
def do_wrap_changed(self, callback_action, widget): | |
self.text_view.set_wrap_mode(callback_action) | |
def do_direction_changed(self, callback_action, widget): | |
self.text_view.set_direction(callback_action) | |
self.text_view.queue_resize() | |
def do_spacing_changed(self, callback_action, widget): | |
if callback_action: | |
self.text_view.set_pixels_above_lines(23) | |
self.text_view.set_pixels_below_lines(21) | |
self.text_view.set_pixels_inside_wrap(9) | |
else: | |
self.text_view.set_pixels_above_lines(0) | |
self.text_view.set_pixels_below_lines(0) | |
self.text_view.set_pixels_inside_wrap(0) | |
def do_editable_changed(self, callback_action, widget): | |
self.text_view.set_editable(callback_action) | |
def do_cursor_visible_changed(self, callback_action, widget): | |
self.text_view.set_cursor_visible(callback_action) | |
def do_color_cycle_changed(self, callback_action, widget): | |
self.text_view.get_buffer().set_colors(callback_action) | |
def do_apply_editable(self, callback_action, widget): | |
buffer = self.text_view.get_buffer() | |
bounds = buffer.get_selection_bounds() | |
if bounds: | |
start, end = bounds | |
if callback_action: | |
buffer.remove_tag(buffer.not_editable_tag, start, end) | |
else: | |
buffer.apply_tag(buffer.not_editable_tag, start, end) | |
def do_apply_invisible(self, callback_action, widget): | |
buffer = self.text_view.get_buffer() | |
bounds = buffer.get_selection_bounds() | |
if bounds: | |
start, end = bounds | |
if callback_action: | |
buffer.remove_tag(buffer.invisible_tag, start, end) | |
else: | |
buffer.apply_tag(buffer.invisible_tag, start, end) | |
def do_apply_tabs(self, callback_action, widget): | |
buffer = self.text_view.get_buffer() | |
bounds = buffer.get_selection_bounds() | |
if bounds: | |
start, end = bounds | |
if callback_action: | |
buffer.remove_tag(buffer.custom_tabs_tag, start, end) | |
else: | |
buffer.apply_tag(buffer.custom_tabs_tag, start, end) | |
def do_apply_colors(self, callback_action, widget): | |
buffer = self.text_view.get_buffer() | |
bounds = buffer.get_selection_bounds() | |
if bounds: | |
start, end = bounds | |
if not callback_action: | |
for tag in buffer.color_tags: | |
buffer.remove_tag(tag, start, end) | |
else: | |
tmp = buffer.color_tags | |
i = 0 | |
next = start.copy() | |
while next.compare(end) < 0: | |
next.forward_chars(2) | |
if next.compare(end) >= 0: | |
next = end | |
buffer.apply_tag(tmp[i], start, next) | |
i += 1 | |
if i >= len(tmp): | |
i = 0 | |
start = next.copy() | |
def do_remove_tags(self, callback_action, widget): | |
buffer = self.text_view.get_buffer() | |
bounds = buffer.get_selection_bounds() | |
if bounds: | |
start, end = bounds | |
buffer.remove_all_tags(start, end) | |
def do_properties(self, callback_action, widget): | |
#create_prop_editor(view.text_view, 0) | |
pass | |
def dialog_response_callback(self, dialog, response_id): | |
if (response_id != RESPONSE_FORWARD and | |
response_id != RESPONSE_BACKWARD): | |
dialog.destroy() | |
return | |
start, end = dialog.buffer.get_bounds() | |
search_string = start.get_text(end) | |
print "Searching for `%s'\n" % search_string | |
buffer = self.text_view.get_buffer() | |
if response_id == RESPONSE_FORWARD: | |
buffer.search_forward(search_string, self) | |
elif response_id == RESPONSE_BACKWARD: | |
buffer.search_backward(search_string, self) | |
dialog.destroy() | |
def do_search(self, callback_action, widget): | |
search_text = gtk.TextView() | |
dialog = gtk.Dialog("Search", self, | |
gtk.DIALOG_DESTROY_WITH_PARENT, | |
("Forward", RESPONSE_FORWARD, | |
"Backward", RESPONSE_BACKWARD, | |
gtk.STOCK_CANCEL, gtk.RESPONSE_NONE)) | |
dialog.vbox.pack_end(search_text, True, True, 0) | |
dialog.buffer = search_text.get_buffer() | |
dialog.connect("response", self.dialog_response_callback) | |
search_text.show() | |
search_text.grab_focus() | |
dialog.show_all() | |
def movable_child_callback(self, child, event): | |
text_view = self.text_view | |
info = child.get_data("testtext-move-info") | |
if not info: | |
info = {} | |
info['start_x'] = -1 | |
info['start_y'] = -1 | |
info['button'] = -1 | |
child.set_data("testtext-move-info", info) | |
if event.type == gtk.gdk.BUTTON_PRESS: | |
if info['button'] < 0: | |
info['button'] = event.button | |
info['start_x'] = child.allocation.x | |
info['start_y'] = child.allocation.y | |
info['click_x'] = child.allocation.x + event.x | |
info['click_y'] = child.allocation.y + event.y | |
elif event.type == gtk.gdk.BUTTON_RELEASE: | |
if info['button'] < 0: | |
return False | |
if info['button'] == event.button: | |
info['button'] = -1; | |
# convert to window coords from event box coords | |
x = info['start_x'] + (event.x + child.allocation.x \ | |
- info['click_x']) | |
y = info['start_y'] + (event.y + child.allocation.y \ | |
- info['click_y']) | |
text_view.move_child(child, x, y) | |
elif gtk.gdk.MOTION_NOTIFY: | |
if info['button'] < 0: | |
return False | |
x, y = child.get_pointer() # ensure more events | |
# to window coords from event box coords | |
x += child.allocation.x | |
y += child.allocation.y | |
x = info['start_x'] + (x - info['click_x']) | |
y = info['start_y'] + (y - info['click_y']) | |
text_view.move_child(child, x, y) | |
return False | |
def add_movable_child(self, text_view, window): | |
label = gtk.Label("Drag me around") | |
event_box = gtk.EventBox() | |
event_box.add_events(gtk.gdk.BUTTON_PRESS_MASK | | |
gtk.gdk.BUTTON_RELEASE_MASK | | |
gtk.gdk.POINTER_MOTION_MASK | | |
gtk.gdk.POINTER_MOTION_HINT_MASK) | |
color = TestText.colormap.alloc_color(0xffff, 0, 0) | |
event_box.modify_bg(gtk.STATE_NORMAL, color) | |
event_box.add(label) | |
event_box.show_all() | |
event_box.connect("event", self.movable_child_callback) | |
text_view.add_child_in_window(event_box, window, 0, 0) | |
def do_add_children(self, callback_action, widget): | |
text_view = self.text_view | |
self.add_movable_child(text_view, gtk.TEXT_WINDOW_WIDGET) | |
self.add_movable_child(text_view, gtk.TEXT_WINDOW_LEFT) | |
self.add_movable_child(text_view, gtk.TEXT_WINDOW_RIGHT) | |
self.add_movable_child(text_view, gtk.TEXT_WINDOW_TEXT) | |
self.add_movable_child(text_view, gtk.TEXT_WINDOW_TOP) | |
self.add_movable_child(text_view, gtk.TEXT_WINDOW_BOTTOM) | |
def do_add_focus_children(self, callback_action, widget): | |
text_view = self.text_view | |
child = gtk.EventBox() | |
b = gtk.Button("Button _A in widget.window") | |
child.add(b) | |
text_view.add_child_in_window(child, gtk.TEXT_WINDOW_WIDGET, 0, 200) | |
child = gtk.EventBox() | |
b = gtk.Button("Button _B in text.window") | |
child.add(b) | |
text_view.add_child_in_window(child, gtk.TEXT_WINDOW_TEXT, 50, 50) | |
child = gtk.EventBox() | |
b = gtk.Button("Button _T in top window") | |
child.add(b) | |
text_view.add_child_in_window(child, gtk.TEXT_WINDOW_TOP, 100, 0) | |
child = gtk.EventBox() | |
b = gtk.Button("Button _W in bottom window") | |
child.add(b) | |
text_view.add_child_in_window(child, gtk.TEXT_WINDOW_BOTTOM, 100, 0) | |
child = gtk.EventBox() | |
b = gtk.Button("Button _C in left window") | |
child.add(b) | |
text_view.add_child_in_window(child, gtk.TEXT_WINDOW_LEFT, 0, 50) | |
child = gtk.EventBox() | |
b = gtk.Button("Button _D in right window") | |
child.add(b) | |
text_view.add_child_in_window(child, gtk.TEXT_WINDOW_RIGHT, 0, 25) | |
buffer = text_view.get_buffer() | |
iter = buffer.get_start_iter() | |
anchor = buffer.create_child_anchor(iter) | |
child = gtk.Button("Button _E in buffer") | |
text_view.add_child_at_anchor(child, anchor) | |
anchor = buffer.create_child_anchor(iter) | |
child = gtk.Button("Button _F in buffer") | |
text_view.add_child_at_anchor(child, anchor) | |
anchor = buffer.create_child_anchor(iter) | |
child = gtk.Button("Button _G in buffer") | |
text_view.add_child_at_anchor(child, anchor) | |
# show all the buttons | |
text_view.show_all() | |
def init_menus(self): | |
text_view = self.text_view | |
direction = text_view.get_direction() | |
wrap_mode = text_view.get_wrap_mode() | |
menu_item = None | |
if direction == gtk.TEXT_DIR_LTR: | |
menu_item = self.item_factory.get_widget("/Settings/Left-to-Right") | |
elif direction == gtk.TEXT_DIR_RTL: | |
menu_item = self.item_factory.get_widget("/Settings/Right-to-Left") | |
if menu_item: | |
menu_item.activate() | |
if wrap_mode == gtk.WRAP_NONE: | |
menu_item = self.item_factory.get_widget("/Settings/Wrap Off") | |
elif wrap_mode == gtk.WRAP_WORD: | |
menu_item = self.item_factory.get_widget("/Settings/Wrap Words") | |
elif wrap_mode == gtk.WRAP_CHAR: | |
menu_item = self.item_factory.get_widget("/Settings/Wrap Chars") | |
if menu_item: | |
menu_item.activate() | |
def close_view(self): | |
TestText.views.remove(self) | |
buffer = self.text_view.get_buffer() | |
buffer.unref() | |
buffer.disconnect(self.bhid) | |
self.text_view.destroy() | |
del self.text_view | |
self.text_view = None | |
self.destroy() | |
del self | |
if not TestText.views: | |
gtk.main_quit() | |
def check_close_view(self): | |
buffer = self.text_view.get_buffer() | |
if (buffer.refcount > 1 or | |
buffer.check_buffer_saved()): | |
self.close_view() | |
def set_view_title(self): | |
pretty_name = self.text_view.get_buffer().pretty_name() | |
title = "testtext - " + pretty_name | |
self.set_title(title) | |
def cursor_set_callback(self, buffer, location, mark): | |
# Redraw tab windows if the cursor moves | |
# on the mapped widget (windows may not exist before realization... | |
text_view = self.text_view | |
if mark == buffer.get_insert(): | |
tab_window = text_view.get_window(gtk.TEXT_WINDOW_TOP) | |
tab_window.invalidate_rect(None, False) | |
#tab_window.invalidate_rect(tab_window.get_geometry()[:4], False) | |
tab_window = text_view.get_window(gtk.TEXT_WINDOW_BOTTOM) | |
tab_window.invalidate_rect(None, False) | |
#tab_window.invalidate_rect(tab_window.get_geometry()[:4], False) | |
def tab_stops_expose(self, widget, event): | |
#print self, widget, event | |
text_view = widget | |
# See if this expose is on the tab stop window | |
top_win = text_view.get_window(gtk.TEXT_WINDOW_TOP) | |
bottom_win = text_view.get_window(gtk.TEXT_WINDOW_BOTTOM) | |
if event.window == top_win: | |
type = gtk.TEXT_WINDOW_TOP | |
target = top_win | |
elif event.window == bottom_win: | |
type = gtk.TEXT_WINDOW_BOTTOM | |
target = bottom_win | |
else: | |
return False | |
first_x = event.area.x | |
last_x = first_x + event.area.width | |
first_x, y = text_view.window_to_buffer_coords(type, first_x, 0) | |
last_x, y = text_view.window_to_buffer_coords(type, last_x, 0) | |
buffer = text_view.get_buffer() | |
insert = buffer.get_iter_at_mark(buffer.get_insert()) | |
attrs = gtk.TextAttributes() | |
insert.get_attributes(attrs) | |
tabslist = [] | |
in_pixels = False | |
if attrs.tabs: | |
tabslist = attrs.tabs.get_tabs() | |
in_pixels = attrs.tabs.get_positions_in_pixels() | |
for align, position in tabslist: | |
if not in_pixels: | |
position = pango.PIXELS(position) | |
pos, y = text_view.buffer_to_window_coords(type, position, 0) | |
target.draw_line(text_view.style.fg_gc[text_view.state], | |
pos, 0, pos, 15) | |
return True | |
def get_lines(self, first_y, last_y, buffer_coords, numbers): | |
text_view = self.text_view | |
# Get iter at first y | |
iter, top = text_view.get_line_at_y(first_y) | |
# For each iter, get its location and add it to the arrays. | |
# Stop when we pass last_y | |
count = 0 | |
size = 0 | |
while not iter.is_end(): | |
y, height = text_view.get_line_yrange(iter) | |
buffer_coords.append(y) | |
line_num = iter.get_line() | |
numbers.append(line_num) | |
count += 1 | |
if (y + height) >= last_y: | |
break | |
iter.forward_line() | |
return count | |
def line_numbers_expose(self, widget, event, user_data=None): | |
text_view = widget | |
# See if this expose is on the line numbers window | |
left_win = text_view.get_window(gtk.TEXT_WINDOW_LEFT) | |
right_win = text_view.get_window(gtk.TEXT_WINDOW_RIGHT) | |
if event.window == left_win: | |
type = gtk.TEXT_WINDOW_LEFT | |
target = left_win | |
elif event.window == right_win: | |
type = gtk.TEXT_WINDOW_RIGHT | |
target = right_win | |
else: | |
return False | |
first_y = event.area.y | |
last_y = first_y + event.area.height | |
x, first_y = text_view.window_to_buffer_coords(type, 0, first_y) | |
x, last_y = text_view.window_to_buffer_coords(type, 0, last_y) | |
numbers = [] | |
pixels = [] | |
count = self.get_lines(first_y, last_y, pixels, numbers) | |
# Draw fully internationalized numbers! | |
layout = widget.create_pango_layout("") | |
for i in range(count): | |
x, pos = text_view.buffer_to_window_coords(type, 0, pixels[i]) | |
str = "%d" % numbers[i] | |
layout.set_text(str) | |
widget.style.paint_layout(target, widget.state, False, | |
None, widget, None, 2, pos + 2, layout) | |
# don't stop emission, need to draw children | |
return False | |
def add_example_widgets(self): | |
buffer = self.text_view.get_buffer() | |
anchor = buffer.get_data("anchor") | |
if (anchor and not anchor.get_deleted()): | |
widget = gtk.Button("Foo") | |
self.text_view.add_child_at_anchor(widget, anchor) | |
widget.show() | |
class Stack(list): | |
def __init__(self): | |
list.__init__(self) | |
def push(self, item): | |
self.insert(-1, item) | |
def pop(self): | |
del self[0] | |
def get(self): | |
return self[0] | |
class TestText: | |
untitled_serial = 1 | |
colormap = None | |
active_window_stack = Stack() | |
buffers = Stack() | |
views = Stack() | |
def __init__(self, filelist): | |
view = View() | |
self.active_window_stack.push(view) | |
for fname in filelist: | |
filename = os.path.abspath(fname) | |
view.open_ok_func(filename) | |
self.active_window_stack.pop() | |
def main(self): | |
gtk.main() | |
return 0 | |
if __name__ == "__main__": | |
testtext = TestText(sys.argv[1:]) | |
testtext.main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment