Compare commits

...

34 Commits

Author SHA1 Message Date
5273c58ed6 Major completion provider overhaul; pluigin load and pattern improvements; css overhaul/cleanup; source view state modes added 2026-02-14 16:15:54 -06:00
4ce4d85842 removed endpoint_registery; relocated builder_wrapper to be widget_registery under libs/ 2026-01-19 13:57:44 -06:00
c6b10ceb17 defaulting to buffer if language not detected 2026-01-19 00:29:10 -06:00
a036dc428b Wiring plugins to controller messages; importing plugin controller to code base; fixed VTE widget adding to bash history 2026-01-18 22:39:52 -06:00
b8ce6e160a Pligins refactor with new context and controller integration 2026-01-18 20:36:17 -06:00
e2f29207ba Preliminary controller layout work for plugin integration; controller base message refactor naming 2026-01-18 13:53:29 -06:00
99f1bffefb Restructuring controller to broaden usability; changed events logic to support that 2026-01-18 00:52:54 -06:00
f7d944f7a9 Created libs.code package and moved pertinant DTOs to it as well as widget.code that can go there too 2026-01-13 11:23:26 -06:00
4469e8189f Improved folder structure for commamd system and controllers 2026-01-13 00:43:51 -06:00
a507469bf8 Added controller manager registery guard; localized event type access for code to event factory 2026-01-12 23:59:13 -06:00
a07123d165 fix singleton typeing and __init__ 2026-01-12 23:34:34 -06:00
00c72a7117 Fixed code view close_file command 2026-01-12 00:15:43 -06:00
f8d73ad74a Fixed code view expansion issues; fixed pre/post code view key mappings not sinking Gtk signals properly 2026-01-12 00:07:44 -06:00
04e0c3caf6 Moved to using event_factory and referencing types from it 2026-01-11 22:44:03 -06:00
a57bfd94fc Created helpers file for commands to reduce duplication 2026-01-11 18:30:51 -06:00
c2060963cc Full 'code' widget refactor to utilize controllers and cross controller event signaling 2026-01-11 18:01:30 -06:00
8253e250d8 fixed pyright pathing; exception typo; and path manager arg typo 2026-01-04 13:27:22 -06:00
be608f645e Added suppression of some errors we don't care about 2026-01-04 00:15:36 -06:00
356da04f46 Improved some exception handling 2026-01-04 00:04:38 -06:00
48182f9775 Created a settings > path_manager class and cleaned up srtting manager class 2026-01-03 23:28:14 -06:00
90b2f050c6 Improved base pligin implementation 2026-01-03 21:57:37 -06:00
79375d34b1 Added undo, redo, go-to, and duplicate lines commands; updated styles for line highlight color; cleanup 2025-12-29 02:20:55 -06:00
5ee587e205 Added temp cut/paste buffer commands; added zoom in/out commands; fixed tab closures leaking memory 2025-12-29 00:46:10 -06:00
3fc39042f1 Wired tabs close and selection; improved files manager next index checking; code event focus ignore attrib added 2025-12-28 22:44:44 -06:00
e18be655e8 Code Widget refactor; observable refactor 2025-12-28 19:53:05 -06:00
12ada8568e Added code load start files command; improved show logic for code widget 2025-12-24 22:31:41 -06:00
4c179974c0 Added dnd commands; moved source view events to mixin; improved/separated pop logic for files manager; added observer pattern for active file for buffer 2025-12-20 01:10:53 -06:00
6acbcb53c2 Moving settings out of global space; added editor commands and wired them 2025-12-16 20:25:24 -06:00
ce22ed6a53 Updated code key bindings 2025-12-15 22:55:55 -06:00
c16493037d Added more commands to code; refactored container classes; general cleanup 2025-12-15 22:50:28 -06:00
849e7611ca Added code view widget and base command system; updated keybinding system 2025-12-14 03:20:03 -06:00
2c453bc621 Fixed IPC issues with passing paths back to instance 2025-12-10 20:09:32 -06:00
530fe7c3ab Updated jeybinding and restructured css 2025-11-30 19:51:04 -06:00
5e5cc50ba8 Fixed empty vte commit error 2025-11-30 00:59:37 -06:00
189 changed files with 7290 additions and 757 deletions

View File

