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 gtksourceview2
as 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_file(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
.View(self
.buffer)
226 pixbuf
= g
.gdk
.pixbuf_new_from_file(rox
.app_dir
+"/images/marker.png")
227 self
.text
.set_mark_category_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_marks(show_line_markers
.int_value
)
517 self
.text
.set_auto_indent(auto_indent
.int_value
)
518 self
.text
.set_tab_width(tab_width
.int_value
)
519 self
.text
.set_insert_spaces_instead_of_tabs(use_spaces_for_tabs
.int_value
)
520 self
.text
.set_right_margin(right_margin
.int_value
)
521 self
.text
.set_show_right_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
!= int(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
)
742 self
.savebox
.set_transient_for(self
)
745 def help(self
, button
= None):
746 filer
.open_dir(os
.path
.join(rox
.app_dir
, 'Help'))
748 def save_to_stream(self
, stream
):
749 s
= self
.buffer.get_start_iter()
750 e
= self
.buffer.get_end_iter()
751 stream
.write(self
.buffer.get_text(s
, e
, True))
753 def set_uri(self
, uri
):
755 self
.buffer.set_modified(False)
761 def change_font(self
):
762 style
= self
.text
.get_style().copy()
763 style
.font
= load_font(options
.get('edit_font'))
764 self
.text
.set_style(style
)
766 def show_options(self
):
769 def set_marked(self
, start
= None, end
= None):
770 "Set the marked region (from the selection if no region is given)."
772 assert not self
.marked
779 start
, end
= self
.get_selection_range()
780 buffer.move_mark(self
.mark_start
, start
)
781 buffer.move_mark(self
.mark_end
, end
)
782 buffer.apply_tag_by_name('marked',
783 buffer.get_iter_at_mark(self
.mark_start
),
784 buffer.get_iter_at_mark(self
.mark_end
))
787 def clear_marked(self
):
792 buffer.remove_tag_by_name('marked',
793 buffer.get_iter_at_mark(self
.mark_start
),
794 buffer.get_iter_at_mark(self
.mark_end
))
796 def undo(self
, widget
= None):
798 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
799 self
.text
.scroll_to_iter(cursor
, 0.05, False)
801 def redo(self
, widget
= None):
803 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
804 self
.text
.scroll_to_iter(cursor
, 0.05, False)
806 def goto(self
, widget
= None):
807 from goto
import Goto
808 self
.set_minibuffer(Goto())
810 def search(self
, widget
= None):
811 if self
.search_minibuffer
is None:
812 from search
import Search
813 self
.search_minibuffer
= Search()
814 self
.set_minibuffer(self
.search_minibuffer
)
816 def search_again(self
, widget
= None):
817 if self
.search_minibuffer
and self
.search_minibuffer
is self
.minibuffer
:
818 self
.minibuffer
.activate() # Search again with same text
820 if self
.minibuffer
is None:
821 # Search mini-buffer not yet open
823 self
.minibuffer
.restore_previous_search()
824 self
.minibuffer
.search_again()
826 def search_replace(self
, widget
= None):
827 from search
import Replace
830 def toggle_bookmark(self
):
831 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
832 line
= cursor
.get_line()
834 markers
= self
.buffer.get_source_marks_at_line(line
, "bookmark")
836 self
.buffer.delete_mark(markers
[0]);
838 marker
= self
.buffer.create_source_mark(name
, "bookmark", cursor
)
840 def next_bookmark(self
):
841 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
842 cursor
.forward_char()
843 found
= self
.buffer.forward_iter_to_source_mark(cursor
, "bookmark")
845 self
.buffer.place_cursor(cursor
)
846 self
.text
.scroll_to_iter(cursor
, 0.05, False)
848 def prev_bookmark(self
):
849 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
850 cursor
.backward_char()
851 found
= self
.buffer.backward_iter_to_source_mark(cursor
, "bookmark")
853 self
.buffer.place_cursor(cursor
)
854 self
.text
.scroll_to_iter(cursor
, 0.05, False)
855 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
857 def set_mini_label(self
, label
):
858 self
.mini_label
.set_text(label
)
860 def set_minibuffer(self
, minibuffer
):
861 assert minibuffer
is None or isinstance(minibuffer
, Minibuffer
)
864 self
.minibuffer
.close()
866 self
.minibuffer
= None
869 self
.mini_entry
.set_text('')
870 self
.minibuffer
= minibuffer
871 minibuffer
.setup(self
)
872 self
.mini_entry
.grab_focus()
873 self
.mini_hbox
.show_all()
875 self
.mini_hbox
.hide()
876 self
.text
.grab_focus()
878 def mini_key_press(self
, entry
, kev
):
879 if kev
.keyval
== g
.keysyms
.Escape
:
880 self
.set_minibuffer(None)
882 if kev
.keyval
== g
.keysyms
.Return
or kev
.keyval
== g
.keysyms
.KP_Enter
:
883 self
.minibuffer
.activate()
886 return self
.minibuffer
.key_press(kev
)
888 def mini_changed(self
, entry
):
889 if not self
.minibuffer
:
891 self
.minibuffer
.changed()
893 def mini_show_info(self
, *unused
):
894 assert self
.minibuffer
896 self
.info_box
.destroy()
897 self
.info_box
= g
.MessageDialog(self
, 0, g
.MESSAGE_INFO
, g
.BUTTONS_OK
,
898 self
.minibuffer
.info
)
899 self
.info_box
.set_title(_('Minibuffer help'))
902 self
.info_box
.connect('destroy', destroy
)
904 self
.info_box
.connect('response', lambda w
, r
: w
.destroy())
906 def process_selected(self
, process
):
907 """Calls process(line) on each line in the selection, or each line in the file
908 if there is no selection. If the result is not None, the text is replaced."""
909 self
.buffer.begin_user_action()
911 self
._process
_selected
(process
)
913 self
.buffer.end_user_action()
915 def _process_selected(self
, process
):
916 if self
.has_selection():
918 start
, end
= self
.get_selection_range()
919 if start
.compare(end
) > 0:
922 start
, end
= self
.get_selection_range()
923 if start
.compare(end
) > 0:
927 return self
.buffer.get_end_iter()
928 start
= self
.buffer.get_start_iter()
931 while start
.compare(end
) <= 0:
932 line_end
= start
.copy()
933 line_end
.forward_to_line_end()
934 if line_end
.compare(end
) >= 0:
936 line
= self
.buffer.get_text(start
, line_end
, False)
939 self
.buffer.move_mark(self
.mark_tmp
, start
)
940 self
.buffer.insert(line_end
, new
)
941 start
= self
.buffer.get_iter_at_mark(self
.mark_tmp
)
942 line_end
= start
.copy()
943 line_end
.forward_chars(len(line
.decode('utf-8')))
944 self
.buffer.delete(start
, line_end
)
946 start
= self
.buffer.get_iter_at_mark(self
.mark_tmp
)
948 if not start
.forward_line(): break
950 def set_word_wrap(self
, value
):
951 self
._word
_wrap
= value
952 self
.wrap_button
.set_active(value
)
954 self
.text
.set_wrap_mode(g
.WRAP_WORD
)
956 self
.text
.set_wrap_mode(g
.WRAP_NONE
)
958 word_wrap
= property(lambda self
: self
._word
_wrap
, set_word_wrap
)
960 class SelectionSaver(Saveable
):
961 def __init__(self
, window
):
965 def save_to_stream(self
, stream
):
966 s
, e
= self
.window
.get_marked_range()
967 stream
.write(self
.window
.buffer.get_text(s
, e
, True))
970 # Called when savebox is remove. Get rid of the selection marker
971 self
.window
.clear_marked()