2 from rox
import g
, filer
, app_options
, mime
4 from rox
.loading
import XDSLoader
5 from rox
.options
import Option
6 from rox
import OptionsBox
7 from rox
.saving
import Saveable
12 # WARNING: This is a temporary hack, until we write a way choose between
13 # the two ways of doing toolbars or we abandon the old method entirely
15 warnings
.filterwarnings('ignore', category
=DeprecationWarning,
19 def optional_section(available
):
20 """If requires is None, the section is enabled. Otherwise,
21 the section is shaded and the requires message is shown at
24 def build_enabled_section(box
, node
, label
):
25 return box
.do_box(node
, None, g
.VBox(False, 0))
26 return build_enabled_section
28 def build_disabled_section(box
, node
, label
):
29 assert label
is not None
30 box
, = box
.do_box(node
, None, g
.VBox(False, 0))
31 box
.set_sensitive(False)
32 frame
= g
.Frame(label
)
33 box
.set_border_width(4)
36 return build_disabled_section
44 to_utf8
= codecs
.getencoder('utf-8')
46 from buffer import Buffer
, have_sourceview
47 from rox
.Menu
import Menu
, set_save_name
, SubMenu
, Separator
, Action
, ToggleItem
48 if have_sourceview
: import gtksourceview
50 OptionsBox
.widget_registry
['source-view-only'] = optional_section(have_sourceview
)
52 default_font
= Option('default_font', 'serif')
54 background_colour
= Option('background', '#fff')
55 foreground_colour
= Option('foreground', '#000')
57 auto_indent
= Option('autoindent', '1')
58 word_wrap
= Option('wordwrap', '1')
60 layout_left_margin
= Option('layout_left_margin', 2)
61 layout_right_margin
= Option('layout_right_margin', 4)
63 layout_before_para
= Option('layout_before_para', 0)
64 layout_after_para
= Option('layout_after_para', 0)
65 layout_inside_para
= Option('layout_inside_para', 0)
66 layout_indent_para
= Option('layout_indent_para', 2)
68 right_margin
= Option('right_margin', 80)
69 show_margin
= Option('show_margin', True)
70 smart_home_end
= Option('smart_home_end', True)
71 show_line_numbers
= Option('show_line_numbers', True)
72 show_line_markers
= Option('show_line_markers', True)
73 tab_width
= Option('tab_width', 4)
74 use_spaces_for_tabs
= Option('use_spaces_for_tabs', False)
76 show_toolbar
= Option('show_toolbar', 1)
78 set_save_name('Edit', site
='rox.sourceforge.net')
81 Action(_('Cut'), 'cut', '<Ctrl>X', g
.STOCK_CUT
),
82 Action(_('Copy'), 'copy', '<Ctrl>C', g
.STOCK_COPY
),
83 Action(_('Paste'), 'paste', '<Ctrl>V', g
.STOCK_PASTE
),
85 Action(_('Undo'), 'undo', '<Ctrl>Z', g
.STOCK_UNDO
),
86 Action(_('Redo'), 'redo', '<Ctrl>Y', g
.STOCK_REDO
),
88 Action(_('Search...'), 'search', 'F4', g
.STOCK_FIND
),
89 Action(_('Search Again'), 'search_again', '<Shift>F4', g
.STOCK_GO_FORWARD
),
90 Action(_('Search and Replace....'), 'search_replace',
91 '<Ctrl>F4', g
.STOCK_FIND_AND_REPLACE
),
92 Action(_('Goto line...'), 'goto', 'F5', g
.STOCK_JUMP_TO
),
97 Action(_('Toggle Bookmark'), 'toggle_bookmark', '<Ctrl>F2'),
98 Action(_('Next Bookmark'), 'next_bookmark', 'F2'),
99 Action(_('Previous Bookmark'), 'prev_bookmark', '<Shift>F2'),
103 edit_menu
+= bookmark_menu
105 menu
= Menu('main', [
107 Action(_('Save'), 'save', '<Ctrl>S', g
.STOCK_SAVE
),
108 Action(_('Save As...'), 'save_as', 'F3', g
.STOCK_SAVE_AS
),
109 Action(_('Open Parent'), 'up', '', g
.STOCK_GO_UP
),
110 Action(_('Show Changes'), 'diff', '', 'rox-diff'),
111 ToggleItem(_('Word Wrap'), 'word_wrap'),
112 Action(_('Close'), 'close', '', g
.STOCK_CLOSE
),
114 Action(_('New'), 'new', '', g
.STOCK_NEW
)]),
116 SubMenu(_('Edit'), edit_menu
),
118 Action(_('Options'), 'show_options', '', g
.STOCK_PROPERTIES
),
119 Action(_('Help'), 'help', 'F1', g
.STOCK_HELP
),
123 "iso8859_1", "iso8859_2", "iso8859_3", "iso8859_4", "iso8859_5",
124 "iso8859_6", "iso8859_7", "iso8859_8", "iso8859_9", "iso8859_10",
125 "iso8859_13", "iso8859_14", "iso8859_15",
126 "ascii", "base64_codec", "charmap",
127 "cp037", "cp1006", "cp1026", "cp1140", "cp1250", "cp1251", "cp1252",
128 "cp1253", "cp1254", "cp1255", "cp1256", "cp1257", "cp1258", "cp424",
129 "cp437", "cp500", "cp737", "cp775", "cp850", "cp852", "cp855", "cp856",
130 "cp857", "cp860", "cp861", "cp862", "cp863", "cp864", "cp865", "cp866",
131 "cp869", "cp874", "cp875", "hex_codec",
134 "mac_cyrillic", "mac_greek", "mac_iceland", "mac_latin2", "mac_roman", "mac_turkish",
135 "mbcs", "quopri_codec", "raw_unicode_escape",
137 "utf_16_be", "utf_16_le", "utf_16", "utf_7", "utf_8", "uu_codec",
141 class Abort(Exception):
146 """Called when the minibuffer is opened."""
148 def key_press(self
, kev
):
149 """A keypress event in the minibuffer text entry."""
152 """The minibuffer text has changed."""
155 """Return or Enter pressed."""
158 """Called when the minibuffer is closed.
159 Remove any widgets created in setup."""
161 info
= 'Press Escape to close the minibuffer.'
163 class DiffLoader(XDSLoader
):
164 def __init__(self
, window
):
165 XDSLoader
.__init
__(self
, ['text/plain'])
168 def xds_load_from_file(self
, path
):
169 self
.window
.diff(path
= path
)
171 def xds_load_from_stream(self
, name
, type, stream
):
172 tmp
= diff
.Tmp(suffix
= '-' + (name
or 'tmp'))
174 shutil
.copyfileobj(stream
, tmp
)
176 self
.window
.diff(path
= tmp
.name
)
178 class DndToolbar(g
.Toolbar
, XDSLoader
):
180 g
.Toolbar
.__init
__(self
)
181 XDSLoader
.__init
__(self
, ['text/plain'])
183 def xds_load_from_path(self
, path
):
186 def xds_load_from_stream(self
, name
, type, stream
):
187 EditWindow(contents
= stream
.read())
189 class EditWindow(rox
.Window
, XDSLoader
, Saveable
):
193 search_minibuffer
= None # (persists for search_again)
195 def __init__(self
, filename
= None, show
= True, line_number
= None, contents
= None):
196 rox
.Window
.__init
__(self
)
197 XDSLoader
.__init
__(self
, ['text/plain', 'UTF8_STRING'])
203 app_options
.add_notify(self
.update_styles
)
207 if not os
.path
.exists(filename
):
209 filename2
, line_number
= filename
.split(':')
210 line_number
= long(line_number
)
212 # Either there was no ':', or it wasn't followed by a number
216 self
.uri
= os
.path
.abspath(filename
)
217 self
.mime_type
= mime
.get_type(self
.uri
, 1)
220 self
.mime_type
= mime
.lookup('text', 'plain')
222 self
.buffer = Buffer()
225 self
.text
= gtksourceview
.SourceView(self
.buffer)
226 pixbuf
= g
.gdk
.pixbuf_new_from_file(rox
.app_dir
+"/images/marker.png")
227 self
.text
.set_marker_pixbuf("bookmark", pixbuf
)
229 self
.buffer.set_type(self
.mime_type
)
231 self
.text
= g
.TextView()
232 self
.text
.set_buffer(self
.buffer)
234 self
.text
.set_size_request(10, 10)
235 self
.xds_proxy_for(self
.text
)
237 self
.insert_mark
= self
.buffer.get_mark('insert')
238 self
.selection_bound_mark
= self
.buffer.get_mark('selection_bound')
239 start
= self
.buffer.get_start_iter()
240 self
.mark_start
= self
.buffer.create_mark('mark_start', start
, True)
241 self
.mark_end
= self
.buffer.create_mark('mark_end', start
, False)
242 self
.mark_tmp
= self
.buffer.create_mark('mark_tmp', start
, False)
243 tag
= self
.buffer.create_tag('marked')
244 tag
.set_property('background', 'green')
247 # When searching, this is where the cursor was when the minibuffer
249 start
= self
.buffer.get_start_iter()
250 self
.search_base
= self
.buffer.create_mark('search_base', start
, True)
256 tools
.set_style(g
.TOOLBAR_ICONS
)
257 vbox
.pack_start(tools
, False, True, 0)
259 self
.status_label
= g
.Label('')
260 tools
.append_widget(self
.status_label
, None, None)
261 tools
.insert_stock(g
.STOCK_HELP
, _('Help'), None, self
.help, None, 0)
262 diff
= tools
.insert_stock('rox-diff', _('Show changes from saved copy.\n'
263 'Or, drop a backup file onto this button to see changes from that.'),
264 None, self
.diff
, None, 0)
265 DiffLoader(self
).xds_proxy_for(diff
)
269 image_spell
= g
.Image()
270 image_spell
.set_from_stock(g
.STOCK_SPELL_CHECK
, tools
.get_icon_size())
271 self
.spell_button
= tools
.insert_element(g
.TOOLBAR_CHILD_TOGGLEBUTTON
,
272 None, _("Check Spelling"), _("Check Spelling"), None,
273 image_spell
, self
.toggle_spell
, None, 0)
275 image_wrap
= g
.Image()
276 image_wrap
.set_from_file(rox
.app_dir
+ '/images/rox-word-wrap.png')
277 self
.wrap_button
= tools
.insert_element(g
.TOOLBAR_CHILD_TOGGLEBUTTON
,
278 None, _("Word Wrap"), _("Word Wrap"), None, image_wrap
,
279 lambda button
: self
.set_word_wrap(button
.get_active()),
281 tools
.insert_stock(g
.STOCK_REDO
, _('Redo'), None, self
.redo
, None, 0)
282 tools
.insert_stock(g
.STOCK_UNDO
, _('Undo'), None, self
.undo
, None, 0)
283 tools
.insert_stock(g
.STOCK_FIND_AND_REPLACE
, _('Replace'), None, self
.search_replace
, None, 0)
284 tools
.insert_stock(g
.STOCK_FIND
, _('Search'), None, self
.search
, None, 0)
285 tools
.insert_stock(g
.STOCK_SAVE_AS
, _('Save As'), None, self
.save_as
, None, 0)
286 self
.save_button
= tools
.insert_stock(g
.STOCK_SAVE
, _('Save'), None, self
.save
, None, 0)
287 tools
.insert_stock(g
.STOCK_GO_UP
, _('Up'), None, self
.up
, None, 0)
288 tools
.insert_stock(g
.STOCK_CLOSE
, _('Close'), None, self
.close
, None, 0)
289 # Set minimum size to ignore the label
290 tools
.set_size_request(tools
.size_request()[0], -1)
294 swin
= g
.ScrolledWindow()
295 swin
.set_policy(g
.POLICY_AUTOMATIC
, g
.POLICY_AUTOMATIC
)
296 vbox
.pack_start(swin
, True, True)
300 # Aim for a width of about 100 chars
301 layout
= self
.text
.create_pango_layout("mmmmmiiiii")
302 default_width
= layout
.get_pixel_extents()[1][2] * 10
303 self
.set_default_size(min(g
.gdk
.screen_width() * 2 / 3, default_width
),
304 g
.gdk
.screen_height() / 2)
312 # Create the minibuffer
313 self
.mini_hbox
= g
.HBox(False)
315 info
= rox
.ButtonMixed(g
.STOCK_DIALOG_INFO
, '')
316 info
.set_relief(g
.RELIEF_NONE
)
317 info
.unset_flags(g
.CAN_FOCUS
)
318 info
.connect('clicked', self
.mini_show_info
)
320 close
= rox
.ButtonMixed(g
.STOCK_STOP
, '')
321 close
.set_relief(g
.RELIEF_NONE
)
322 close
.unset_flags(g
.CAN_FOCUS
)
323 close
.connect('clicked', lambda e
: self
.set_minibuffer(None))
325 self
.mini_hbox
.pack_end(info
, False, True, 0)
326 self
.mini_hbox
.pack_start(close
, False, True, 0)
327 self
.mini_label
= g
.Label('')
328 self
.mini_hbox
.pack_start(self
.mini_label
, False, True, 0)
329 self
.mini_entry
= g
.Entry()
330 self
.mini_hbox
.pack_start(self
.mini_entry
, True, True, 0)
331 vbox
.pack_start(self
.mini_hbox
, False, True)
332 self
.mini_entry
.connect('key-press-event', self
.mini_key_press
)
333 self
.mini_entry
.connect('changed', self
.mini_changed
)
335 self
.connect('destroy', self
.destroyed
)
337 self
.connect('delete-event', self
.delete_event
)
338 self
.text
.grab_focus()
339 self
.text
.connect('key-press-event', self
.key_press
)
341 # FIXME: why does this freeze Edit?
343 #if self.mime_type.media == 'text' and self.mime_type.subtype == 'plain':
345 ##self.spell.set_language ("en_US")
347 def update_current_line(*unused
):
348 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
349 bound
= self
.buffer.get_iter_at_mark(self
.selection_bound_mark
)
350 if cursor
.compare(bound
) == 0:
351 n_lines
= self
.buffer.get_line_count()
352 self
.status_label
.set_text(_('Line %s of %d') % (cursor
.get_line() + 1, n_lines
))
354 n_lines
= abs(cursor
.get_line() - bound
.get_line()) + 1
356 n_chars
= abs(cursor
.get_line_offset() - bound
.get_line_offset())
358 bytes
= to_utf8(self
.buffer.get_text(cursor
, bound
, False))[0]
359 self
.status_label
.set_text(_('One character selected (%s)') %
360 ' '.join(map(lambda x
: '0x%2x' % ord(x
), bytes
)))
362 self
.status_label
.set_text(_('%d characters selected') % n_chars
)
364 self
.status_label
.set_text(_('%d lines selected') % n_lines
)
365 self
.buffer.connect('mark-set', update_current_line
)
366 self
.buffer.connect('changed', update_current_line
)
369 # Loading might take a while, so get something on the screen
375 self
.load_file(filename
)
377 self
.save_last_stat
= os
.stat(filename
)
382 self
.insert_data(contents
)
384 self
.buffer.connect('modified-changed', self
.update_title
)
385 self
.buffer.set_modified(False)
387 def button_press(text
, event
):
388 if event
.button
!= 3:
390 #self.text.emit('populate-popup', menu.menu)
391 menu
.popup(self
, event
)
393 self
.text
.connect('button-press-event', button_press
)
394 self
.text
.connect('popup-menu', lambda text
: menu
.popup(self
, None))
396 menu
.attach(self
, self
)
397 self
.buffer.place_cursor(self
.buffer.get_start_iter())
398 self
.buffer.start_undo_history()
401 iter = self
.buffer.get_iter_at_line(int(line_number
) - 1)
402 self
.buffer.place_cursor(iter)
403 self
.text
.scroll_to_mark(self
.insert_mark
, 0.05, False)
405 def key_press(self
, text
, kev
):
406 if kev
.keyval
== g
.keysyms
.Return
or kev
.keyval
== g
.keysyms
.KP_Enter
:
407 return self
.auto_indent()
408 elif kev
.keyval
== g
.keysyms
.Tab
or kev
.keyval
== g
.keysyms
.KP_Tab
:
409 return self
.indent_block()
410 elif kev
.keyval
== g
.keysyms
.ISO_Left_Tab
:
411 return self
.unindent_block()
412 elif kev
.keyval
== g
.keysyms
.Escape
:
413 self
.set_minibuffer(None)
417 def auto_indent(self
):
418 if not auto_indent
.int_value
:
421 start
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
423 start
.set_line_offset(0)
424 end
.forward_to_line_end()
425 line
= self
.buffer.get_text(start
, end
, False)
428 if self
.mime_type
.subtype
== 'x-python':
430 l
= line
.split('\n')[0]
433 if l
.endswith(':') and not l
.startswith('#'):
434 if use_spaces_for_tabs
.int_value
:
435 indent
+= ' ' * tab_width
.int_value
438 elif have_sourceview
:
447 self
.buffer.begin_user_action()
448 self
.buffer.insert_at_cursor('\n' + indent
)
449 self
.buffer.end_user_action()
452 def indent_block(self
):
454 (start
, end
) = self
.buffer.get_selection_bounds()
455 start_line
= start
.get_line()
456 end_line
= end
.get_line()
457 self
.buffer.begin_user_action()
458 for i
in range(start_line
, end_line
+1):
459 iter = self
.buffer.get_iter_at_line(i
)
460 self
.buffer.insert(iter, '\t')
461 self
.buffer.end_user_action()
466 def unindent_block(self
):
468 (start
, end
) = self
.buffer.get_selection_bounds()
469 start_line
= start
.get_line()
470 end_line
= end
.get_line()
471 self
.buffer.begin_user_action()
472 for i
in range(start_line
, end_line
+1):
473 iter = self
.buffer.get_iter_at_line(i
)
474 if iter.get_char() == '\t':
475 next_char
= iter.copy()
476 next_char
.forward_char()
477 self
.buffer.delete(iter, next_char
)
478 self
.buffer.end_user_action()
483 def destroyed(self
, widget
):
484 app_options
.remove_notify(self
.update_styles
)
486 def update_styles(self
):
489 font
= pango
.FontDescription(default_font
.value
)
490 bg
= g
.gdk
.color_parse(background_colour
.value
)
491 fg
= g
.gdk
.color_parse(foreground_colour
.value
)
493 self
.text
.set_left_margin(layout_left_margin
.int_value
)
494 self
.text
.set_right_margin(layout_right_margin
.int_value
)
496 self
.text
.set_pixels_above_lines(layout_before_para
.int_value
)
497 self
.text
.set_pixels_below_lines(layout_after_para
.int_value
)
498 self
.text
.set_pixels_inside_wrap(layout_inside_para
.int_value
)
499 self
.text
.set_indent(layout_indent_para
.int_value
)
501 self
.word_wrap
= bool(word_wrap
.int_value
)
503 if show_toolbar
.int_value
:
508 rox
.report_exception()
510 self
.text
.modify_font(font
)
511 self
.text
.modify_base(g
.STATE_NORMAL
, bg
)
512 self
.text
.modify_text(g
.STATE_NORMAL
, fg
)
515 self
.text
.set_show_line_numbers(show_line_numbers
.int_value
)
516 self
.text
.set_show_line_markers(show_line_markers
.int_value
)
517 self
.text
.set_auto_indent(auto_indent
.int_value
)
518 self
.text
.set_tabs_width(tab_width
.int_value
)
519 self
.text
.set_insert_spaces_instead_of_tabs(use_spaces_for_tabs
.int_value
)
520 self
.text
.set_margin(right_margin
.int_value
)
521 self
.text
.set_show_margin(show_margin
.int_value
)
522 self
.text
.set_smart_home_end(smart_home_end
.int_value
)
523 if self
.buffer.language
== 'Python':
524 self
.text
.set_auto_indent(False)
526 def cut(self
): self
.text
.emit('cut_clipboard')
527 def copy(self
): self
.text
.emit('copy_clipboard')
528 def paste(self
): self
.text
.emit('paste_clipboard')
530 def delete_event(self
, window
, event
):
531 if self
.buffer.get_modified():
532 self
.save_as(discard
= 1)
536 def update_title(self
, *unused
):
537 title
= self
.uri
or '<'+_('Untitled')+'>'
538 if self
.buffer.get_modified():
540 self
.save_button
.set_sensitive(True)
542 self
.save_button
.set_sensitive(False)
543 self
.set_title(title
)
545 def xds_load_from_stream(self
, name
, t
, stream
):
546 if t
== 'UTF8_STRING':
547 return # Gtk will handle it
549 dnd_mark
= self
.buffer.get_mark('gtk_drag_target')
551 dnd_pos
= self
.buffer.get_iter_at_mark(dnd_mark
)
552 self
.buffer.move_mark(self
.insert_mark
, dnd_pos
)
553 self
.insert_data(stream
.read())
557 def get_encoding(self
, message
):
558 "Returns (encoding, errors), or raises Abort to cancel."
559 box
= g
.MessageDialog(self
, 0, g
.MESSAGE_QUESTION
, g
.BUTTONS_CANCEL
, message
)
560 box
.set_has_separator(False)
563 box
.vbox
.pack_start(frame
, True, True)
564 frame
.set_border_width(6)
566 hbox
= g
.HBox(False, 4)
567 hbox
.set_border_width(6)
569 hbox
.pack_start(g
.Label(_('Encoding:')), False, True, 0)
571 combo
.disable_activate()
572 combo
.entry
.connect('activate', lambda w
: box
.activate_default())
573 combo
.set_popdown_strings(known_codecs
)
574 hbox
.pack_start(combo
, True, True, 0)
575 ignore_errors
= g
.CheckButton(_('Ignore errors'))
576 hbox
.pack_start(ignore_errors
, False, True)
581 box
.add_button(g
.STOCK_CONVERT
, g
.RESPONSE_YES
)
582 box
.set_default_response(g
.RESPONSE_YES
)
585 combo
.entry
.grab_focus()
588 if resp
!= g
.RESPONSE_YES
:
592 if ignore_errors
.get_active():
596 encoding
= combo
.entry
.get_text()
598 codecs
.getdecoder(encoding
)
601 rox
.alert(_("Unknown encoding '%s'") % encoding
)
605 return encoding
, errors
607 def insert_data(self
, data
):
612 decoder
= codecs
.getdecoder(encoding
)
614 data
= decoder(data
, errors
)[0]
615 if errors
== 'strict':
616 assert '\0' not in data
619 data
= data
.replace('\0', '\\0')
624 encoding
, errors
= self
.get_encoding(
625 _("Data is not valid %s. Please select the file's encoding. "
626 "Turn on 'ignore errors' to try and load it anyway.")
629 self
.buffer.begin_user_action()
630 self
.buffer.insert_at_cursor(data
)
631 self
.buffer.end_user_action()
634 def load_file(self
, path
):
639 file = open(path
, 'r')
640 contents
= file.read()
644 self
.buffer.begin_not_undoable_action()
645 self
.insert_data(contents
)
646 self
.buffer.end_not_undoable_action()
650 rox
.report_exception()
653 def close(self
, button
= None):
654 if self
.buffer.get_modified():
655 self
.save_as(discard
= 1)
662 def up(self
, button
= None):
664 filer
.show_file(self
.uri
)
666 rox
.alert(_('File is not saved to disk yet'))
669 def toggle_spell(self
, button
= None):
673 self
.spell_button
.set_active(False)
674 elif not self
.spell_button
.get_active():
675 # Probably a failed attempt to turn it on
679 self
.spell
= gtkspell
.Spell(self
.text
)
680 self
.spell_button
.set_active(True)
681 except Exception, ex
:
683 self
.spell_button
.set_active(False)
684 rox
.report_exception()
686 #self.spell_button.set_active(self.spell != None)
688 def diff(self
, button
= None, path
= None):
689 path
= path
or self
.uri
691 rox
.alert(_('This file has never been saved; nothing to compare it to!\n'
692 'Note: you can drop a file onto the toolbar button to see '
693 'the changes from that file.'))
695 diff
.show_diff(path
, self
.save_to_stream
)
697 def has_selection(self
):
698 s
, e
= self
.get_selection_range()
699 return not e
.equal(s
)
701 def get_marked_range(self
):
702 s
= self
.buffer.get_iter_at_mark(self
.mark_start
)
703 e
= self
.buffer.get_iter_at_mark(self
.mark_end
)
708 def get_selection_range(self
):
709 s
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
710 e
= self
.buffer.get_iter_at_mark(self
.selection_bound_mark
)
715 def save(self
, widget
= None):
717 self
.save_to_file(self
.uri
)
718 self
.buffer.set_modified(False)
720 self
.save_as(discard
=0)
722 def save_as(self
, widget
= None, discard
= 0):
723 from rox
.saving
import SaveBox
726 self
.savebox
.destroy()
729 self
.mime_type
= mime
.get_type(self
.uri
, 1)
731 self
.mime_type
= mime
.lookup('text', 'plain')
733 mime_text
= self
.mime_type
.media
+ '/' + self
.mime_type
.subtype
735 if self
.has_selection() and not discard
:
736 saver
= SelectionSaver(self
)
737 self
.savebox
= SaveBox(saver
, 'Selection', mime_text
)
738 self
.savebox
.connect('destroy', lambda w
: saver
.destroy())
740 uri
= self
.uri
or _('TextFile')
741 self
.savebox
= SaveBox(self
, uri
, mime_text
, discard
)
744 def help(self
, button
= None):
745 filer
.open_dir(os
.path
.join(rox
.app_dir
, 'Help'))
747 def save_to_stream(self
, stream
):
748 s
= self
.buffer.get_start_iter()
749 e
= self
.buffer.get_end_iter()
750 stream
.write(self
.buffer.get_text(s
, e
, True))
752 def set_uri(self
, uri
):
754 self
.buffer.set_modified(False)
760 def change_font(self
):
761 style
= self
.text
.get_style().copy()
762 style
.font
= load_font(options
.get('edit_font'))
763 self
.text
.set_style(style
)
765 def show_options(self
):
768 def set_marked(self
, start
= None, end
= None):
769 "Set the marked region (from the selection if no region is given)."
771 assert not self
.marked
778 start
, end
= self
.get_selection_range()
779 buffer.move_mark(self
.mark_start
, start
)
780 buffer.move_mark(self
.mark_end
, end
)
781 buffer.apply_tag_by_name('marked',
782 buffer.get_iter_at_mark(self
.mark_start
),
783 buffer.get_iter_at_mark(self
.mark_end
))
786 def clear_marked(self
):
791 buffer.remove_tag_by_name('marked',
792 buffer.get_iter_at_mark(self
.mark_start
),
793 buffer.get_iter_at_mark(self
.mark_end
))
795 def undo(self
, widget
= None):
797 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
798 self
.text
.scroll_to_iter(cursor
, 0.05, False)
800 def redo(self
, widget
= None):
802 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
803 self
.text
.scroll_to_iter(cursor
, 0.05, False)
805 def goto(self
, widget
= None):
806 from goto
import Goto
807 self
.set_minibuffer(Goto())
809 def search(self
, widget
= None):
810 if self
.search_minibuffer
is None:
811 from search
import Search
812 self
.search_minibuffer
= Search()
813 self
.set_minibuffer(self
.search_minibuffer
)
815 def search_again(self
, widget
= None):
816 if self
.search_minibuffer
and self
.search_minibuffer
is self
.minibuffer
:
817 self
.minibuffer
.activate() # Search again with same text
819 if self
.minibuffer
is None:
820 # Search mini-buffer not yet open
822 self
.minibuffer
.restore_previous_search()
823 self
.minibuffer
.search_again()
825 def search_replace(self
, widget
= None):
826 from search
import Replace
829 def toggle_bookmark(self
):
830 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
831 name
= str(cursor
.get_line())
832 marker
= self
.buffer.get_marker(name
)
834 self
.buffer.delete_marker(marker
);
836 marker
= self
.buffer.create_marker(name
, "bookmark", cursor
);
838 def next_bookmark(self
):
839 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
840 cursor
.forward_char()
841 marker
= self
.buffer.get_next_marker(cursor
)
843 self
.buffer.get_iter_at_marker (cursor
, marker
)
844 self
.buffer.place_cursor(cursor
)
845 self
.text
.scroll_to_iter(cursor
, 0.05, False)
847 def prev_bookmark(self
):
848 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
849 cursor
.backward_char()
850 marker
= self
.buffer.get_prev_marker(cursor
)
852 self
.buffer.get_iter_at_marker (cursor
, marker
)
853 self
.buffer.place_cursor(cursor
)
854 self
.text
.scroll_to_iter(cursor
, 0.05, False)
856 def set_mini_label(self
, label
):
857 self
.mini_label
.set_text(label
)
859 def set_minibuffer(self
, minibuffer
):
860 assert minibuffer
is None or isinstance(minibuffer
, Minibuffer
)
863 self
.minibuffer
.close()
865 self
.minibuffer
= None
868 self
.mini_entry
.set_text('')
869 self
.minibuffer
= minibuffer
870 minibuffer
.setup(self
)
871 self
.mini_entry
.grab_focus()
872 self
.mini_hbox
.show_all()
874 self
.mini_hbox
.hide()
875 self
.text
.grab_focus()
877 def mini_key_press(self
, entry
, kev
):
878 if kev
.keyval
== g
.keysyms
.Escape
:
879 self
.set_minibuffer(None)
881 if kev
.keyval
== g
.keysyms
.Return
or kev
.keyval
== g
.keysyms
.KP_Enter
:
882 self
.minibuffer
.activate()
885 return self
.minibuffer
.key_press(kev
)
887 def mini_changed(self
, entry
):
888 if not self
.minibuffer
:
890 self
.minibuffer
.changed()
892 def mini_show_info(self
, *unused
):
893 assert self
.minibuffer
895 self
.info_box
.destroy()
896 self
.info_box
= g
.MessageDialog(self
, 0, g
.MESSAGE_INFO
, g
.BUTTONS_OK
,
897 self
.minibuffer
.info
)
898 self
.info_box
.set_title(_('Minibuffer help'))
901 self
.info_box
.connect('destroy', destroy
)
903 self
.info_box
.connect('response', lambda w
, r
: w
.destroy())
905 def process_selected(self
, process
):
906 """Calls process(line) on each line in the selection, or each line in the file
907 if there is no selection. If the result is not None, the text is replaced."""
908 self
.buffer.begin_user_action()
910 self
._process
_selected
(process
)
912 self
.buffer.end_user_action()
914 def _process_selected(self
, process
):
915 if self
.has_selection():
917 start
, end
= self
.get_selection_range()
918 if start
.compare(end
) > 0:
921 start
, end
= self
.get_selection_range()
922 if start
.compare(end
) > 0:
926 return self
.buffer.get_end_iter()
927 start
= self
.buffer.get_start_iter()
930 while start
.compare(end
) <= 0:
931 line_end
= start
.copy()
932 line_end
.forward_to_line_end()
933 if line_end
.compare(end
) >= 0:
935 line
= self
.buffer.get_text(start
, line_end
, False)
938 self
.buffer.move_mark(self
.mark_tmp
, start
)
939 self
.buffer.insert(line_end
, new
)
940 start
= self
.buffer.get_iter_at_mark(self
.mark_tmp
)
941 line_end
= start
.copy()
942 line_end
.forward_chars(len(line
.decode('utf-8')))
943 self
.buffer.delete(start
, line_end
)
945 start
= self
.buffer.get_iter_at_mark(self
.mark_tmp
)
947 if not start
.forward_line(): break
949 def set_word_wrap(self
, value
):
950 self
._word
_wrap
= value
951 self
.wrap_button
.set_active(value
)
953 self
.text
.set_wrap_mode(g
.WRAP_WORD
)
955 self
.text
.set_wrap_mode(g
.WRAP_NONE
)
957 word_wrap
= property(lambda self
: self
._word
_wrap
, set_word_wrap
)
959 class SelectionSaver(Saveable
):
960 def __init__(self
, window
):
964 def save_to_stream(self
, stream
):
965 s
, e
= self
.window
.get_marked_range()
966 stream
.write(self
.window
.buffer.get_text(s
, e
, True))
969 # Called when savebox is remove. Get rid of the selection marker
970 self
.window
.clear_marked()