@@ -10,19 +10,19 @@ class Manifest:
author: str = "John Doe" author: str = "John Doe"
version: str = "0.0.1" version: str = "0.0.1"
support: str = "" support: str = ""
pre_launch: bool = False
requests: {} = { requests: {} = {
'pass_ui_objects': ["plugin_control_list"], 'pass_ui_objects': ["plugin_control_list"],
'pass_events': "true", 'pass_events': True,
'bind_keys': [] 'bind_keys': []
} }
pre_launch: bool = False
``` ```
### Requests ### Requests
``` ```
requests: {} = { requests: {} = {
'pass_events': "true", # If empty or not present will be ignored. 'pass_events': true, # If empty or not present will be ignored.
"pass_ui_objects": [""], # Request reference to a UI component. Will be passed back as array to plugin. "pass_ui_objects": [""], # Request reference to a UI component. Will be passed back as array to plugin.
'bind_keys': [f"{name}||send_message:<Control>f"], 'bind_keys': [f"{name}||send_message:<Control>f"],
f"{name}||do_save:<Control>s"] # Bind keys with method and key pare using list. Must pass "name" like shown with delimiter to its right. f"{name}||do_save:<Control>s"] # Bind keys with method and key pare using list. Must pass "name" like shown with delimiter to its right.

View File

@@ -0,0 +1,3 @@
"""
Pligin Module
"""

View File

@@ -0,0 +1,3 @@
"""
Pligin Package
"""

View File

@@ -0,0 +1,7 @@
{
"name": "LSP Completer",
"author": "ITDominator",
"version": "0.0.1",
"support": "",
"requests": {}
}

View File

@@ -0,0 +1,43 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from libs.dto.base_event import BaseEvent
from libs.event_factory import Event_Factory
from plugins.plugin_types import PluginCode
from .provider import Provider
class Plugin(PluginCode):
def __init__(self):
super(Plugin, self).__init__()
self.provider: Provider = None
def _controller_message(self, event: BaseEvent):
...
def load(self):
self.provider = Provider()
event = Event_Factory.create_event(
"register_provider",
provider_name = "LSP Completer",
provider = self.provider,
language_ids = []
)
self.message_to("completion", event)
def run(self):
...
def generate_plugin_element(self):
...

View File

@@ -0,0 +1,79 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import GObject
# Application imports
from .provider_response_cache import ProviderResponseCache
class Provider(GObject.Object, GtkSource.CompletionProvider):
"""
This code is an LSP code completion plugin for Newton.
# NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
"""
__gtype_name__ = 'LSPProvider'
def __init__(self):
GObject.Object.__init__(self)
self.response_cache: ProviderResponseCache = ProviderResponseCache()
def pre_populate(self, context):
...
def do_get_name(self):
return "LSP Code Completion"
def get_iter_correctly(self, context):
return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
def do_match(self, context):
word = self.response_cache.get_word(context)
if not word or len(word) < 2: return False
iter = self.get_iter_correctly(context)
iter.backward_char()
ch = iter.get_char()
# NOTE: Look to re-add or apply supprting logic to use spaces
# As is it slows down the editor in certain contexts...
# if not (ch in ('_', '.', ' ') or ch.isalnum()):
if not (ch in ('_', '.') or ch.isalnum()):
return False
buffer = iter.get_buffer()
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
return False
return True
def do_get_priority(self):
return 5
def do_get_activation(self):
""" The context for when a provider will show results """
# return GtkSource.CompletionActivation.NONE
return GtkSource.CompletionActivation.USER_REQUESTED
# return GtkSource.CompletionActivation.INTERACTIVE
def do_populate(self, context):
proposals = self.get_completion_filter(context)
context.add_proposals(self, proposals, True)
def get_completion_filter(self, context):
proposals = [
self.response_cache.create_completion_item(
"LSP Class",
"LSP Code",
"A test LSP completion item..."
)
]
return proposals

View File

@@ -0,0 +1,45 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from libs.event_factory import Code_Event_Types
from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
class ProviderResponseCache(ProviderResponseCacheBase):
def __init__(self):
super(ProviderResponseCache, self).__init__()
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
...
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
...
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
...
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
...
def filter(self, word: str):
...
def filter_with_context(self, context: GtkSource.CompletionContext):
proposals = [
self.create_completion_item(
"LSP Class",
"LSP Code",
"A test LSP completion item..."
)
]
return proposals

View File

@@ -0,0 +1,3 @@
"""
Pligin Module
"""

View File

@@ -0,0 +1,3 @@
"""
Pligin Package
"""

View File

@@ -0,0 +1,3 @@
from .parser import load, loads
from .writer import dump, dumps
from .speg import ParseError

View File

@@ -0,0 +1,295 @@
from .speg import peg
import re, sys
if sys.version_info[0] == 2:
_chr = unichr
else:
_chr = chr
def load(fin):
return loads(fin.read())
def loads(s):
if isinstance(s, bytes):
s = s.decode('utf-8')
if s.startswith(u'\ufeff'):
s = s[1:]
return peg(s.replace('\r\n', '\n'), _p_root)
def _p_ws(p):
p('[ \t]*')
def _p_nl(p):
p(r'([ \t]*(?:#[^\n]*)?\r?\n)+')
def _p_ews(p):
with p:
p(_p_nl)
p(_p_ws)
def _p_id(p):
return p(r'[$a-zA-Z_][$0-9a-zA-Z_]*')
_escape_table = {
'r': '\r',
'n': '\n',
't': '\t',
'f': '\f',
'b': '\b',
}
def _p_unescape(p):
esc = p('\\\\(?:u[0-9a-fA-F]{4}|[^\n])')
if esc[1] == 'u':
return _chr(int(esc[2:], 16))
return _escape_table.get(esc[1:], esc[1:])
_re_indent = re.compile(r'[ \t]*')
def _p_block_str(p, c):
p(r'{c}{c}{c}'.format(c=c))
lines = [['']]
with p:
while True:
s = p(r'(?:{c}(?!{c}{c})|[^{c}\\])*'.format(c=c))
l = s.split('\n')
lines[-1].append(l[0])
lines.extend([x] for x in l[1:])
if p(r'(?:\\\n[ \t]*)*'):
continue
p.commit()
lines[-1].append(p(_p_unescape))
p(r'{c}{c}{c}'.format(c=c))
lines = [''.join(l) for l in lines]
strip_ws = len(lines) > 1
if strip_ws and all(c in ' \t' for c in lines[-1]):
lines.pop()
indent = None
for line in lines[1:]:
if not line:
continue
if indent is None:
indent = _re_indent.match(line).group(0)
continue
for i, (c1, c2) in enumerate(zip(indent, line)):
if c1 != c2:
indent = indent[:i]
break
ind_len = len(indent or '')
if strip_ws and all(c in ' \t' for c in lines[0]):
lines = [line[ind_len:] for line in lines[1:]]
else:
lines[1:] = [line[ind_len:] for line in lines[1:]]
return '\n'.join(lines)
_re_mstr_nl = re.compile(r'(?:[ \t]*\n)+[ \t]*')
_re_mstr_trailing_nl = re.compile(_re_mstr_nl.pattern + r'\Z')
def _p_multiline_str(p, c):
p('{c}(?!{c}{c})(?:[ \t]*\n[ \t]*)?'.format(c=c))
string_parts = []
with p:
while True:
string_parts.append(p(r'[^{c}\\]*'.format(c=c)))
if p(r'(?:\\\n[ \t]*)*'):
string_parts.append('')
continue
p.commit()
string_parts.append(p(_p_unescape))
p(c)
string_parts[-1] = _re_mstr_trailing_nl.sub('', string_parts[-1])
string_parts[::2] = [_re_mstr_nl.sub(' ', part) for part in string_parts[::2]]
return ''.join(string_parts)
def _p_string(p):
with p:
return p(_p_block_str, '"')
with p:
return p(_p_block_str, "'")
with p:
return p(_p_multiline_str, '"')
return p(_p_multiline_str, "'")
def _p_array_value(p):
with p:
p(_p_nl)
return p(_p_object)
with p:
p(_p_ws)
return p(_p_line_object)
p(_p_ews)
return p(_p_simple_value)
def _p_key(p):
with p:
return p(_p_id)
return p(_p_string)
def _p_flow_kv(p):
k = p(_p_key)
p(_p_ews)
p(':')
with p:
p(_p_nl)
return k, p(_p_object)
with p:
p(_p_ws)
return k, p(_p_line_object)
p(_p_ews)
return k, p(_p_simple_value)
def _p_flow_obj_sep(p):
with p:
p(_p_ews)
p(',')
p(_p_ews)
return
p(_p_nl)
p(_p_ws)
def _p_simple_value(p):
with p:
p('null')
return None
with p:
p('false')
return False
with p:
p('true')
return True
with p:
return int(p('0b[01]+')[2:], 2)
with p:
return int(p('0o[0-7]+')[2:], 8)
with p:
return int(p('0x[0-9a-fA-F]+')[2:], 16)
with p:
return float(p(r'-?(?:[1-9][0-9]*|0)?\.[0-9]+(?:[Ee][\+-]?[0-9]+)?|(?:[1-9][0-9]*|0)(?:\.[0-9]+)?[Ee][\+-]?[0-9]+'))
with p:
return int(p('-?[1-9][0-9]*|0'), 10)
with p:
return p(_p_string)
with p:
p(r'\[')
r = []
with p:
p.set('I', '')
r.append(p(_p_array_value))
with p:
while True:
with p:
p(_p_ews)
p(',')
rr = p(_p_array_value)
if not p:
p(_p_nl)
with p:
rr = p(_p_object)
if not p:
p(_p_ews)
rr = p(_p_simple_value)
r.append(rr)
p.commit()
with p:
p(_p_ews)
p(',')
p(_p_ews)
p(r'\]')
return r
p(r'\{')
r = {}
p(_p_ews)
with p:
p.set('I', '')
k, v = p(_p_flow_kv)
r[k] = v
with p:
while True:
p(_p_flow_obj_sep)
k, v = p(_p_flow_kv)
r[k] = v
p.commit()
p(_p_ews)
with p:
p(',')
p(_p_ews)
p(r'\}')
return r
def _p_line_kv(p):
k = p(_p_key)
p(_p_ws)
p(':')
p(_p_ws)
with p:
p(_p_nl)
p(p.get('I'))
return k, p(_p_indented_object)
with p:
return k, p(_p_line_object)
with p:
return k, p(_p_simple_value)
p(_p_nl)
p(p.get('I'))
p('[ \t]')
p(_p_ws)
return k, p(_p_simple_value)
def _p_line_object(p):
k, v = p(_p_line_kv)
r = { k: v }
with p:
while True:
p(_p_ws)
p(',')
p(_p_ws)
k, v = p(_p_line_kv)
r[k] = v # uniqueness
p.commit()
return r
def _p_object(p):
p.set('I', p.get('I') + p('[ \t]*'))
r = p(_p_line_object)
with p:
while True:
p(_p_ws)
with p:
p(',')
p(_p_nl)
p(p.get('I'))
rr = p(_p_line_object)
r.update(rr) # unqueness
p.commit()
return r
def _p_indented_object(p):
p.set('I', p.get('I') + p('[ \t]'))
return p(_p_object)
def _p_root(p):
with p:
p(_p_nl)
with p:
p.set('I', '')
r = p(_p_object)
p(_p_ws)
with p:
p(',')
if not p:
p(_p_ws)
r = p(_p_simple_value)
p(_p_ews)
p(p.eof)
return r

View File

@@ -0,0 +1 @@
from .peg import peg, ParseError

View File

@@ -0,0 +1,147 @@
import sys, re
class ParseError(Exception):
def __init__(self, msg, text, offset, line, col):
self.msg = msg
self.text = text
self.offset = offset
self.line = line
self.col = col
super(ParseError, self).__init__(msg, offset, line, col)
if sys.version_info[0] == 2:
_basestr = basestring
else:
_basestr = str
def peg(s, r):
p = _Peg(s)
try:
return p(r)
except _UnexpectedError as e:
offset = max(p._errors)
err = p._errors[offset]
raise ParseError(err.msg, s, offset, err.line, err.col)
class _UnexpectedError(RuntimeError):
def __init__(self, state, expr):
self.state = state
self.expr = expr
class _PegState:
def __init__(self, pos, line, col):
self.pos = pos
self.line = line
self.col = col
self.vars = {}
self.commited = False
class _PegError:
def __init__(self, msg, line, col):
self.msg = msg
self.line = line
self.col = col
class _Peg:
def __init__(self, s):
self._s = s
self._states = [_PegState(0, 1, 1)]
self._errors = {}
self._re_cache = {}
def __call__(self, r, *args, **kw):
if isinstance(r, _basestr):
compiled = self._re_cache.get(r)
if not compiled:
compiled = re.compile(r)
self._re_cache[r] = compiled
st = self._states[-1]
m = compiled.match(self._s[st.pos:])
if not m:
self.error(expr=r, err=kw.get('err'))
ms = m.group(0)
st.pos += len(ms)
nl_pos = ms.rfind('\n')
if nl_pos < 0:
st.col += len(ms)
else:
st.col = len(ms) - nl_pos
st.line += ms[:nl_pos].count('\n') + 1
return ms
else:
kw.pop('err', None)
return r(self, *args, **kw)
def __repr__(self):
pos = self._states[-1].pos
vars = {}
for st in self._states:
vars.update(st.vars)
return '_Peg(%r, %r)' % (self._s[:pos] + '*' + self._s[pos:], vars)
@staticmethod
def eof(p):
if p._states[-1].pos != len(p._s):
p.error()
def error(self, err=None, expr=None):
st = self._states[-1]
if err is None:
err = 'expected {!r}, found {!r}'.format(expr, self._s[st.pos:st.pos+4])
self._errors[st.pos] = _PegError(err, st.line, st.col)
raise _UnexpectedError(st, expr)
def get(self, key, default=None):
for state in self._states[::-1]:
if key in state.vars:
return state.vars[key][0]
return default
def set(self, key, value):
self._states[-1].vars[key] = value, False
def set_global(self, key, value):
self._states[-1].vars[key] = value, True
def opt(self, *args, **kw):
with self:
return self(*args, **kw)
def not_(self, s, *args, **kw):
with self:
self(s)
self.error()
def __enter__(self):
self._states[-1].committed = False
self._states.append(_PegState(self._states[-1].pos, self._states[-1].line, self._states[-1].col))
def __exit__(self, type, value, traceback):
if type is None:
self.commit()
self._states.pop()
return type == _UnexpectedError
def commit(self):
cur = self._states[-1]
prev = self._states[-2]
for key in cur.vars:
val, g = cur.vars[key]
if not g:
continue
if key in prev.vars:
prev.vars[key] = val, prev.vars[key][1]
else:
prev.vars[key] = val, True
prev.pos = cur.pos
prev.line = cur.line
prev.col = cur.col
prev.committed = True
def __nonzero__(self):
return self._states[-1].committed
__bool__ = __nonzero__

View File

@@ -0,0 +1,191 @@
import re, json, sys
if sys.version_info[0] == 2:
def _is_num(o):
return isinstance(o, int) or isinstance(o, long) or isinstance(o, float)
def _stringify(o):
if isinstance(o, str):
return unicode(o)
if isinstance(o, unicode):
return o
return None
else:
def _is_num(o):
return isinstance(o, int) or isinstance(o, float)
def _stringify(o):
if isinstance(o, bytes):
return o.decode()
if isinstance(o, str):
return o
return None
_id_re = re.compile(r'[$a-zA-Z_][$0-9a-zA-Z_]*\Z')
class CSONEncoder:
def __init__(self, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False,
indent=None, default=None):
self._skipkeys = skipkeys
self._ensure_ascii = ensure_ascii
self._allow_nan = allow_nan
self._sort_keys = sort_keys
self._indent = ' ' * (indent or 4)
self._default = default
if check_circular:
self._obj_stack = set()
else:
self._obj_stack = None
def _format_simple_val(self, o):
if o is None:
return 'null'
if isinstance(o, bool):
return 'true' if o else 'false'
if _is_num(o):
return str(o)
s = _stringify(o)
if s is not None:
return self._escape_string(s)
return None
def _escape_string(self, s):
r = json.dumps(s, ensure_ascii=self._ensure_ascii)
return u"'{}'".format(r[1:-1].replace("'", r"\'"))
def _escape_key(self, s):
if s is None or isinstance(s, bool) or _is_num(s):
s = str(s)
s = _stringify(s)
if s is None:
if self._skipkeys:
return None
raise TypeError('keys must be a string')
if not _id_re.match(s):
return self._escape_string(s)
return s
def _push_obj(self, o):
if self._obj_stack is not None:
if id(o) in self._obj_stack:
raise ValueError('Circular reference detected')
self._obj_stack.add(id(o))
def _pop_obj(self, o):
if self._obj_stack is not None:
self._obj_stack.remove(id(o))
def _encode(self, o, obj_val=False, indent='', force_flow=False):
if isinstance(o, list):
if not o:
if obj_val:
yield ' []\n'
else:
yield indent
yield '[]\n'
else:
if obj_val:
yield ' [\n'
else:
yield indent
yield '[\n'
indent = indent + self._indent
self._push_obj(o)
for v in o:
for chunk in self._encode(v, obj_val=False, indent=indent, force_flow=True):
yield chunk
self._pop_obj(o)
yield indent[:-len(self._indent)]
yield ']\n'
elif isinstance(o, dict):
items = [(self._escape_key(k), v) for k, v in o.items()]
if self._skipkeys:
items = [(k, v) for k, v in items if k is not None]
if self._sort_keys:
items.sort()
if force_flow or not items:
if not items:
if obj_val:
yield ' {}\n'
else:
yield indent
yield '{}\n'
else:
if obj_val:
yield ' {\n'
else:
yield indent
yield '{\n'
indent = indent + self._indent
self._push_obj(o)
for k, v in items:
yield indent
yield k
yield ':'
for chunk in self._encode(v, obj_val=True, indent=indent + self._indent, force_flow=False):
yield chunk
self._pop_obj(o)
yield indent[:-len(self._indent)]
yield '}\n'
else:
if obj_val:
yield '\n'
self._push_obj(o)
for k, v in items:
yield indent
yield k
yield ':'
for chunk in self._encode(v, obj_val=True, indent=indent + self._indent, force_flow=False):
yield chunk
self._pop_obj(o)
else:
v = self._format_simple_val(o)
if v is None:
self._push_obj(o)
v = self.default(o)
for chunk in self._encode(v, obj_val=obj_val, indent=indent, force_flow=force_flow):
yield chunk
self._pop_obj(o)
else:
if obj_val:
yield ' '
else:
yield indent
yield v
yield '\n'
def iterencode(self, o):
return self._encode(o)
def encode(self, o):
return ''.join(self.iterencode(o))
def default(self, o):
if self._default is None:
raise TypeError('Cannot serialize an object of type {}'.format(type(o).__name__))
return self._default(o)
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None,
indent=None, default=None, sort_keys=False, **kw):
if indent is None and cls is None:
return json.dump(obj, fp, skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
allow_nan=allow_nan, default=default, sort_keys=sort_keys, separators=(',', ':'))
if cls is None:
cls = CSONEncoder
encoder = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
allow_nan=allow_nan, sort_keys=sort_keys, indent=indent, default=default, **kw)
for chunk in encoder.iterencode(obj):
fp.write(chunk)
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None,
default=None, sort_keys=False, **kw):
if indent is None and cls is None:
return json.dumps(obj, skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
allow_nan=allow_nan, default=default, sort_keys=sort_keys, separators=(',', ':'))
if cls is None:
cls = CSONEncoder
encoder = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
allow_nan=allow_nan, sort_keys=sort_keys, indent=indent, default=default, **kw)
return encoder.encode(obj)

View File

@@ -0,0 +1,7 @@
{
"name": "Snippets Completer",
"author": "ITDominator",
"version": "0.0.1",
"support": "",
"requests": {}
}

View File

@@ -0,0 +1,40 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from libs.dto.base_event import BaseEvent
from libs.event_factory import Event_Factory
from plugins.plugin_types import PluginCode
from .provider import Provider
class Plugin(PluginCode):
def __init__(self):
super(Plugin, self).__init__()
self.provider: Provider = None
def _controller_message(self, event: BaseEvent):
...
def load(self):
self.provider = Provider()
event = Event_Factory.create_event(
"register_provider",
provider_name = "Snippets Completer",
provider = self.provider,
language_ids = []
)
self.message_to("completion", event)
def run(self):
...

View File

@@ -0,0 +1,63 @@
# Python imports
import re
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4')
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import GtkSource
# Application imports
from .provider_response_cache import ProviderResponseCache
class Provider(GObject.GObject, GtkSource.CompletionProvider):
"""
This is a Snippits Completion Provider.
# NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
"""
__gtype_name__ = 'SnippetsCompletionProvider'
def __init__(self):
GObject.Object.__init__(self)
self.response_cache: ProviderResponseCache = ProviderResponseCache()
def do_get_name(self):
return 'Snippits Completion'
def do_match(self, context):
word = self.response_cache.get_word(context)
if not word or len(word) < 2: return False
return True
def do_get_priority(self):
return 2
def do_get_activation(self):
""" The context for when a provider will show results """
# return GtkSource.CompletionActivation.NONE
return GtkSource.CompletionActivation.USER_REQUESTED
# return GtkSource.CompletionActivation.INTERACTIVE
def do_populate(self, context):
word = self.response_cache.get_word(context)
results = self.response_cache.filter(word)
proposals = []
for entry in results:
proposals.append(
self.response_cache.create_completion_item(
entry["label"],
entry["text"],
entry["info"]
)
)
context.add_proposals(self, proposals, True)

View File

@@ -0,0 +1,63 @@
# Python imports
from os import path
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GLib
from gi.repository import GtkSource
# Application imports
from libs.event_factory import Code_Event_Types
from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
from . import cson
class ProviderResponseCache(ProviderResponseCacheBase):
def __init__(self):
super(ProviderResponseCache, self).__init__()
self.matchers: dict = {}
self.load_snippets()
def load_snippets(self):
fpath = path.join(path.dirname(path.realpath(__file__)), "snippets.cson")
with open(fpath, 'rb') as f:
self.snippets = cson.load(f)
for group in self.snippets:
self.snippets[group]
for entry in self.snippets[group]:
data = self.snippets[group][entry]
self.matchers[ data["prefix"] ] = {
"label": entry,
"text": data["body"],
"info": GLib.markup_escape_text( data["body"] )
}
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
...
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
...
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
...
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
...
def filter(self, word: str):
response: list = []
for entry in self.matchers:
if not word in entry: continue
data = self.matchers[entry]
response.append(data)
return response

View File

