Improved loading files and added saving functionality

This commit is contained in:
itdominator 2023-03-22 02:12:40 -05:00
parent 404aba0c2e
commit 5cf1861929
6 changed files with 184 additions and 92 deletions

View File

@ -30,3 +30,7 @@ class EditorControllerMixin:
self.keyboard_prev_tab(page_num) self.keyboard_prev_tab(page_num)
if action == "keyboard_next_tab": if action == "keyboard_next_tab":
self.keyboard_next_tab(page_num) self.keyboard_next_tab(page_num)
if action == "save_file":
source_view.save_file()
if action == "save_file_as":
source_view.save_file_as()

View File

@ -13,7 +13,10 @@ class EditorEventsMixin:
def _keyboard_close_tab(self): def _keyboard_close_tab(self):
self.action_controller("close_tab") self.action_controller("close_tab")
def _keyboard_create_tab(self, _gfile): def _keyboard_open_file(self, gfile):
self.open_file(gfile)
def _keyboard_create_tab(self, _gfile=None):
self.create_view(gfile=_gfile) self.create_view(gfile=_gfile)
def _keyboard_next_tab(self): def _keyboard_next_tab(self):
@ -29,10 +32,10 @@ class EditorEventsMixin:
self.action_controller("scale_down_text") self.action_controller("scale_down_text")
def _keyboard_save_file(self): def _keyboard_save_file(self):
... self.action_controller("save_file")
def _keyboard_save_file_as(self): def _keyboard_save_file_as(self):
... self.action_controller("save_file_as")
def _text_search(self, widget = None, eve = None): def _text_search(self, widget = None, eve = None):
self.action_controller("do_text_search", widget.get_text()) self.action_controller("do_text_search", widget.get_text())

View File

@ -47,6 +47,7 @@ class EditorNotebook(EditorEventsMixin, EditorControllerMixin, Gtk.Notebook):
event_system.subscribe("set_buffer_style", self.action_controller) event_system.subscribe("set_buffer_style", self.action_controller)
event_system.subscribe("toggle_highlight_line", self._toggle_highlight_line) event_system.subscribe("toggle_highlight_line", self._toggle_highlight_line)
event_system.subscribe("keyboard_create_tab", self._keyboard_create_tab) event_system.subscribe("keyboard_create_tab", self._keyboard_create_tab)
event_system.subscribe("keyboard_open_file", self._keyboard_open_file)
event_system.subscribe("keyboard_close_tab", self._keyboard_close_tab) event_system.subscribe("keyboard_close_tab", self._keyboard_close_tab)
event_system.subscribe("keyboard_prev_tab", self._keyboard_prev_tab) event_system.subscribe("keyboard_prev_tab", self._keyboard_prev_tab)
event_system.subscribe("keyboard_next_tab", self._keyboard_next_tab) event_system.subscribe("keyboard_next_tab", self._keyboard_next_tab)
@ -81,6 +82,10 @@ class EditorNotebook(EditorEventsMixin, EditorControllerMixin, Gtk.Notebook):
def _load_widgets(self): def _load_widgets(self):
self.create_view() self.create_view()
def _dbl_click_create_view(self, notebook, eve):
if eve.type == Gdk.EventType.DOUBLE_BUTTON_PRESS and eve.button == 1: # l-click
...
def create_view(self, widget = None, eve = None, gfile = None): def create_view(self, widget = None, eve = None, gfile = None):
container = SourceViewContainer(self.close_tab) container = SourceViewContainer(self.close_tab)
@ -98,17 +103,20 @@ class EditorNotebook(EditorEventsMixin, EditorControllerMixin, Gtk.Notebook):
self.show_all() self.show_all()
self.set_current_page(index) self.set_current_page(index)
def open_file(self, gfile):
page_num = self.get_current_page()
container = self.get_nth_page( page_num )
source_view = container.get_source_view()
if source_view._current_filename == "":
source_view.open_file(gfile)
else:
self.create_view(None, None, gfile)
def close_tab(self, button, container, source_view, eve = None): def close_tab(self, button, container, source_view, eve = None):
if self.get_n_pages() == 1: if self.get_n_pages() == 1:
return return
page_num = self.page_num(container) page_num = self.page_num(container)
watcher = source_view.get_file_watcher() source_view._cancel_current_file_watchers()
if watcher:
watcher.cancel()
self.remove_page(page_num) self.remove_page(page_num)
def _dbl_click_create_view(self, notebook, eve):
if eve.type == Gdk.EventType.DOUBLE_BUTTON_PRESS and eve.button == 1: # l-click
...

View File

