3 from rox
import g
, filer
, app_options
, mime
5 from rox
.loading
import XDSLoader
6 from rox
.options
import Option
7 from rox
import OptionsBox
8 from rox
.saving
import Saveable
13 # WARNING: This is a temporary hack, until we write a way choose between
14 # the two ways of doing toolbars or we abandon the old method entirely
16 warnings
.filterwarnings('ignore', category
=DeprecationWarning,
20 def optional_section(available
):
21 """If requires is None, the section is enabled. Otherwise,
22 the section is shaded and the requires message is shown at
25 def build_enabled_section(box
, node
, label
):
26 return box
.do_box(node
, None, g
.VBox(False, 0))
27 return build_enabled_section
29 def build_disabled_section(box
, node
, label
):
30 assert label
is not None
31 box
, = box
.do_box(node
, None, g
.VBox(False, 0))
32 box
.set_sensitive(False)
33 frame
= g
.Frame(label
)
34 box
.set_border_width(4)
37 return build_disabled_section
45 to_utf8
= codecs
.getencoder('utf-8')
47 from buffer import Buffer
, have_sourceview
, gtksourceview
48 from rox
.Menu
import Menu
, set_save_name
, SubMenu
, Separator
, Action
, ToggleItem
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'])
198 self
.set_default_size(g
.gdk
.screen_width() * 2 / 3,
199 g
.gdk
.screen_height() / 2)
205 app_options
.add_notify(self
.update_styles
)
209 if not os
.path
.exists(filename
):
211 filename2
, line_number
= filename
.split(':')
212 line_number
= long(line_number
)
214 # Either there was no ':', or it wasn't followed by a number
218 self
.uri
= os
.path
.abspath(filename
)
219 self
.mime_type
= mime
.get_type(self
.uri
, 1)
222 self
.mime_type
= mime
.lookup('text', 'plain')
224 self
.buffer = Buffer()
227 self
.text
= gtksourceview
.SourceView(self
.buffer)
228 pixbuf
= g
.gdk
.pixbuf_new_from_file(rox
.app_dir
+"/images/marker.png")
229 self
.text
.set_marker_pixbuf("bookmark", pixbuf
)
231 self
.buffer.set_type(self
.mime_type
)
233 self
.text
= g
.TextView()
234 self
.text
.set_buffer(self
.buffer)
236 self
.text
.set_size_request(10, 10)
237 self
.xds_proxy_for(self
.text
)
239 self
.insert_mark
= self
.buffer.get_mark('insert')
240 self
.selection_bound_mark
= self
.buffer.get_mark('selection_bound')
241 start
= self
.buffer.get_start_iter()
242 self
.mark_start
= self
.buffer.create_mark('mark_start', start
, True)
243 self
.mark_end
= self
.buffer.create_mark('mark_end', start
, False)
244 self
.mark_tmp
= self
.buffer.create_mark('mark_tmp', start
, False)
245 tag
= self
.buffer.create_tag('marked')
246 tag
.set_property('background', 'green')
249 # When searching, this is where the cursor was when the minibuffer
251 start
= self
.buffer.get_start_iter()
252 self
.search_base
= self
.buffer.create_mark('search_base', start
, True)
258 tools
.set_style(g
.TOOLBAR_ICONS
)
259 vbox
.pack_start(tools
, False, True, 0)
261 self
.status_label
= g
.Label('')
262 tools
.append_widget(self
.status_label
, None, None)
263 tools
.insert_stock(g
.STOCK_HELP
, _('Help'), None, self
.help, None, 0)
264 diff
= tools
.insert_stock('rox-diff', _('Show changes from saved copy.\n'
265 'Or, drop a backup file onto this button to see changes from that.'),
266 None, self
.diff
, None, 0)
267 DiffLoader(self
).xds_proxy_for(diff
)
271 image_spell
= g
.Image()
272 image_spell
.set_from_stock(g
.STOCK_SPELL_CHECK
, tools
.get_icon_size())
273 self
.spell_button
= tools
.insert_element(g
.TOOLBAR_CHILD_TOGGLEBUTTON
,
274 None, _("Check Spelling"), _("Check Spelling"), None,
275 image_spell
, self
.toggle_spell
, None, 0)
277 image_wrap
= g
.Image()
278 image_wrap
.set_from_file(rox
.app_dir
+ '/images/rox-word-wrap.png')
279 self
.wrap_button
= tools
.insert_element(g
.TOOLBAR_CHILD_TOGGLEBUTTON
,
280 None, _("Word Wrap"), _("Word Wrap"), None, image_wrap
,
281 lambda button
: self
.set_word_wrap(button
.get_active()),
283 tools
.insert_stock(g
.STOCK_REDO
, _('Redo'), None, self
.redo
, None, 0)
284 tools
.insert_stock(g
.STOCK_UNDO
, _('Undo'), None, self
.undo
, None, 0)
285 tools
.insert_stock(g
.STOCK_FIND_AND_REPLACE
, _('Replace'), None, self
.search_replace
, None, 0)
286 tools
.insert_stock(g
.STOCK_FIND
, _('Search'), None, self
.search
, None, 0)
287 tools
.insert_stock(g
.STOCK_SAVE_AS
, _('Save As'), None, self
.save_as
, None, 0)
288 self
.save_button
= tools
.insert_stock(g
.STOCK_SAVE
, _('Save'), None, self
.save
, None, 0)
289 tools
.insert_stock(g
.STOCK_GO_UP
, _('Up'), None, self
.up
, None, 0)
290 tools
.insert_stock(g
.STOCK_CLOSE
, _('Close'), None, self
.close
, None, 0)
291 # Set minimum size to ignore the label
292 tools
.set_size_request(tools
.size_request()[0], -1)
296 swin
= g
.ScrolledWindow()
297 swin
.set_policy(g
.POLICY_AUTOMATIC
, g
.POLICY_AUTOMATIC
)
298 vbox
.pack_start(swin
, True, True)
308 # Create the minibuffer
309 self
.mini_hbox
= g
.HBox(False)
311 info
= rox
.ButtonMixed(g
.STOCK_DIALOG_INFO
, '')
312 info
.set_relief(g
.RELIEF_NONE
)
313 info
.unset_flags(g
.CAN_FOCUS
)
314 info
.connect('clicked', self
.mini_show_info
)
316 close
= rox
.ButtonMixed(g
.STOCK_STOP
, '')
317 close
.set_relief(g
.RELIEF_NONE
)
318 close
.unset_flags(g
.CAN_FOCUS
)
319 close
.connect('clicked', lambda e
: self
.set_minibuffer(None))
321 self
.mini_hbox
.pack_end(info
, False, True, 0)
322 self
.mini_hbox
.pack_start(close
, False, True, 0)
323 self
.mini_label
= g
.Label('')
324 self
.mini_hbox
.pack_start(self
.mini_label
, False, True, 0)
325 self
.mini_entry
= g
.Entry()
326 self
.mini_hbox
.pack_start(self
.mini_entry
, True, True, 0)
327 vbox
.pack_start(self
.mini_hbox
, False, True)
328 self
.mini_entry
.connect('key-press-event', self
.mini_key_press
)
329 self
.mini_entry
.connect('changed', self
.mini_changed
)
331 self
.connect('destroy', self
.destroyed
)
333 self
.connect('delete-event', self
.delete_event
)
334 self
.text
.grab_focus()
335 self
.text
.connect('key-press-event', self
.key_press
)
337 # FIXME: why does this freeze Edit?
339 #if self.mime_type.media == 'text' and self.mime_type.subtype == 'plain':
341 ##self.spell.set_language ("en_US")
343 def update_current_line(*unused
):
344 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
345 bound
= self
.buffer.get_iter_at_mark(self
.selection_bound_mark
)
346 if cursor
.compare(bound
) == 0:
347 n_lines
= self
.buffer.get_line_count()
348 self
.status_label
.set_text(_('Line %s of %d') % (cursor
.get_line() + 1, n_lines
))
350 n_lines
= abs(cursor
.get_line() - bound
.get_line()) + 1
352 n_chars
= abs(cursor
.get_line_offset() - bound
.get_line_offset())
354 bytes
= to_utf8(self
.buffer.get_text(cursor
, bound
, False))[0]
355 self
.status_label
.set_text(_('One character selected (%s)') %
356 ' '.join(map(lambda x
: '0x%2x' % ord(x
), bytes
)))
358 self
.status_label
.set_text(_('%d characters selected') % n_chars
)
360 self
.status_label
.set_text(_('%d lines selected') % n_lines
)
361 self
.buffer.connect('mark-set', update_current_line
)
362 self
.buffer.connect('changed', update_current_line
)
365 # Loading might take a while, so get something on the screen
371 self
.load_file(filename
)
373 self
.save_last_stat
= os
.stat(filename
)
378 self
.insert_data(contents
)
380 self
.buffer.connect('modified-changed', self
.update_title
)
381 self
.buffer.set_modified(False)
383 def button_press(text
, event
):
384 if event
.button
!= 3:
386 #self.text.emit('populate-popup', menu.menu)
387 menu
.popup(self
, event
)
389 self
.text
.connect('button-press-event', button_press
)
390 self
.text
.connect('popup-menu', lambda text
: menu
.popup(self
, None))
392 menu
.attach(self
, self
)
393 self
.buffer.place_cursor(self
.buffer.get_start_iter())
394 self
.buffer.start_undo_history()
397 iter = self
.buffer.get_iter_at_line(int(line_number
) - 1)
398 self
.buffer.place_cursor(iter)
399 self
.text
.scroll_to_mark(self
.insert_mark
, 0.05, False)
401 def key_press(self
, text
, kev
):
402 if kev
.keyval
== g
.keysyms
.Return
or kev
.keyval
== g
.keysyms
.KP_Enter
:
403 return self
.auto_indent()
404 elif kev
.keyval
== g
.keysyms
.Tab
or kev
.keyval
== g
.keysyms
.KP_Tab
:
405 return self
.indent_block()
406 elif kev
.keyval
== g
.keysyms
.ISO_Left_Tab
:
407 return self
.unindent_block()
408 elif kev
.keyval
== g
.keysyms
.Escape
:
409 self
.set_minibuffer(None)
413 def auto_indent(self
):
414 if not auto_indent
.int_value
:
417 start
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
419 start
.set_line_offset(0)
420 end
.forward_to_line_end()
421 line
= self
.buffer.get_text(start
, end
, False)
424 if self
.mime_type
.subtype
== 'x-python':
426 l
= line
.split('\n')[0]
429 if l
.endswith(':') and not l
.startswith('#'):
430 if use_spaces_for_tabs
.int_value
:
431 indent
+= ' ' * tab_width
.int_value
434 elif have_sourceview
:
443 self
.buffer.begin_user_action()
444 self
.buffer.insert_at_cursor('\n' + indent
)
445 self
.buffer.end_user_action()
448 def indent_block(self
):
450 (start
, end
) = self
.buffer.get_selection_bounds()
451 start_line
= start
.get_line()
452 end_line
= end
.get_line()
453 self
.buffer.begin_user_action()
454 for i
in range(start_line
, end_line
+1):
455 iter = self
.buffer.get_iter_at_line(i
)
456 self
.buffer.insert(iter, '\t')
457 self
.buffer.end_user_action()
462 def unindent_block(self
):
464 (start
, end
) = self
.buffer.get_selection_bounds()
465 start_line
= start
.get_line()
466 end_line
= end
.get_line()
467 self
.buffer.begin_user_action()
468 for i
in range(start_line
, end_line
+1):
469 iter = self
.buffer.get_iter_at_line(i
)
470 if iter.get_char() == '\t':
471 next_char
= iter.copy()
472 next_char
.forward_char()
473 self
.buffer.delete(iter, next_char
)
474 self
.buffer.end_user_action()
479 def destroyed(self
, widget
):
480 app_options
.remove_notify(self
.update_styles
)
482 def update_styles(self
):
485 font
= pango
.FontDescription(default_font
.value
)
486 bg
= g
.gdk
.color_parse(background_colour
.value
)
487 fg
= g
.gdk
.color_parse(foreground_colour
.value
)
489 self
.text
.set_left_margin(layout_left_margin
.int_value
)
490 self
.text
.set_right_margin(layout_right_margin
.int_value
)
492 self
.text
.set_pixels_above_lines(layout_before_para
.int_value
)
493 self
.text
.set_pixels_below_lines(layout_after_para
.int_value
)
494 self
.text
.set_pixels_inside_wrap(layout_inside_para
.int_value
)
495 self
.text
.set_indent(layout_indent_para
.int_value
)
497 self
.word_wrap
= bool(word_wrap
.int_value
)
499 if show_toolbar
.int_value
:
504 rox
.report_exception()
506 self
.text
.modify_font(font
)
507 self
.text
.modify_base(g
.STATE_NORMAL
, bg
)
508 self
.text
.modify_text(g
.STATE_NORMAL
, fg
)
511 self
.text
.set_show_line_numbers(show_line_numbers
.int_value
)
512 self
.text
.set_show_line_markers(show_line_markers
.int_value
)
513 self
.text
.set_auto_indent(auto_indent
.int_value
)
514 self
.text
.set_tabs_width(tab_width
.int_value
)
515 self
.text
.set_insert_spaces_instead_of_tabs(use_spaces_for_tabs
.int_value
)
516 self
.text
.set_margin(right_margin
.int_value
)
517 self
.text
.set_show_margin(show_margin
.int_value
)
518 self
.text
.set_smart_home_end(smart_home_end
.int_value
)
519 if self
.buffer.language
== 'Python':
520 self
.text
.set_auto_indent(False)
522 def cut(self
): self
.text
.emit('cut_clipboard')
523 def copy(self
): self
.text
.emit('copy_clipboard')
524 def paste(self
): self
.text
.emit('paste_clipboard')
526 def delete_event(self
, window
, event
):
527 if self
.buffer.get_modified():
528 self
.save_as(discard
= 1)
532 def update_title(self
, *unused
):
533 title
= self
.uri
or '<'+_('Untitled')+'>'
534 if self
.buffer.get_modified():
536 self
.save_button
.set_sensitive(True)
538 self
.save_button
.set_sensitive(False)
539 self
.set_title(title
)
541 def xds_load_from_stream(self
, name
, t
, stream
):
542 if t
== 'UTF8_STRING':
543 return # Gtk will handle it
545 dnd_mark
= self
.buffer.get_mark('gtk_drag_target')
547 dnd_pos
= self
.buffer.get_iter_at_mark(dnd_mark
)
548 self
.buffer.move_mark(self
.insert_mark
, dnd_pos
)
549 self
.insert_data(stream
.read())
553 def get_encoding(self
, message
):
554 "Returns (encoding, errors), or raises Abort to cancel."
555 box
= g
.MessageDialog(self
, 0, g
.MESSAGE_QUESTION
, g
.BUTTONS_CANCEL
, message
)
556 box
.set_has_separator(False)
559 box
.vbox
.pack_start(frame
, True, True)
560 frame
.set_border_width(6)
562 hbox
= g
.HBox(False, 4)
563 hbox
.set_border_width(6)
565 hbox
.pack_start(g
.Label(_('Encoding:')), False, True, 0)
567 combo
.disable_activate()
568 combo
.entry
.connect('activate', lambda w
: box
.activate_default())
569 combo
.set_popdown_strings(known_codecs
)
570 hbox
.pack_start(combo
, True, True, 0)
571 ignore_errors
= g
.CheckButton(_('Ignore errors'))
572 hbox
.pack_start(ignore_errors
, False, True)
577 box
.add_button(g
.STOCK_CONVERT
, g
.RESPONSE_YES
)
578 box
.set_default_response(g
.RESPONSE_YES
)
581 combo
.entry
.grab_focus()
584 if resp
!= g
.RESPONSE_YES
:
588 if ignore_errors
.get_active():
592 encoding
= combo
.entry
.get_text()
594 codecs
.getdecoder(encoding
)
597 rox
.alert(_("Unknown encoding '%s'") % encoding
)
601 return encoding
, errors
603 def insert_data(self
, data
):
608 decoder
= codecs
.getdecoder(encoding
)
610 data
= decoder(data
, errors
)[0]
611 if errors
== 'strict':
612 assert '\0' not in data
615 data
= data
.replace('\0', '\\0')
620 encoding
, errors
= self
.get_encoding(
621 _("Data is not valid %s. Please select the file's encoding. "
622 "Turn on 'ignore errors' to try and load it anyway.")
625 self
.buffer.begin_user_action()
626 self
.buffer.insert_at_cursor(data
)
627 self
.buffer.end_user_action()
630 def load_file(self
, path
):
635 file = open(path
, 'r')
636 contents
= file.read()
640 self
.buffer.begin_not_undoable_action()
641 self
.insert_data(contents
)
642 self
.buffer.end_not_undoable_action()
646 rox
.report_exception()
649 def close(self
, button
= None):
650 if self
.buffer.get_modified():
651 self
.save_as(discard
= 1)
658 def up(self
, button
= None):
660 filer
.show_file(self
.uri
)
662 rox
.alert(_('File is not saved to disk yet'))
665 def toggle_spell(self
, button
= None):
669 self
.spell_button
.set_active(False)
672 self
.spell
= gtkspell
.Spell(self
.text
)
673 self
.spell_button
.set_active(True)
676 self
.spell_button
.set_active(False)
678 #self.spell_button.set_active(self.spell != None)
680 def diff(self
, button
= None, path
= None):
681 path
= path
or self
.uri
683 rox
.alert(_('This file has never been saved; nothing to compare it to!\n'
684 'Note: you can drop a file onto the toolbar button to see '
685 'the changes from that file.'))
687 diff
.show_diff(path
, self
.save_to_stream
)
689 def has_selection(self
):
690 s
, e
= self
.get_selection_range()
691 return not e
.equal(s
)
693 def get_marked_range(self
):
694 s
= self
.buffer.get_iter_at_mark(self
.mark_start
)
695 e
= self
.buffer.get_iter_at_mark(self
.mark_end
)
700 def get_selection_range(self
):
701 s
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
702 e
= self
.buffer.get_iter_at_mark(self
.selection_bound_mark
)
707 def save(self
, widget
= None):
709 self
.save_to_file(self
.uri
)
710 self
.buffer.set_modified(False)
712 self
.save_as(discard
=0)
714 def save_as(self
, widget
= None, discard
= 0):
715 from rox
.saving
import SaveBox
718 self
.savebox
.destroy()
721 self
.mime_type
= mime
.get_type(self
.uri
, 1)
723 self
.mime_type
= mime
.lookup('text', 'plain')
725 mime_text
= self
.mime_type
.media
+ '/' + self
.mime_type
.subtype
727 if self
.has_selection() and not discard
:
728 saver
= SelectionSaver(self
)
729 self
.savebox
= SaveBox(saver
, 'Selection', mime_text
)
730 self
.savebox
.connect('destroy', lambda w
: saver
.destroy())
732 uri
= self
.uri
or _('TextFile')
733 self
.savebox
= SaveBox(self
, uri
, mime_text
, discard
)
736 def help(self
, button
= None):
737 filer
.open_dir(os
.path
.join(rox
.app_dir
, 'Help'))
739 def save_to_stream(self
, stream
):
740 s
= self
.buffer.get_start_iter()
741 e
= self
.buffer.get_end_iter()
742 stream
.write(self
.buffer.get_text(s
, e
, True))
744 def set_uri(self
, uri
):
746 self
.buffer.set_modified(False)
752 def change_font(self
):
753 style
= self
.text
.get_style().copy()
754 style
.font
= load_font(options
.get('edit_font'))
755 self
.text
.set_style(style
)
757 def show_options(self
):
760 def set_marked(self
, start
= None, end
= None):
761 "Set the marked region (from the selection if no region is given)."
763 assert not self
.marked
770 start
, end
= self
.get_selection_range()
771 buffer.move_mark(self
.mark_start
, start
)
772 buffer.move_mark(self
.mark_end
, end
)
773 buffer.apply_tag_by_name('marked',
774 buffer.get_iter_at_mark(self
.mark_start
),
775 buffer.get_iter_at_mark(self
.mark_end
))
778 def clear_marked(self
):
783 buffer.remove_tag_by_name('marked',
784 buffer.get_iter_at_mark(self
.mark_start
),
785 buffer.get_iter_at_mark(self
.mark_end
))
787 def undo(self
, widget
= None):
789 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
790 self
.text
.scroll_to_iter(cursor
, 0.05, False)
792 def redo(self
, widget
= None):
794 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
795 self
.text
.scroll_to_iter(cursor
, 0.05, False)
797 def goto(self
, widget
= None):
798 from goto
import Goto
799 self
.set_minibuffer(Goto())
801 def search(self
, widget
= None):
802 if self
.search_minibuffer
is None:
803 from search
import Search
804 self
.search_minibuffer
= Search()
805 self
.set_minibuffer(self
.search_minibuffer
)
807 def search_again(self
, widget
= None):
808 if self
.search_minibuffer
and self
.search_minibuffer
is self
.minibuffer
:
809 self
.minibuffer
.activate() # Search again with same text
811 if self
.minibuffer
is None:
812 # Search mini-buffer not yet open
814 self
.minibuffer
.restore_previous_search()
815 self
.minibuffer
.search_again()
817 def search_replace(self
, widget
= None):
818 from search
import Replace
821 def toggle_bookmark(self
):
822 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
823 name
= str(cursor
.get_line())
824 marker
= self
.buffer.get_marker(name
)
826 self
.buffer.delete_marker(marker
);
828 marker
= self
.buffer.create_marker(name
, "bookmark", cursor
);
830 def next_bookmark(self
):
831 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
832 cursor
.forward_char()
833 marker
= self
.buffer.get_next_marker(cursor
)
835 self
.buffer.get_iter_at_marker (cursor
, marker
)
836 self
.buffer.place_cursor(cursor
)
837 self
.text
.scroll_to_iter(cursor
, 0.05, False)
839 def prev_bookmark(self
):
840 cursor
= self
.buffer.get_iter_at_mark(self
.insert_mark
)
841 cursor
.backward_char()
842 marker
= self
.buffer.get_prev_marker(cursor
)
844 self
.buffer.get_iter_at_marker (cursor
, marker
)
845 self
.buffer.place_cursor(cursor
)
846 self
.text
.scroll_to_iter(cursor
, 0.05, False)
848 def set_mini_label(self
, label
):
849 self
.mini_label
.set_text(label
)
851 def set_minibuffer(self
, minibuffer
):
852 assert minibuffer
is None or isinstance(minibuffer
, Minibuffer
)
855 self
.minibuffer
.close()
857 self
.minibuffer
= None
860 self
.mini_entry
.set_text('')
861 self
.minibuffer
= minibuffer
862 minibuffer
.setup(self
)
863 self
.mini_entry
.grab_focus()
864 self
.mini_hbox
.show_all()
866 self
.mini_hbox
.hide()
867 self
.text
.grab_focus()
869 def mini_key_press(self
, entry
, kev
):
870 if kev
.keyval
== g
.keysyms
.Escape
:
871 self
.set_minibuffer(None)
873 if kev
.keyval
== g
.keysyms
.Return
or kev
.keyval
== g
.keysyms
.KP_Enter
:
874 self
.minibuffer
.activate()
877 return self
.minibuffer
.key_press(kev
)
879 def mini_changed(self
, entry
):
880 if not self
.minibuffer
:
882 self
.minibuffer
.changed()
884 def mini_show_info(self
, *unused
):
885 assert self
.minibuffer
887 self
.info_box
.destroy()
888 self
.info_box
= g
.MessageDialog(self
, 0, g
.MESSAGE_INFO
, g
.BUTTONS_OK
,
889 self
.minibuffer
.info
)
890 self
.info_box
.set_title(_('Minibuffer help'))
893 self
.info_box
.connect('destroy', destroy
)
895 self
.info_box
.connect('response', lambda w
, r
: w
.destroy())
897 def process_selected(self
, process
):
898 """Calls process(line) on each line in the selection, or each line in the file
899 if there is no selection. If the result is not None, the text is replaced."""
900 self
.buffer.begin_user_action()
902 self
._process
_selected
(process
)
904 self
.buffer.end_user_action()
906 def _process_selected(self
, process
):
907 if self
.has_selection():
909 start
, end
= self
.get_selection_range()
910 if start
.compare(end
) > 0:
913 start
, end
= self
.get_selection_range()
914 if start
.compare(end
) > 0:
918 return self
.buffer.get_end_iter()
919 start
= self
.buffer.get_start_iter()
922 while start
.compare(end
) <= 0:
923 line_end
= start
.copy()
924 line_end
.forward_to_line_end()
925 if line_end
.compare(end
) >= 0:
927 line
= self
.buffer.get_text(start
, line_end
, False)
930 self
.buffer.move_mark(self
.mark_tmp
, start
)
931 self
.buffer.insert(line_end
, new
)
932 start
= self
.buffer.get_iter_at_mark(self
.mark_tmp
)
933 line_end
= start
.copy()
934 line_end
.forward_chars(len(line
.decode('utf-8')))
935 self
.buffer.delete(start
, line_end
)
937 start
= self
.buffer.get_iter_at_mark(self
.mark_tmp
)
939 if not start
.forward_line(): break
941 def set_word_wrap(self
, value
):
942 self
._word
_wrap
= value
943 self
.wrap_button
.set_active(value
)
945 self
.text
.set_wrap_mode(g
.WRAP_WORD
)
947 self
.text
.set_wrap_mode(g
.WRAP_NONE
)
949 word_wrap
= property(lambda self
: self
._word
_wrap
, set_word_wrap
)
951 class SelectionSaver(Saveable
):
952 def __init__(self
, window
):
956 def save_to_stream(self
, stream
):
957 s
, e
= self
.window
.get_marked_range()
958 stream
.write(self
.window
.buffer.get_text(s
, e
, True))
961 # Called when savebox is remove. Get rid of the selection marker
962 self
.window
.clear_marked()