@@ -0,0 +1,614 @@
# Your snippets
#
# Atom snippets allow you to enter a simple prefix in the editor and hit tab to
# expand the prefix into a larger code block with templated values.
#
# You can create a new snippet in this file by typing "snip" and then hitting
# tab.
#
# An example CoffeeScript snippet to expand log to console.log:
#
# '.source.coffee':
# 'Console log':
# 'prefix': 'log'
# 'body': 'console.log $1'
#
# Each scope (e.g. '.source.coffee' above) can only be declared once.
#
# This file uses CoffeeScript Object Notation (CSON).
# If you are unfamiliar with CSON, you can read more about it in the
# Atom Flight Manual:
# http://flight-manual.atom.io/using-atom/sections/basic-customization/#_cson
### HTML SNIPPETS ###
'.text.html.basic':
'HTML Template':
'prefix': 'html'
'body': """<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<link rel="shortcut icon" href="fave_icon.png">
<link rel="stylesheet" href="resources/css/base.css">
<link rel="stylesheet" href="resources/css/main.css">
</head>
<body>
<script src="resources/js/.js" charset="utf-8"></script>
<script src="resources/js/.js" charset="utf-8"></script>
</body>
</html>
"""
'Canvas Tag':
'prefix': 'canvas'
'body': """<canvas id="canvas" width="800" height="600" style="border:1px solid #c3c3c3;"></canvas>"""
'Img Tag':
'prefix': 'img'
'body': """<img class="" src="" alt="" />"""
'Br Tag':
'prefix': 'br'
'body': """<br/>"""
'Hr Tag':
'prefix': 'hr'
'body': """<hr/>"""
'Server Side Events':
'prefix': 'sse'
'body': """// SSE events if supported
if(typeof(EventSource) !== "undefined") {
let source = new EventSource("resources/php/sse.php");
source.onmessage = (event) => {
if (event.data === "<yourDataStringToLookFor>") {
// code here
}
};
} else {
console.log("SSE Not Supported In Browser...");
}
"""
'AJAX Template Function':
'prefix': 'ajax template'
'body': """const doAjax = async (actionPath, data) => {
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
if (this.responseText != null) { // this.responseXML if getting XML fata
handleReturnData(JSON.parse(this.responseText));
} else {
console.log("No content returned. Check the file path.");
}
}
};
xhttp.open("POST", actionPath, true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// Force return to be JSON NOTE: Use application/xml to force XML
xhttp.overrideMimeType('application/json');
xhttp.send(data);
}
"""
'CSS Message Colors':
'prefix': 'css colors'
'body': """.error { color: rgb(255, 0, 0); }
.warning { color: rgb(255, 168, 0); }
.success { color: rgb(136, 204, 39); }
"""
### JS SNIPPETS ###
'.source.js':
'Server Side Events':
'prefix': 'sse'
'body': """// SSE events if supported
if(typeof(EventSource) !== "undefined") {
let source = new EventSource("resources/php/sse.php");
source.onmessage = (event) => {
if (event.data === "<yourDataStringToLookFor>") {
// code here
}
};
} else {
console.log("SSE Not Supported In Browser...");
}
"""
'AJAX Template Function':
'prefix': 'ajax template'
'body': """const doAjax = async (actionPath, data) => {
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
if (this.responseText != null) { // this.responseXML if getting XML fata
handleReturnData(JSON.parse(this.responseText));
} else {
console.log("No content returned. Check the file path.");
}
}
};
xhttp.open("POST", actionPath, true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// Force return to be JSON NOTE: Use application/xml to force XML
xhttp.overrideMimeType('application/json');
xhttp.send(data);
}
"""
'SE6 Function':
'prefix': 'function se6'
'body': """const funcName = (arg = "") => {
}
"""
### CSS SNIPPETS ###
'.source.css':
'CSS Message Colors':
'prefix': 'css colors'
'body': """.error { color: rgb(255, 0, 0); }
.warning { color: rgb(255, 168, 0); }
.success { color: rgb(136, 204, 39); }
"""
### PHP SNIPPETS ###
'.text.html.php':
'SSE PHP':
'prefix': 'sse php'
'body': """<?php
// Start the session
session_start();
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
echo "data:dataToReturn\\\\n\\\\n";
flush();
?>
"""
'PHP Template':
'prefix': 'php'
'body': """<?php
// Start the session
session_start();
// Determin action
chdir("../../"); // Note: If in resources/php/
if (isset($_POST['yourPostID'])) {
// code here
} else {
$message = "Server: [Error] --> Illegal Access Method!";
serverMessage("error", $message);
}
?>
"""
'HTML Template':
'prefix': 'html'
'body': """<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<link rel="shortcut icon" href="fave_icon.png">
<link rel="stylesheet" href="resources/css/base.css">
<link rel="stylesheet" href="resources/css/main.css">
</head>
<body>
<script src="resources/js/.js" charset="utf-8"></script>
<script src="resources/js/.js" charset="utf-8"></script>
</body>
</html>
"""
### BASH SNIPPETS ###
'.source.shell':
'Bash or Shell Template':
'prefix': 'bash template'
'body': """#!/bin/bash
# . CONFIG.sh
# set -o xtrace ## To debug scripts
# set -o errexit ## To exit on error
# set -o errunset ## To exit if a variable is referenced but not set
function main() {
cd "$(dirname "$0")"
echo "Working Dir: " $(pwd)
file="$1"
if [ -z "${file}" ]; then
echo "ERROR: No file argument supplied..."
exit
fi
if [[ -f "${file}" ]]; then
echo "SUCCESS: The path and file exists!"
else
echo "ERROR: The path or file '${file}' does NOT exist..."
fi
}
main "$@";
"""
'Bash or Shell Config':
'prefix': 'bash config'
'body': """#!/bin/bash
shopt -s expand_aliases
alias echo="echo -e"
"""
### PYTHON SNIPPETS ###
'.source.python':
'Glade __main__ Class Template':
'prefix': 'glade __main__ class'
'body': """#!/usr/bin/python3
# Python imports
import argparse
import faulthandler
import traceback
import signal
from setproctitle import setproctitle
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
# Application imports
from app import Application
def run():
try:
setproctitle('<replace this>')
faulthandler.enable() # For better debug info
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
parser = argparse.ArgumentParser()
# Add long and short arguments
parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
parser.add_argument("--no-plugins", "-np", default="false", help="Do not load plugins.")
parser.add_argument("--new-tab", "-t", default="", help="Open a file into new tab.")
parser.add_argument("--new-window", "-w", default="", help="Open a file into a new window.")
# Read arguments (If any...)
args, unknownargs = parser.parse_known_args()
main = Application(args, unknownargs)
Gtk.main()
except Exception as e:
traceback.print_exc()
quit()
if __name__ == "__main__":
''' Set process title, get arguments, and create GTK main thread. '''
run()
"""
'Glade __main__ Testing Template':
'prefix': 'glade testing class'
'body': """#!/usr/bin/python3
# Python imports
import traceback
import faulthandler
import signal
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
# Application imports
app_name = "Gtk Quick Test"
class Application(Gtk.ApplicationWindow):
def __init__(self):
super(Application, self).__init__()
self._setup_styling()
self._setup_signals()
self._load_widgets()
self.add(Gtk.Box())
self.show_all()
def _setup_styling(self):
self.set_default_size(1670, 830)
self.set_title(f"{app_name}")
# self.set_icon_from_file( settings.get_window_icon() )
self.set_gravity(5) # 5 = CENTER
self.set_position(1) # 1 = CENTER, 4 = CENTER_ALWAYS
def _setup_signals(self):
self.connect("delete-event", Gtk.main_quit)
def _load_widgets(self):
...
def run():
try:
faulthandler.enable() # For better debug info
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
main = Application()
Gtk.main()
except Exception as e:
traceback.print_exc()
quit()
if __name__ == "__main__":
''' Set process title, get arguments, and create GTK main thread. '''
run()
"""
'Glade _init_ Class Template':
'prefix': 'glade __init__ class'
'body': """# Python imports
import inspect
# Lib imports
# Application imports
from utils import Settings
from signal_classes import CrossClassSignals
class Main:
def __init__(self, args):
settings = Settings()
builder = settings.returnBuilder()
# Gets the methods from the classes and sets to handler.
# Then, builder connects to any signals it needs.
classes = [CrossClassSignals(settings)]
handlers = {}
for c in classes:
methods = inspect.getmembers(c, predicate=inspect.ismethod)
handlers.update(methods)
builder.connect_signals(handlers)
window = settings.createWindow()
window.show()
"""
'Class Method':
'prefix': 'def1'
'body': """
def fname(self):
...
"""
'Gtk Class Method':
'prefix': 'def2'
'body': """
def fname(self, widget, eve):
...
"""
'Python Glade Settings Template':
'prefix': 'glade settings class'
'body': """# Python imports
import os
# Lib imports
import gi, cairo
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
# Application imports
class Settings:
def __init__(self):
self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/"
self.builder = Gtk.Builder()
self.builder.add_from_file(self.SCRIPT_PTH + "../resources/Main_Window.glade")
# 'Filters'
self.office = ('.doc', '.docx', '.xls', '.xlsx', '.xlt', '.xltx', '.xlm',
'.ppt', 'pptx', '.pps', '.ppsx', '.odt', '.rtf')
self.vids = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv',
'.mpeg', '.mp4', '.webm')
self.txt = ('.txt', '.text', '.sh', '.cfg', '.conf')
self.music = ('.psf', '.mp3', '.ogg' , '.flac')
self.images = ('.png', '.jpg', '.jpeg', '.gif')
self.pdf = ('.pdf')
def createWindow(self):
# Get window and connect signals
window = self.builder.get_object("Main_Window")
window.connect("delete-event", gtk.main_quit)
self.setWindowData(window, False)
return window
def setWindowData(self, window, paintable):
screen = window.get_screen()
visual = screen.get_rgba_visual()
if visual != None and screen.is_composited():
window.set_visual(visual)
# bind css file
cssProvider = gtk.CssProvider()
cssProvider.load_from_path(self.SCRIPT_PTH + '../resources/stylesheet.css')
screen = Gdk.Screen.get_default()
styleContext = Gtk.StyleContext()
styleContext.add_provider_for_screen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER)
window.set_app_paintable(paintable)
if paintable:
window.connect("draw", self.area_draw)
def getMonitorData(self):
screen = self.builder.get_object("Main_Window").get_screen()
monitors = []
for m in range(screen.get_n_monitors()):
monitors.append(screen.get_monitor_geometry(m))
for monitor in monitors:
print(str(monitor.width) + "x" + str(monitor.height) + "+" + str(monitor.x) + "+" + str(monitor.y))
return monitors
def area_draw(self, widget, cr):
cr.set_source_rgba(0, 0, 0, 0.54)
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.paint()
cr.set_operator(cairo.OPERATOR_OVER)
def returnBuilder(self): return self.builder
# Filter returns
def returnOfficeFilter(self): return self.office
def returnVidsFilter(self): return self.vids
def returnTextFilter(self): return self.txt
def returnMusicFilter(self): return self.music
def returnImagesFilter(self): return self.images
def returnPdfFilter(self): return self.pdf
"""
'Python Glade CrossClassSignals Template':
'prefix': 'glade crossClassSignals class'
'body': """# Python imports
import threading
import subprocess
import os
# Lib imports
# Application imports
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class CrossClassSignals:
def __init__(self, settings):
self.settings = settings
self.builder = self.settings.returnBuilder()
def getClipboardData(self):
proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout=subprocess.PIPE)
retcode = proc.wait()
data = proc.stdout.read()
return data.decode("utf-8").strip()
def setClipboardData(self, data):
proc = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE)
proc.stdin.write(data)
proc.stdin.close()
retcode = proc.wait()
"""
'Python Glade Generic Template':
'prefix': 'glade generic class'
'body': """# Python imports
# Lib imports
# Application imports
class GenericClass:
def __init__(self):
super(GenericClass, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
def _setup_styling(self):
...
def _setup_signals(self):
...
def _subscribe_to_events(self):
event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
def _load_widgets(self):
...
"""

View File

@@ -1,13 +1,9 @@
{ {
"manifest": {
"name": "Example Plugin", "name": "Example Plugin",
"author": "John Doe", "author": "John Doe",
"version": "0.0.1", "version": "0.0.1",
"support": "", "support": "",
"requests": { "requests": {
"ui_target": "plugin_control_list",
"pass_events": "true",
"bind_keys": ["Example Plugin||send_message:<Control>f"] "bind_keys": ["Example Plugin||send_message:<Control>f"]
} }
}
} }

View File

@@ -1,8 +1,4 @@
# Python imports # Python imports
import os
import threading
import subprocess
import time
# Lib imports # Lib imports
import gi import gi
@@ -10,42 +6,36 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from libs.dto.base_event import BaseEvent
from plugins.plugin_base import PluginBase from plugins.plugin_base import PluginBase
# NOTE: Threads WILL NOT die with parent's destruction.
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
return wrapper
# NOTE: Threads WILL die with parent's destruction.
def daemon_threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
return wrapper
class Plugin(PluginBase): class Plugin(PluginBase):
def __init__(self): def __init__(self):
super().__init__() super(Plugin, self).__init__()
self.name = "Example Plugin" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
# where self.name should not be needed for message comms
def generate_reference_ui_element(self): def _controller_message(self, event: BaseEvent):
button = Gtk.Button(label=self.name) ...
button.connect("button-release-event", self.send_message)
return button def load(self):
ui_element = self.requests_ui_element("plugin_control_list")
ui_element.add( self.generate_plugin_element() )
def run(self): def run(self):
... ...
def send_message(self, widget=None, eve=None): def generate_plugin_element(self):
button = Gtk.Button(label = self.name)
button.connect("button-release-event", self.send_message)
button.show()
return button
def send_message(self, widget = None, eve = None):
message = "Hello, World!" message = "Hello, World!"
event_system.emit("display_message", ("warning", message, None)) self.emit("display_message", ("warning", message, None))