@ -23,12 +23,30 @@ class SourceView(SourceViewEventsMixin, GtkSource.View):
self._style_scheme_manager = GtkSource.StyleSchemeManager() self._style_scheme_manager = GtkSource.StyleSchemeManager()
self._general_style_tag = None self._general_style_tag = None
self._file_watcher = None self._file_change_watcher = None
self._is_changed = False self._file_cdr_watcher = None
self._is_changed = False
self._ignore_internal_change = False
self._last_eve_in_queue = None
self._current_file: Gio.File = None self._current_file: Gio.File = None
self._file_loader = None self._current_filename: str = ""
self._buffer = self.get_buffer() self._file_loader = None
self._buffer = self.get_buffer()
self._file_filter_text = Gtk.FileFilter()
self._file_filter_text.set_name("Text Files")
# TODO: Need to externalize to settings file...
pattern = ["*.txt", "*.py", "*.c", "*.h", "*.cpp", "*.csv", "*.m3*", "*.lua", "*.js", "*.toml", "*.xml", "*.pom", "*.htm", "*.md" "*.vala", "*.tsv", "*.css", "*.html", ".json", "*.java", "*.go", "*.php", "*.ts", "*.rs"]
for p in pattern:
self._file_filter_text.add_pattern(p)
self._file_filter_all = Gtk.FileFilter()
self._file_filter_all.set_name("All Files")
self._file_filter_all.add_pattern("*.*")
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
@ -69,40 +87,10 @@ class SourceView(SourceViewEventsMixin, GtkSource.View):
def _load_widgets(self): def _load_widgets(self):
... ...
def _is_modified(self, *args): def _create_default_tag(self):
self._is_changed = True self._general_style_tag = self._buffer.create_tag('general_style')
self.update_cursor_position() self._general_style_tag.set_property('size', 100)
self._general_style_tag.set_property('scale', 100)
def get_file_watcher(self):
return self._file_watcher
def create_file_watcher(self, file_path = None):
if not file_path:
return
if self._file_watcher:
self._file_watcher.cancel()
self._file_watcher = None
self._file_watcher = Gio.File.new_for_path(file_path) \
.monitor_file([
Gio.FileMonitorFlags.WATCH_MOVES,
Gio.FileMonitorFlags.WATCH_HARD_LINKS
], Gio.Cancellable())
self._file_watcher.connect("changed", self.file_watch_updates)
def file_watch_updates(self, file_monitor, file, other_file=None, eve_type=None, data=None):
if settings.is_debug():
logger.debug(eve_type)
if eve_type in [Gio.FileMonitorEvent.CREATED,
Gio.FileMonitorEvent.DELETED,
Gio.FileMonitorEvent.RENAMED]:
...
if eve_type in [ Gio.FileMonitorEvent.CHANGED ]:
...
def _set_up_dnd(self): def _set_up_dnd(self):
URI_TARGET_TYPE = 80 URI_TARGET_TYPE = 80
@ -137,42 +125,96 @@ class SourceView(SourceViewEventsMixin, GtkSource.View):
event_system.emit('create_view', (None, None, gfile,)) event_system.emit('create_view', (None, None, gfile,))
def _is_modified(self, *args):
self._is_changed = True
self.update_cursor_position()
def open_file(self, gfile, *args): def _on_cursor_move(self, buf, cursor_iter, mark, user_data = None):
self._current_file = gfile if mark != buf.get_insert(): return
self.load_file_info(gfile) self.update_cursor_position()
self.load_file_async(gfile)
self.grab_focus()
def load_file_info(self, gfile): def _create_file_watcher(self, gfile = None):
info = gfile.query_info("standard::*", 0, cancellable=None) if not gfile: return
content_type = info.get_content_type()
display_name = info.get_display_name()
tab_widget = self.get_parent().get_tab_widget()
try: self._cancel_current_file_watchers()
lm = self._language_manager.guess_language(None, content_type) self._file_change_watcher = gfile.monitor(Gio.FileMonitorFlags.NONE, Gio.Cancellable())
self.set_buffer_language( lm.get_id() ) self._file_change_watcher.connect("changed", self._file_monitor)
except Exception as e:
...
logger.debug(f"Detected Content Type: {content_type}") def _file_monitor(self, file_monitor, file, other_file = None, eve_type = None, data = None):
tab_widget.set_tab_label(display_name) if not file.get_path() == self._current_file.get_path():
event_system.emit("set_bottom_labels", (gfile, info)) return
def load_file_async(self, gfile): if eve_type in [Gio.FileMonitorEvent.CREATED,
file = GtkSource.File() Gio.FileMonitorEvent.DELETED,
file.set_location(gfile) Gio.FileMonitorEvent.RENAMED,
self._file_loader = GtkSource.FileLoader.new(self._buffer, file) Gio.FileMonitorEvent.MOVED_IN,
Gio.FileMonitorEvent.MOVED_OUT]:
self._is_changed = True
def finish_load_callback(obj, res, user_data=None): if eve_type in [ Gio.FileMonitorEvent.CHANGES_DONE_HINT ]:
self._file_loader.load_finish(res) if self._ignore_internal_change:
self._is_changed = False self._ignore_internal_change = False
return
self._file_loader.load_async(io_priority=98, # TODO: Any better way to load the difference??
cancellable=None, if self._current_file.query_exists():
progress_callback=None, self.load_file_async(self._current_file)
progress_callback_data=None,
callback=finish_load_callback, def _cancel_current_file_watchers(self):
user_data=(None)) if self._file_change_watcher:
self._file_change_watcher.cancel()
self._file_change_watcher = None
if self._file_cdr_watcher:
self._file_cdr_watcher.cancel()
self._file_cdr_watcher = None
def save_file(self):
if not self._current_file:
self.save_file_as()
return
self._write_file(self._current_file)
def save_file_as(self):
# TODO: Move Chooser logic to own widget
dlg = Gtk.FileChooserDialog(title="Please choose a file...", parent = None, action = 1)
dlg.add_buttons("Cancel", Gtk.ResponseType.CANCEL, "Save", Gtk.ResponseType.OK)
dlg.set_do_overwrite_confirmation(True)
dlg.add_filter(self._file_filter_text)
dlg.add_filter(self._file_filter_all)
if self._current_filename == "":
dlg.set_current_name("new.txt")
else:
dlg.set_current_folder(self._current_file.get_parent())
dlg.set_current_name(self._current_filename)
response = dlg.run()
file = dlg.get_filename() if response == Gtk.ResponseType.OK else ""
dlg.destroy()
if not file == "":
gfile = Gio.File.new_for_path(file)
self._write_file(gfile, True)
def _write_file(self, gfile, save_as = False):
with open(gfile.get_path(), 'w') as f:
if not save_as:
self._ignore_internal_change = True
self._is_changed = False
start_itr = self._buffer.get_start_iter()
end_itr = self._buffer.get_end_iter()
text = self._buffer.get_text(start_itr, end_itr, True)
f.write(text)
f.close()
if self._current_filename == "" and save_as:
self.open_file(gfile)
else:
event_system.emit("create_view", (None, None, gfile,))

