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
, gtksourceview
47 from rox
.Menu
import Menu
, set_save_name
, SubMenu
, Separator
, Action
, ToggleItem
49 OptionsBox
.widget_registry
['source-view-only'] = optional_section(have_sourceview
)
51 default_font
= Option('default_font', 'serif')
53 background_colour
= Option('background', '#fff')
54 foreground_colour
= Option('foreground', '#000')
56 auto_indent
= Option('autoindent', '1')
57 word_wrap
= Option('wordwrap', '1')
59 layout_left_margin
= Option('layout_left_margin', 2)
60 layout_right_margin
= Option('layout_right_margin', 4)
62 layout_before_para
= Option('layout_before_para', 0)
63 layout_after_para
= Option('layout_after_para', 0)
64 layout_inside_para
= Option('layout_inside_para', 0)
65 layout_indent_para
= Option('layout_indent_para', 2)
67 right_margin
= Option('right_margin', 80)
68 show_margin
= Option('show_margin', True)
69 smart_home_end
= Option('smart_home_end', True)
70 show_line_numbers
= Option('show_line_numbers', True)
71 show_line_markers
= Option('show_line_markers', True)
72 tab_width
= Option('tab_width', 4)
73 use_spaces_for_tabs
= Option('use_spaces_for_tabs', False)
75 show_toolbar
= Option('show_toolbar', 1)
77 set_save_name('Edit', site
='rox.sourceforge.net')
80 Action(_('Cut'), 'cut', '<Ctrl>X', g
.STOCK_CUT
),
81 Action(_('Copy'), 'copy', '<Ctrl>C', g
.STOCK_COPY
),
82 Action(_('Paste'), 'paste', '<Ctrl>V', g
.STOCK_PASTE
),
84 Action(_('Undo'), 'undo', '<Ctrl>Z', g
.STOCK_UNDO
),
85 Action(_('Redo'), 'redo', '<Ctrl>Y', g
.STOCK_REDO
),
87 Action(_('Search...'), 'search', 'F4', g
.STOCK_FIND
),
88 Action(_('Search Again'), 'search_again', '<Shift>F4', g
.STOCK_GO_FORWARD
),
89 Action(_('Search and Replace....'), 'search_replace',
90 '<Ctrl>F4', g
.STOCK_FIND_AND_REPLACE
),
91 Action(_('Goto line...'), 'goto', 'F5', g
.STOCK_JUMP_TO
),
96 Action(_('Toggle Bookmark'), 'toggle_bookmark', '<Ctrl>F2'),
97 Action(_('Next Bookmark'), 'next_bookmark', 'F2'),
98 Action(_('Previous Bookmark'), 'prev_bookmark', '<Shift>F2'),
102 edit_menu
+= bookmark_menu
104 menu
= Menu('main', [
106 Action(_('Save'), 'save', '<Ctrl>S', g
.STOCK_SAVE
),
107 Action(_('Save As...'), 'save_as', 'F3', g
.STOCK_SAVE_AS
),
108 Action(_('Open Parent'), 'up', '', g
.STOCK_GO_UP
),
109 Action(_('Show Changes'), 'diff', '', 'rox-diff'),
110 ToggleItem(_('Word Wrap'), 'word_wrap'),
111 Action(_('Close'), 'close', '', g
.STOCK_CLOSE
),
113 Action(_('New'), 'new', '', g
.STOCK_NEW
)]),
115 SubMenu(_('Edit'), edit_menu
),
117 Action(_('Options'), 'show_options', '', g
.STOCK_PROPERTIES
),
118 Action(_('Help'), 'help', 'F1', g
.STOCK_HELP
),
122 "iso8859_1", "iso8859_2", "iso8859_3", "iso8859_4", "iso8859_5",
123 "iso8859_6", "iso8859_7", "iso8859_8", "iso8859_9", "iso8859_10",
124 "iso8859_13", "iso8859_14", "iso8859_15",
125 "ascii", "base64_codec", "charmap",
126 "cp037", "cp1006", "cp1026", "cp1140", "cp1250", "cp1251", "cp1252",
127 "cp1253", "cp1254", "cp1255", "cp1256", "cp1257", "cp1258", "cp424",
128 "cp437", "cp500", "cp737", "cp775", "cp850", "cp852", "cp855", "cp856",
129 "cp857", "cp860", "cp861", "cp862", "cp863", "cp864", "cp865", "cp866",
130 "cp869", "cp874", "cp875", "hex_codec",
133 "mac_cyrillic", "mac_greek", "mac_iceland", "mac_latin2", "mac_roman", "mac_turkish",
134 "mbcs", "quopri_codec", "raw_unicode_escape",
136 "utf_16_be", "utf_16_le", "utf_16", "utf_7", "utf_8", "uu_codec",
140 class Abort(Exception):
145 """Called when the minibuffer is opened."""
147 def key_press(self
, kev
):
148 """A keypress event in the minibuffer text entry."""
151 """The minibuffer text has changed."""
154 """Return or Enter pressed."""
157 """Called when the minibuffer is closed.
158 Remove any widgets created in setup."""
160 info
= 'Press Escape to close the minibuffer.'
162 class DiffLoader(XDSLoader
):
163 def __init__(self
, window
):
164 XDSLoader
.__init
__(self
, ['text/plain'])
167 def xds_load_from_file(self
, path
):
168 self
.window
.diff(path
= path
)
170 def xds_load_from_stream(self
, name
, type, stream
):
171 tmp
= diff
.Tmp(suffix
= '-' + (name
or 'tmp'))
173 shutil
.copyfileobj(stream
, tmp
)
175 self
.window
.diff(path
= tmp
.name
)
177 class DndToolbar(g
.Toolbar
, XDSLoader
):
179 g
.Toolbar
.__init
__(self
)
180 XDSLoader
.__init
__(self
, ['text/plain'])
182 def xds_load_from_path(self
, path
):
185 def xds_load_from_stream(self
, name
, type, stream
):
186 EditWindow(contents
= stream
.read())
188 class EditWindow(rox
.Window
, XDSLoader
, Saveable
):
192 search_minibuffer
= None # (persists for search_again)
194 def __init__(self
, filename
= None, show
= True, line_number
= None, contents
= None):
195 rox
.Window
.__init
__(self
)
196 XDSLoader
.__init
__(self
, ['text/plain', 'UTF8_STRING'])
202 app_options
.add_notify(self
.update_styles
)
206 if not os
.path
.exists(filename
):
208 filename2
, line_number
= filename
.split(':')
209 line_number
= long(line_number
)
211 # Either there was no ':', or it wasn't followed by a number
215 self
.uri
= os
.path
.abspath(filename
)
216 self
.mime_type
= mime
.get_type(self
.uri
, 1)
219 self
.mime_type
= mime
.lookup('text', 'plain')
221 self
.buffer = Buffer()
224 self
.text
= gtksourceview
.SourceView(self
.buffer)
225 pixbuf
= g
.gdk
.pixbuf_new_from_file(rox
.app_dir
+"/images/marker.png")
226 self
.text
.set_marker_pixbuf("bookmark", pixbuf
)
228 self
.buffer.set_type(self
.mime_type
)
230 self
.text
= g
.TextView()
231 self
.text
.set_buffer(self
.buffer)
233 self
.text
.set_size_request(10, 10)
234 self
.xds_proxy_for(self
.text
)
236 self
.insert_mark
= self
.buffer.get_mark('insert')
237 self
.selection_bound_mark
= self
.buffer.get_mark('selection_bound')
238 start
= self
.buffer.get_start_iter()
239 self
.mark_start
= self
.buffer.create_mark('mark_start', start
, True)
240 self
.mark_end
= self
.buffer.create_mark('mark_end', start
, False)
241 self
.mark_tmp
= self
.buffer.create_mark('mark_tmp', start
, False)
242 tag
= self
.buffer.create_tag('marked')
243 tag
.set_property('background', 'green')
246 # When searching, this is where the cursor was when the minibuffer
248 start
= self
.buffer.get_start_iter()
249 self
.search_base
= self
.buffer.create_mark('search_base', start
, True)
255 tools
.set_style(g
.TOOLBAR_ICONS
)
256 vbox
.pack_start(tools
, False, True, 0)
258 self
.status_label
= g
.Label('')
259 tools
.append_widget(self
.status_label
, None, None)
260 tools
.insert_stock(g
.STOCK_HELP
, _('Help'), None, self
.help, None, 0)
261 diff
= tools
.insert_stock('rox-diff', _('Show changes from saved copy.\n'
262 'Or, drop a backup file onto this button to see changes from that.'),
263 None, self
.diff
, None, 0)
264 DiffLoader(self
).xds_proxy_for(diff
)
268 image_spell
= g
.Image()
269 image_spell
.set_from_stock(g
.STOCK_SPELL_CHECK
, tools
.get_icon_size())
270 self
.spell_button
= tools
.insert_element(g
.TOOLBAR_CHILD_TOGGLEBUTTON
,
271 None, _("Check Spelling"), _("Check Spelling"), None,
272 image_spell
, self
.toggle_spell
, None, 0)
274 image_wrap
= g
.Image()
275 image_wrap
.set_from_file(rox
.app_dir
+ '/images/rox-word-wrap.png')
276 self
.wrap_button
= tools
.insert_element(g
.TOOLBAR_CHILD_TOGGLEBUTTON
,
277 None, _("Word Wrap"), _("Word Wrap"), None, image_wrap
,
278 lambda button
: self
.set_word_wrap(button
.get_active()),
280 tools
.insert_stock(g
.STOCK_REDO
, _('Redo'), None, self
.redo
, None, 0)
281 tools
.insert_stock(g
.STOCK_UNDO
, _('Undo'), None, self
.undo
, None, 0)
282 tools
.insert_stock(g
.STOCK_FIND_AND_REPLACE
, _('Replace'), None, self
.search_replace
, None, 0)
283 tools
.insert_stock(g
.STOCK_FIND
, _('Search'), None, self
.search
, None, 0)
284 tools
.insert_stock(g
.STOCK_SAVE_AS
, _('Save As'), None, self
.save_as
, None, 0)
285 self
.save_button
= tools
.insert_stock(g
.STOCK_SAVE
, _('Save'), None, self
.save
, None, 0)
286 tools
.insert_stock(g
.STOCK_GO_UP
, _('Up'), None, self
.up
, None, 0)
287 tools
.insert_stock(g
.STOCK_CLOSE
, _('Close'), None, self
.close
, None, 0)
288 # Set minimum size to ignore the label
289 tools
.set_size_request(tools
.size_request()[0], -1)
293 swin
= g
.ScrolledWindow()
294 swin
.set_policy(g
.POLICY_AUTOMATIC
, g
.POLICY_AUTOMATIC
)
295 vbox
.pack_start(swin
, True, True)
299 # Aim for a width of about 100 chars
300 layout
= self
.text
.create_pango_layout("mmmmmiiiii")
301 default_width
= layout
.get_pixel_extents()[1][2] * 10
302 self
.set_default_size(min(g
.gdk
.screen_width() * 2 / 3, default_width
),
303 g
.gdk
.screen_height() / 2)
311 # Create the minibuffer
312 self
.mini_hbox
= g
.HBox(False)
314 info
= rox
.ButtonMixed(g
.STOCK_DIALOG_INFO
, '')
315 info
.set_relief(g
.RELIEF_NONE
)
316 info
.unset_flags(g
.CAN_FOCUS
)
317 info
.connect('clicked', self
.mini_show_info
)
319 close
= rox
.ButtonMixed(g
.STOCK_STOP
, '')
320 close
.set_relief(g
.RELIEF_NONE
)
321 close
.unset_flags(g
.CAN_FOCUS
)
322 close
.connect('clicked', lambda e
: self
.set_minibuffer(None))
324 self
.mini_hbox
.pack_end(info
, False, True, 0)
325 self
.mini_hbox
.pack_start(close
, False, True, 0)
326 self
.mini_label
= g
.Label('')
327 self
.mini_hbox
.pack_start(self
.mini_label
, False, True, 0)
328 self
.mini_entry
= g
.Entry()
329 self
.mini_hbox
.pack_start(self
.mini_entry
, True, True, 0)
330 vbox
.pack_start(self
.mini_hbox
, False, True)
331 self
.mini_entry
.connect('key-press-event', self
.mini_key_press
)
332 self
.mini_entry
.connect('changed', self
.mini_changed
)
334 self
.connect('destroy', self
.destroyed
)
336 self
.connect('delete-event', self
.delete_event
)
337 self
.text
.grab_focus()
338 self
.text
.connect('key-press-event', self
.key_press
)
340 # FIXME: why does this freeze Edit?
342 #if self.mime_type.media == 'text' and self.mime_type.subtype == 'plain':
344 ##self.spell.set_language ("en_US")
346 def update_current_line(*unused
):
347 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
348 bound
= self
.buffer.get_iter_at_mark(self
.selection_bound_mark
)
349 if cursor
.compare(bound
) == 0:
350 n_lines
= self
.buffer.get_line_count()
351 self
.status_label
.set_text(_('Line %s of %d') % (cursor
.get_line() + 1, n_lines
))
353 n_lines
= abs(cursor
.get_line() - bound
.get_line()) + 1
355 n_chars
= abs(cursor
.get_line_offset() - bound
.get_line_offset())
357 bytes
= to_utf8(self
.buffer.get_text(cursor
, bound
, False))[0]
358 self
.status_label
.set_text(_('One character selected (%s)') %
359 ' '.join(map(lambda x
: '0x%2x' % ord(x
), bytes
)))
361 self
.status_label
.set_text(_('%d characters selected') % n_chars
)
363 self
.status_label
.set_text(_('%d lines selected') % n_lines
)
364 self
.buffer.connect('mark-set', update_current_line
)
365 self
.buffer.connect('changed', update_current_line
)
368 # Loading might take a while, so get something on the screen
374 self
.load_file(filename
)
376 self
.save_last_stat
= os
.stat(filename
)
381 self
.insert_data(contents
)
383 self
.buffer.connect('modified-changed', self
.update_title
)
384 self
.buffer.set_modified(False)
386 def button_press(text
, event
):
387 if event
.button
!= 3:
389 #self.text.emit('populate-popup', menu.menu)
390 menu
.popup(self
, event
)
392 self
.text
.connect('button-press-event', button_press
)
393 self
.text
.connect('popup-menu', lambda text
: menu
.popup(self
, None))
395 menu
.attach(self
, self
)
396 self
.buffer.place_cursor(self
.buffer.get_start_iter())
397 self
.buffer.start_undo_history()
400 iter = self
.buffer.get_iter_at_line(int(line_number
) - 1)
401 self
.buffer.place_cursor(iter)
402 self
.text
.scroll_to_mark(self
.insert_mark
, 0.05, False)
404 def key_press(self
, text
, kev
):
405 if kev
.keyval
== g
.keysyms
.Return
or kev
.keyval
== g
.keysyms
.KP_Enter
:
406 return self
.auto_indent()
407 elif kev
.keyval
== g
.keysyms
.Tab
or kev
.keyval
== g
.keysyms
.KP_Tab
:
408 return self
.indent_block()
409 elif kev
.keyval
== g
.keysyms
.ISO_Left_Tab
:
410 return self
.unindent_block()
411 elif kev
.keyval
== g
.keysyms
.Escape
:
412 self
.set_minibuffer(None)
416 def auto_indent(self
):
417 if not auto_indent
.int_value
:
420 start
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
422 start
.set_line_offset(0)
423 end
.forward_to_line_end()
424 line
= self
.buffer.get_text(start
, end
, False)
427 if self
.mime_type
.subtype
== 'x-python':
429 l
= line
.split('\n')[0]
432 if l
.endswith(':') and not l
.startswith('#'):
433 if use_spaces_for_tabs
.int_value
:
434 indent
+= ' ' * tab_width
.int_value
437 elif have_sourceview
:
446 self
.buffer.begin_user_action()
447 self
.buffer.insert_at_cursor('\n' + indent
)
448 self
.buffer.end_user_action()
451 def indent_block(self
):
453 (start
, end
) = self
.buffer.get_selection_bounds()
454 start_line
= start
.get_line()
455 end_line
= end
.get_line()
456 self
.buffer.begin_user_action()
457 for i
in range(start_line
, end_line
+1):
458 iter = self
.buffer.get_iter_at_line(i
)
459 self
.buffer.insert(iter, '\t')
460 self
.buffer.end_user_action()
465 def unindent_block(self
):
467 (start
, end
) = self
.buffer.get_selection_bounds()
468 start_line
= start
.get_line()
469 end_line
= end
.get_line()
470 self
.buffer.begin_user_action()
471 for i
in range(start_line
, end_line
+1):
472 iter = self
.buffer.get_iter_at_line(i
)
473 if iter.get_char() == '\t':
474 next_char
= iter.copy()
475 next_char
.forward_char()
476 self
.buffer.delete(iter, next_char
)
477 self
.buffer.end_user_action()
482 def destroyed(self
, widget
):
483 app_options
.remove_notify(self
.update_styles
)
485 def update_styles(self
):
488 font
= pango
.FontDescription(default_font
.value
)
489 bg
= g
.gdk
.color_parse(background_colour
.value
)
490 fg
= g
.gdk
.color_parse(foreground_colour
.value
)
492 self
.text
.set_left_margin(layout_left_margin
.int_value
)
493 self
.text
.set_right_margin(layout_right_margin
.int_value
)
495 self
.text
.set_pixels_above_lines(layout_before_para
.int_value
)
496 self
.text
.set_pixels_below_lines(layout_after_para
.int_value
)
497 self
.text
.set_pixels_inside_wrap(layout_inside_para
.int_value
)
498 self
.text
.set_indent(layout_indent_para
.int_value
)
500 self
.word_wrap
= bool(word_wrap
.int_value
)
502 if show_toolbar
.int_value
:
507 rox
.report_exception()
509 self
.text
.modify_font(font
)
510 self
.text
.modify_base(g
.STATE_NORMAL
, bg
)
511 self
.text
.modify_text(g
.STATE_NORMAL
, fg
)
514 self
.text
.set_show_line_numbers(show_line_numbers
.int_value
)
515 self
.text
.set_show_line_markers(show_line_markers
.int_value
)
516 self
.text
.set_auto_indent(auto_indent
.int_value
)
517 self
.text
.set_tabs_width(tab_width
.int_value
)
518 self
.text
.set_insert_spaces_instead_of_tabs(use_spaces_for_tabs
.int_value
)
519 self
.text
.set_margin(right_margin
.int_value
)
520 self
.text
.set_show_margin(show_margin
.int_value
)
521 self
.text
.set_smart_home_end(smart_home_end
.int_value
)
522 if self
.buffer.language
== 'Python':
523 self
.text
.set_auto_indent(False)
525 def cut(self
): self
.text
.emit('cut_clipboard')
526 def copy(self
): self
.text
.emit('copy_clipboard')
527 def paste(self
): self
.text
.emit('paste_clipboard')
529 def delete_event(self
, window
, event
):
530 if self
.buffer.get_modified():
531 self
.save_as(discard
= 1)
535 def update_title(self
, *unused
):
536 title
= self
.uri
or '<'+_('Untitled')+'>'
537 if self
.buffer.get_modified():
539 self
.save_button
.set_sensitive(True)
541 self
.save_button
.set_sensitive(False)
542 self
.set_title(title
)
544 def xds_load_from_stream(self
, name
, t
, stream
):
545 if t
== 'UTF8_STRING':
546 return # Gtk will handle it
548 dnd_mark
= self
.buffer.get_mark('gtk_drag_target')
550 dnd_pos
= self
.buffer.get_iter_at_mark(dnd_mark
)
551 self
.buffer.move_mark(self
.insert_mark
, dnd_pos
)
552 self
.insert_data(stream
.read())
556 def get_encoding(self
, message
):
557 "Returns (encoding, errors), or raises Abort to cancel."
558 box
= g
.MessageDialog(self
, 0, g
.MESSAGE_QUESTION
, g
.BUTTONS_CANCEL
, message
)
559 box
.set_has_separator(False)
562 box
.vbox
.pack_start(frame
, True, True)
563 frame
.set_border_width(6)
565 hbox
= g
.HBox(False, 4)
566 hbox
.set_border_width(6)
568 hbox
.pack_start(g
.Label(_('Encoding:')), False, True, 0)
570 combo
.disable_activate()
571 combo
.entry
.connect('activate', lambda w
: box
.activate_default())
572 combo
.set_popdown_strings(known_codecs
)
573 hbox
.pack_start(combo
, True, True, 0)
574 ignore_errors
= g
.CheckButton(_('Ignore errors'))
575 hbox
.pack_start(ignore_errors
, False, True)
580 box
.add_button(g
.STOCK_CONVERT
, g
.RESPONSE_YES
)
581 box
.set_default_response(g
.RESPONSE_YES
)
584 combo
.entry
.grab_focus()
587 if resp
!= g
.RESPONSE_YES
:
591 if ignore_errors
.get_active():
595 encoding
= combo
.entry
.get_text()
597 codecs
.getdecoder(encoding
)
600 rox
.alert(_("Unknown encoding '%s'") % encoding
)
604 return encoding
, errors
606 def insert_data(self
, data
):
611 decoder
= codecs
.getdecoder(encoding
)
613 data
= decoder(data
, errors
)[0]
614 if errors
== 'strict':
615 assert '\0' not in data
618 data
= data
.replace('\0', '\\0')
623 encoding
, errors
= self
.get_encoding(
624 _("Data is not valid %s. Please select the file's encoding. "
625 "Turn on 'ignore errors' to try and load it anyway.")
628 self
.buffer.begin_user_action()
629 self
.buffer.insert_at_cursor(data
)
630 self
.buffer.end_user_action()
633 def load_file(self
, path
):
638 file = open(path
, 'r')
639 contents
= file.read()
643 self
.buffer.begin_not_undoable_action()
644 self
.insert_data(contents
)
645 self
.buffer.end_not_undoable_action()
649 rox
.report_exception()
652 def close(self
, button
= None):
653 if self
.buffer.get_modified():
654 self
.save_as(discard
= 1)
661 def up(self
, button
= None):
663 filer
.show_file(self
.uri
)
665 rox
.alert(_('File is not saved to disk yet'))
668 def toggle_spell(self
, button
= None):
672 self
.spell_button
.set_active(False)
673 elif not self
.spell_button
.get_active():
674 # Probably a failed attempt to turn it on
678 self
.spell
= gtkspell
.Spell(self
.text
)
679 self
.spell_button
.set_active(True)
680 except Exception, ex
:
682 self
.spell_button
.set_active(False)
683 rox
.report_exception()
685 #self.spell_button.set_active(self.spell != None)
687 def diff(self
, button
= None, path
= None):
688 path
= path
or self
.uri
690 rox
.alert(_('This file has never been saved; nothing to compare it to!\n'
691 'Note: you can drop a file onto the toolbar button to see '
692 'the changes from that file.'))
694 diff
.show_diff(path
, self
.save_to_stream
)
696 def has_selection(self
):
697 s
, e
= self
.get_selection_range()
698 return not e
.equal(s
)
700 def get_marked_range(self
):
701 s
= self
.buffer.get_iter_at_mark(self
.mark_start
)
702 e
= self
.buffer.get_iter_at_mark(self
.mark_end
)
707 def get_selection_range(self
):
708 s
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
709 e
= self
.buffer.get_iter_at_mark(self
.selection_bound_mark
)
714 def save(self
, widget
= None):
716 self
.save_to_file(self
.uri
)
717 self
.buffer.set_modified(False)
719 self
.save_as(discard
=0)
721 def save_as(self
, widget
= None, discard
= 0):
722 from rox
.saving
import SaveBox
725 self
.savebox
.destroy()
728 self
.mime_type
= mime
.get_type(self
.uri
, 1)
730 self
.mime_type
= mime
.lookup('text', 'plain')
732 mime_text
= self
.mime_type
.media
+ '/' + self
.mime_type
.subtype
734 if self
.has_selection() and not discard
:
735 saver
= SelectionSaver(self
)
736 self
.savebox
= SaveBox(saver
, 'Selection', mime_text
)
737 self
.savebox
.connect('destroy', lambda w
: saver
.destroy())
739 uri
= self
.uri
or _('TextFile')
740 self
.savebox
= SaveBox(self
, uri
, mime_text
, discard
)
743 def help(self
, button
= None):
744 filer
.open_dir(os
.path
.join(rox
.app_dir
, 'Help'))
746 def save_to_stream(self
, stream
):
747 s
= self
.buffer.get_start_iter()
748 e
= self
.buffer.get_end_iter()
749 stream
.write(self
.buffer.get_text(s
, e
, True))
751 def set_uri(self
, uri
):
753 self
.buffer.set_modified(False)
759 def change_font(self
):
760 style
= self
.text
.get_style().copy()
761 style
.font
= load_font(options
.get('edit_font'))
762 self
.text
.set_style(style
)
764 def show_options(self
):
767 def set_marked(self
, start
= None, end
= None):
768 "Set the marked region (from the selection if no region is given)."
770 assert not self
.marked
777 start
, end
= self
.get_selection_range()
778 buffer.move_mark(self
.mark_start
, start
)
779 buffer.move_mark(self
.mark_end
, end
)
780 buffer.apply_tag_by_name('marked',
781 buffer.get_iter_at_mark(self
.mark_start
),
782 buffer.get_iter_at_mark(self
.mark_end
))
785 def clear_marked(self
):
790 buffer.remove_tag_by_name('marked',
791 buffer.get_iter_at_mark(self
.mark_start
),
792 buffer.get_iter_at_mark(self
.mark_end
))
794 def undo(self
, widget
= None):
796 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
797 self
.text
.scroll_to_iter(cursor
, 0.05, False)
799 def redo(self
, widget
= None):
801 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
802 self
.text
.scroll_to_iter(cursor
, 0.05, False)
804 def goto(self
, widget
= None):
805 from goto
import Goto
806 self
.set_minibuffer(Goto())
808 def search(self
, widget
= None):
809 if self
.search_minibuffer
is None:
810 from search
import Search
811 self
.search_minibuffer
= Search()
812 self
.set_minibuffer(self
.search_minibuffer
)
814 def search_again(self
, widget
= None):
815 if self
.search_minibuffer
and self
.search_minibuffer
is self
.minibuffer
:
816 self
.minibuffer
.activate() # Search again with same text
818 if self
.minibuffer
is None:
819 # Search mini-buffer not yet open
821 self
.minibuffer
.restore_previous_search()
822 self
.minibuffer
.search_again()
824 def search_replace(self
, widget
= None):
825 from search
import Replace
828 def toggle_bookmark(self
):
829 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
830 name
= str(cursor
.get_line())
831 marker
= self
.buffer.get_marker(name
)
833 self
.buffer.delete_marker(marker
);
835 marker
= self
.buffer.create_marker(name
, "bookmark", cursor
);
837 def next_bookmark(self
):
838 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
839 cursor
.forward_char()
840 marker
= self
.buffer.get_next_marker(cursor
)
842 self
.buffer.get_iter_at_marker (cursor
, marker
)
843 self
.buffer.place_cursor(cursor
)
844 self
.text
.scroll_to_iter(cursor
, 0.05, False)
846 def prev_bookmark(self
):
847 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
848 cursor
.backward_char()
849 marker
= self
.buffer.get_prev_marker(cursor
)
851 self
.buffer.get_iter_at_marker (cursor
, marker
)
852 self
.buffer.place_cursor(cursor
)
853 self
.text
.scroll_to_iter(cursor
, 0.05, False)
855 def set_mini_label(self
, label
):
856 self
.mini_label
.set_text(label
)
858 def set_minibuffer(self
, minibuffer
):
859 assert minibuffer
is None or isinstance(minibuffer
, Minibuffer
)
862 self
.minibuffer
.close()
864 self
.minibuffer
= None
867 self
.mini_entry
.set_text('')
868 self
.minibuffer
= minibuffer
869 minibuffer
.setup(self
)
870 self
.mini_entry
.grab_focus()
871 self
.mini_hbox
.show_all()
873 self
.mini_hbox
.hide()
874 self
.text
.grab_focus()
876 def mini_key_press(self
, entry
, kev
):
877 if kev
.keyval
== g
.keysyms
.Escape
:
878 self
.set_minibuffer(None)
880 if kev
.keyval
== g
.keysyms
.Return
or kev
.keyval
== g
.keysyms
.KP_Enter
:
881 self
.minibuffer
.activate()
884 return self
.minibuffer
.key_press(kev
)
886 def mini_changed(self
, entry
):
887 if not self
.minibuffer
:
889 self
.minibuffer
.changed()
891 def mini_show_info(self
, *unused
):
892 assert self
.minibuffer
894 self
.info_box
.destroy()
895 self
.info_box
= g
.MessageDialog(self
, 0, g
.MESSAGE_INFO
, g
.BUTTONS_OK
,
896 self
.minibuffer
.info
)
897 self
.info_box
.set_title(_('Minibuffer help'))
900 self
.info_box
.connect('destroy', destroy
)
902 self
.info_box
.connect('response', lambda w
, r
: w
.destroy())
904 def process_selected(self
, process
):
905 """Calls process(line) on each line in the selection, or each line in the file
906 if there is no selection. If the result is not None, the text is replaced."""
907 self
.buffer.begin_user_action()
909 self
._process
_selected
(process
)
911 self
.buffer.end_user_action()
913 def _process_selected(self
, process
):
914 if self
.has_selection():
916 start
, end
= self
.get_selection_range()
917 if start
.compare(end
) > 0:
920 start
, end
= self
.get_selection_range()
921 if start
.compare(end
) > 0:
925 return self
.buffer.get_end_iter()
926 start
= self
.buffer.get_start_iter()
929 while start
.compare(end
) <= 0:
930 line_end
= start
.copy()
931 line_end
.forward_to_line_end()
932 if line_end
.compare(end
) >= 0:
934 line
= self
.buffer.get_text(start
, line_end
, False)
937 self
.buffer.move_mark(self
.mark_tmp
, start
)
938 self
.buffer.insert(line_end
, new
)
939 start
= self
.buffer.get_iter_at_mark(self
.mark_tmp
)
940 line_end
= start
.copy()
941 line_end
.forward_chars(len(line
.decode('utf-8')))
942 self
.buffer.delete(start
, line_end
)
944 start
= self
.buffer.get_iter_at_mark(self
.mark_tmp
)
946 if not start
.forward_line(): break
948 def set_word_wrap(self
, value
):
949 self
._word
_wrap
= value
950 self
.wrap_button
.set_active(value
)
952 self
.text
.set_wrap_mode(g
.WRAP_WORD
)
954 self
.text
.set_wrap_mode(g
.WRAP_NONE
)
956 word_wrap
= property(lambda self
: self
._word
_wrap
, set_word_wrap
)
958 class SelectionSaver(Saveable
):
959 def __init__(self
, window
):
963 def save_to_stream(self
, stream
):
964 s
, e
= self
.window
.get_marked_range()
965 stream
.write(self
.window
.buffer.get_text(s
, e
, True))
968 # Called when savebox is remove. Get rid of the selection marker
969 self
.window
.clear_marked()