View File

@@ -5,7 +5,7 @@
"reportDuplicateImport": true, "reportDuplicateImport": true,
"executionEnvironments": [ "executionEnvironments": [
{ {
"root": "./src/versions/solarfm-0.0.1/solarfm" "root": "./src/"
} }
], ],
"venvPath": ".", "venvPath": ".",

View File

@@ -9,10 +9,10 @@ import sys
# Application imports # Application imports
# from libs.db import DB # from libs.db import DB
from libs.event_system import EventSystem from libs.event_system import EventSystem
from libs.endpoint_registry import EndpointRegistry
from libs.keybindings import Keybindings from libs.keybindings import Keybindings
from libs.logger import Logger from libs.logger import Logger
from libs.settings.manager import SettingsManager from libs.settings.manager import SettingsManager
from libs.widget_registery import WidgetRegisteryController
@@ -34,12 +34,8 @@ def daemon_threaded_wrapper(fn):
def call_chain_wrapper(fn): def call_chain_wrapper(fn):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
print()
print()
for line in traceback.format_stack(): for line in traceback.format_stack():
print( line.strip() ) print( line.strip() )
print()
print()
return fn(*args, **kwargs) return fn(*args, **kwargs)
return wrapper return wrapper
@@ -51,17 +47,16 @@ builtins.APP_NAME = "<change_me>"
builtins.keybindings = Keybindings() builtins.keybindings = Keybindings()
builtins.event_system = EventSystem() builtins.event_system = EventSystem()
builtins.endpoint_registry = EndpointRegistry()
builtins.settings_manager = SettingsManager() builtins.settings_manager = SettingsManager()
builtins.widget_registery = WidgetRegisteryController()
# builtins.db = DB() # builtins.db = DB()
settings_manager.load_settings() settings_manager.load_settings()
builtins.settings = settings_manager.settings
builtins.logger = Logger( builtins.logger = Logger(
settings_manager.get_home_config_path(), \ settings_manager.path_manager.get_home_config_path(), \
_ch_log_lvl = settings.debugging.ch_log_lvl, \ _ch_log_lvl = settings_manager.settings.debugging.ch_log_lvl, \
_fh_log_lvl = settings.debugging.fh_log_lvl _fh_log_lvl = settings_manager.settings.debugging.fh_log_lvl
).get_logger() ).get_logger()
builtins.threaded = threaded_wrapper builtins.threaded = threaded_wrapper

View File

@@ -23,13 +23,14 @@ class Application:
def __init__(self): def __init__(self):
super(Application, self).__init__() super(Application, self).__init__()
if not settings_manager.is_trace_debug():
self.load_ipc()
self.setup_debug_hook() self.setup_debug_hook()
def run(self): def run(self):
if not settings_manager.is_trace_debug():
if not self.load_ipc():
return
win = Window() win = Window()
win.start() win.start()
@@ -39,22 +40,30 @@ class Application:
ipc_server = IPCServer() ipc_server = IPCServer()
self.ipc_realization_check(ipc_server) self.ipc_realization_check(ipc_server)
if not ipc_server.is_ipc_alive: if ipc_server.is_ipc_alive:
return True
logger.warning(f"{app_name} IPC Server Exists: Have sent path(s) to it and closing...")
for arg in unknownargs + [args.new_tab,]: for arg in unknownargs + [args.new_tab,]:
if os.path.isfile(arg): if os.path.isfile(arg):
message = f"FILE|{arg}" message = f"FILE|{arg}"
ipc_server.send_ipc_message(message) ipc_server.send_ipc_message(message)
raise AppLaunchException(f"{APP_NAME} IPC Server Exists: Have sent path(s) to it and closing...") if os.path.isdir(arg):
message = f"DIR|{arg}"
ipc_server.send_ipc_message(message)
return False
def ipc_realization_check(self, ipc_server): def ipc_realization_check(self, ipc_server):
try: try:
ipc_server.create_ipc_listener() ipc_server.create_ipc_listener()
except Exception: except (OSError, PermissionError) as e:
logger.info(f"IPC listener creation failed: {e}, falling back to test message")
ipc_server.send_test_ipc_message()
except Exception as e:
logger.error(f"Unexpected IPC setup error: {e}")
ipc_server.send_test_ipc_message() ipc_server.send_test_ipc_message()
with suppress(Exception):
ipc_server.create_ipc_listener()
def setup_debug_hook(self): def setup_debug_hook(self):
# Typically: ValueError: signal only works in main thread # Typically: ValueError: signal only works in main thread

View File

@@ -1,33 +0,0 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
class BuilderWrapper(Gtk.Builder):
"""docstring for BuilderWrapper."""
def __init__(self):
super(BuilderWrapper, self).__init__()
self.objects = {}
def get_object(self, id: str, use_gtk: bool = True) -> any:
if not use_gtk:
return self.objects[id]
return super(BuilderWrapper, self).get_object(id)
def expose_object(self, id: str, object: any, use_gtk: bool = True) -> None:
if not use_gtk:
self.objects[id] = object
else:
super(BuilderWrapper, self).expose_object(id, object)
def dereference_object(self, id: str) -> None:
del self.objects[id]

View File

@@ -16,8 +16,6 @@ class BaseContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(BaseContainer, self).__init__() super(BaseContainer, self).__init__()
self.ctx = self.get_style_context()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
@@ -27,9 +25,11 @@ class BaseContainer(Gtk.Box):
def _setup_styling(self): def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL) self.ctx = self.get_style_context()
self.ctx.add_class("base-container") self.ctx.add_class("base-container")
self.set_orientation(Gtk.Orientation.VERTICAL)
def _setup_signals(self): def _setup_signals(self):
... ...
@@ -38,12 +38,12 @@ class BaseContainer(Gtk.Box):
event_system.subscribe("remove-transparency", self._remove_transparency) event_system.subscribe("remove-transparency", self._remove_transparency)
def _load_widgets(self): def _load_widgets(self):
self.add(HeaderContainer()) self.add( HeaderContainer() )
self.add(BodyContainer()) self.add( BodyContainer() )
self.add(FooterContainer()) self.add( FooterContainer() )
def _update_transparency(self): def _update_transparency(self):
self.ctx.add_class(f"mw_transparency_{settings.theming.transparency}") self.ctx.add_class(f"mw_transparency_{settings_manager.settings.theming.transparency}")
def _remove_transparency(self): def _remove_transparency(self):
self.ctx.remove_class(f"mw_transparency_{settings.theming.transparency}") self.ctx.remove_class(f"mw_transparency_{settings_manager.settings.theming.transparency}")

View File

@@ -16,8 +16,6 @@ class BodyContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(BodyContainer, self).__init__() super(BodyContainer, self).__init__()
self.ctx = self.get_style_context()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
@@ -27,8 +25,10 @@ class BodyContainer(Gtk.Box):
def _setup_styling(self): def _setup_styling(self):
self.set_orientation(Gtk.Orientation.HORIZONTAL) self.ctx = self.get_style_context()
self.ctx.add_class("body-container") self.ctx.add_class("body-container")
self.set_orientation(Gtk.Orientation.HORIZONTAL)
self.set_homogeneous(True) self.set_homogeneous(True)
def _setup_signals(self): def _setup_signals(self):
@@ -37,8 +37,7 @@ class BodyContainer(Gtk.Box):
def _subscribe_to_events(self): def _subscribe_to_events(self):
... ...
def _load_widgets(self): def _load_widgets(self):
self.add(LeftContainer()) self.add( LeftContainer() )
self.add(CenterContainer()) self.add( CenterContainer() )
self.add(RightContainer()) self.add( RightContainer() )

View File

@@ -14,7 +14,6 @@ class CenterContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(CenterContainer, self).__init__() super(CenterContainer, self).__init__()
self._builder = settings_manager.get_builder()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
@@ -25,14 +24,13 @@ class CenterContainer(Gtk.Box):
def _setup_styling(self): def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL) self.ctx = self.get_style_context()
self.ctx.add_class("center-container")
self.set_orientation(Gtk.Orientation.VERTICAL)
self.set_hexpand(True) self.set_hexpand(True)
self.set_vexpand(True) self.set_vexpand(True)
ctx = self.get_style_context()
ctx.add_class("center-container")
def _setup_signals(self): def _setup_signals(self):
... ...
@@ -40,7 +38,7 @@ class CenterContainer(Gtk.Box):
... ...
def _load_widgets(self): def _load_widgets(self):
glade_box = self._builder.get_object("glade_box") glade_box = widget_registery.get_object("glade_box")
button = Gtk.Button(label = "Click Me!") button = Gtk.Button(label = "Click Me!")
button.connect("clicked", self._hello_world) button.connect("clicked", self._hello_world)

View File

@@ -0,0 +1,3 @@
"""
Containers Package
"""

View File

@@ -0,0 +1,64 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from ...widgets.code.code_base import CodeBase
from ...widgets.separator_widget import Separator
from ...widgets.code.mini_view_widget import MiniViewWidget
from .editors_container import EditorsContainer
class CodeContainer(Gtk.Box):
def __init__(self):
super(CodeContainer, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show_all()
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
def _setup_signals(self):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
code_base = CodeBase()
self.add( self._create_tabs_widgets(code_base) )
self.add( self._create_editor_widget(code_base) )
def _create_tabs_widgets(self, code_base: CodeBase):
scrolled_window = Gtk.ScrolledWindow()
viewport = Gtk.Viewport()
scrolled_window.set_overlay_scrolling(False)
viewport.add( code_base.get_tabs_widget() )
scrolled_window.add( viewport )
return scrolled_window
def _create_editor_widget(self, code_base: CodeBase):
editors_container = Gtk.Box()
editors_container.add( Separator("separator_left") )
editors_container.add( EditorsContainer(code_base) )
editors_container.add( Separator("separator_right") )
editors_container.add( code_base.get_mini_view_widget() )
return editors_container

View File

@@ -0,0 +1,71 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
# Application imports
class EditorsContainer(Gtk.Paned):
def __init__(self, code_base: any):
super(EditorsContainer, self).__init__()
self.code_base = code_base
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
def _setup_styling(self):
self.ctx = self.get_style_context()
self.ctx.add_class("paned-editors-container")
self.set_hexpand(True)
self.set_vexpand(True)
self.set_wide_handle(True)
def _setup_signals(self):
self.map_id = self.connect("map", self._init_map)
def _subscribe_to_events(self):
...
def _load_widgets(self):
self.scrolled_win1, \
self.scrolled_win2 = self._create_views()
self.pack1( self.scrolled_win1, True, True )
self.pack2( self.scrolled_win2, True, True )
def _create_views(self):
scrolled_win1 = Gtk.ScrolledWindow()
scrolled_win2 = Gtk.ScrolledWindow()
source_view1 = self.code_base.create_source_view()
source_view2 = self.code_base.create_source_view()
source_view1.sibling_right = source_view2
source_view2.sibling_left = source_view1
scrolled_win1.add( source_view1 )
scrolled_win2.add( source_view2 )
return scrolled_win1, scrolled_win2
def _init_map(self, view):
def _first_show_init():
self.disconnect(self.map_id)
del self.map_id
self.code_base.first_map_load()
del self.code_base
return False
GLib.timeout_add(100, _first_show_init)

View File

@@ -6,6 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from .code.code_container import CodeContainer
@@ -13,8 +14,6 @@ class FooterContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(FooterContainer, self).__init__() super(FooterContainer, self).__init__()
self.ctx = self.get_style_context()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
@@ -24,18 +23,17 @@ class FooterContainer(Gtk.Box):
def _setup_styling(self): def _setup_styling(self):
self.set_orientation(Gtk.Orientation.HORIZONTAL) self.ctx = self.get_style_context()
self.set_hexpand(True)
self.ctx.add_class("footer-container") self.ctx.add_class("footer-container")
self.set_orientation(Gtk.Orientation.HORIZONTAL)
self.set_hexpand(True)
def _setup_signals(self): def _setup_signals(self):
... ...
def _subscribe_to_events(self): def _subscribe_to_events(self):
... ...
def _load_widgets(self): def _load_widgets(self):
... self.add( CodeContainer() )

View File

@@ -15,8 +15,6 @@ class HeaderContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(HeaderContainer, self).__init__() super(HeaderContainer, self).__init__()
self.ctx = self.get_style_context()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
@@ -26,19 +24,18 @@ class HeaderContainer(Gtk.Box):
def _setup_styling(self): def _setup_styling(self):
self.set_orientation(Gtk.Orientation.HORIZONTAL) self.ctx = self.get_style_context()
self.set_hexpand(True)
self.ctx.add_class("header-container") self.ctx.add_class("header-container")
self.set_orientation(Gtk.Orientation.HORIZONTAL)
self.set_hexpand(True)
def _setup_signals(self): def _setup_signals(self):
... ...
def _subscribe_to_events(self): def _subscribe_to_events(self):
event_system.subscribe("tggl-top-main-menubar", self.tggl_top_main_menubar) event_system.subscribe("tggl-top-main-menubar", self.tggl_top_main_menubar)
def _load_widgets(self): def _load_widgets(self):
button = Gtk.Button(label = "Interactive Debug") button = Gtk.Button(label = "Interactive Debug")
button.connect("clicked", self._interactive_debug) button.connect("clicked", self._interactive_debug)

View File

@@ -22,13 +22,12 @@ class LeftContainer(Gtk.Box):
def _setup_styling(self): def _setup_styling(self):
self.ctx = self.get_style_context()
self.ctx.add_class("left-container")
self.set_orientation(Gtk.Orientation.VERTICAL) self.set_orientation(Gtk.Orientation.VERTICAL)
self.set_vexpand(True) self.set_vexpand(True)
ctx = self.get_style_context()
ctx.add_class("left-container")
def _setup_signals(self): def _setup_signals(self):
... ...

View File

@@ -23,13 +23,12 @@ class RightContainer(Gtk.Box):
def _setup_styling(self): def _setup_styling(self):
self.ctx = self.get_style_context()
self.ctx.add_class("right-container")
self.set_orientation(Gtk.Orientation.VERTICAL) self.set_orientation(Gtk.Orientation.VERTICAL)
self.set_vexpand(True) self.set_vexpand(True)
ctx = self.get_style_context()
ctx.add_class("right-container")
def _setup_signals(self): def _setup_signals(self):
... ...

View File

@@ -6,33 +6,48 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from plugins import plugins_controller
from libs.mixins.ipc_signals_mixin import IPCSignalsMixin from libs.mixins.ipc_signals_mixin import IPCSignalsMixin
from libs.mixins.keyboard_signals_mixin import KeyboardSignalsMixin from libs.mixins.keyboard_signals_mixin import KeyboardSignalsMixin
from ..containers.base_container import BaseContainer from ..containers.base_container import BaseContainer
from .base_controller_data import BaseControllerData from .base_controller_mixin import BaseControllerMixin
from .bridge_controller import BridgeController from .bridge_controller import BridgeController
class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData): class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerMixin):
""" docstring for BaseController. """ """ docstring for BaseController. """
def __init__(self): def __init__(self):
self._setup_controller_data() self._setup_controller_data()
self._load_plugins(is_pre = True)
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
self._load_controllers() self._load_controllers()
self._load_plugins_and_files() self._load_plugins(is_pre = False)
self._load_files()
logger.info(f"Made it past {self.__class__} loading...") logger.info(f"Made it past {self.__class__} loading...")
settings_manager.set_end_load_time() settings_manager.set_end_load_time()
settings_manager.log_load_time() settings_manager.log_load_time()
def _setup_controller_data(self):
self.window = settings_manager.get_main_window()
self.base_container = BaseContainer()
self.plugins_controller = plugins_controller
widget_registery.expose_object("main_window", self.window)
settings_manager.register_signals_to_builder([self, self.base_container])
self._collect_files_dirs()
def _setup_styling(self): def _setup_styling(self):
... ...
@@ -50,23 +65,22 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
def _load_controllers(self): def _load_controllers(self):
BridgeController() BridgeController()
def _load_plugins_and_files(self): def _load_plugins(self, is_pre: bool):
args, unknownargs = settings_manager.get_starting_args() args, unknownargs = settings_manager.get_starting_args()
if args.no_plugins == "false": if args.no_plugins == "true": return
self.plugins_controller.pre_launch_plugins()
self.plugins_controller.post_launch_plugins()
if is_pre:
self.plugins_controller.pre_launch_plugins()
return
if not is_pre:
self.plugins_controller.post_launch_plugins()
return
def _load_files(self):
for file in settings_manager.get_starting_files(): for file in settings_manager.get_starting_files():
event_system.emit("post-file-to-ipc", file) event_system.emit("post-file-to-ipc", file)
def _tggl_top_main_menubar(self): def _tggl_top_main_menubar(self):
logger.debug("_tggl_top_main_menubar > stub...") logger.debug("_tggl_top_main_menubar > stub...")
def _load_glade_file(self):
self.builder.add_from_file( settings_manager.get_glade_file() )
self.builder.expose_object("main_window", self.window)
settings_manager.set_builder(self.builder)
self.base_container = BaseContainer()
settings_manager.register_signals_to_builder([self, self.base_container])

