cadquery-freecad-module/CadQuery/Libs/pyqode/core/modes/filewatcher.py
2015-10-26 16:25:38 -04:00

188 lines
6.7 KiB
Python

# -*- coding: utf-8 -*-
"""
Contains the mode that control the external changes of file.
"""
import os
from pyqode.core.api import TextHelper
from pyqode.core.api.mode import Mode
from pyqode.qt import QtCore, QtWidgets
from pyqode.core.cache import Cache
class FileWatcherMode(Mode, QtCore.QObject):
""" Watches the current file for external modifications.
FileWatcher mode, check if the opened file has changed externally.
"""
#: Signal emitted when the file has been deleted. The Signal is emitted
#: with the current editor instance so that user have a chance to close
#: the editor.
file_deleted = QtCore.Signal(object)
#: Signal emitted when the file has been reloaded in the editor.
file_reloaded = QtCore.Signal()
@property
def auto_reload(self):
"""
Automatically reloads changed files
"""
return self._auto_reload
@auto_reload.setter
def auto_reload(self, value):
self._auto_reload = value
if self.editor:
# propagate changes to every clone
for clone in self.editor.clones:
try:
clone.modes.get(FileWatcherMode).auto_reload = value
except KeyError:
# this should never happen since we're working with clones
pass
def __init__(self):
QtCore.QObject.__init__(self)
Mode.__init__(self)
self._auto_reload = False
self._flg_notify = False
self._data = (None, None)
self._timer = QtCore.QTimer()
self._timer.setInterval(1000)
self._timer.timeout.connect(self._check_mtime)
self._mtime = 0
self._notification_pending = False
self._processing = False
def on_state_changed(self, state):
if state:
self.editor.new_text_set.connect(self._update_mtime)
self.editor.new_text_set.connect(self._timer.start)
self.editor.text_saving.connect(self._cancel_next_change)
self.editor.text_saved.connect(self._update_mtime)
self.editor.text_saved.connect(self._restart_monitoring)
self.editor.focused_in.connect(self._check_for_pending)
else:
self._timer.stop()
self.editor.new_text_set.connect(self._update_mtime)
self.editor.new_text_set.connect(self._timer.start)
self.editor.text_saving.disconnect(self._cancel_next_change)
self.editor.text_saved.disconnect(self._restart_monitoring)
self.editor.focused_in.disconnect(self._check_for_pending)
self._timer.stop()
def _cancel_next_change(self):
self._timer.stop()
for e in self.editor.clones:
try:
w = e.modes.get(self.__class__)
except KeyError:
pass
else:
w._cancel_next_change()
def _restart_monitoring(self):
self._update_mtime()
for e in self.editor.clones:
try:
w = e.modes.get(self.__class__)
except KeyError:
pass
else:
w._restart_monitoring()
self._timer.start()
def _update_mtime(self):
""" Updates modif time """
try:
self._mtime = os.path.getmtime(self.editor.file.path)
except OSError:
# file_path does not exists.
self._mtime = 0
self._timer.stop()
except (TypeError, AttributeError):
# file path is none, this happen if you use setPlainText instead of
# openFile. This is perfectly fine, we just do not have anything to
# watch
self._timer.stop()
def _check_mtime(self):
"""
Checks watched file moficiation time.
"""
if self.editor and self.editor.file.path:
if not os.path.exists(self.editor.file.path) and self._mtime:
self._notify_deleted_file()
else:
mtime = os.path.getmtime(self.editor.file.path)
if mtime > self._mtime:
self._mtime = mtime
self._notify_change()
def _notify(self, title, message, expected_action=None):
"""
Notify user from external event
"""
inital_value = self.editor.save_on_focus_out
self.editor.save_on_focus_out = False
self._flg_notify = True
dlg_type = (QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
expected_action = (
lambda *x: None) if not expected_action else expected_action
if (self._auto_reload or QtWidgets.QMessageBox.question(
self.editor, title, message, dlg_type,
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes):
expected_action(self.editor.file.path)
self._update_mtime()
self.editor.save_on_focus_out = inital_value
def _notify_change(self):
"""
Notify user from external change if autoReloadChangedFiles is False
then reload the changed file in the editor
"""
def inner_action(*args):
""" Inner action: open file """
# cache cursor position before reloading so that the cursor
# position is restored automatically after reload has finished.
# See OpenCobolIDE/OpenCobolIDE#97
Cache().set_cursor_position(
self.editor.file.path,
self.editor.textCursor().position())
self.editor.file.open(self.editor.file.path)
self.file_reloaded.emit()
args = ("File changed",
"The file <i>%s</i> has changed externally.\nDo you want to "
"reload it?" % os.path.basename(self.editor.file.path))
kwargs = {"expected_action": inner_action}
if self.editor.hasFocus() or self.auto_reload:
self._notify(*args, **kwargs)
else:
# show the reload prompt as soon as the editor has focus
self._notification_pending = True
self._data = (args, kwargs)
def _check_for_pending(self, *args, **kwargs):
"""
Checks if a notification is pending.
"""
if self._notification_pending and not self._processing:
self._processing = True
args, kwargs = self._data
self._notify(*args, **kwargs)
self._notification_pending = False
self._processing = False
def _notify_deleted_file(self):
"""
Notify user from external file deletion.
"""
self.file_deleted.emit(self.editor)
# file deleted, disable file watcher
self.enabled = False
def clone_settings(self, original):
self.auto_reload = original.auto_reload