View File

@ -1,17 +1,17 @@
# Python imports # Python imports
# Lib imports # Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4')
from gi.repository import Gtk
from gi.repository import GtkSource
# Application imports # Application imports
class SourceViewEventsMixin: class SourceViewEventsMixin:
def _create_default_tag(self):
self._general_style_tag = self._buffer.create_tag('general_style')
self._general_style_tag.set_property('size', 100)
self._general_style_tag.set_property('scale', 100)
def set_buffer_language(self, language = "python3"): def set_buffer_language(self, language = "python3"):
self._buffer.set_language( self._language_manager.get_language(language) ) self._buffer.set_language( self._language_manager.get_language(language) )
@ -38,12 +38,6 @@ class SourceViewEventsMixin:
tag.set_property('scale', tag.get_property('scale') - scale_step) tag.set_property('scale', tag.get_property('scale') - scale_step)
self._buffer.apply_tag(tag, start_itr, end_itr) self._buffer.apply_tag(tag, start_itr, end_itr)
def _on_cursor_move(self, buf, cursor_iter, mark, user_data = None):
if mark != buf.get_insert():
return
self.update_cursor_position()
def update_cursor_position(self): def update_cursor_position(self):
iter = self._buffer.get_iter_at_mark(self._buffer.get_insert()) iter = self._buffer.get_iter_at_mark(self._buffer.get_insert())
chars = iter.get_offset() chars = iter.get_offset()
@ -71,3 +65,44 @@ class SourceViewEventsMixin:
def action_comment_out_selection(self): def action_comment_out_selection(self):
pass pass
def open_file(self, gfile, *args):
self._current_file = gfile
self.load_file_info(gfile)
self.load_file_async(gfile)
self._create_file_watcher(gfile)
self.grab_focus()
def load_file_info(self, gfile):
info = gfile.query_info("standard::*", 0, cancellable=None)
content_type = info.get_content_type()
display_name = info.get_display_name()
tab_widget = self.get_parent().get_tab_widget()
self._current_filename = display_name
try:
lm = self._language_manager.guess_language(None, content_type)
self.set_buffer_language( lm.get_id() )
except Exception as e:
...
logger.debug(f"Detected Content Type: {content_type}")
tab_widget.set_tab_label(display_name)
event_system.emit("set_bottom_labels", (gfile, info))
def load_file_async(self, gfile):
file = GtkSource.File()
file.set_location(gfile)
self._file_loader = GtkSource.FileLoader.new(self._buffer, file)
def finish_load_callback(obj, res, user_data=None):
self._file_loader.load_finish(res)
self._is_changed = False
self._file_loader.load_async(io_priority = 98,
cancellable = None,
progress_callback = None,
progress_callback_data = None,
callback = finish_load_callback,
user_data = (None))

View File

@ -55,6 +55,6 @@ class OpenFileButton(Gtk.Button):
if filename: if filename:
path = filename if os.path.isabs(filename) else os.path.abspath(filename) path = filename if os.path.isabs(filename) else os.path.abspath(filename)
_gfile = Gio.File.new_for_path(path) _gfile = Gio.File.new_for_path(path)
event_system.emit("keyboard_create_tab", (_gfile,)) event_system.emit("keyboard_open_file", (_gfile,))
chooser.destroy() chooser.destroy()