View File

@@ -6,28 +6,11 @@ from shutil import which
# Lib imports # Lib imports
# Application imports # Application imports
from plugins.plugins_controller import PluginsController
from ..builder_wrapper import BuilderWrapper
class BaseControllerData: class BaseControllerMixin:
''' BaseControllerData contains most of the state of the app at ay given time. It also has some support methods. ''' ''' BaseControllerMixin contains most of the state of the app at ay given time. It also has some support methods. '''
def _setup_controller_data(self) -> None:
self.window = settings_manager.get_main_window()
self.builder = BuilderWrapper()
self.plugins_controller = PluginsController()
self.base_container = None
self.was_midified_key = False
self.ctrl_down = False
self.shift_down = False
self.alt_down = False
self._collect_files_dirs()
self._load_glade_file()
def _collect_files_dirs(self): def _collect_files_dirs(self):
args, \ args, \

View File

@@ -0,0 +1,3 @@
"""
Code Package
"""

View File

@@ -0,0 +1,61 @@
# Python imports
# Lib imports
# Application imports
from plugins import plugins_controller
from libs.controllers.controller_manager import ControllerManager
from .controllers.files_controller import FilesController
from .controllers.tabs_controller import TabsController
from .controllers.commands_controller import CommandsController
from .controllers.completion_controller import CompletionController
from .controllers.views.source_views_controller import SourceViewsController
from .mini_view_widget import MiniViewWidget
class CodeBase:
def __init__(self):
super(CodeBase, self).__init__()
self.controller_manager: ControllerManager = ControllerManager()
self.miniview_widget: MiniViewWidget = MiniViewWidget()
self._load_controllers()
def _load_controllers(self):
files_controller = FilesController()
tabs_controller = TabsController()
commands_controller = CommandsController()
completion_controller = CompletionController()
source_views_controller = SourceViewsController()
# self.controller_manager.register_controller("base", self)
self.controller_manager.register_controller("files", files_controller)
self.controller_manager.register_controller("tabs", tabs_controller)
self.controller_manager.register_controller("commands", commands_controller)
self.controller_manager.register_controller("completion", completion_controller)
self.controller_manager.register_controller("source_views", source_views_controller)
self.controller_manager.register_controller("plugins", plugins_controller)
self.controller_manager.register_controller("widgets", widget_registery)
def get_tabs_widget(self):
return self.controller_manager["tabs"].get_tabs_widget()
def get_mini_view_widget(self):
return self.miniview_widget
def create_source_view(self):
source_view = self.controller_manager["source_views"].create_source_view()
self.controller_manager["completion"].register_completer(
source_view.get_completion()
)
return source_view
def first_map_load(self):
self.controller_manager["source_views"].first_map_load()

View File

@@ -0,0 +1,5 @@
"""
Code Command System Package
"""
from .command_system import CommandSystem

View File

@@ -0,0 +1,27 @@
# Python imports
# Lib imports
from gi.repository import GtkSource
# Application imports
def set_language_and_style(view, file):
language = view.language_manager.guess_language(file.fname, None)
file.ftype = "buffer" if not language else language
file.buffer.set_language(language)
file.buffer.set_style_scheme(view.syntax_theme)
return language
def update_info_bar_if_focused(command_system, view: GtkSource):
has_focus = command_system.exec("has_focus")
if has_focus:
command_system.exec("update_info_bar")
def get_file_and_buffer(view: GtkSource):
file = view.command.get_file(view)
buffer = file.buffer
return file, buffer

View File

@@ -0,0 +1,97 @@
# Python imports
# Lib imports
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from ..source_view import SourceView
from . import commands
class CommandSystem:
def __init__(self):
super(CommandSystem, self).__init__()
self.data: list = ()
def set_data(self, *args, **kwargs):
self.data = (args, kwargs)
def exec(self, command: str) -> any:
if not hasattr(commands, command): return
method = getattr(commands, command)
args, kwargs = self.data
if kwargs:
return method.execute(*args, kwargs)
else:
return method.execute(*args)
def exec_with_args(self, command: str, args: list) -> any:
if not hasattr(commands, command): return
method = getattr(commands, command)
return method.execute(*args)
def emit(self, event: Code_Event_Types.CodeEvent):
""" Monkey patch 'emit' from command controller... """
...
def emit_to(self, controller: str, event: Code_Event_Types.CodeEvent):
""" Monkey patch 'emit_to' from command controller... """
...
def get_file(self, view: SourceView):
event = Event_Factory.create_event(
"get_file",
view = view,
buffer = view.get_buffer()
)
self.emit_to("files", event)
return event.response
def get_swap_file(self, view: SourceView):
event = Event_Factory.create_event(
"get_swap_file",
view = view,
buffer = view.get_buffer()
)
self.emit_to("files", event)
return event.response
def new_file(self, view: SourceView):
event = Event_Factory.create_event("add_new_file", view = view)
self.emit_to("files", event)
return event.response
def remove_file(self, view: SourceView):
event = Event_Factory.create_event(
"removed_file",
view = view,
buffer = view.get_buffer()
)
self.emit_to("files", event)
return event.response
def request_completion(self, view: SourceView):
event = Event_Factory.create_event(
"request_completion",
view = view,
buffer = view.get_buffer()
)
self.emit_to("completion", event)

View File

@@ -0,0 +1,16 @@
"""
Code Commands Package
"""
import pkgutil
import importlib
__all__ = []
for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
module = importlib.import_module(f"{__name__}.{module_name}")
globals()[module_name] = module # Add module to package namespace
__all__.append(module_name)
del pkgutil
del importlib

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Buffer Redo")
buffer = view.get_buffer()
undo_manager = buffer.get_undo_manager()
if undo_manager.can_redo():
buffer.redo()

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Buffer Undo")
buffer = view.get_buffer()
undo_manager = buffer.get_undo_manager()
if undo_manager.can_undo():
buffer.undo()

View File

@@ -0,0 +1,20 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..command_helpers import update_info_bar_if_focused
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Close File")
view.command.remove_file(view)
update_info_bar_if_focused(view.command, view)

View File

@@ -0,0 +1,36 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Cut to Temp Buffer")
view.clear_temp_cut_buffer_delayed()
buffer = view.get_buffer()
itr = buffer.get_iter_at_mark( buffer.get_insert() )
start_itr = itr.copy()
end_itr = itr.copy()
start_line = itr.get_line() + 1
start_char = itr.get_line_offset()
start_itr.backward_visible_line()
start_itr.forward_line()
end_itr.forward_line()
line_str = buffer.get_slice(start_itr, end_itr, True)
view._cut_buffer += f"{line_str}"
buffer.delete(start_itr, end_itr)
view.set_temp_cut_buffer_delayed()

View File

@@ -0,0 +1,34 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import Gio
# Application imports
from ..command_helpers import update_info_bar_if_focused
def execute(
view: GtkSource.View,
uri: str
):
logger.debug("Command: DnD Load File To Buffer")
file = view.command.get_file(view)
buffer = file.buffer
if not file.ftype == "buffer":
file = view.command.new_file(view)
gfile = Gio.File.new_for_uri(uri)
view.command.exec_with_args(
"load_file",
(view, gfile, file)
)
update_info_bar_if_focused(view.command, view)

View File

@@ -0,0 +1,26 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import Gio
# Application imports
def execute(
view: GtkSource.View,
uris: list = []
):
logger.debug("Command: DnD Load Files")
for uri in uris:
try:
gfile = Gio.File.new_for_uri(uri)
except Exception as e:
gfile = Gio.File.new_for_path(uri)
view.command.exec_with_args("load_file", (view, gfile))

View File

@@ -0,0 +1,53 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Duplicate Line")
buffer = view.get_buffer()
if not buffer.get_has_selection():
had_selection = False
itr = buffer.get_iter_at_mark( buffer.get_insert() )
start_itr = itr.copy()
end_itr = itr.copy()
start_line = itr.get_line() + 1
start_char = itr.get_line_offset()
else:
had_selection = True
start_itr, end_itr = buffer.get_selection_bounds()
sline = start_itr.get_line()
eline = end_itr.get_line()
start_line = eline + 1
start_char = start_itr.get_line_offset()
end_char = end_itr.get_line_offset()
range_line_size = eline - sline
start_itr.backward_visible_line()
start_itr.forward_line()
end_itr.forward_line()
end_itr.backward_char()
line_str = buffer.get_slice(start_itr, end_itr, True)
end_itr.forward_char()
buffer.insert(end_itr, f"{line_str}\n", -1)
if not had_selection:
new_itr = buffer.get_iter_at_line_offset(start_line, start_char)
buffer.place_cursor(new_itr)
else:
new_itr = buffer.get_iter_at_line_offset(start_line, start_char)
new_end_itr = buffer.get_iter_at_line_offset((start_line + range_line_size), end_char)
buffer.select_range(new_itr, new_end_itr)

View File

@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Focus Left Sibling")
if not view.sibling_left: return
view.sibling_left.grab_focus()

View File

@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Focus Right Sibling")
if not view.sibling_right: return
view.sibling_right.grab_focus()

View File

@@ -0,0 +1,21 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Get Current File")
file = view.command.get_file(view)
return file

View File

@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Get File Type")
file = view.command.get_file(view)
return file.ftype

View File

@@ -0,0 +1,21 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Get Text")
buffer = view.get_buffer()
start_itr, end_itr = buffer.get_bounds()
return buffer.get_text(start_itr, end_itr, True)

View File

@@ -0,0 +1,31 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Go-To")
file = view.command.get_file(view)
gfile = file.get_location()
uri = gfile.get_uri()
buffer = view.get_buffer()
iter = buffer.get_iter_at_mark( buffer.get_insert() )
line = iter.get_line()
offset = iter.get_line_offset()
event_system.emit(
"textDocument/definition",
(view, file.ftype, uri, line, offset,)
)

View File

@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Has Focus")
ctx = view.get_parent().get_style_context()
return ctx.has_class("source-view-focused")

View File

@@ -0,0 +1,18 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Line Down")
view.emit("move-lines", True)

View File

@@ -0,0 +1,18 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Line Up")
view.emit("move-lines", False)

View File

@@ -0,0 +1,28 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import Gio
# Application imports
from ...source_file import SourceFile
from ..command_helpers import set_language_and_style
def execute(
view: GtkSource.View,
gfile: Gio.File,
file: SourceFile = None,
):
logger.debug("Command: Load File")
if not file:
file = view.command.new_file(view)
file.load_path(gfile)
set_language_and_style(view, file)

View File

@@ -0,0 +1,40 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import Gio
# Application imports
def execute(
view: GtkSource.View,
):
logger.debug("Command: Load Start File(s)")
starting_files = settings_manager.get_starting_files()
if len(starting_files) == 0: return
file = starting_files.pop()
file = file.replace("FILE|", "")
gfile = Gio.File.new_for_path(file)
file = view.command.get_file(view)
view.command.exec_with_args(
"load_file",
(view, gfile, file)
)
if len(starting_files) == 0: return
for file in starting_files:
file = file.replace("FILE|", "")
gfile = Gio.File.new_for_path(file)
view.command.exec_with_args("load_file", (view, gfile))

View File

@@ -0,0 +1,29 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Move To Left Sibling")
if not view.sibling_left: return
buffer = view.get_buffer()
popped_file, next_file = view.command.get_swap_file(view)
view.sibling_left.set_buffer(buffer)
view.sibling_left.grab_focus()
if next_file:
view.set_buffer(next_file.buffer)
else:
view.command.exec("new_file")

View File

@@ -0,0 +1,29 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Move To Right Sibling")
if not view.sibling_right: return
buffer = view.get_buffer()
popped_file, next_file = view.command.get_swap_file(view)
view.sibling_right.set_buffer(buffer)
view.sibling_right.grab_focus()
if next_file:
view.set_buffer(next_file.buffer)
else:
view.command.exec("new_file")

View File

@@ -0,0 +1,27 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..command_helpers import set_language_and_style, update_info_bar_if_focused
def execute(
view: GtkSource.View = None
):
logger.debug("Command: New File")
file = view.command.new_file(view)
set_language_and_style(view, file)
view.set_buffer(file.buffer)
update_info_bar_if_focused(view.command, view)
return file

View File

@@ -0,0 +1,30 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..command_helpers import update_info_bar_if_focused
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Open File(s)")
gfiles = event_system.emit_and_await("open-files")
if not gfiles: return
file = view.command.get_file(view)
if file.ftype == "buffer":
gfile = gfiles.pop()
view.command.exec_with_args("load_file", (view, gfile, file))
view.set_buffer(file.buffer)
update_info_bar_if_focused(view.command, view)
for i, gfile in enumerate(gfiles):
view.command.exec_with_args("load_file", (view, gfile))

View File

@@ -0,0 +1,28 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GLib
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Paste Temp Buffer")
view.clear_temp_cut_buffer_delayed()
buffer = view.get_buffer()
itr = buffer.get_iter_at_mark( buffer.get_insert() )
insert_itr = itr.copy()
buffer.insert(insert_itr, view._cut_buffer, -1)
view.set_temp_cut_buffer_delayed()

View File

@@ -0,0 +1,28 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..command_helpers import set_language_and_style
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Save File")
file = view.command.get_file(view)
buffer = file.buffer
if file.ftype == "buffer":
file.save_as()
set_language_and_style(view, file)
return
file.save()

View File

@@ -0,0 +1,26 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..command_helpers import set_language_and_style, update_info_bar_if_focused
def execute(
view: GtkSource.View = None
):
logger.info("Command: Save File As")
file = view.command.get_file(view)
buffer = file.buffer
file.save_as()
set_language_and_style(view, file)
update_info_bar_if_focused(view.command, view)

View File

@@ -0,0 +1,30 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ...source_file import SourceFile
from ..command_helpers import update_info_bar_if_focused
def execute(
view: GtkSource.View,
file: SourceFile
):
logger.debug("Command: Set Buffer")
if not file:
view.command.new_file(view)
return
view.set_buffer(file.buffer)
update_info_bar_if_focused(view.command, view)

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View,
language: str
):
logger.debug("Command: Set Buffer Language")
buffer = view.get_buffer()
buffer.set_language(
view.language_manager.get_language(language)
)

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View,
style: str
):
logger.debug("Command: Set Buffer Style")
buffer = view.get_buffer()
buffer.set_style_scheme(
view.style_scheme_manager.get_scheme(style)
)

View File

@@ -0,0 +1,26 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Set Focus Border")
ctx = view.get_parent().get_style_context()
ctx.add_class("source-view-focused")
if view.sibling_right:
ctx = view.sibling_right.get_parent().get_style_context()
elif view.sibling_left:
ctx = view.sibling_left.get_parent().get_style_context()
ctx.remove_class("source-view-focused")

View File

@@ -0,0 +1,18 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View,
):
logger.debug("Command: Set MiniView")
event_system.emit("set-mini-view", (view,))

View File

@@ -0,0 +1,28 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Show Completion")
completer = view.get_completion()
providers = completer.get_providers()
if not providers:
view.command.request_completion(view)
return
completer.start(
providers,
completer.create_context()
)

View File

@@ -0,0 +1,31 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View,
):
logger.debug("Command: Update Info Bar")
file = view.command.get_file(view)
buffer = file.buffer
if not file: return
iter = buffer.get_iter_at_mark( buffer.get_insert() )
line = iter.get_line() + 1
column = iter.get_line_offset()
ftype = file.ftype.get_id() if hasattr(file.ftype, "get_id") else file.ftype
event_system.emit(
"set-info-labels",
(file.fpath, f"{line}:{column}", ftype, file.encoding)
)

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Zoom In")
ctx = view.get_style_context()
if view.zoom_level < 99:
ctx.remove_class(f"px{view.zoom_level}")
view.zoom_level += 1
ctx.add_class(f"px{view.zoom_level}")

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Zoom Out")
ctx = view.get_style_context()
if view.zoom_level > 1:
ctx.remove_class(f"px{view.zoom_level}")
view.zoom_level -= 1
ctx.add_class(f"px{view.zoom_level}")

View File

@@ -0,0 +1,3 @@
"""
Code Completion Providers Package
"""

View File

@@ -0,0 +1,87 @@
# Python imports
import re
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4')
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import GtkSource
# Application imports
class ProviderResponseCacheException(Exception):
...
class ProviderResponseCacheBase:
def __init__(self):
super(ProviderResponseCacheBase, self).__init__()
self._icon_theme = Gtk.IconTheme.get_default()
def process_file_load(self, buffer: GtkSource.Buffer):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_file_load' not implemented...")
def process_file_close(self, buffer: GtkSource.Buffer):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_file_close' not implemented...")
def process_file_save(self, buffer: GtkSource.Buffer):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_file_save' not implemented...")
def process_file_change(self, buffer: GtkSource.Buffer):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_change' not implemented...")
def filter(self, word: str):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter' not implemented...")
def filter_with_context(self, context: GtkSource.CompletionContext):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter_with_context' not implemented...")
def create_completion_item(
self,
label: str = "",
text: str = "",
info: str = "",
completion: any = None
):
if not label or not text: return
comp_item = GtkSource.CompletionItem.new()
comp_item.set_label(label)
comp_item.set_text(text)
if info:
comp_item.set_info(info)
# comp_item.set_markup(f"<h3>{info}</h3>")
if completion:
comp_item.set_icon(
self.get_icon_for_type(completion.type)
)
return comp_item
def get_word(self, context):
start_iter = context.get_iter()
end_iter = None
if not isinstance(start_iter, Gtk.TextIter):
_, start_iter = context.get_iter()
end_iter = start_iter.copy()
if not start_iter.starts_word():
start_iter.backward_word_start()
end_iter.forward_word_end()
buffer = start_iter.get_buffer()
return buffer.get_text(start_iter, end_iter, False)

View File

@@ -0,0 +1,3 @@
"""
Code Controllers Package
"""

View File

@@ -0,0 +1,30 @@
# Python imports
# Lib imports
# Application imports
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Code_Event_Types
from ..command_system import CommandSystem
class CommandsController(ControllerBase, list):
def __init__(self):
super(CommandsController, self).__init__()
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.GetCommandSystemEvent):
event.response = self.get_command_system()
def get_command_system(self):
command_system = CommandSystem()
command_system.emit = self.emit
command_system.emit_to = self.emit_to
self.append(command_system)
return command_system

View File

@@ -0,0 +1,94 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GLib
from gi.repository import GtkSource
# Application imports
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Event_Factory, Code_Event_Types
class CompletionController(ControllerBase):
def __init__(self):
super(CompletionController, self).__init__()
self.words_provider = GtkSource.CompletionWords.new("words", None)
self.words_provider.props.activation = GtkSource.CompletionActivation.INTERACTIVE
self._completers: list[GtkSource.Completion] = []
self._providers: dict[str, GtkSource.CompletionProvider] = {}
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.RegisterProviderEvent):
self.register_provider(
event.provider_name,
event.provider,
event.language_ids
)
elif isinstance(event, Code_Event_Types.AddedNewFileEvent):
self.provider_process_file_load(event)
elif isinstance(event, Code_Event_Types.RemovedFileEvent):
self.provider_process_file_close(event)
elif isinstance(event, Code_Event_Types.SavedFileEvent):
self.provider_process_file_save(event)
elif isinstance(event, Code_Event_Types.TextChangedEvent):
self.provider_process_file_change(event)
# elif isinstance(event, Code_Event_Types.RequestCompletionEvent):
# self.request_unbound_completion( event.view.get_completion() )
def register_completer(self, completer: GtkSource.Completion):
self._completers.append(completer)
completer.add_provider(self.words_provider)
for provider in self._providers.values():
completer.add_provider(provider)
def register_provider(
self,
provider_name: str,
provider: GtkSource.CompletionProvider,
language_ids: list = []
):
self._providers[provider_name] = provider
for completer in self._completers:
completer.add_provider(provider)
def unregister_provider(self, provider_name: str):
provider = self._providers[provider_name]
del self._providers[provider_name]
for completer in self._completers:
completer.remove_provider(provider)
def provider_process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
self.words_provider.register(event.file.buffer)
for provider in self._providers.values():
provider.response_cache.process_file_load(event)
def provider_process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
self.words_provider.unregister(event.file.buffer)
for provider in self._providers.values():
provider.response_cache.process_file_close(event)
def provider_process_file_save(self, event: Code_Event_Types.SavedFileEvent):
for provider in self._providers.values():
provider.response_cache.process_file_save(event)
def provider_process_file_change(self, event: Code_Event_Types.TextChangedEvent):
for provider in self._providers.values():
provider.response_cache.process_file_change(event)
def request_unbound_completion(self, completer: GtkSource.Completion):
completer.start(
[ *self._providers.values() ],
completer.create_context()
)

View File

@@ -0,0 +1,149 @@
# Python imports
# Lib imports
# Application imports
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Event_Factory, Code_Event_Types
from ..source_file import SourceFile
from ..source_buffer import SourceBuffer
class FilesController(ControllerBase, list):
def __init__(self):
super(FilesController, self).__init__()
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.AddNewFileEvent):
self.new_file(event)
elif isinstance(event, Code_Event_Types.SwapFileEvent):
self.swap_file(event)
elif isinstance(event, Code_Event_Types.PopFileEvent):
self.pop_file(event)
elif isinstance(event, Code_Event_Types.RemoveFileEvent):
self.remove_file(event)
elif isinstance(event, Code_Event_Types.GetFileEvent):
self.get_file(event)
elif isinstance(event, Code_Event_Types.GetSwapFileEvent):
self.get_swap_file(event)
def get_file(self, event: Code_Event_Types.GetFileEvent):
if not event.buffer: return
for file in self:
if not event.buffer == file.buffer: continue
event.response = file
return file
def get_swap_file(self, event: Code_Event_Types.GetSwapFileEvent):
if not event.buffer: return
for i, file in enumerate(self):
if not event.buffer == file.buffer: continue
j = self.next_index(i)
next_file = self[j]
swapped_file = self[j] if not j == -1 else None
event.response = [swapped_file, next_file]
return swapped_file, next_file
def new_file(self, event: Code_Event_Types.AddNewFileEvent):
file = SourceFile()
file.emit = self.emit
file.emit_to = self.emit_to
event.response = file
eve = Event_Factory.create_event(
"added_new_file",
view = event.view,
file = file
)
self.message(eve)
self.append(file)
return file
def swap_file(self, event: Code_Event_Types.GetSwapFileEvent):
if not event.buffer: return
for i, file in enumerate(self):
if not event.buffer == file.buffer: continue
j = self.next_index(i)
next_file = self[j]
swapped_file = self[j] if not j == -1 else None
event.response = [swapped_file, next_file]
return swapped_file, next_file
def pop_file(self, event: Code_Event_Types.PopFileEvent):
if not event.buffer: return
for i, file in enumerate(self):
if not event.buffer == file.buffer: continue
j = self.next_index(i)
next_file = self[j] if not j == -1 else None
popped_file = self.pop(i)
event.response = [popped_file, next_file]
eve = Event_Factory.create_event(
"popped_file",
view = view,
file = popped_file,
next_file = next_file
)
self.message(eve)
return popped_file, next_file
def remove_file(self, event: Code_Event_Types.RemoveFileEvent):
if not event.buffer: return
for i, file in enumerate(self):
if not event.buffer == file.buffer: continue
j = self.next_index(i)
next_file = self[j] if not j == -1 else None
event.response = next_file
eve = Event_Factory.create_event(
"removed_file",
view = event.view,
ignore_focus = True,
file = file,
next_file = next_file
)
self.message(eve)
self.remove(file)
file.close()
return next_file
def next_index(self, i):
size = len(self)
if (i == 0) & (size >= 2):
j = i + 1
elif (i == (size - 1)) & (size >= 2):
j = i - 1
elif (size - 1) == 0:
j = -1
else:
j = i + 1
return j

View File

@@ -0,0 +1,84 @@
# Python imports
# Lib imports
# Application imports
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Event_Factory, Code_Event_Types
from ..tabs_widget import TabsWidget
from ..tab_widget import TabWidget
from ..source_view import SourceView
class TabsController(ControllerBase):
def __init__(self):
super(TabsController, self).__init__()
self.active_view: SourceView = None
self.tabs_widget: TabsWidget = TabsWidget()
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.FocusedViewEvent):
self.active_view = event.view
elif isinstance(event, Code_Event_Types.FilePathSetEvent):
self.update_tab_label(event)
elif isinstance(event, Code_Event_Types.AddedNewFileEvent):
self.add_tab(event)
elif isinstance(event, Code_Event_Types.PoppedFileEvent):
...
elif isinstance(event, Code_Event_Types.RemovedFileEvent):
self.remove_tab(event)
def get_tabs_widget(self):
return self.tabs_widget
def update_tab_label(self, event: Code_Event_Types.FilePathSetEvent):
for tab in self.tabs_widget.get_children():
if not event.file == tab.file: continue
tab.label.set_label(event.file.fname)
break
def add_tab(self, event: Code_Event_Types.AddedNewFileEvent):
def set_active_tab(tab, eve, file):
event = Event_Factory.create_event(
"set_active_file",
buffer = tab.get_parent().file.buffer
)
self.active_view.set_buffer(
tab.get_parent().file.buffer
)
self.message(event)
def close_tab(tab, eve, file):
event = Event_Factory.create_event(
"remove_file",
buffer = tab.get_parent().file.buffer
)
self.message(event)
tab = TabWidget()
tab.file = event.file
tab.label.set_label(event.file.fname)
tab.set_select_signal(set_active_tab)
tab.set_close_signal(close_tab)
self.tabs_widget.add(tab)
tab.show()
def remove_tab(self, event: Code_Event_Types.RemovedFileEvent):
for tab in self.tabs_widget.get_children():
if not event.file == tab.file: continue
tab.clear_signals_and_data()
tab.run_dispose()
tab.destroy()
del tab
break

View File

@@ -0,0 +1,10 @@
"""
Code Controllers Package
"""
from .state_manager import SourceViewStateManager
from .signal_mapper import SourceViewSignalMapper
from .source_views_controller import SourceViewsController
# State imports
from .states import *

View File

@@ -0,0 +1,63 @@
# Python imports
# Lib imports
# Application imports
from ...source_view import SourceView
class SourceViewSignalMapper:
def __init__(self):
self.active_view: SourceView = None
def bind_emit(self, emit: callable):
self.emit = emit
def set_state_manager(self, state_manager):
self.state_manager = state_manager
def connect_signals(self, source_view: SourceView):
signal_mappings = self._get_signal_mappings()
for signal, handler in signal_mappings.items():
source_view.connect(signal, handler)
def disconnect_signals(self, source_view: SourceView):
signal_mappings = self._get_signal_mappings()
for signal, handler in signal_mappings.items():
source_view.disconnect_by_func(handler)
def insert_text(self, file, string: str):
return self.state_manager.handle_insert_text(self.active_view, file, string)
def _get_signal_mappings(self):
return {
"focus-in-event": self._focus_in_event,
"move-cursor": self._move_cursor,
"key-press-event": self._key_press_event,
"key-release-event": self._key_release_event,
"button-press-event": self._button_press_event,
"button-release-event": self._button_release_event
}
def _focus_in_event(self, source_view: SourceView, eve):
self.active_view = source_view
return self.state_manager.handle_focus_in_event(source_view, eve, self.emit)
def _move_cursor(self, source_view: SourceView, step, count, extend_selection):
return self.state_manager.handle_move_cursor(
source_view, step, count, extend_selection, self.emit
)
def _button_press_event(self, source_view: SourceView, eve):
return self.state_manager.handle_button_press_event(source_view, eve)
def _button_release_event(self, source_view: SourceView, eve):
return self.state_manager.handle_button_release_event(source_view, eve)
def _key_press_event(self, source_view: SourceView, eve):
return self.state_manager.handle_key_press_event(source_view, eve)
def _key_release_event(self, source_view: SourceView, eve):
return self.state_manager.handle_key_release_event(source_view, eve)

View File

@@ -0,0 +1,74 @@
# Python imports
# Lib imports
# Application imports
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Event_Factory, Code_Event_Types
from ...source_view import SourceView
from .state_manager import SourceViewStateManager
from .signal_mapper import SourceViewSignalMapper
class SourceViewsController(ControllerBase, list):
def __init__(self):
super(SourceViewsController, self).__init__()
self.state_manager: SourceViewStateManager = SourceViewStateManager()
self.signal_mapper: SourceViewSignalMapper = SourceViewSignalMapper()
self.signal_mapper.bind_emit(self.emit)
self.signal_mapper.set_state_manager(self.state_manager)
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.RemovedFileEvent):
self._remove_file(event)
if not self.signal_mapper.active_view: return
if isinstance(event, Code_Event_Types.TextChangedEvent):
if not self.signal_mapper.active_view: return
self.signal_mapper.active_view.command.exec("update_info_bar")
elif isinstance(event, Code_Event_Types.TextChangedEvent):
self.signal_mapper.active_view.command.exec("update_info_bar")
elif isinstance(event, Code_Event_Types.TextInsertedEvent):
self.signal_mapper.insert_text(event.file, event.text)
def _get_command_system(self):
event = Event_Factory.create_event("get_command_system")
self.message_to("commands", event)
command = event.response
del event
return command
def _remove_file(self, event: Code_Event_Types.RemovedFileEvent):
for source_view in self:
if not event.file.buffer == source_view.get_buffer(): continue
if not event.next_file:
source_view.command.exec("new_file")
continue
source_view.set_buffer(event.next_file.buffer)
def create_source_view(self):
source_view: SourceView = SourceView()
source_view.command = self._get_command_system()
source_view.command.set_data(source_view)
self.signal_mapper.connect_signals(source_view)
self.append(source_view)
return source_view
def first_map_load(self):
for source_view in self:
source_view.command.exec("new_file")
source_view = self[0]
source_view.grab_focus()
source_view.command.exec("load_start_files")

View File

@@ -0,0 +1,65 @@
# Python imports
# Lib imports
# Application imports
from libs.dto.states import SourceViewStates
from ...key_mapper import KeyMapper
from .states import *
class SourceViewStateManager:
def __init__(self):
self.key_mapper: KeyMapper = KeyMapper()
self.states: dict = {
SourceViewStates.INSERT: SourceViewsInsertState(),
SourceViewStates.MULTIINSERT: SourceViewsMultiInsertState(),
SourceViewStates.COMMAND: SourceViewsCommandState(),
SourceViewStates.READONLY: SourceViewsReadOnlyState()
}
def handle_focus_in_event(self, source_view, eve, emit):
return self.states[source_view.state].focus_in_event(source_view, eve, emit)
def handle_insert_text(self, source_view, file, text):
return self.states[source_view.state].insert_text(file, text)
def handle_move_cursor(self, source_view, step, count, extend_selection, emit):
return self.states[source_view.state].move_cursor(
source_view, step, count, extend_selection, emit
)
def handle_button_press_event(self, source_view, eve):
return self.states[source_view.state].button_press_event(source_view, eve)
def handle_button_release_event(self, source_view, eve):
# Handle state transitions (multi-insert toggling)
self._handle_multi_insert_toggle(source_view, eve)
return self.states[source_view.state].button_release_event(source_view, eve)
def handle_key_press_event(self, source_view, eve):
return self.states[source_view.state].key_press_event(
source_view, eve, self.key_mapper
)
def handle_key_release_event(self, source_view, eve):
return self.states[source_view.state].key_release_event(
source_view, eve, self.key_mapper
)
def _handle_multi_insert_toggle(self, source_view, eve):
is_control = self.key_mapper.is_control(eve)
if is_control and not source_view.state == SourceViewStates.MULTIINSERT:
logger.debug("Entered Multi-Insert Mode...")
source_view.state = SourceViewStates.MULTIINSERT
if not is_control and source_view.state == SourceViewStates.MULTIINSERT:
logger.debug("Entered Regular Insert Mode...")
self.states[source_view.state].clear_markers(source_view)
source_view.state = SourceViewStates.INSERT

View File

@@ -0,0 +1,8 @@
"""
Code Controllers Views States Package
"""
from .source_view_insert_state import SourceViewsInsertState
from .source_view_multi_insert_state import SourceViewsMultiInsertState
from .source_view_command_state import SourceViewsCommandState
from .source_view_read_only_state import SourceViewsReadOnlyState

View File

@@ -0,0 +1,36 @@
# Python imports
# Lib imports
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from libs.dto.states import SourceViewStates
class SourceViewsCommandState:
def __init__(self):
super(SourceViewsCommandState, self).__init__()
def focus_in_event(self, source_view, eve, emit):
return True
def move_cursor(self, source_view, step, count, extend_selection, emit):
return True
def insert_text(self, file, text):
return True
def button_press_event(self, source_view, eve):
return True
def button_release_event(self, source_view, eve):
return True
def key_press_event(self, source_view, eve, key_mapper):
return True
def key_release_event(self, source_view, eve, key_mapper):
return True

View File

@@ -0,0 +1,71 @@
# Python imports
# Lib imports
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from libs.dto.states import SourceViewStates
class SourceViewsInsertState:
def __init__(self):
super(SourceViewsInsertState, self).__init__()
def focus_in_event(self, source_view, eve, emit):
source_view.command.exec("set_miniview")
source_view.command.exec("set_focus_border")
source_view.command.exec("update_info_bar")
event = Event_Factory.create_event("focused_view", view = source_view)
emit(event)
def insert_text(self, file, text):
return True
def move_cursor(self, source_view, step, count, extend_selection, emit):
buffer = source_view.get_buffer()
itr = buffer.get_iter_at_mark( buffer.get_insert() )
line = itr.get_line()
char = itr.get_line_offset()
event = Event_Factory.create_event(
"cursor_moved",
view = source_view,
buffer = buffer,
line = line,
char = char
)
emit(event)
source_view.command.exec("update_info_bar")
def button_press_event(self, source_view, eve):
source_view.command.exec("update_info_bar")
def button_release_event(self, source_view, eve):
source_view.command.exec("update_info_bar")
def key_press_event(self, source_view, eve, key_mapper):
command = key_mapper._key_press_event(eve)
is_future = key_mapper._key_release_event(eve)
if is_future: return True
if not command: return False
source_view.command.exec(command)
return True
def key_release_event(self, source_view, eve, key_mapper):
command = key_mapper._key_release_event(eve)
is_past = key_mapper._key_press_event(eve)
if is_past: return True
if not command: return False
source_view.command.exec(command)
return True

View File

@@ -0,0 +1,131 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from libs.dto.states import SourceViewStates, MoveDirection, CursorAction
from ....mixins.source_mark_events_mixin import MarkEventsMixin
class SourceViewsMultiInsertState(MarkEventsMixin):
def __init__(self):
super(SourceViewsMultiInsertState, self).__init__()
self.cursor_action: CursorAction = 0
self.move_direction: MoveDirection = 0
self.insert_markers: list = []
def focus_in_event(self, source_view, eve, emit):
source_view.command.exec("set_miniview")
source_view.command.exec("set_focus_border")
source_view.command.exec("update_info_bar")
event = Event_Factory.create_event("focused_view", view = source_view)
emit(event)
def insert_text(self, file, text):
if not self.insert_markers: return False
buffer = file.buffer
# freeze buffer and insert to each mark (if any)
buffer.block_insert_after_signal()
buffer.begin_user_action()
with buffer.freeze_notify():
for mark in self.insert_markers:
itr = buffer.get_iter_at_mark(mark)
buffer.insert(itr, text, -1)
buffer.end_user_action()
buffer.unblock_insert_after_signal()
return True
def move_cursor(self, source_view, step, count, extend_selection, emit):
buffer = source_view.get_buffer()
self._process_move_direction(buffer)
self._signal_cursor_moved(source_view, emit)
source_view.command.exec("update_info_bar")
def button_press_event(self, source_view, eve):
source_view.command.exec("update_info_bar")
return True
def button_release_event(self, source_view, eve):
buffer = source_view.get_buffer()
insert_iter = buffer.get_iter_at_mark( buffer.get_insert() )
data = source_view.window_to_buffer_coords(
Gtk.TextWindowType.TEXT,
eve.x,
eve.y
)
is_over_text, \
target_iter, \
is_trailing = source_view.get_iter_at_position(data.buffer_x, data.buffer_y)
if not is_over_text:
# NOTE: Trying to put at very end of line if not over text (aka, clicking right of text)
target_iter.forward_visible_line()
target_iter.backward_char()
self._insert_mark(insert_iter, target_iter, buffer)
def key_press_event(self, source_view, eve, key_mapper):
char = key_mapper.get_raw_keyname(eve)
for action in CursorAction:
if not action.name == char.upper(): continue
self.cursor_action = action.value
self._process_cursor_action(source_view.get_buffer())
return False
for direction in MoveDirection:
if not direction.name == char.upper(): continue
self.move_direction = direction.value
return False
is_future = key_mapper._key_release_event(eve)
if is_future: return False
command = key_mapper._key_press_event(eve)
if not command: return False
source_view.command.exec(command)
return True
def key_release_event(self, source_view, eve, key_mapper):
command = key_mapper._key_release_event(eve)
is_past = key_mapper._key_press_event(eve)
if is_past: return False
if not command: return False
source_view.command.exec(command)
return True
def _signal_cursor_moved(self, source_view, emit):
buffer = source_view.get_buffer()
itr = buffer.get_iter_at_mark( buffer.get_insert() )
line = itr.get_line()
char = itr.get_line_offset()
event = Event_Factory.create_event(
"cursor_moved",
view = source_view,
buffer = buffer,
line = line,
char = char
)
emit(event)

View File

@@ -0,0 +1,36 @@
# Python imports
# Lib imports
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from libs.dto.states import SourceViewStates
class SourceViewsReadOnlyState:
def __init__(self):
super(SourceViewsReadOnlyState, self).__init__()
def focus_in_event(self, source_view, eve, emit):
return True
def move_cursor(self, source_view, step, count, extend_selection, emit):
return True
def insert_text(self, file, text):
return True
def button_press_event(self, source_view, eve):
return True
def button_release_event(self, source_view, eve):
return True
def key_press_event(self, source_view, eve, key_mapper):
return True
def key_release_event(self, source_view, eve, key_mapper):
return True

View File

@@ -0,0 +1,100 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Pango
from gi.repository import Gio
# Application imports
class InfoBarWidget(Gtk.Box):
""" docstring for InfoBarWidget. """
def __init__(self):
super(InfoBarWidget, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show_all()
def _setup_styling(self):
self.set_margin_start(25)
self.set_margin_end(25)
def _setup_signals(self):
...
def _subscribe_to_events(self):
event_system.subscribe("set-info-labels", self._set_info_labels)
event_system.subscribe("set-path-label", self._set_path_label)
event_system.subscribe("set-encoding-label", self._set_encoding_label)
event_system.subscribe("set-line-char-label", self._set_line_char_label)
event_system.subscribe("set-file-type-label", self._set_file_type_label)
def _load_widgets(self):
self.path_label = Gtk.Label(label = "...")
self.line_char_label = Gtk.Label(label = "1:0")
self.encoding_label = Gtk.Label(label = "utf-8")
self.file_type_label = Gtk.Label(label = "buffer")
self.add(self.path_label)
self.add(self.line_char_label)
self.add(self.encoding_label)
self.add(self.file_type_label)
self.path_label.set_hexpand(True)
self.path_label.set_ellipsize(Pango.EllipsizeMode.START)
self.path_label.set_single_line_mode(True)
self.path_label.set_max_width_chars(48)
self.line_char_label.set_hexpand(True)
self.encoding_label.set_hexpand(True)
self.file_type_label.set_hexpand(True)
def _set_info_labels(
self,
path: Gio.File or str = None,
line_char: str = None,
file_type: str = None,
encoding_type: str = None
):
self._set_path_label(path)
self._set_line_char_label(line_char)
self._set_file_type_label(file_type)
self._set_encoding_label(encoding_type)
def _set_path_label(self, gfile: Gio.File or str = "..."):
gfile = "" if not gfile else gfile
if isinstance(gfile, str):
self.path_label.set_text( gfile )
self.path_label.set_tooltip_text( gfile )
else:
self.path_label.set_text( gfile.get_path() )
self.path_label.set_tooltip_text( gfile.get_path() )
def _set_line_char_label(self, line_char = "1:1"):
line_char = "1:1" if not line_char else line_char
self.line_char_label.set_text(line_char)
def _set_file_type_label(self, file_type = "buffer"):
file_type = "buffer" if not file_type else file_type
self.file_type_label.set_text(file_type)
def _set_encoding_label(self, encoding_type = "utf-8"):
encoding_type = "utf-8" if not encoding_type else encoding_type
self.encoding_label.set_text(encoding_type)

View File

@@ -0,0 +1,137 @@
# Python imports
import copy
import json
# Lib imports
import gi
gi.require_version('Gdk', '3.0')
from gi.repository import Gdk
# Application imports
class NoKeyState:
held: dict = {}
released: dict = {}
class CtrlKeyState:
held: dict = {}
released: dict = {}
class ShiftKeyState:
held: dict = {}
released: dict = {}
class AltKeyState:
held: dict = {}
released: dict = {}
class CtrlShiftKeyState:
held: dict = {}
released: dict = {}
class CtrlAltKeyState:
held: dict = {}
released: dict = {}
class AltShiftKeyState:
held: dict = {}
released: dict = {}
class CtrlShiftAltKeyState:
held: dict = {}
released: dict = {}
class KeyMapper:
def __init__(self):
super(KeyMapper, self).__init__()
self.state = NoKeyState
self._map = {
NoKeyState: NoKeyState(),
NoKeyState | CtrlKeyState : CtrlKeyState(),
NoKeyState | ShiftKeyState: ShiftKeyState(),
NoKeyState | AltKeyState : AltKeyState(),
NoKeyState | CtrlKeyState | ShiftKeyState : CtrlShiftKeyState(),
NoKeyState | CtrlKeyState | AltKeyState : CtrlAltKeyState(),
NoKeyState | AltKeyState | ShiftKeyState : AltShiftKeyState(),
NoKeyState | CtrlKeyState | ShiftKeyState | AltKeyState: CtrlShiftAltKeyState(),
}
self.load_map()
def load_map(self):
self.states = copy.deepcopy(self._map)
bindings_file = f"{settings_manager.path_manager.get_home_config_path()}/code-key-bindings.json"
with open(bindings_file, 'r') as f:
data = json.load(f)["keybindings"]
for command in data:
press_state = "held" if "held" in data[command] else "released"
keyname = data[command][press_state]
state = NoKeyState
if "<Control>" in keyname:
state = state | CtrlKeyState
if "<Shift>" in keyname:
state = state | ShiftKeyState
if "<Alt>" in keyname:
state = state | AltKeyState
keyname = keyname.replace("<Control>", "") \
.replace("<Shift>", "") \
.replace("<Alt>", "") \
.lower()
getattr(self.states[state], press_state)[keyname] = command
def re_map(self):
self.states = copy.deepcopy(self._map)
def _key_press_event(self, eve):
keyname = Gdk.keyval_name(eve.keyval).lower()
self._set_key_state(eve)
if keyname in self.states[self.state].held:
return self.states[self.state].held[keyname]
def _key_release_event(self, eve):
keyname = Gdk.keyval_name(eve.keyval).lower()
self._set_key_state(eve)
if keyname in self.states[self.state].released:
return self.states[self.state].released[keyname]
def _set_key_state(self, eve):
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
is_control = True if modifiers & Gdk.ModifierType.CONTROL_MASK else False
is_shift = True if modifiers & Gdk.ModifierType.SHIFT_MASK else False
try:
is_alt = True if modifiers & Gdk.ModifierType.ALT_MASK else False
except Exception:
is_alt = True if modifiers & Gdk.ModifierType.MOD1_MASK else False
self.state = NoKeyState
if is_control:
self.state = self.state | CtrlKeyState
if is_shift:
self.state = self.state | ShiftKeyState
if is_alt:
self.state = self.state | AltKeyState
def is_control(self, eve):
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
return True if modifiers & Gdk.ModifierType.CONTROL_MASK else False
def is_shift(self, eve):
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
return True if modifiers & Gdk.ModifierType.SHIFT_MASK else False
def get_raw_keyname(self, eve):
return Gdk.keyval_name(eve.keyval)

View File

@@ -0,0 +1,39 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository.GtkSource import Map
# Application imports
class MiniViewWidget(Map):
def __init__(self):
super(MiniViewWidget, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
def _setup_styling(self):
ctx = self.get_style_context()
ctx.add_class("mini-view")
self.set_hexpand(False)
def _setup_signals(self):
...
def _subscribe_to_events(self):
event_system.subscribe(f"set-mini-view", self.set_smini_view)
def _load_widgets(self):
...
def set_smini_view(self, source_view):
self.set_view(source_view)

View File

@@ -0,0 +1,3 @@
"""
Code Mixins Package
"""

View File

@@ -0,0 +1,107 @@
# Python imports
import random
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from libs.dto.states import SourceViewStates, MoveDirection, CursorAction
class MarkEventsMixin:
def clear_markers(self, source_view):
buffer = source_view.get_buffer()
for mark in self.insert_markers:
mark.set_visible(False)
buffer.delete_mark(mark)
self.insert_markers.clear()
def _insert_mark(self, insert_iter, target_iter, buffer):
mark_found = self._check_for_insert_marks(target_iter, buffer)
if mark_found: return
random_bits = random.getrandbits(128)
hash = "%032x" % random_bits
mark = Gtk.TextMark.new(
name = f"multi_insert_{hash}",
left_gravity = False
)
buffer.add_mark(mark, target_iter)
mark.set_visible(True)
self.insert_markers.append(mark)
def _check_for_insert_marks(self, target_iter, buffer):
marks = target_iter.get_marks()
for mark in marks:
if mark in self.insert_markers[:]:
mark.set_visible(False)
self.insert_markers.remove(mark)
buffer.delete_mark(mark)
return True
insert_itr = buffer.get_iter_at_mark( buffer.get_insert() )
if target_iter.equal(insert_itr): return True
return False
def _process_cursor_action(self, buffer):
if not self.insert_markers: return
if self.cursor_action == CursorAction.NONE.value: return
action = self.cursor_action
for mark in self.insert_markers:
itr = buffer.get_iter_at_mark(mark)
if action == CursorAction.BACKSPACE.value:
buffer.backspace(itr, interactive = True, default_editable = True)
elif action == CursorAction.DELETE.value:
itr.forward_char()
buffer.backspace(itr, interactive = True, default_editable = True)
elif action == CursorAction.ENTER.value:
...
self.cursor_action = CursorAction.NONE.value
def _process_move_direction(self, buffer):
if not self.insert_markers: return
if self.move_direction == MoveDirection.NONE.value: return
direction = self.move_direction
for mark in self.insert_markers:
itr = buffer.get_iter_at_mark(mark)
if direction == MoveDirection.UP.value:
new_line = itr.get_line() - 1
new_itr = buffer.get_iter_at_line_offset(
new_line,
itr.get_line_index()
)
elif direction == MoveDirection.DOWN.value:
new_line = itr.get_line() + 1
new_itr = buffer.get_iter_at_line_offset(
new_line,
itr.get_line_index()
)
elif direction == MoveDirection.LEFT.value:
if not itr.backward_char(): break
new_itr = itr
elif direction == MoveDirection.RIGHT.value:
if not itr.forward_char(): break
new_itr = itr
else:
continue
buffer.move_mark_by_name(mark.get_name(), new_itr)
self.move_direction = MoveDirection.NONE

View File

@@ -0,0 +1,40 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
class SourceViewDnDMixin:
def _set_up_dnd(self):
PLAIN_TEXT_TARGET_TYPE = 70
URI_TARGET_TYPE = 80
text_target = Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags(0), PLAIN_TEXT_TARGET_TYPE)
uri_target = Gtk.TargetEntry.new('text/uri-list', Gtk.TargetFlags(0), URI_TARGET_TYPE)
targets = [ text_target, uri_target ]
self.drag_dest_set_target_list(targets)
def _on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
if info == 70: return
if info == 80:
uris = data.get_uris()
if len(uris) == 0:
uris = data.get_text().split("\n")
self._on_uri_data_received(uris)
def _on_uri_data_received(self, uris: []):
uri = uris.pop(0)
self.command.exec_with_args("dnd_load_file_to_buffer", (self, uri))
if len(uris) == 0: return
self.command.exec_with_args("dnd_load_files", (self, uris))

View File

@@ -0,0 +1,61 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
class SourceBuffer(GtkSource.Buffer):
def __init__(self):
super(SourceBuffer, self).__init__()
self._handler_ids = []
def set_signals(
self,
_changed,
_mark_set,
_insert_text,
_after_insert_text,
_modified_changed,
):
self._handler_ids = [
self.connect("changed", _changed),
self.connect("mark-set", _mark_set),
self.connect("insert-text", _insert_text),
self.connect_after("insert-text", _after_insert_text),
self.connect("modified-changed", _modified_changed)
]
def block_changed_signal(self):
self.handler_block(self._handler_ids[0])
def block_insert_after_signal(self):
self.handler_block(self._handler_ids[3])
def block_modified_changed_signal(self):
self.handler_block(self._handler_ids[4])
def unblock_changed_signal(self):
self.handler_unblock(self._handler_ids[0])
def unblock_insert_after_signal(self):
self.handler_unblock(self._handler_ids[3])
def unblock_modified_changed_signal(self):
self.handler_unblock(self._handler_ids[4])
def clear_signals(self):
for handle_id in self._handler_ids:
self.disconnect(handle_id)
def __del__(self):
for handle_id in self._handler_ids:
self.disconnect(handle_id)

Some files were not shown because too many files have changed in this diff Show More