Compare commits
18 Commits
Author | SHA1 | Date |
---|---|---|
itdominator | 3a2e8eeb08 | |
itdominator | 35456f2bca | |
itdominator | 9d3a5b9f3b | |
itdominator | ce00970171 | |
itdominator | 2f954f4c79 | |
itdominator | a362039e73 | |
itdominator | 02c31719d1 | |
itdominator | d65ea8dec8 | |
itdominator | fec0d26ab7 | |
itdominator | a47bd23e78 | |
itdominator | 44ef6ea2bb | |
itdominator | be7be00f78 | |
itdominator | 8e5ae4824c | |
itdominator | 37e3265be5 | |
itdominator | 4cafb7ff9f | |
itdominator | 9336df2afa | |
itdominator | d936b17429 | |
itdominator | e6739c3087 |
|
@ -8,7 +8,7 @@ Additionally, if not building a .deb then just move the contents of user_config
|
||||||
Copy the share/solarfm folder to your user .config/ directory too.
|
Copy the share/solarfm folder to your user .config/ directory too.
|
||||||
|
|
||||||
`pyrightconfig.json`
|
`pyrightconfig.json`
|
||||||
<p>The pyrightconfig file needs to stay on same level as the .git folders in order to have settings detected when using pyright with lsp functionality.</p>
|
<p>The pyrightconfig file needs to stay on same level as the .git folders in order to have settings detected when using pyright with lsp functionality. "pyrightconfig.json" can prompt IDEs such as Zed on settings to use and where imports are located- look at venvPath and venv. "venvPath" is parent path of "venv" where "venv" is just the name of the folder under the parent path that is the python created venv.
|
||||||
|
|
||||||
<h6>Install Setup</h6>
|
<h6>Install Setup</h6>
|
||||||
```
|
```
|
||||||
|
|
|
@ -14,6 +14,7 @@ class Manifest:
|
||||||
'ui_target': "plugin_control_list",
|
'ui_target': "plugin_control_list",
|
||||||
'pass_fm_events': "true"
|
'pass_fm_events': "true"
|
||||||
}
|
}
|
||||||
|
pre_launch: bool = False
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,6 @@ class Plugin(PluginBase):
|
||||||
uri = state.uris[0]
|
uri = state.uris[0]
|
||||||
path = state.tab.get_current_directory()
|
path = state.tab.get_current_directory()
|
||||||
|
|
||||||
|
|
||||||
properties = self._set_ui_data(uri, path)
|
properties = self._set_ui_data(uri, path)
|
||||||
response = self._properties_dialog.run()
|
response = self._properties_dialog.run()
|
||||||
if response in [Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT]:
|
if response in [Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT]:
|
||||||
|
@ -168,13 +167,13 @@ class Plugin(PluginBase):
|
||||||
|
|
||||||
def _set_ui_data(self, uri, path):
|
def _set_ui_data(self, uri, path):
|
||||||
properties = Properties()
|
properties = Properties()
|
||||||
file_info = Gio.File.new_for_path(uri).query_info(attributes="standard::*,owner::*,time::access,time::changed",
|
file_info = Gio.File.new_for_path(uri).query_info(attributes = "standard::*,owner::*,time::access,time::changed",
|
||||||
flags=Gio.FileQueryInfoFlags.NONE,
|
flags = Gio.FileQueryInfoFlags.NONE,
|
||||||
cancellable=None)
|
cancellable = None)
|
||||||
|
|
||||||
is_symlink = file_info.get_attribute_as_string("standard::is-symlink")
|
is_symlink = file_info.get_attribute_as_string("standard::is-symlink")
|
||||||
properties.file_uri = uri
|
properties.file_uri = uri
|
||||||
properties.file_target = file_info.get_attribute_as_string("standard::symlink-target") if is_symlink else ""
|
properties.file_target = file_info.get_attribute_as_string("standard::symlink-target") if is_symlink in [True, "TRUE"] else ""
|
||||||
properties.file_name = file_info.get_display_name()
|
properties.file_name = file_info.get_display_name()
|
||||||
properties.file_location = path
|
properties.file_location = path
|
||||||
properties.mime_type = file_info.get_content_type()
|
properties.mime_type = file_info.get_content_type()
|
||||||
|
@ -186,7 +185,7 @@ class Plugin(PluginBase):
|
||||||
|
|
||||||
# NOTE: Read = 4, Write = 2, Exec = 1
|
# NOTE: Read = 4, Write = 2, Exec = 1
|
||||||
command = ["stat", "-c", "%a", uri]
|
command = ["stat", "-c", "%a", uri]
|
||||||
with subprocess.Popen(command, stdout=subprocess.PIPE) as proc:
|
with subprocess.Popen(command, stdout = subprocess.PIPE) as proc:
|
||||||
properties.chmod_stat = list(proc.stdout.read().decode("UTF-8").strip())
|
properties.chmod_stat = list(proc.stdout.read().decode("UTF-8").strip())
|
||||||
owner = self._chmod_map[f"{properties.chmod_stat[0]}"]
|
owner = self._chmod_map[f"{properties.chmod_stat[0]}"]
|
||||||
group = self._chmod_map[f"{properties.chmod_stat[1]}"]
|
group = self._chmod_map[f"{properties.chmod_stat[1]}"]
|
||||||
|
|
|
@ -48,7 +48,7 @@ class GrepPreviewWidget(Gtk.Box):
|
||||||
return bytes(f"\n<span foreground='{color}'>{target}</span>", "utf-8").decode("utf-8")
|
return bytes(f"\n<span foreground='{color}'>{target}</span>", "utf-8").decode("utf-8")
|
||||||
|
|
||||||
def make_utf8_line_highlight(self, buffer, itr, i, color, target, query):
|
def make_utf8_line_highlight(self, buffer, itr, i, color, target, query):
|
||||||
parts = re.split(r"(" + query + ")(?i)", target.replace("\n", ""))
|
parts = re.split(r"(?i)(" + query + ")", target.replace("\n", ""))
|
||||||
for part in parts:
|
for part in parts:
|
||||||
itr = buffer.get_end_iter()
|
itr = buffer.get_end_iter()
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"ui_target": "plugin_control_list",
|
"ui_target": "plugin_control_list",
|
||||||
"pass_fm_events": "true",
|
"pass_fm_events": "true",
|
||||||
"bind_keys": ["Example Plugin||send_message:<Control>f"]
|
"bind_keys": ["Example Plugin||send_message:<Control>f"]
|
||||||
}
|
},
|
||||||
|
"pre_launch": "false"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
Pligin Module
|
||||||
|
"""
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
Pligin Package
|
||||||
|
"""
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Python imports
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
from gi.repository import Gtk
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from .icon import Icon
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class IconController(Icon):
|
||||||
|
def __init__(self):
|
||||||
|
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
# NOTE: app_name should be defined using python 'builtins' and so too must be logger used in the various classes
|
||||||
|
app_name_exists = False
|
||||||
|
try:
|
||||||
|
app_name
|
||||||
|
app_name_exists = True
|
||||||
|
except Exception as e:
|
||||||
|
...
|
||||||
|
|
||||||
|
APP_CONTEXT = f"{app_name.lower()}" if app_name_exists else "shellfm"
|
||||||
|
USR_APP_CONTEXT = f"/usr/share/{APP_CONTEXT}"
|
||||||
|
USER_HOME = path.expanduser('~')
|
||||||
|
CONFIG_PATH = f"{USER_HOME}/.config/{APP_CONTEXT}"
|
||||||
|
self.DEFAULT_ICONS = f"{CONFIG_PATH}/icons"
|
||||||
|
self.DEFAULT_ICON = f"{self.DEFAULT_ICONS}/text.png"
|
||||||
|
self.FFMPG_THUMBNLR = f"{CONFIG_PATH}/ffmpegthumbnailer" # Thumbnail generator binary
|
||||||
|
self.BLENDER_THUMBNLR = f"{CONFIG_PATH}/blender-thumbnailer" # Blender thumbnail generator binary
|
||||||
|
|
||||||
|
self.ICON_DIRS = ["/usr/share/icons", f"{USER_HOME}/.icons" "/usr/share/pixmaps"]
|
||||||
|
self.BASE_THUMBS_PTH = f"{USER_HOME}/.thumbnails"
|
||||||
|
self.ABS_THUMBS_PTH = f"{self.BASE_THUMBS_PTH}/normal"
|
||||||
|
self.STEAM_ICONS_PTH = f"{self.BASE_THUMBS_PTH}/steam_icons"
|
||||||
|
|
||||||
|
if not path.isdir(self.BASE_THUMBS_PTH):
|
||||||
|
os.mkdir(self.BASE_THUMBS_PTH)
|
||||||
|
|
||||||
|
if not path.isdir(self.ABS_THUMBS_PTH):
|
||||||
|
os.mkdir(self.ABS_THUMBS_PTH)
|
||||||
|
|
||||||
|
if not path.isdir(self.STEAM_ICONS_PTH):
|
||||||
|
os.mkdir(self.STEAM_ICONS_PTH)
|
||||||
|
|
||||||
|
if not os.path.exists(self.DEFAULT_ICONS):
|
||||||
|
self.DEFAULT_ICONS = f"{USR_APP_CONTEXT}/icons"
|
||||||
|
self.DEFAULT_ICON = f"{self.DEFAULT_ICONS}/text.png"
|
||||||
|
|
||||||
|
CONFIG_FILE = f"{CURRENT_PATH}/../settings.json"
|
||||||
|
with open(CONFIG_FILE) as f:
|
||||||
|
settings = json.load(f)
|
||||||
|
config = settings["config"]
|
||||||
|
|
||||||
|
self.container_icon_wh = config["container_icon_wh"]
|
||||||
|
self.video_icon_wh = config["video_icon_wh"]
|
||||||
|
self.sys_icon_wh = config["sys_icon_wh"]
|
||||||
|
|
||||||
|
# Filters
|
||||||
|
filters = settings["filters"]
|
||||||
|
self.fmeshs = tuple(filters["meshs"])
|
||||||
|
self.fcode = tuple(filters["code"])
|
||||||
|
self.fvideos = tuple(filters["videos"])
|
||||||
|
self.foffice = tuple(filters["office"])
|
||||||
|
self.fimages = tuple(filters["images"])
|
||||||
|
self.ftext = tuple(filters["text"])
|
||||||
|
self.fmusic = tuple(filters["music"])
|
||||||
|
self.fpdf = tuple(filters["pdf"])
|
|
@ -138,6 +138,7 @@ class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin):
|
||||||
def _call_gtk_thread(event, result):
|
def _call_gtk_thread(event, result):
|
||||||
result.append( self.get_system_thumbnail(full_path, size) )
|
result.append( self.get_system_thumbnail(full_path, size) )
|
||||||
event.set()
|
event.set()
|
||||||
|
return False
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
event = threading.Event()
|
event = threading.Event()
|
||||||
|
@ -151,11 +152,11 @@ class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin):
|
||||||
gio_file = Gio.File.new_for_path(full_path)
|
gio_file = Gio.File.new_for_path(full_path)
|
||||||
info = gio_file.query_info('standard::icon' , 0, None)
|
info = gio_file.query_info('standard::icon' , 0, None)
|
||||||
icon = info.get_icon().get_names()[0]
|
icon = info.get_icon().get_names()[0]
|
||||||
data = settings_manager.get_icon_theme().lookup_icon(icon , size , 0)
|
data = settings_manager.get_icon_theme().lookup_icon(icon , size, 0)
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
icon_path = data.get_filename()
|
icon_path = data.get_filename()
|
||||||
return GdkPixbuf.Pixbuf.new_from_file(icon_path)
|
return GdkPixbuf.Pixbuf.new_from_file_at_size(icon_path, width = size, height = size)
|
||||||
|
|
||||||
raise IconException("No system icon found...")
|
raise IconException("No system icon found...")
|
||||||
except IconException:
|
except IconException:
|
|
@ -14,4 +14,4 @@ class MeshsIconMixin:
|
||||||
proc = subprocess.Popen([self.BLENDER_THUMBNLR, full_path, hash_img_path])
|
proc = subprocess.Popen([self.BLENDER_THUMBNLR, full_path, hash_img_path])
|
||||||
proc.wait()
|
proc.wait()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.debug(repr(e))
|
logger.debug(repr(e))
|
|
@ -14,7 +14,7 @@ class VideoIconMixin:
|
||||||
proc = subprocess.Popen([self.FFMPG_THUMBNLR, "-t", scrub_percent, "-s", "300", "-c", "jpg", "-i", full_path, "-o", hash_img_path])
|
proc = subprocess.Popen([self.FFMPG_THUMBNLR, "-t", scrub_percent, "-s", "300", "-c", "jpg", "-i", full_path, "-o", hash_img_path])
|
||||||
proc.wait()
|
proc.wait()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.debug(repr(e))
|
logger.info(repr(e))
|
||||||
self.ffprobe_generate_video_thumbnail(full_path, hash_img_path)
|
self.ffprobe_generate_video_thumbnail(full_path, hash_img_path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,5 +51,4 @@ class VideoIconMixin:
|
||||||
proc.wait()
|
proc.wait()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Video thumbnail generation issue in thread:")
|
print("Video thumbnail generation issue in thread:")
|
||||||
print( repr(e) )
|
logger.info(repr(e))
|
||||||
self.logger.debug(repr(e))
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"manifest": {
|
||||||
|
"name": "Thumbnailer",
|
||||||
|
"author": "ITDominator",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"support": "",
|
||||||
|
"requests": {
|
||||||
|
"pass_fm_events": "true"
|
||||||
|
},
|
||||||
|
"pre_launch": "true"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Python imports
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from plugins.plugin_base import PluginBase
|
||||||
|
from .icons.controller import IconController
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Plugin(PluginBase):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.name = "Thumbnailer" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
|
||||||
|
# where self.name should not be needed for message comms
|
||||||
|
# self.path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.icon_controller = IconController()
|
||||||
|
self._event_system.subscribe("create-thumbnail", self.create_thumbnail)
|
||||||
|
|
||||||
|
def generate_reference_ui_element(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def create_thumbnail(self, dir, file) -> str:
|
||||||
|
return self.icon_controller.create_icon(dir, file)
|
||||||
|
|
||||||
|
def get_video_icons(self, dir) -> list:
|
||||||
|
data = []
|
||||||
|
|
||||||
|
def get_video_icons(self) -> list:
|
||||||
|
data = []
|
||||||
|
fvideos = self.icon_controller.fvideos
|
||||||
|
vids = [ file for file in os.path.list_dir(dir) if file.lower().endswith(fvideos) ]
|
||||||
|
|
||||||
|
for file in vids:
|
||||||
|
img_hash, hash_img_path = self.create_video_thumbnail(full_path = f"{dir}/{file}", returnHashInstead = True)
|
||||||
|
data.append([img_hash, hash_img_path])
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_pixbuf_icon_str_combo(self, dir) -> list:
|
||||||
|
data = []
|
||||||
|
for file in os.path.list_dir(dir):
|
||||||
|
icon = self.icon_controller.create_icon(dir, file).get_pixbuf()
|
||||||
|
data.append([icon, file])
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_gtk_icon_str_combo(self, dir) -> list:
|
||||||
|
data = []
|
||||||
|
for file in os.path.list_dir(dir):
|
||||||
|
icon = self.icon_controller.create_icon(dir, file)
|
||||||
|
data.append([icon, file[0]])
|
||||||
|
|
||||||
|
return data
|
|
@ -0,0 +1,101 @@
|
||||||
|
{
|
||||||
|
"config":{
|
||||||
|
"thumbnailer_path":"ffmpegthumbnailer",
|
||||||
|
"blender_thumbnailer_path":"",
|
||||||
|
"container_icon_wh":[
|
||||||
|
128,
|
||||||
|
128
|
||||||
|
],
|
||||||
|
"video_icon_wh":[
|
||||||
|
128,
|
||||||
|
64
|
||||||
|
],
|
||||||
|
"sys_icon_wh":[
|
||||||
|
56,
|
||||||
|
56
|
||||||
|
],
|
||||||
|
"steam_cdn_url":"https://steamcdn-a.akamaihd.net/steam/apps/",
|
||||||
|
"remux_folder_max_disk_usage":"8589934592"
|
||||||
|
},
|
||||||
|
"filters":{
|
||||||
|
"meshs":[
|
||||||
|
".dae",
|
||||||
|
".fbx",
|
||||||
|
".gltf",
|
||||||
|
".obj",
|
||||||
|
".stl"
|
||||||
|
],
|
||||||
|
"code":[
|
||||||
|
".cpp",
|
||||||
|
".css",
|
||||||
|
".c",
|
||||||
|
".go",
|
||||||
|
".html",
|
||||||
|
".htm",
|
||||||
|
".java",
|
||||||
|
".js",
|
||||||
|
".json",
|
||||||
|
".lua",
|
||||||
|
".md",
|
||||||
|
".py",
|
||||||
|
".rs",
|
||||||
|
".toml",
|
||||||
|
".xml",
|
||||||
|
".pom"
|
||||||
|
],
|
||||||
|
"videos":[
|
||||||
|
".mkv",
|
||||||
|
".mp4",
|
||||||
|
".webm",
|
||||||
|
".avi",
|
||||||
|
".mov",
|
||||||
|
".m4v",
|
||||||
|
".mpg",
|
||||||
|
".mpeg",
|
||||||
|
".wmv",
|
||||||
|
".flv"
|
||||||
|
],
|
||||||
|
"office":[
|
||||||
|
".doc",
|
||||||
|
".docx",
|
||||||
|
".xls",
|
||||||
|
".xlsx",
|
||||||
|
".xlt",
|
||||||
|
".xltx",
|
||||||
|
".xlm",
|
||||||
|
".ppt",
|
||||||
|
".pptx",
|
||||||
|
".pps",
|
||||||
|
".ppsx",
|
||||||
|
".odt",
|
||||||
|
".rtf"
|
||||||
|
],
|
||||||
|
"images":[
|
||||||
|
".png",
|
||||||
|
".jpg",
|
||||||
|
".jpeg",
|
||||||
|
".gif",
|
||||||
|
".ico",
|
||||||
|
".tga",
|
||||||
|
".webp"
|
||||||
|
],
|
||||||
|
"text":[
|
||||||
|
".txt",
|
||||||
|
".text",
|
||||||
|
".sh",
|
||||||
|
".cfg",
|
||||||
|
".conf",
|
||||||
|
".log"
|
||||||
|
],
|
||||||
|
"music":[
|
||||||
|
".psf",
|
||||||
|
".mp3",
|
||||||
|
".ogg",
|
||||||
|
".flac",
|
||||||
|
".m4a"
|
||||||
|
],
|
||||||
|
"pdf":[
|
||||||
|
".pdf"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -184,8 +184,8 @@ class Plugin(PluginBase):
|
||||||
response = requests.post(self.vqd_link, headers=self.vqd_headers, data=self.vqd_data, timeout=2)
|
response = requests.post(self.vqd_link, headers=self.vqd_headers, data=self.vqd_data, timeout=2)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
data = response.content
|
data = response.content
|
||||||
vqd_start_index = data.index(b"vqd='") + 5
|
vqd_start_index = data.index(b"vqd=\"") + 5
|
||||||
vqd_end_index = data.index(b"'", vqd_start_index)
|
vqd_end_index = data.index(b"\"", vqd_start_index)
|
||||||
self._vqd_attrib = data[vqd_start_index:vqd_end_index].decode("utf-8")
|
self._vqd_attrib = data[vqd_start_index:vqd_end_index].decode("utf-8")
|
||||||
|
|
||||||
print(f"Translation VQD: {self._vqd_attrib}")
|
print(f"Translation VQD: {self._vqd_attrib}")
|
||||||
|
|
|
@ -111,6 +111,8 @@ class Plugin(PluginBase):
|
||||||
for uri in state.uris:
|
for uri in state.uris:
|
||||||
self.trashman.trash(uri, verbocity)
|
self.trashman.trash(uri, verbocity)
|
||||||
|
|
||||||
|
self.trashman.regenerate()
|
||||||
|
|
||||||
def restore_trash_files(self, widget = None, eve = None, verbocity = False):
|
def restore_trash_files(self, widget = None, eve = None, verbocity = False):
|
||||||
self._event_system.emit("get_current_state")
|
self._event_system.emit("get_current_state")
|
||||||
state = self._fm_state
|
state = self._fm_state
|
||||||
|
|
|
@ -43,4 +43,4 @@ class Trash(object):
|
||||||
|
|
||||||
def restore(self, filename, verbose):
|
def restore(self, filename, verbose):
|
||||||
"""Restore a file from trash."""
|
"""Restore a file from trash."""
|
||||||
raise NotImplementedError(_('Backend didn’t \ implement this functionality'))
|
raise NotImplementedError(_('Backend didn’t implement this functionality'))
|
||||||
|
|
|
@ -127,7 +127,7 @@ DeletionDate={}
|
||||||
f.write(infofile)
|
f.write(infofile)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
self.regenerate()
|
# self.regenerate()
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
sys.stderr.write(_('trashed \'{}\'\n').format(filename))
|
sys.stderr.write(_('trashed \'{}\'\n').format(filename))
|
||||||
|
|
|
@ -7,5 +7,7 @@
|
||||||
{
|
{
|
||||||
"root": "./src/versions/solarfm-0.0.1/solarfm"
|
"root": "./src/versions/solarfm-0.0.1/solarfm"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"venvPath": "/home/abaddon/Portable_Apps/py-venvs/pylsp-venv/",
|
||||||
|
"venv": "venv"
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,17 @@ from utils.settings_manager.manager import SettingsManager
|
||||||
# NOTE: Threads WILL NOT die with parent's destruction.
|
# NOTE: Threads WILL NOT die with parent's destruction.
|
||||||
def threaded_wrapper(fn):
|
def threaded_wrapper(fn):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
|
thread = threading.Thread(target = fn, args = args, kwargs = kwargs, daemon = False)
|
||||||
|
thread.start()
|
||||||
|
return thread
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
# NOTE: Threads WILL die with parent's destruction.
|
# NOTE: Threads WILL die with parent's destruction.
|
||||||
def daemon_threaded_wrapper(fn):
|
def daemon_threaded_wrapper(fn):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
|
thread = threading.Thread(target = fn, args = args, kwargs = kwargs, daemon = True)
|
||||||
|
thread.start()
|
||||||
|
return thread
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def sizeof_fmt_def(num, suffix="B"):
|
def sizeof_fmt_def(num, suffix="B"):
|
||||||
|
|
|
@ -3,14 +3,13 @@
|
||||||
# Python imports
|
# Python imports
|
||||||
import argparse
|
import argparse
|
||||||
import faulthandler
|
import faulthandler
|
||||||
import locale
|
|
||||||
import traceback
|
import traceback
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
|
import tracemalloc
|
||||||
|
tracemalloc.start()
|
||||||
|
|
||||||
# Lib imports
|
# Lib imports
|
||||||
import gi
|
|
||||||
gi.require_version('Gtk', '3.0')
|
|
||||||
from gi.repository import Gtk
|
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from __builtins__ import *
|
from __builtins__ import *
|
||||||
|
@ -18,24 +17,8 @@ from app import Application
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def main(args, unknownargs):
|
||||||
try:
|
setproctitle(f'{app_name}')
|
||||||
locale.setlocale(locale.LC_NUMERIC, 'C')
|
|
||||||
|
|
||||||
setproctitle(f"{app_name}")
|
|
||||||
faulthandler.enable() # For better debug info
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
if args.debug == "true":
|
if args.debug == "true":
|
||||||
settings_manager.set_debug(True)
|
settings_manager.set_debug(True)
|
||||||
|
@ -45,12 +28,27 @@ def run():
|
||||||
|
|
||||||
settings_manager.do_dirty_start_check()
|
settings_manager.do_dirty_start_check()
|
||||||
Application(args, unknownargs)
|
Application(args, unknownargs)
|
||||||
Gtk.main()
|
|
||||||
except Exception as e:
|
|
||||||
traceback.print_exc()
|
|
||||||
quit()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
""" Set process title, get arguments, and create GTK main thread. """
|
''' Set process title, get arguments, and create GTK main thread. '''
|
||||||
run()
|
|
||||||
|
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", "-nt", default="false", help="Opens a 'New Tab' if a handler is set for it.")
|
||||||
|
parser.add_argument("--file", "-f", default="default", help="JUST SOME FILE ARG.")
|
||||||
|
|
||||||
|
# Read arguments (If any...)
|
||||||
|
args, unknownargs = parser.parse_known_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
faulthandler.enable() # For better debug info
|
||||||
|
main(args, unknownargs)
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
quit()
|
||||||
|
|
|
@ -15,35 +15,40 @@ class AppLaunchException(Exception):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
class Application(IPCServer):
|
|
||||||
|
class Application:
|
||||||
""" docstring for Application. """
|
""" docstring for Application. """
|
||||||
|
|
||||||
def __init__(self, args, unknownargs):
|
def __init__(self, args, unknownargs):
|
||||||
super(Application, self).__init__()
|
super(Application, self).__init__()
|
||||||
|
|
||||||
if not settings_manager.is_trace_debug():
|
if not settings_manager.is_trace_debug():
|
||||||
self.socket_realization_check()
|
self.load_ipc(args, unknownargs)
|
||||||
|
|
||||||
if not self.is_ipc_alive:
|
|
||||||
for arg in unknownargs + [args.new_tab,]:
|
|
||||||
if os.path.isdir(arg):
|
|
||||||
message = f"FILE|{arg}"
|
|
||||||
self.send_ipc_message(message)
|
|
||||||
|
|
||||||
raise AppLaunchException(f"{app_name} IPC Server Exists: Will send path(s) to it and close...")
|
|
||||||
|
|
||||||
self.setup_debug_hook()
|
self.setup_debug_hook()
|
||||||
Window(args, unknownargs)
|
Window(args, unknownargs).main()
|
||||||
|
|
||||||
|
|
||||||
def socket_realization_check(self):
|
def load_ipc(self, args, unknownargs):
|
||||||
|
ipc_server = IPCServer()
|
||||||
|
self.ipc_realization_check(ipc_server)
|
||||||
|
|
||||||
|
if not ipc_server.is_ipc_alive:
|
||||||
|
for arg in unknownargs + [args.new_tab,]:
|
||||||
|
if os.path.isfile(arg):
|
||||||
|
message = f"FILE|{arg}"
|
||||||
|
ipc_server.send_ipc_message(message)
|
||||||
|
|
||||||
|
raise AppLaunchException(f"{app_name} IPC Server Exists: Have sent path(s) to it and closing...")
|
||||||
|
|
||||||
|
def ipc_realization_check(self, ipc_server):
|
||||||
try:
|
try:
|
||||||
self.create_ipc_listener()
|
ipc_server.create_ipc_listener()
|
||||||
except Exception:
|
except Exception:
|
||||||
self.send_test_ipc_message()
|
ipc_server.send_test_ipc_message()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.create_ipc_listener()
|
ipc_server.create_ipc_listener()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -51,7 +56,7 @@ class Application(IPCServer):
|
||||||
try:
|
try:
|
||||||
# kill -SIGUSR2 <pid> from Linux/Unix or SIGBREAK signal from Windows
|
# kill -SIGUSR2 <pid> from Linux/Unix or SIGBREAK signal from Windows
|
||||||
signal.signal(
|
signal.signal(
|
||||||
vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR1"),
|
vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR2"),
|
||||||
debug_signal_handler
|
debug_signal_handler
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
|
|
@ -44,10 +44,13 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
||||||
self._subscribe_to_events()
|
self._subscribe_to_events()
|
||||||
self._load_widgets()
|
self._load_widgets()
|
||||||
|
|
||||||
|
if args.no_plugins == "false":
|
||||||
|
self.plugins_controller.pre_launch_plugins()
|
||||||
|
|
||||||
self._generate_file_views(self.fm_controller_data)
|
self._generate_file_views(self.fm_controller_data)
|
||||||
|
|
||||||
if args.no_plugins == "false":
|
if args.no_plugins == "false":
|
||||||
self.plugins.launch_plugins()
|
self.plugins_controller.post_launch_plugins()
|
||||||
|
|
||||||
for arg in unknownargs + [args.new_tab,]:
|
for arg in unknownargs + [args.new_tab,]:
|
||||||
if os.path.isdir(arg):
|
if os.path.isdir(arg):
|
||||||
|
@ -78,6 +81,7 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
||||||
event_system.subscribe("do_action_from_menu_controls", self.do_action_from_menu_controls)
|
event_system.subscribe("do_action_from_menu_controls", self.do_action_from_menu_controls)
|
||||||
event_system.subscribe("set_clipboard_data", self.set_clipboard_data)
|
event_system.subscribe("set_clipboard_data", self.set_clipboard_data)
|
||||||
|
|
||||||
|
|
||||||
def _load_glade_file(self):
|
def _load_glade_file(self):
|
||||||
self.builder.add_from_file( settings_manager.get_glade_file() )
|
self.builder.add_from_file( settings_manager.get_glade_file() )
|
||||||
self.builder.expose_object("main_window", self.window)
|
self.builder.expose_object("main_window", self.window)
|
||||||
|
@ -113,8 +117,9 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
||||||
if not settings_manager.is_trace_debug():
|
if not settings_manager.is_trace_debug():
|
||||||
self.fm_controller.save_state()
|
self.fm_controller.save_state()
|
||||||
|
|
||||||
|
|
||||||
def reload_plugins(self, widget=None, eve=None):
|
def reload_plugins(self, widget=None, eve=None):
|
||||||
self.plugins.reload_plugins()
|
self.plugins_controller.reload_plugins()
|
||||||
|
|
||||||
|
|
||||||
def do_action_from_menu_controls(self, _action=None, eve=None):
|
def do_action_from_menu_controls(self, _action=None, eve=None):
|
||||||
|
@ -130,44 +135,48 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
||||||
event_system.emit("hide_rename_file_menu")
|
event_system.emit("hide_rename_file_menu")
|
||||||
|
|
||||||
if action == "open":
|
if action == "open":
|
||||||
event_system.emit("open_files")
|
event_system.emit_and_await("open_files")
|
||||||
if action == "open_with":
|
if action == "open_with":
|
||||||
event_system.emit("show_appchooser_menu")
|
event_system.emit_and_await("show_appchooser_menu")
|
||||||
|
if action == "open_2_new_tab":
|
||||||
|
event_system.emit_and_await("open_2_new_tab")
|
||||||
if action == "execute":
|
if action == "execute":
|
||||||
event_system.emit("execute_files")
|
event_system.emit_and_await("execute_files")
|
||||||
if action == "execute_in_terminal":
|
if action == "execute_in_terminal":
|
||||||
event_system.emit("execute_files", (True,))
|
event_system.emit_and_await("execute_files", (True,))
|
||||||
if action == "rename":
|
if action == "rename":
|
||||||
event_system.emit("rename_files")
|
event_system.emit_and_await("rename_files")
|
||||||
if action == "cut":
|
if action == "cut":
|
||||||
event_system.emit("cut_files")
|
event_system.emit_and_await("cut_files")
|
||||||
if action == "copy":
|
if action == "copy":
|
||||||
event_system.emit("copy_files")
|
event_system.emit_and_await("copy_files")
|
||||||
if action == "copy_path":
|
if action == "copy_path":
|
||||||
event_system.emit("copy_path")
|
event_system.emit_and_await("copy_path")
|
||||||
if action == "copy_name":
|
if action == "copy_name":
|
||||||
event_system.emit("copy_name")
|
event_system.emit_and_await("copy_name")
|
||||||
if action == "copy_path_name":
|
if action == "copy_path_name":
|
||||||
event_system.emit("copy_path_name")
|
event_system.emit_and_await("copy_path_name")
|
||||||
if action == "paste":
|
if action == "paste":
|
||||||
event_system.emit("paste_files")
|
event_system.emit_and_await("paste_files")
|
||||||
if action == "create":
|
if action == "create":
|
||||||
event_system.emit("create_files")
|
event_system.emit_and_await("create_files")
|
||||||
if action in ["save_session", "save_session_as", "load_session"]:
|
if action in ["save_session", "save_session_as", "load_session"]:
|
||||||
event_system.emit("save_load_session", (action))
|
event_system.emit_and_await("save_load_session", (action))
|
||||||
|
|
||||||
if action == "about_page":
|
if action == "about_page":
|
||||||
event_system.emit("show_about_page")
|
event_system.emit_and_await("show_about_page")
|
||||||
if action == "io_popup":
|
if action == "io_popup":
|
||||||
event_system.emit("show_io_popup")
|
event_system.emit_and_await("show_io_popup")
|
||||||
if action == "plugins_popup":
|
if action == "plugins_popup":
|
||||||
event_system.emit("show_plugins_popup")
|
event_system.emit_and_await("show_plugins_popup")
|
||||||
if action == "messages_popup":
|
if action == "messages_popup":
|
||||||
event_system.emit("show_messages_popup")
|
event_system.emit_and_await("show_messages_popup")
|
||||||
if action == "ui_debug":
|
if action == "ui_debug":
|
||||||
event_system.emit("load_interactive_debug")
|
event_system.emit_and_await("load_interactive_debug")
|
||||||
if action == "tear_down":
|
if action == "tear_down":
|
||||||
event_system.emit("tear_down")
|
event_system.emit_and_await("tear_down")
|
||||||
|
|
||||||
|
action = None
|
||||||
|
|
||||||
|
|
||||||
def go_home(self, widget=None, eve=None):
|
def go_home(self, widget=None, eve=None):
|
||||||
|
@ -185,11 +194,14 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
||||||
def tggl_top_main_menubar(self, widget=None, eve=None):
|
def tggl_top_main_menubar(self, widget=None, eve=None):
|
||||||
top_main_menubar = self.builder.get_object("top_main_menubar")
|
top_main_menubar = self.builder.get_object("top_main_menubar")
|
||||||
top_main_menubar.hide() if top_main_menubar.is_visible() else top_main_menubar.show()
|
top_main_menubar.hide() if top_main_menubar.is_visible() else top_main_menubar.show()
|
||||||
|
top_main_menubar = None
|
||||||
|
|
||||||
def open_terminal(self, widget=None, eve=None):
|
def open_terminal(self, widget=None, eve=None):
|
||||||
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
||||||
tab.execute([f"{tab.terminal_app}"], start_dir=tab.get_current_directory())
|
tab.execute([f"{tab.terminal_app}"], start_dir=tab.get_current_directory())
|
||||||
|
|
||||||
|
wid, tid, tab = None, None, None
|
||||||
|
|
||||||
def go_to_path(self, path: str):
|
def go_to_path(self, path: str):
|
||||||
self.builder.get_object("path_entry").set_text(path)
|
self.builder.get_object("path_entry").set_text(path)
|
|
@ -29,7 +29,7 @@ class Controller_Data:
|
||||||
|
|
||||||
self._load_glade_file()
|
self._load_glade_file()
|
||||||
self.fm_controller = WindowController()
|
self.fm_controller = WindowController()
|
||||||
self.plugins = PluginsController()
|
self.plugins_controller = PluginsController()
|
||||||
self.fm_controller_data = self.fm_controller.get_state_from_file()
|
self.fm_controller_data = self.fm_controller.get_state_from_file()
|
||||||
|
|
||||||
self.window1 = self.builder.get_object("window_1")
|
self.window1 = self.builder.get_object("window_1")
|
||||||
|
@ -70,23 +70,16 @@ class Controller_Data:
|
||||||
Returns:
|
Returns:
|
||||||
state (obj): State
|
state (obj): State
|
||||||
'''
|
'''
|
||||||
# state = State()
|
|
||||||
state = self._state
|
state = self._state
|
||||||
state.fm_controller = self.fm_controller
|
state.fm_controller = self.fm_controller
|
||||||
state.notebooks = self.notebooks
|
state.notebooks = self.notebooks
|
||||||
state.wid, state.tid = self.fm_controller.get_active_wid_and_tid()
|
state.wid, state.tid = self.fm_controller.get_active_wid_and_tid()
|
||||||
state.tab = self.get_fm_window(state.wid).get_tab_by_id(state.tid)
|
state.tab = self.get_fm_window(state.wid).get_tab_by_id(state.tid)
|
||||||
state.icon_grid = self.builder.get_object(f"{state.wid}|{state.tid}|icon_grid", use_gtk = False)
|
state.icon_grid = self.builder.get_object(f"{state.wid}|{state.tid}|icon_grid", use_gtk = False)
|
||||||
# state.icon_grid = event_system.emit_and_await("get_files_view_icon_grid", (state.wid, state.tid))
|
|
||||||
state.store = state.icon_grid.get_model()
|
state.store = state.icon_grid.get_model()
|
||||||
|
|
||||||
# NOTE: Need to watch this as I thought we had issues with just using single reference upon closing it.
|
|
||||||
# But, I found that not doing it this way caused objects to generate upon every click... (Because we're getting state info, duh)
|
|
||||||
# Yet interactive debug view shows them just pilling on and never clearing...
|
|
||||||
state.message_dialog = self.message_dialog
|
state.message_dialog = self.message_dialog
|
||||||
state.user_pass_dialog = self.user_pass_dialog
|
state.user_pass_dialog = self.user_pass_dialog
|
||||||
# state.message_dialog = MessageWidget()
|
|
||||||
# state.user_pass_dialog = UserPassWidget()
|
|
||||||
|
|
||||||
selected_files = state.icon_grid.get_selected_items()
|
selected_files = state.icon_grid.get_selected_items()
|
||||||
if selected_files:
|
if selected_files:
|
||||||
|
@ -121,6 +114,9 @@ class Controller_Data:
|
||||||
|
|
||||||
uris.append(fpath)
|
uris.append(fpath)
|
||||||
|
|
||||||
|
tab = None
|
||||||
|
dir = None
|
||||||
|
|
||||||
return uris
|
return uris
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ class FileSystemActions(HandlerMixin, CRUDMixin):
|
||||||
|
|
||||||
event_system.subscribe("open_files", self.open_files)
|
event_system.subscribe("open_files", self.open_files)
|
||||||
event_system.subscribe("open_with_files", self.open_with_files)
|
event_system.subscribe("open_with_files", self.open_with_files)
|
||||||
|
event_system.subscribe("open_2_new_tab", self.open_2_new_tab)
|
||||||
event_system.subscribe("execute_files", self.execute_files)
|
event_system.subscribe("execute_files", self.execute_files)
|
||||||
|
|
||||||
event_system.subscribe("cut_files", self.cut_files)
|
event_system.subscribe("cut_files", self.cut_files)
|
||||||
|
@ -104,6 +105,12 @@ class FileSystemActions(HandlerMixin, CRUDMixin):
|
||||||
|
|
||||||
state.tab.app_chooser_exec(app_info, uris)
|
state.tab.app_chooser_exec(app_info, uris)
|
||||||
|
|
||||||
|
def open_2_new_tab(self):
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
uri = state.uris[0]
|
||||||
|
message = f"FILE|{uri}"
|
||||||
|
logger.info(message)
|
||||||
|
event_system.emit("post_file_to_ipc", message)
|
||||||
|
|
||||||
def execute_files(self, in_terminal=False):
|
def execute_files(self, in_terminal=False):
|
||||||
state = event_system.emit_and_await("get_current_state")
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
|
|
@ -110,26 +110,30 @@ class HandlerMixin:
|
||||||
tab.move_file(fPath, tPath)
|
tab.move_file(fPath, tPath)
|
||||||
else:
|
else:
|
||||||
io_widget = IOWidget(action, file)
|
io_widget = IOWidget(action, file)
|
||||||
|
io_list = self._builder.get_object("io_list")
|
||||||
|
|
||||||
|
io_list.add(io_widget)
|
||||||
|
io_list.show_all()
|
||||||
|
|
||||||
if action == "copy":
|
if action == "copy":
|
||||||
file.copy_async(destination=target,
|
file.copy_async(destination=target,
|
||||||
flags=Gio.FileCopyFlags.BACKUP,
|
flags=Gio.FileCopyFlags.BACKUP,
|
||||||
io_priority=98,
|
io_priority=45,
|
||||||
cancellable=io_widget.cancle_eve,
|
cancellable=io_widget.cancle_eve,
|
||||||
progress_callback=io_widget.update_progress,
|
progress_callback=io_widget.update_progress,
|
||||||
callback=io_widget.finish_callback)
|
callback=io_widget.finish_callback)
|
||||||
|
|
||||||
self._builder.get_object("io_list").add(io_widget)
|
|
||||||
if action == "move" or action == "rename":
|
if action == "move" or action == "rename":
|
||||||
file.move_async(destination=target,
|
file.move_async(destination=target,
|
||||||
flags=Gio.FileCopyFlags.BACKUP,
|
flags=Gio.FileCopyFlags.BACKUP,
|
||||||
io_priority=98,
|
io_priority=45,
|
||||||
cancellable=io_widget.cancle_eve,
|
cancellable=io_widget.cancle_eve,
|
||||||
progress_callback=None,
|
|
||||||
# NOTE: progress_callback here causes seg fault when set
|
# NOTE: progress_callback here causes seg fault when set
|
||||||
|
progress_callback=None,
|
||||||
callback=io_widget.finish_callback)
|
callback=io_widget.finish_callback)
|
||||||
|
|
||||||
self._builder.get_object("io_list").add(io_widget)
|
io_widget = None
|
||||||
|
io_list = None
|
||||||
|
|
||||||
except GObject.GError as e:
|
except GObject.GError as e:
|
||||||
raise OSError(e)
|
raise OSError(e)
|
||||||
|
|
|
@ -30,57 +30,53 @@ class FileActionSignalsMixin:
|
||||||
|
|
||||||
wid = tab.get_wid()
|
wid = tab.get_wid()
|
||||||
tid = tab.get_id()
|
tid = tab.get_id()
|
||||||
dir_watcher.connect("changed", self.dir_watch_updates, (f"{wid}|{tid}",))
|
dir_watcher.connect("changed", self.dir_watch_updates, *(f"{wid}|{tid}",))
|
||||||
tab.set_dir_watcher(dir_watcher)
|
tab.set_dir_watcher(dir_watcher)
|
||||||
|
|
||||||
# NOTE: Too lazy to impliment a proper update handler and so just regen store and update tab.
|
def dir_watch_updates(self, file_monitor, file, other_file = None, eve_type = None, tab_widget_id = None):
|
||||||
# Use a lock system to prevent too many update calls for certain instances but user can manually refresh if they have urgency
|
|
||||||
def dir_watch_updates(self, file_monitor, file, other_file = None, eve_type = None, data = None):
|
|
||||||
if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED,
|
if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED,
|
||||||
Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN,
|
Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN,
|
||||||
Gio.FileMonitorEvent.MOVED_OUT]:
|
Gio.FileMonitorEvent.MOVED_OUT]:
|
||||||
logger.debug(eve_type)
|
|
||||||
|
|
||||||
if eve_type in [Gio.FileMonitorEvent.MOVED_IN, Gio.FileMonitorEvent.MOVED_OUT]:
|
self.soft_lock_countdown(tab_widget_id)
|
||||||
self.update_on_soft_lock_end(data[0])
|
|
||||||
elif data[0] in self.soft_update_lock.keys():
|
|
||||||
self.soft_update_lock[data[0]]["last_update_time"] = time.time()
|
|
||||||
else:
|
|
||||||
self.soft_lock_countdown(data[0])
|
|
||||||
|
|
||||||
@threaded
|
def soft_lock_countdown(self, tab_widget_id):
|
||||||
def soft_lock_countdown(self, tab_widget):
|
if tab_widget_id in self.soft_update_lock:
|
||||||
self.soft_update_lock[tab_widget] = { "last_update_time": time.time()}
|
timeout_id = self.soft_update_lock[tab_widget_id]["timeout_id"]
|
||||||
|
GLib.source_remove(timeout_id)
|
||||||
|
|
||||||
lock = True
|
timeout_id = GLib.timeout_add(0, self.update_on_soft_lock_end, 600, *(tab_widget_id,))
|
||||||
while lock:
|
self.soft_update_lock[tab_widget_id] = { "timeout_id": timeout_id }
|
||||||
time.sleep(0.6)
|
|
||||||
last_update_time = self.soft_update_lock[tab_widget]["last_update_time"]
|
|
||||||
current_time = time.time()
|
|
||||||
if (current_time - last_update_time) > 0.6:
|
|
||||||
lock = False
|
|
||||||
|
|
||||||
self.soft_update_lock.pop(tab_widget, None)
|
|
||||||
GLib.idle_add(self.update_on_soft_lock_end, *(tab_widget,))
|
|
||||||
|
|
||||||
|
|
||||||
def update_on_soft_lock_end(self, tab_widget):
|
def update_on_soft_lock_end(self, timout_ms, tab_widget_id):
|
||||||
wid, tid = tab_widget.split("|")
|
self.soft_update_lock.pop(tab_widget_id, None)
|
||||||
|
|
||||||
|
wid, tid = tab_widget_id.split("|")
|
||||||
notebook = self.builder.get_object(f"window_{wid}")
|
notebook = self.builder.get_object(f"window_{wid}")
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
||||||
icon_grid = self.builder.get_object(f"{wid}|{tid}|icon_grid", use_gtk = False)
|
icon_grid = self.builder.get_object(f"{wid}|{tid}|icon_grid", use_gtk = False)
|
||||||
store = icon_grid.get_model()
|
store = icon_grid.get_model()
|
||||||
_store, tab_widget_label = self.get_store_and_label_from_notebook(notebook, f"{wid}|{tid}")
|
_store, tab_widget_id_label = self.get_store_and_label_from_notebook(notebook, f"{wid}|{tid}")
|
||||||
|
|
||||||
tab.load_directory()
|
tab.load_directory()
|
||||||
icon_grid.clear_and_set_new_store()
|
icon_grid.clear_and_set_new_store()
|
||||||
self.load_store(tab, icon_grid.get_store())
|
self.load_store(tab, icon_grid.get_store())
|
||||||
|
|
||||||
tab_widget_label.set_label(tab.get_end_of_path())
|
tab_widget_id_label.set_label(tab.get_end_of_path())
|
||||||
state = self.get_current_state()
|
state = self.get_current_state()
|
||||||
if [wid, tid] in [state.wid, state.tid]:
|
if [wid, tid] in [state.wid, state.tid]:
|
||||||
self.set_bottom_labels(tab)
|
self.set_bottom_labels(tab)
|
||||||
|
|
||||||
|
wid, tid = None, None
|
||||||
|
notebook = None
|
||||||
|
tab = None
|
||||||
|
icon_grid = None
|
||||||
|
store = None
|
||||||
|
_store, tab_widget_id_label = None, None
|
||||||
|
state = None
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def do_file_search(self, widget, eve = None):
|
def do_file_search(self, widget, eve = None):
|
||||||
if not self.ctrl_down and not self.shift_down and not self.alt_down:
|
if not self.ctrl_down and not self.shift_down and not self.alt_down:
|
||||||
|
|
|
@ -11,8 +11,8 @@ from gi.repository import Gdk
|
||||||
# Application imports
|
# Application imports
|
||||||
|
|
||||||
|
|
||||||
valid_keyvalue_pat = re.compile(r"[a-z0-9A-Z-_\[\]\(\)\| ]")
|
|
||||||
|
|
||||||
|
valid_keyvalue_pat = re.compile(r"[a-z0-9A-Z-_\[\]\(\)\| ]")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,13 +20,25 @@ class KeyboardSignalsMixin:
|
||||||
""" KeyboardSignalsMixin keyboard hooks controller. """
|
""" KeyboardSignalsMixin keyboard hooks controller. """
|
||||||
|
|
||||||
# TODO: Need to set methods that use this to somehow check the keybindings state instead.
|
# TODO: Need to set methods that use this to somehow check the keybindings state instead.
|
||||||
def unset_keys_and_data(self, widget=None, eve=None):
|
def unset_keys_and_data(self, widget = None, eve = None):
|
||||||
self.ctrl_down = False
|
self.ctrl_down = False
|
||||||
self.shift_down = False
|
self.shift_down = False
|
||||||
self.alt_down = False
|
self.alt_down = False
|
||||||
|
|
||||||
|
def unmap_special_keys(self, keyname):
|
||||||
|
if "control" in keyname:
|
||||||
|
self.ctrl_down = False
|
||||||
|
if "shift" in keyname:
|
||||||
|
self.shift_down = False
|
||||||
|
if "alt" in keyname:
|
||||||
|
self.alt_down = False
|
||||||
|
|
||||||
def on_global_key_press_controller(self, eve, user_data):
|
def on_global_key_press_controller(self, eve, user_data):
|
||||||
keyname = Gdk.keyval_name(user_data.keyval).lower()
|
keyname = Gdk.keyval_name(user_data.keyval).lower()
|
||||||
|
modifiers = Gdk.ModifierType(user_data.get_state() & ~Gdk.ModifierType.LOCK_MASK)
|
||||||
|
|
||||||
|
self.was_midified_key = True if modifiers != 0 else False
|
||||||
|
|
||||||
if keyname.replace("_l", "").replace("_r", "") in ["control", "alt", "shift"]:
|
if keyname.replace("_l", "").replace("_r", "") in ["control", "alt", "shift"]:
|
||||||
if "control" in keyname:
|
if "control" in keyname:
|
||||||
self.ctrl_down = True
|
self.ctrl_down = True
|
||||||
|
@ -36,52 +48,49 @@ class KeyboardSignalsMixin:
|
||||||
self.alt_down = True
|
self.alt_down = True
|
||||||
|
|
||||||
def on_global_key_release_controller(self, widget, event):
|
def on_global_key_release_controller(self, widget, event):
|
||||||
"""Handler for keyboard events"""
|
""" Handler for keyboard events """
|
||||||
keyname = Gdk.keyval_name(event.keyval).lower()
|
keyname = Gdk.keyval_name(event.keyval).lower()
|
||||||
|
modifiers = Gdk.ModifierType(event.get_state() & ~Gdk.ModifierType.LOCK_MASK)
|
||||||
|
|
||||||
if keyname.replace("_l", "").replace("_r", "") in ["control", "alt", "shift"]:
|
if keyname.replace("_l", "").replace("_r", "") in ["control", "alt", "shift"]:
|
||||||
if "control" in keyname:
|
should_return = self.was_midified_key and (self.ctrl_down or self.shift_down or self.alt_down)
|
||||||
self.ctrl_down = False
|
self.unmap_special_keys(keyname)
|
||||||
if "shift" in keyname:
|
|
||||||
self.shift_down = False
|
if should_return:
|
||||||
if "alt" in keyname:
|
self.was_midified_key = False
|
||||||
self.alt_down = False
|
return
|
||||||
|
|
||||||
mapping = keybindings.lookup(event)
|
mapping = keybindings.lookup(event)
|
||||||
|
logger.debug(f"on_global_key_release_controller > key > {keyname}")
|
||||||
|
logger.debug(f"on_global_key_release_controller > keyval > {event.keyval}")
|
||||||
|
logger.debug(f"on_global_key_release_controller > mapping > {mapping}")
|
||||||
|
|
||||||
if mapping:
|
if mapping:
|
||||||
# See if in filemanager scope
|
self.handle_mapped_key_event(mapping)
|
||||||
|
else:
|
||||||
|
self.handle_as_key_event_scope(keyname)
|
||||||
|
|
||||||
|
def handle_mapped_key_event(self, mapping):
|
||||||
try:
|
try:
|
||||||
getattr(self, mapping)()
|
self.handle_as_controller_scope(mapping)
|
||||||
return True
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# Must be plugins scope, event call, OR we forgot to add method to file manager scope
|
self.handle_as_plugin_scope(mapping)
|
||||||
|
|
||||||
|
def handle_as_controller_scope(self, mapping):
|
||||||
|
getattr(self, mapping)()
|
||||||
|
|
||||||
|
def handle_as_plugin_scope(self, mapping):
|
||||||
if "||" in mapping:
|
if "||" in mapping:
|
||||||
sender, eve_type = mapping.split("||")
|
sender, eve_type = mapping.split("||")
|
||||||
else:
|
else:
|
||||||
sender = ""
|
sender = ""
|
||||||
eve_type = mapping
|
eve_type = mapping
|
||||||
|
|
||||||
self.handle_plugin_key_event(sender, eve_type)
|
self.handle_key_event_system(sender, eve_type)
|
||||||
else:
|
|
||||||
logger.debug(f"on_global_key_release_controller > key > {keyname}")
|
|
||||||
|
|
||||||
if self.ctrl_down:
|
def handle_as_key_event_scope(self, keyname):
|
||||||
if keyname in ["1", "kp_1", "2", "kp_2", "3", "kp_3", "4", "kp_4"]:
|
if self.ctrl_down and not keyname in ["1", "kp_1", "2", "kp_2", "3", "kp_3", "4", "kp_4"]:
|
||||||
self.builder.get_object(f"tggl_notebook_{keyname.strip('kp_')}").released()
|
self.handle_key_event_system(None, keyname)
|
||||||
|
|
||||||
def handle_plugin_key_event(self, sender, eve_type):
|
def handle_key_event_system(self, sender, eve_type):
|
||||||
event_system.emit(eve_type)
|
event_system.emit(eve_type)
|
||||||
|
|
||||||
def keyboard_close_tab(self):
|
|
||||||
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
|
||||||
notebook = self.builder.get_object(f"window_{wid}")
|
|
||||||
scroll = self.builder.get_object(f"{wid}|{tid}", use_gtk = False)
|
|
||||||
page = notebook.page_num(scroll)
|
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
|
||||||
watcher = tab.get_dir_watcher()
|
|
||||||
watcher.cancel()
|
|
||||||
|
|
||||||
self.get_fm_window(wid).delete_tab_by_id(tid)
|
|
||||||
notebook.remove_page(page)
|
|
||||||
if not trace_debug:
|
|
||||||
self.fm_controller.save_state()
|
|
||||||
self.set_window_title()
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import gi
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
|
from gi.repository import Gio
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from ...widgets.tab_header_widget import TabHeaderWidget
|
from ...widgets.tab_header_widget import TabHeaderWidget
|
||||||
|
@ -19,6 +20,17 @@ class GridMixin:
|
||||||
"""docstring for GridMixin"""
|
"""docstring for GridMixin"""
|
||||||
|
|
||||||
def load_store(self, tab, store, save_state = False, use_generator = False):
|
def load_store(self, tab, store, save_state = False, use_generator = False):
|
||||||
|
# dir = tab.get_current_directory()
|
||||||
|
# file = Gio.File.new_for_path(dir)
|
||||||
|
# dir_list = Gtk.DirectoryList.new("standard::*", file)
|
||||||
|
# store.set(dir_list)
|
||||||
|
|
||||||
|
# file = Gio.File.new_for_path(dir)
|
||||||
|
# for file in file.enumerate_children("standard::*", Gio.FILE_ATTRIBUTE_STANDARD_NAME, None):
|
||||||
|
# store.append(file)
|
||||||
|
|
||||||
|
# return
|
||||||
|
|
||||||
dir = tab.get_current_directory()
|
dir = tab.get_current_directory()
|
||||||
files = tab.get_files()
|
files = tab.get_files()
|
||||||
|
|
||||||
|
@ -26,61 +38,77 @@ class GridMixin:
|
||||||
store.append([None, file[0]])
|
store.append([None, file[0]])
|
||||||
|
|
||||||
Gtk.main_iteration()
|
Gtk.main_iteration()
|
||||||
# for i, file in enumerate(files):
|
self.generate_icons(tab, store, dir, files)
|
||||||
# self.create_icon(i, tab, store, dir, file[0])
|
# GLib.Thread("", self.generate_icons, tab, store, dir, files)
|
||||||
|
|
||||||
if use_generator:
|
|
||||||
# NOTE: tab > icon > _get_system_thumbnail_gtk_thread must not be used
|
|
||||||
# as the attempted promotion back to gtk threading stalls the generator. (We're already in main gtk thread)
|
|
||||||
for i, icon in enumerate( self.create_icons_generator(tab, dir, files) ):
|
|
||||||
self.load_icon(i, store, icon)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
loop = asyncio.get_running_loop()
|
|
||||||
except RuntimeError:
|
|
||||||
loop = None
|
|
||||||
|
|
||||||
if loop and loop.is_running():
|
|
||||||
loop.create_task( self.create_icons(tab, store, dir, files) )
|
|
||||||
else:
|
|
||||||
asyncio.run( self.create_icons(tab, store, dir, files) )
|
|
||||||
|
|
||||||
# NOTE: Not likely called often from here but it could be useful
|
# NOTE: Not likely called often from here but it could be useful
|
||||||
if save_state and not trace_debug:
|
if save_state and not trace_debug:
|
||||||
self.fm_controller.save_state()
|
self.fm_controller.save_state()
|
||||||
|
|
||||||
async def create_icons(self, tab, store, dir, files):
|
dir = None
|
||||||
tasks = [self.update_store(i, store, dir, tab, file[0]) for i, file in enumerate(files)]
|
files = None
|
||||||
await asyncio.gather(*tasks)
|
|
||||||
|
|
||||||
async def load_icon(self, i, store, icon):
|
@daemon_threaded
|
||||||
self.update_store(i, store, icon)
|
def generate_icons(self, tab, store, dir, files):
|
||||||
|
for i, file in enumerate(files):
|
||||||
|
# GLib.Thread(f"{i}", self.make_and_load_icon, i, store, tab, dir, file[0])
|
||||||
|
self.make_and_load_icon( i, store, tab, dir, file[0])
|
||||||
|
|
||||||
async def update_store(self, i, store, dir, tab, file):
|
def update_store(self, i, store, icon):
|
||||||
icon = tab.create_icon(dir, file)
|
|
||||||
itr = store.get_iter(i)
|
itr = store.get_iter(i)
|
||||||
|
GLib.idle_add(self.insert_store, store, itr, icon.copy())
|
||||||
|
itr = None
|
||||||
|
del icon
|
||||||
|
|
||||||
|
@daemon_threaded
|
||||||
|
def make_and_load_icon(self, i, store, tab, dir, file):
|
||||||
|
icon = tab.create_icon(dir, file)
|
||||||
|
self.update_store(i, store, icon)
|
||||||
|
icon = None
|
||||||
|
|
||||||
|
def get_icon(self, tab, dir, file):
|
||||||
|
tab.create_icon(dir, file)
|
||||||
|
|
||||||
|
|
||||||
|
# @daemon_threaded
|
||||||
|
# def generate_icons(self, tab, store, dir, files):
|
||||||
|
# try:
|
||||||
|
# loop = asyncio.get_running_loop()
|
||||||
|
# except RuntimeError:
|
||||||
|
# loop = None
|
||||||
|
|
||||||
|
# if loop and loop.is_running():
|
||||||
|
# loop = asyncio.get_event_loop()
|
||||||
|
# loop.create_task( self.create_icons(tab, store, dir, files) )
|
||||||
|
# else:
|
||||||
|
# asyncio.run( self.create_icons(tab, store, dir, files) )
|
||||||
|
|
||||||
|
# async def create_icons(self, tab, store, dir, files):
|
||||||
|
# icons = [self.get_icon(tab, dir, file[0]) for file in files]
|
||||||
|
# data = await asyncio.gather(*icons)
|
||||||
|
# tasks = [self.update_store(i, store, icon) for i, icon in enumerate(data)]
|
||||||
|
# asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
# async def update_store(self, i, store, icon):
|
||||||
|
# itr = store.get_iter(i)
|
||||||
|
# GLib.idle_add(self.insert_store, store, itr, icon)
|
||||||
|
|
||||||
|
# async def get_icon(self, tab, dir, file):
|
||||||
|
# return tab.create_icon(dir, file)
|
||||||
|
|
||||||
|
|
||||||
|
def insert_store(self, store, itr, icon):
|
||||||
store.set_value(itr, 0, icon)
|
store.set_value(itr, 0, icon)
|
||||||
|
|
||||||
def create_icons_generator(self, tab, dir, files):
|
# Note: If the function returns GLib.SOURCE_REMOVE or False it is automatically removed from the list of event sources and will not be called again.
|
||||||
for file in files:
|
return False
|
||||||
icon = tab.create_icon(dir, file[0])
|
|
||||||
yield icon
|
|
||||||
|
|
||||||
# @daemon_threaded
|
def do_ui_update(self):
|
||||||
# def create_icon(self, i, tab, store, dir, file):
|
Gtk.main_iteration()
|
||||||
# icon = tab.create_icon(dir, file)
|
return False
|
||||||
# GLib.idle_add(self.update_store, *(i, store, icon,))
|
|
||||||
#
|
|
||||||
# @daemon_threaded
|
|
||||||
# def load_icon(self, i, store, icon):
|
|
||||||
# GLib.idle_add(self.update_store, *(i, store, icon,))
|
|
||||||
|
|
||||||
# def update_store(self, i, store, icon):
|
def create_tab_widget(self):
|
||||||
# itr = store.get_iter(i)
|
return TabHeaderWidget(self.close_tab)
|
||||||
# store.set_value(itr, 0, icon)
|
|
||||||
|
|
||||||
def create_tab_widget(self, tab):
|
|
||||||
return TabHeaderWidget(tab, self.close_tab)
|
|
||||||
|
|
||||||
def create_scroll_and_store(self, tab, wid, use_tree_view = False):
|
def create_scroll_and_store(self, tab, wid, use_tree_view = False):
|
||||||
scroll = Gtk.ScrolledWindow()
|
scroll = Gtk.ScrolledWindow()
|
||||||
|
@ -137,6 +165,7 @@ class GridMixin:
|
||||||
store = icon_grid.get_model()
|
store = icon_grid.get_model()
|
||||||
tab_label = notebook.get_tab_label(obj).get_children()[0]
|
tab_label = notebook.get_tab_label(obj).get_children()[0]
|
||||||
|
|
||||||
|
icon_grid = None
|
||||||
return store, tab_label
|
return store, tab_label
|
||||||
|
|
||||||
def get_icon_grid_from_notebook(self, notebook, _name):
|
def get_icon_grid_from_notebook(self, notebook, _name):
|
||||||
|
|
|
@ -34,27 +34,46 @@ class TabMixin(GridMixin):
|
||||||
else:
|
else:
|
||||||
tab.set_path(path)
|
tab.set_path(path)
|
||||||
|
|
||||||
tab_widget = self.create_tab_widget(tab)
|
tab_widget = self.get_tab_widget(tab)
|
||||||
scroll, store = self.create_scroll_and_store(tab, wid)
|
scroll, store = self.create_scroll_and_store(tab, wid)
|
||||||
index = notebook.append_page(scroll, tab_widget)
|
index = notebook.append_page(scroll, tab_widget)
|
||||||
notebook.set_tab_detachable(scroll, True)
|
notebook.set_tab_detachable(scroll, True)
|
||||||
|
notebook.set_tab_reorderable(scroll, True)
|
||||||
|
|
||||||
self.fm_controller.set_wid_and_tid(wid, tab.get_id())
|
self.fm_controller.set_wid_and_tid(wid, tab.get_id())
|
||||||
path_entry.set_text(tab.get_current_directory())
|
# path_entry.set_text(tab.get_current_directory())
|
||||||
|
event_system.emit("go_to_path", (tab.get_current_directory(),)) # NOTE: Not efficent if I understand how
|
||||||
notebook.show_all()
|
notebook.show_all()
|
||||||
notebook.set_current_page(index)
|
notebook.set_current_page(index)
|
||||||
|
|
||||||
ctx = notebook.get_style_context()
|
ctx = notebook.get_style_context()
|
||||||
ctx.add_class("notebook-unselected-focus")
|
ctx.add_class("notebook-unselected-focus")
|
||||||
notebook.set_tab_reorderable(scroll, True)
|
|
||||||
self.load_store(tab, store)
|
self.load_store(tab, store)
|
||||||
self.set_window_title()
|
event_system.emit("set_window_title", (tab.get_current_directory(),))
|
||||||
self.set_file_watcher(tab)
|
self.set_file_watcher(tab)
|
||||||
|
|
||||||
|
tab_widget = None
|
||||||
|
scroll, store = None, None
|
||||||
|
index = None
|
||||||
|
notebook = None
|
||||||
|
path_entry = None
|
||||||
|
tab = None
|
||||||
|
ctx = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_tab_widget(self, tab):
|
||||||
|
tab_widget = self.create_tab_widget()
|
||||||
|
tab_widget.tab = tab
|
||||||
|
|
||||||
|
tab_widget.label.set_label(f"{tab.get_end_of_path()}")
|
||||||
|
tab_widget.label.set_width_chars(len(tab.get_end_of_path()))
|
||||||
|
|
||||||
|
return tab_widget
|
||||||
|
|
||||||
def close_tab(self, button, eve = None):
|
def close_tab(self, button, eve = None):
|
||||||
notebook = button.get_parent().get_parent()
|
notebook = button.get_parent().get_parent()
|
||||||
if notebook.get_n_pages() == 1:
|
if notebook.get_n_pages() == 1:
|
||||||
|
notebook = None
|
||||||
return
|
return
|
||||||
|
|
||||||
tab_box = button.get_parent()
|
tab_box = button.get_parent()
|
||||||
|
@ -72,23 +91,35 @@ class TabMixin(GridMixin):
|
||||||
self.builder.dereference_object(f"{wid}|{tid}|icon_grid")
|
self.builder.dereference_object(f"{wid}|{tid}|icon_grid")
|
||||||
self.builder.dereference_object(f"{wid}|{tid}")
|
self.builder.dereference_object(f"{wid}|{tid}")
|
||||||
|
|
||||||
|
iter = store.get_iter_first()
|
||||||
|
while iter:
|
||||||
|
next_iter = store.iter_next(iter)
|
||||||
|
store.unref_node(iter)
|
||||||
|
iter = next_iter
|
||||||
|
|
||||||
store.clear()
|
store.clear()
|
||||||
icon_grid.destroy()
|
store.run_dispose()
|
||||||
scroll.destroy()
|
|
||||||
tab_box.destroy()
|
|
||||||
|
|
||||||
del store
|
icon_grid.set_model(None)
|
||||||
del icon_grid
|
icon_grid.run_dispose()
|
||||||
del scroll
|
scroll.run_dispose()
|
||||||
del tab_box
|
tab_box.run_dispose()
|
||||||
del watcher
|
|
||||||
del tab
|
iter = None
|
||||||
|
wid, tid = None, None
|
||||||
|
store = None
|
||||||
|
icon_grid = None
|
||||||
|
scroll = None
|
||||||
|
tab_box = None
|
||||||
|
watcher = None
|
||||||
|
tab = None
|
||||||
|
notebook = None
|
||||||
|
|
||||||
gc.collect()
|
|
||||||
if not settings_manager.is_trace_debug():
|
if not settings_manager.is_trace_debug():
|
||||||
self.fm_controller.save_state()
|
self.fm_controller.save_state()
|
||||||
|
|
||||||
self.set_window_title()
|
self.set_window_title()
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
# NOTE: Not actually getting called even tho set in the glade file...
|
# NOTE: Not actually getting called even tho set in the glade file...
|
||||||
def on_tab_dnded(self, notebook, page, x, y):
|
def on_tab_dnded(self, notebook, page, x, y):
|
||||||
|
@ -111,15 +142,23 @@ class TabMixin(GridMixin):
|
||||||
if not settings_manager.is_trace_debug():
|
if not settings_manager.is_trace_debug():
|
||||||
self.fm_controller.save_state()
|
self.fm_controller.save_state()
|
||||||
|
|
||||||
|
wid, tid = None, None
|
||||||
|
window = None
|
||||||
|
tab = None
|
||||||
|
|
||||||
|
|
||||||
def on_tab_switch_update(self, notebook, content = None, index = None):
|
def on_tab_switch_update(self, notebook, content = None, index = None):
|
||||||
self.selected_files.clear()
|
self.selected_files.clear()
|
||||||
wid, tid = content.get_children()[0].get_name().split("|")
|
wid, tid = content.get_children()[0].get_name().split("|")
|
||||||
|
|
||||||
self.fm_controller.set_wid_and_tid(wid, tid)
|
self.fm_controller.set_wid_and_tid(wid, tid)
|
||||||
self.set_path_text(wid, tid)
|
self.set_path_text(wid, tid)
|
||||||
self.set_window_title()
|
self.set_window_title()
|
||||||
|
|
||||||
|
wid, tid = None, None
|
||||||
|
|
||||||
def get_id_from_tab_box(self, tab_box):
|
def get_id_from_tab_box(self, tab_box):
|
||||||
return tab_box.get_children()[2].get_text()
|
return tab_box.tab.get_id()
|
||||||
|
|
||||||
def get_tab_label(self, notebook, icon_grid):
|
def get_tab_label(self, notebook, icon_grid):
|
||||||
return notebook.get_tab_label(icon_grid.get_parent()).get_children()[0]
|
return notebook.get_tab_label(icon_grid.get_parent()).get_children()[0]
|
||||||
|
@ -135,6 +174,8 @@ class TabMixin(GridMixin):
|
||||||
state.tab.load_directory()
|
state.tab.load_directory()
|
||||||
self.load_store(state.tab, state.store)
|
self.load_store(state.tab, state.store)
|
||||||
|
|
||||||
|
state = None
|
||||||
|
|
||||||
def update_tab(self, tab_label, tab, store, wid, tid):
|
def update_tab(self, tab_label, tab, store, wid, tid):
|
||||||
self.load_store(tab, store)
|
self.load_store(tab, store)
|
||||||
self.set_path_text(wid, tid)
|
self.set_path_text(wid, tid)
|
||||||
|
@ -175,16 +216,38 @@ class TabMixin(GridMixin):
|
||||||
if isinstance(focused_obj, Gtk.Entry):
|
if isinstance(focused_obj, Gtk.Entry):
|
||||||
self.process_path_menu(widget, tab, dir)
|
self.process_path_menu(widget, tab, dir)
|
||||||
|
|
||||||
|
action = None
|
||||||
|
store = None
|
||||||
|
|
||||||
if path.endswith(".") or path == dir:
|
if path.endswith(".") or path == dir:
|
||||||
|
tab_label = None
|
||||||
|
notebook = None
|
||||||
|
wid, tid = None, None
|
||||||
|
path = None
|
||||||
|
tab = None
|
||||||
return
|
return
|
||||||
|
|
||||||
if not tab.set_path(path):
|
if not tab.set_path(path):
|
||||||
|
tab_label = None
|
||||||
|
notebook = None
|
||||||
|
wid, tid = None, None
|
||||||
|
path = None
|
||||||
|
tab = None
|
||||||
return
|
return
|
||||||
|
|
||||||
icon_grid = self.get_icon_grid_from_notebook(notebook, f"{wid}|{tid}")
|
icon_grid = self.get_icon_grid_from_notebook(notebook, f"{wid}|{tid}")
|
||||||
icon_grid.clear_and_set_new_store()
|
icon_grid.clear_and_set_new_store()
|
||||||
self.update_tab(tab_label, tab, icon_grid.get_store(), wid, tid)
|
self.update_tab(tab_label, tab, icon_grid.get_store(), wid, tid)
|
||||||
|
|
||||||
|
action = None
|
||||||
|
wid, tid = None, None
|
||||||
|
notebook = None
|
||||||
|
store, tab_label = None, None
|
||||||
|
path = None
|
||||||
|
tab = None
|
||||||
|
icon_grid = None
|
||||||
|
|
||||||
|
|
||||||
def process_path_menu(self, gtk_entry, tab, dir):
|
def process_path_menu(self, gtk_entry, tab, dir):
|
||||||
path_menu_buttons = self.builder.get_object("path_menu_buttons")
|
path_menu_buttons = self.builder.get_object("path_menu_buttons")
|
||||||
query = gtk_entry.get_text().replace(dir, "")
|
query = gtk_entry.get_text().replace(dir, "")
|
||||||
|
@ -201,11 +264,16 @@ class TabMixin(GridMixin):
|
||||||
path_menu_buttons.add(button)
|
path_menu_buttons.add(button)
|
||||||
show_path_menu = True
|
show_path_menu = True
|
||||||
|
|
||||||
|
query = None
|
||||||
|
files = None
|
||||||
|
|
||||||
if not show_path_menu:
|
if not show_path_menu:
|
||||||
|
path_menu_buttons = None
|
||||||
event_system.emit("hide_path_menu")
|
event_system.emit("hide_path_menu")
|
||||||
else:
|
else:
|
||||||
event_system.emit("show_path_menu")
|
event_system.emit("show_path_menu")
|
||||||
buttons = path_menu_buttons.get_children()
|
buttons = path_menu_buttons.get_children()
|
||||||
|
path_menu_buttons = None
|
||||||
|
|
||||||
if len(buttons) == 1:
|
if len(buttons) == 1:
|
||||||
self.slowed_focus(buttons[0])
|
self.slowed_focus(buttons[0])
|
||||||
|
@ -218,6 +286,7 @@ class TabMixin(GridMixin):
|
||||||
def do_focused_click(self, button):
|
def do_focused_click(self, button):
|
||||||
button.grab_focus()
|
button.grab_focus()
|
||||||
button.clicked()
|
button.clicked()
|
||||||
|
return False
|
||||||
|
|
||||||
def set_path_entry(self, button = None, eve = None):
|
def set_path_entry(self, button = None, eve = None):
|
||||||
self.path_auto_filled = True
|
self.path_auto_filled = True
|
||||||
|
@ -230,6 +299,10 @@ class TabMixin(GridMixin):
|
||||||
path_entry.set_position(-1)
|
path_entry.set_position(-1)
|
||||||
event_system.emit("hide_path_menu")
|
event_system.emit("hide_path_menu")
|
||||||
|
|
||||||
|
state = None
|
||||||
|
path = None
|
||||||
|
path_entry = None
|
||||||
|
|
||||||
|
|
||||||
def show_hide_hidden_files(self):
|
def show_hide_hidden_files(self):
|
||||||
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
||||||
|
@ -237,3 +310,6 @@ class TabMixin(GridMixin):
|
||||||
tab.set_hiding_hidden(not tab.is_hiding_hidden())
|
tab.set_hiding_hidden(not tab.is_hiding_hidden())
|
||||||
tab.load_directory()
|
tab.load_directory()
|
||||||
self.builder.get_object("refresh_tab").released()
|
self.builder.get_object("refresh_tab").released()
|
||||||
|
|
||||||
|
wid, tid = None, None
|
||||||
|
tab = None
|
|
@ -46,11 +46,19 @@ class WindowMixin(TabMixin):
|
||||||
self.window.set_title(f"{app_name} ~ {dir}")
|
self.window.set_title(f"{app_name} ~ {dir}")
|
||||||
self.set_bottom_labels(tab)
|
self.set_bottom_labels(tab)
|
||||||
|
|
||||||
|
wid, tid = None, None
|
||||||
|
notebook = None
|
||||||
|
tab = None
|
||||||
|
dir = None
|
||||||
|
|
||||||
def set_path_text(self, wid, tid):
|
def set_path_text(self, wid, tid):
|
||||||
path_entry = self.builder.get_object("path_entry")
|
path_entry = self.builder.get_object("path_entry")
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
||||||
path_entry.set_text(tab.get_current_directory())
|
path_entry.set_text(tab.get_current_directory())
|
||||||
|
|
||||||
|
path_entry = None
|
||||||
|
tab = None
|
||||||
|
|
||||||
def grid_set_selected_items(self, icons_grid):
|
def grid_set_selected_items(self, icons_grid):
|
||||||
new_items = icons_grid.get_selected_items()
|
new_items = icons_grid.get_selected_items()
|
||||||
items_size = len(new_items)
|
items_size = len(new_items)
|
||||||
|
@ -122,6 +130,10 @@ class WindowMixin(TabMixin):
|
||||||
self.update_tab(tab_label, state.tab, state.icon_grid.get_store(), state.wid, state.tid)
|
self.update_tab(tab_label, state.tab, state.icon_grid.get_store(), state.wid, state.tid)
|
||||||
else:
|
else:
|
||||||
event_system.emit("open_files")
|
event_system.emit("open_files")
|
||||||
|
|
||||||
|
state = None
|
||||||
|
notebook = None
|
||||||
|
tab_label = None
|
||||||
except WindowException as e:
|
except WindowException as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
self.display_message(settings.theming.error_color, f"{repr(e)}")
|
self.display_message(settings.theming.error_color, f"{repr(e)}")
|
||||||
|
@ -164,6 +176,12 @@ class WindowMixin(TabMixin):
|
||||||
if target not in current:
|
if target not in current:
|
||||||
self.fm_controller.set_wid_and_tid(wid, tid)
|
self.fm_controller.set_wid_and_tid(wid, tid)
|
||||||
|
|
||||||
|
current = None
|
||||||
|
target = None
|
||||||
|
wid, tid = None, None
|
||||||
|
store = None
|
||||||
|
path_at_loc = None
|
||||||
|
|
||||||
|
|
||||||
def grid_on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
|
def grid_on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
|
||||||
if info == 80:
|
if info == 80:
|
||||||
|
@ -177,6 +195,10 @@ class WindowMixin(TabMixin):
|
||||||
if from_uri != dest:
|
if from_uri != dest:
|
||||||
event_system.emit("move_files", (uris, dest))
|
event_system.emit("move_files", (uris, dest))
|
||||||
|
|
||||||
|
Gtk.drag_finish(drag_context, True, False, time)
|
||||||
|
return
|
||||||
|
|
||||||
|
Gtk.drag_finish(drag_context, False, False, time)
|
||||||
|
|
||||||
def create_new_tab_notebook(self, widget=None, wid=None, path=None):
|
def create_new_tab_notebook(self, widget=None, wid=None, path=None):
|
||||||
self.create_tab(wid, None, path)
|
self.create_tab(wid, None, path)
|
|
@ -6,6 +6,7 @@ gi.require_version('Gtk', '3.0')
|
||||||
gi.require_version('Gdk', '3.0')
|
gi.require_version('Gdk', '3.0')
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from gi.repository import Gdk
|
from gi.repository import Gdk
|
||||||
|
from gi.repository import GLib
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from .mixins.ui.pane_mixin import PaneMixin
|
from .mixins.ui.pane_mixin import PaneMixin
|
||||||
|
@ -34,18 +35,20 @@ class UIMixin(PaneMixin, WindowMixin):
|
||||||
nickname = session["window"]["Nickname"]
|
nickname = session["window"]["Nickname"]
|
||||||
tabs = session["window"]["tabs"]
|
tabs = session["window"]["tabs"]
|
||||||
isHidden = True if session["window"]["isHidden"] == "True" else False
|
isHidden = True if session["window"]["isHidden"] == "True" else False
|
||||||
event_system.emit("load_files_view_state", (nickname, tabs))
|
event_system.emit_and_await("load_files_view_state", (nickname, tabs, isHidden))
|
||||||
|
|
||||||
|
|
||||||
@daemon_threaded
|
|
||||||
def _focus_last_visible_notebook(self, icon_grid):
|
def _focus_last_visible_notebook(self, icon_grid):
|
||||||
import time
|
import time
|
||||||
|
|
||||||
window = settings_manager.get_main_window()
|
window = settings_manager.get_main_window()
|
||||||
while not window.is_visible() and not window.get_realized():
|
while not window.is_visible() and not window.get_realized():
|
||||||
time.sleep(0.1)
|
time.sleep(0.2)
|
||||||
|
|
||||||
icon_grid.event(Gdk.Event().new(type = Gdk.EventType.BUTTON_RELEASE))
|
icon_grid.event(Gdk.Event().new(type = Gdk.EventType.BUTTON_RELEASE))
|
||||||
|
|
||||||
|
window = None
|
||||||
|
|
||||||
def _current_loading_process(self, session_json = None):
|
def _current_loading_process(self, session_json = None):
|
||||||
if session_json:
|
if session_json:
|
||||||
for j, value in enumerate(session_json):
|
for j, value in enumerate(session_json):
|
||||||
|
@ -77,7 +80,7 @@ class UIMixin(PaneMixin, WindowMixin):
|
||||||
|
|
||||||
scroll_win = notebook.get_children()[-1]
|
scroll_win = notebook.get_children()[-1]
|
||||||
icon_grid = scroll_win.get_children()[0]
|
icon_grid = scroll_win.get_children()[0]
|
||||||
self._focus_last_visible_notebook(icon_grid)
|
GLib.Thread("", self._focus_last_visible_notebook, icon_grid)
|
||||||
except UIMixinException as e:
|
except UIMixinException as e:
|
||||||
logger.info("\n: The saved session might be missing window data! :\nLocation: ~/.config/solarfm/session.json\nFix: Back it up and delete it to reset.\n")
|
logger.info("\n: The saved session might be missing window data! :\nLocation: ~/.config/solarfm/session.json\nFix: Back it up and delete it to reset.\n")
|
||||||
logger.debug(repr(e))
|
logger.debug(repr(e))
|
||||||
|
|
|
@ -16,10 +16,7 @@ class ContextMenuWidget(Gtk.Menu):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(ContextMenuWidget, self).__init__()
|
super(ContextMenuWidget, self).__init__()
|
||||||
|
|
||||||
self.builder = settings_manager.get_builder()
|
|
||||||
self._builder = Gtk.Builder()
|
self._builder = Gtk.Builder()
|
||||||
self._context_menu_data = settings_manager.get_context_menu_data()
|
|
||||||
self._window = settings_manager.get_main_window()
|
|
||||||
|
|
||||||
self._setup_styling()
|
self._setup_styling()
|
||||||
self._setup_signals()
|
self._setup_signals()
|
||||||
|
@ -32,24 +29,57 @@ class ContextMenuWidget(Gtk.Menu):
|
||||||
def _setup_signals(self):
|
def _setup_signals(self):
|
||||||
event_system.subscribe("show_context_menu", self.show_context_menu)
|
event_system.subscribe("show_context_menu", self.show_context_menu)
|
||||||
event_system.subscribe("hide_context_menu", self.hide_context_menu)
|
event_system.subscribe("hide_context_menu", self.hide_context_menu)
|
||||||
settings_manager.register_signals_to_builder([self,], self._builder)
|
settings_manager.register_signals_to_builder(self, self._builder)
|
||||||
|
|
||||||
def _load_widgets(self):
|
def _load_widgets(self):
|
||||||
|
self.builder = settings_manager.get_builder()
|
||||||
|
self._window = settings_manager.get_main_window()
|
||||||
|
self._context_menu_data = settings_manager.get_context_menu_data()
|
||||||
|
|
||||||
|
self.builder.expose_object("context_menu", self)
|
||||||
|
|
||||||
self.build_context_menu()
|
self.build_context_menu()
|
||||||
|
|
||||||
def _emit(self, menu_item, type):
|
def _emit(self, menu_item, type):
|
||||||
event_system.emit("do_action_from_menu_controls", type)
|
event_system.emit("do_action_from_menu_controls", type)
|
||||||
|
|
||||||
|
|
||||||
def make_submenu(self, name, data, keys):
|
def build_context_menu(self) -> None:
|
||||||
|
data = self._context_menu_data
|
||||||
|
plugins_entry = None
|
||||||
|
|
||||||
|
for key, value in data.items():
|
||||||
|
entry = self.make_menu_item(key, value)
|
||||||
|
self.append(entry)
|
||||||
|
if key == "Plugins":
|
||||||
|
plugins_entry = entry
|
||||||
|
|
||||||
|
self.attach_to_widget(self._window, None)
|
||||||
|
self.show_all()
|
||||||
|
|
||||||
|
if plugins_entry:
|
||||||
|
self.builder.expose_object("context_menu_plugins", plugins_entry.get_submenu())
|
||||||
|
|
||||||
|
def make_menu_item(self, label, data) -> Gtk.MenuItem:
|
||||||
|
if isinstance(data, dict):
|
||||||
|
return self.make_submenu(label, data)
|
||||||
|
elif isinstance(data, list):
|
||||||
|
entry = Gtk.ImageMenuItem(label)
|
||||||
|
icon = getattr(Gtk, f"{data[0]}")
|
||||||
|
entry.set_image( Gtk.Image(stock=icon) )
|
||||||
|
entry.set_always_show_image(True)
|
||||||
|
entry.connect("activate", self._emit, (data[1]))
|
||||||
|
return entry
|
||||||
|
|
||||||
|
def make_submenu(self, name, data):
|
||||||
menu = Gtk.Menu()
|
menu = Gtk.Menu()
|
||||||
menu_item = Gtk.MenuItem(name)
|
menu_item = Gtk.MenuItem(name)
|
||||||
|
|
||||||
for key in keys:
|
for key, value in data.items():
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
entry = self.make_menu_item(key, data[key])
|
entry = self.make_menu_item(key, value)
|
||||||
elif isinstance(data, list):
|
elif isinstance(data, list):
|
||||||
entry = self.make_menu_item(key, data)
|
entry = self.make_menu_item(key, value)
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -58,36 +88,9 @@ class ContextMenuWidget(Gtk.Menu):
|
||||||
menu_item.set_submenu(menu)
|
menu_item.set_submenu(menu)
|
||||||
return menu_item
|
return menu_item
|
||||||
|
|
||||||
def make_menu_item(self, name, data) -> Gtk.MenuItem:
|
|
||||||
if isinstance(data, dict):
|
|
||||||
return self.make_submenu(name, data, data.keys())
|
|
||||||
elif isinstance(data, list):
|
|
||||||
entry = Gtk.ImageMenuItem(name)
|
|
||||||
icon = getattr(Gtk, f"{data[0]}")
|
|
||||||
entry.set_image( Gtk.Image(stock=icon) )
|
|
||||||
entry.set_always_show_image(True)
|
|
||||||
entry.connect("activate", self._emit, (data[1]))
|
|
||||||
return entry
|
|
||||||
|
|
||||||
def build_context_menu(self) -> None:
|
def show_context_menu(self, widget = None, eve = None):
|
||||||
data = self._context_menu_data
|
|
||||||
dkeys = data.keys()
|
|
||||||
plugins_entry = None
|
|
||||||
|
|
||||||
for dkey in dkeys:
|
|
||||||
entry = self.make_menu_item(dkey, data[dkey])
|
|
||||||
self.append(entry)
|
|
||||||
if dkey == "Plugins":
|
|
||||||
plugins_entry = entry
|
|
||||||
|
|
||||||
self.attach_to_widget(self._window, None)
|
|
||||||
self.show_all()
|
|
||||||
self.builder.expose_object("context_menu", self)
|
|
||||||
if plugins_entry:
|
|
||||||
self.builder.expose_object("context_menu_plugins", plugins_entry.get_submenu())
|
|
||||||
|
|
||||||
def show_context_menu(self, widget=None, eve=None):
|
|
||||||
self.builder.get_object("context_menu").popup_at_pointer(None)
|
self.builder.get_object("context_menu").popup_at_pointer(None)
|
||||||
|
|
||||||
def hide_context_menu(self, widget=None, eve=None):
|
def hide_context_menu(self, widget = None, eve = None):
|
||||||
self.builder.get_object("context_menu").popdown()
|
self.builder.get_object("context_menu").popdown()
|
||||||
|
|
|
@ -39,11 +39,10 @@ class AboutWidget:
|
||||||
self.about_page = self._builder.get_object("about_page")
|
self.about_page = self._builder.get_object("about_page")
|
||||||
builder.expose_object(f"about_page", self.about_page)
|
builder.expose_object(f"about_page", self.about_page)
|
||||||
|
|
||||||
|
def show_about_page(self, widget = None, eve = None):
|
||||||
def show_about_page(self, widget=None, eve=None):
|
|
||||||
response = self.about_page.run()
|
response = self.about_page.run()
|
||||||
if response in [Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT]:
|
if response in [Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT]:
|
||||||
self.hide_about_page()
|
self.hide_about_page()
|
||||||
|
|
||||||
def hide_about_page(self, widget=None, eve=None):
|
def hide_about_page(self, widget = None, eve = None):
|
||||||
self.about_page.hide()
|
self.about_page.hide()
|
||||||
|
|
|
@ -50,6 +50,9 @@ class RenameWidget:
|
||||||
def show_rename_file_menu(self, widget=None, eve=None):
|
def show_rename_file_menu(self, widget=None, eve=None):
|
||||||
if widget:
|
if widget:
|
||||||
widget.grab_focus()
|
widget.grab_focus()
|
||||||
|
end_i = widget.get_text().rfind(".")
|
||||||
|
if end_i > 0:
|
||||||
|
widget.select_region(0, end_i)
|
||||||
|
|
||||||
response = self._rename_file_menu.run()
|
response = self._rename_file_menu.run()
|
||||||
if response == Gtk.ResponseType.CLOSE:
|
if response == Gtk.ResponseType.CLOSE:
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
# Python imports
|
# Python imports
|
||||||
|
|
||||||
# Lib imports
|
# Lib imports
|
||||||
import gi
|
|
||||||
gi.require_version('Gtk', '3.0')
|
|
||||||
from gi.repository import Gtk
|
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
|
from ...sfm_builder import SFMBuilder
|
||||||
from ...mixins.signals.file_action_signals_mixin import FileActionSignalsMixin
|
from ...mixins.signals.file_action_signals_mixin import FileActionSignalsMixin
|
||||||
from .window_mixin import WindowMixin
|
from .window_mixin import WindowMixin
|
||||||
|
|
||||||
|
@ -27,7 +25,8 @@ class FilesWidget(FileActionSignalsMixin, WindowMixin):
|
||||||
|
|
||||||
self.INDEX = self.ccount
|
self.INDEX = self.ccount
|
||||||
self.NAME = f"window_{self.INDEX}"
|
self.NAME = f"window_{self.INDEX}"
|
||||||
self.builder = Gtk.Builder()
|
self.builder = SFMBuilder()
|
||||||
|
|
||||||
self.files_view = None
|
self.files_view = None
|
||||||
self.fm_controller = None
|
self.fm_controller = None
|
||||||
|
|
||||||
|
@ -41,7 +40,7 @@ class FilesWidget(FileActionSignalsMixin, WindowMixin):
|
||||||
...
|
...
|
||||||
|
|
||||||
def _setup_signals(self):
|
def _setup_signals(self):
|
||||||
settings_manager.register_signals_to_builder([self,], self.builder)
|
settings_manager.register_signals_to_builder([self], self.builder)
|
||||||
|
|
||||||
def _subscribe_to_events(self):
|
def _subscribe_to_events(self):
|
||||||
event_system.subscribe("load_files_view_state", self._load_files_view_state)
|
event_system.subscribe("load_files_view_state", self._load_files_view_state)
|
||||||
|
@ -52,9 +51,10 @@ class FilesWidget(FileActionSignalsMixin, WindowMixin):
|
||||||
self.files_view = _builder.get_object(f"{self.NAME}")
|
self.files_view = _builder.get_object(f"{self.NAME}")
|
||||||
|
|
||||||
self.files_view.set_group_name("files_widget")
|
self.files_view.set_group_name("files_widget")
|
||||||
|
|
||||||
self.builder.expose_object(f"{self.NAME}", self.files_view)
|
self.builder.expose_object(f"{self.NAME}", self.files_view)
|
||||||
|
|
||||||
def _load_files_view_state(self, win_name = None, tabs = None):
|
def _load_files_view_state(self, win_name = None, tabs = None, isHidden = False):
|
||||||
if win_name == self.NAME:
|
if win_name == self.NAME:
|
||||||
if tabs:
|
if tabs:
|
||||||
for tab in tabs:
|
for tab in tabs:
|
||||||
|
@ -62,6 +62,9 @@ class FilesWidget(FileActionSignalsMixin, WindowMixin):
|
||||||
else:
|
else:
|
||||||
self.create_new_tab_notebook(None, self.INDEX, None)
|
self.create_new_tab_notebook(None, self.INDEX, None)
|
||||||
|
|
||||||
|
if isHidden:
|
||||||
|
self.files_view.hide()
|
||||||
|
|
||||||
def _get_files_view_icon_grid(self, win_index = None, tid = None):
|
def _get_files_view_icon_grid(self, win_index = None, tid = None):
|
||||||
if win_index == str(self.INDEX):
|
if win_index == str(self.INDEX):
|
||||||
return self.builder.get_object(f"{self.INDEX}|{tid}|icon_grid", use_gtk = False)
|
return self.builder.get_object(f"{self.INDEX}|{tid}|icon_grid", use_gtk = False)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import gi
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
|
from gi.repository import Gio
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from ...widgets.tab_header_widget import TabHeaderWidget
|
from ...widgets.tab_header_widget import TabHeaderWidget
|
||||||
|
@ -19,6 +20,17 @@ class GridMixin:
|
||||||
"""docstring for GridMixin"""
|
"""docstring for GridMixin"""
|
||||||
|
|
||||||
def load_store(self, tab, store, save_state = False, use_generator = False):
|
def load_store(self, tab, store, save_state = False, use_generator = False):
|
||||||
|
# dir = tab.get_current_directory()
|
||||||
|
# file = Gio.File.new_for_path(dir)
|
||||||
|
# dir_list = Gtk.DirectoryList.new("standard::*", file)
|
||||||
|
# store.set(dir_list)
|
||||||
|
|
||||||
|
# file = Gio.File.new_for_path(dir)
|
||||||
|
# for file in file.enumerate_children("standard::*", Gio.FILE_ATTRIBUTE_STANDARD_NAME, None):
|
||||||
|
# store.append(file)
|
||||||
|
|
||||||
|
# return
|
||||||
|
|
||||||
dir = tab.get_current_directory()
|
dir = tab.get_current_directory()
|
||||||
files = tab.get_files()
|
files = tab.get_files()
|
||||||
|
|
||||||
|
@ -26,60 +38,76 @@ class GridMixin:
|
||||||
store.append([None, file[0]])
|
store.append([None, file[0]])
|
||||||
|
|
||||||
Gtk.main_iteration()
|
Gtk.main_iteration()
|
||||||
if use_generator:
|
self.generate_icons(tab, store, dir, files)
|
||||||
# NOTE: tab > icon > _get_system_thumbnail_gtk_thread must not be used
|
# GLib.Thread("", self.generate_icons, tab, store, dir, files)
|
||||||
# as the attempted promotion back to gtk threading stalls the generator. (We're already in main gtk thread)
|
|
||||||
for i, icon in enumerate( self.create_icons_generator(tab, dir, files) ):
|
|
||||||
self.load_icon(i, store, icon)
|
|
||||||
else:
|
|
||||||
# for i, file in enumerate(files):
|
|
||||||
# self.create_icon(i, tab, store, dir, file[0])
|
|
||||||
try:
|
|
||||||
loop = asyncio.get_running_loop()
|
|
||||||
except RuntimeError:
|
|
||||||
loop = None
|
|
||||||
|
|
||||||
if loop and loop.is_running():
|
|
||||||
loop.create_task( self.create_icons(tab, store, dir, files) )
|
|
||||||
else:
|
|
||||||
asyncio.run( self.create_icons(tab, store, dir, files) )
|
|
||||||
|
|
||||||
# NOTE: Not likely called often from here but it could be useful
|
# NOTE: Not likely called often from here but it could be useful
|
||||||
if save_state and not trace_debug:
|
if save_state and not trace_debug:
|
||||||
self.fm_controller.save_state()
|
self.fm_controller.save_state()
|
||||||
|
|
||||||
async def create_icons(self, tab, store, dir, files):
|
dir = None
|
||||||
tasks = [self.update_store(i, store, dir, tab, file[0]) for i, file in enumerate(files)]
|
files = None
|
||||||
await asyncio.gather(*tasks)
|
|
||||||
|
|
||||||
async def load_icon(self, i, store, icon):
|
@daemon_threaded
|
||||||
self.update_store(i, store, icon)
|
def generate_icons(self, tab, store, dir, files):
|
||||||
|
for i, file in enumerate(files):
|
||||||
|
# GLib.Thread(f"{i}", self.make_and_load_icon, i, store, tab, dir, file[0])
|
||||||
|
self.make_and_load_icon( i, store, tab, dir, file[0])
|
||||||
|
|
||||||
async def update_store(self, i, store, dir, tab, file):
|
def update_store(self, i, store, icon):
|
||||||
icon = tab.create_icon(dir, file)
|
|
||||||
itr = store.get_iter(i)
|
itr = store.get_iter(i)
|
||||||
|
GLib.idle_add(self.insert_store, store, itr, icon)
|
||||||
|
itr = None
|
||||||
|
|
||||||
|
@daemon_threaded
|
||||||
|
def make_and_load_icon(self, i, store, tab, dir, file):
|
||||||
|
icon = tab.create_icon(dir, file)
|
||||||
|
self.update_store(i, store, icon)
|
||||||
|
icon = None
|
||||||
|
|
||||||
|
def get_icon(self, tab, dir, file):
|
||||||
|
tab.create_icon(dir, file)
|
||||||
|
|
||||||
|
|
||||||
|
# @daemon_threaded
|
||||||
|
# def generate_icons(self, tab, store, dir, files):
|
||||||
|
# try:
|
||||||
|
# loop = asyncio.get_running_loop()
|
||||||
|
# except RuntimeError:
|
||||||
|
# loop = None
|
||||||
|
|
||||||
|
# if loop and loop.is_running():
|
||||||
|
# loop = asyncio.get_event_loop()
|
||||||
|
# loop.create_task( self.create_icons(tab, store, dir, files) )
|
||||||
|
# else:
|
||||||
|
# asyncio.run( self.create_icons(tab, store, dir, files) )
|
||||||
|
|
||||||
|
# async def create_icons(self, tab, store, dir, files):
|
||||||
|
# icons = [self.get_icon(tab, dir, file[0]) for file in files]
|
||||||
|
# data = await asyncio.gather(*icons)
|
||||||
|
# tasks = [self.update_store(i, store, icon) for i, icon in enumerate(data)]
|
||||||
|
# asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
# async def update_store(self, i, store, icon):
|
||||||
|
# itr = store.get_iter(i)
|
||||||
|
# GLib.idle_add(self.insert_store, store, itr, icon)
|
||||||
|
|
||||||
|
# async def get_icon(self, tab, dir, file):
|
||||||
|
# return tab.create_icon(dir, file)
|
||||||
|
|
||||||
|
|
||||||
|
def insert_store(self, store, itr, icon):
|
||||||
store.set_value(itr, 0, icon)
|
store.set_value(itr, 0, icon)
|
||||||
|
|
||||||
def create_icons_generator(self, tab, dir, files):
|
# Note: If the function returns GLib.SOURCE_REMOVE or False it is automatically removed from the list of event sources and will not be called again.
|
||||||
for file in files:
|
return False
|
||||||
icon = tab.create_icon(dir, file[0])
|
|
||||||
yield icon
|
|
||||||
|
|
||||||
# @daemon_threaded
|
def do_ui_update(self):
|
||||||
# def create_icon(self, i, tab, store, dir, file):
|
Gtk.main_iteration()
|
||||||
# icon = tab.create_icon(dir, file)
|
return False
|
||||||
# GLib.idle_add(self.update_store, *(i, store, icon,))
|
|
||||||
#
|
|
||||||
# @daemon_threaded
|
|
||||||
# def load_icon(self, i, store, icon):
|
|
||||||
# GLib.idle_add(self.update_store, *(i, store, icon,))
|
|
||||||
#
|
|
||||||
# def update_store(self, i, store, icon):
|
|
||||||
# itr = store.get_iter(i)
|
|
||||||
# store.set_value(itr, 0, icon)
|
|
||||||
|
|
||||||
def create_tab_widget(self, tab):
|
def create_tab_widget(self):
|
||||||
return TabHeaderWidget(tab, self.close_tab)
|
return TabHeaderWidget(self.close_tab)
|
||||||
|
|
||||||
def create_scroll_and_store(self, tab, wid, use_tree_view = False):
|
def create_scroll_and_store(self, tab, wid, use_tree_view = False):
|
||||||
scroll = Gtk.ScrolledWindow()
|
scroll = Gtk.ScrolledWindow()
|
||||||
|
@ -137,3 +165,10 @@ class GridMixin:
|
||||||
tab_label = notebook.get_tab_label(obj).get_children()[0]
|
tab_label = notebook.get_tab_label(obj).get_children()[0]
|
||||||
|
|
||||||
return store, tab_label
|
return store, tab_label
|
||||||
|
|
||||||
|
def get_icon_grid_from_notebook(self, notebook, _name):
|
||||||
|
for obj in notebook.get_children():
|
||||||
|
icon_grid = obj.get_children()[0]
|
||||||
|
name = icon_grid.get_name()
|
||||||
|
if name == _name:
|
||||||
|
return icon_grid
|
||||||
|
|
|
@ -34,29 +34,46 @@ class TabMixin(GridMixin):
|
||||||
else:
|
else:
|
||||||
tab.set_path(path)
|
tab.set_path(path)
|
||||||
|
|
||||||
tab_widget = self.create_tab_widget(tab)
|
tab_widget = self.get_tab_widget(tab)
|
||||||
scroll, store = self.create_scroll_and_store(tab, wid)
|
scroll, store = self.create_scroll_and_store(tab, wid)
|
||||||
index = notebook.append_page(scroll, tab_widget)
|
index = notebook.append_page(scroll, tab_widget)
|
||||||
notebook.set_tab_detachable(scroll, True)
|
notebook.set_tab_detachable(scroll, True)
|
||||||
|
notebook.set_tab_reorderable(scroll, True)
|
||||||
|
|
||||||
self.fm_controller.set_wid_and_tid(wid, tab.get_id())
|
self.fm_controller.set_wid_and_tid(wid, tab.get_id())
|
||||||
event_system.emit("go_to_path", (tab.get_current_directory(),)) # NOTE: Not efficent if I understand how
|
|
||||||
# path_entry.set_text(tab.get_current_directory())
|
# path_entry.set_text(tab.get_current_directory())
|
||||||
|
event_system.emit("go_to_path", (tab.get_current_directory(),)) # NOTE: Not efficent if I understand how
|
||||||
notebook.show_all()
|
notebook.show_all()
|
||||||
notebook.set_current_page(index)
|
notebook.set_current_page(index)
|
||||||
|
|
||||||
ctx = notebook.get_style_context()
|
ctx = notebook.get_style_context()
|
||||||
ctx.add_class("notebook-unselected-focus")
|
ctx.add_class("notebook-unselected-focus")
|
||||||
notebook.set_tab_reorderable(scroll, True)
|
|
||||||
self.load_store(tab, store)
|
self.load_store(tab, store)
|
||||||
# self.set_window_title()
|
|
||||||
event_system.emit("set_window_title", (tab.get_current_directory(),))
|
event_system.emit("set_window_title", (tab.get_current_directory(),))
|
||||||
self.set_file_watcher(tab)
|
self.set_file_watcher(tab)
|
||||||
|
|
||||||
|
tab_widget = None
|
||||||
|
scroll, store = None, None
|
||||||
|
index = None
|
||||||
|
notebook = None
|
||||||
|
# path_entry = None
|
||||||
|
tab = None
|
||||||
|
ctx = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_tab_widget(self, tab):
|
||||||
|
tab_widget = self.create_tab_widget()
|
||||||
|
tab_widget.tab_id = tab.get_id()
|
||||||
|
|
||||||
|
tab_widget.label.set_label(f"{tab.get_end_of_path()}")
|
||||||
|
tab_widget.label.set_width_chars(len(tab.get_end_of_path()))
|
||||||
|
|
||||||
|
return tab_widget
|
||||||
|
|
||||||
def close_tab(self, button, eve = None):
|
def close_tab(self, button, eve = None):
|
||||||
notebook = button.get_parent().get_parent()
|
notebook = button.get_parent().get_parent()
|
||||||
if notebook.get_n_pages() == 1:
|
if notebook.get_n_pages() == 1:
|
||||||
|
notebook = None
|
||||||
return
|
return
|
||||||
|
|
||||||
tab_box = button.get_parent()
|
tab_box = button.get_parent()
|
||||||
|
@ -74,28 +91,37 @@ class TabMixin(GridMixin):
|
||||||
self.builder.dereference_object(f"{wid}|{tid}|icon_grid")
|
self.builder.dereference_object(f"{wid}|{tid}|icon_grid")
|
||||||
self.builder.dereference_object(f"{wid}|{tid}")
|
self.builder.dereference_object(f"{wid}|{tid}")
|
||||||
|
|
||||||
|
iter = store.get_iter_first()
|
||||||
|
while iter:
|
||||||
|
next_iter = store.iter_next(iter)
|
||||||
|
store.unref_node(iter)
|
||||||
|
iter = next_iter
|
||||||
|
|
||||||
store.clear()
|
store.clear()
|
||||||
# store.run_dispose()
|
store.run_dispose()
|
||||||
icon_grid.destroy()
|
|
||||||
# icon_grid.run_dispose()
|
|
||||||
scroll.destroy()
|
|
||||||
#scroll.run_dispose()
|
|
||||||
tab_box.destroy()
|
|
||||||
#tab_box.run_dispose()
|
|
||||||
|
|
||||||
del store
|
icon_grid.set_model(None)
|
||||||
del icon_grid
|
icon_grid.run_dispose()
|
||||||
del scroll
|
scroll.run_dispose()
|
||||||
del tab_box
|
tab_box.run_dispose()
|
||||||
del watcher
|
|
||||||
del tab
|
iter = None
|
||||||
|
wid, tid = None, None
|
||||||
|
store = None
|
||||||
|
icon_grid = None
|
||||||
|
scroll = None
|
||||||
|
tab_box = None
|
||||||
|
watcher = None
|
||||||
|
tab = None
|
||||||
|
notebook = None
|
||||||
|
|
||||||
gc.collect()
|
|
||||||
if not settings_manager.is_trace_debug():
|
if not settings_manager.is_trace_debug():
|
||||||
self.fm_controller.save_state()
|
self.fm_controller.save_state()
|
||||||
|
|
||||||
self.set_window_title()
|
self.set_window_title()
|
||||||
|
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
# NOTE: Not actually getting called even tho set in the glade file...
|
# NOTE: Not actually getting called even tho set in the glade file...
|
||||||
def on_tab_dnded(self, notebook, page, x, y):
|
def on_tab_dnded(self, notebook, page, x, y):
|
||||||
...
|
...
|
||||||
|
@ -117,15 +143,22 @@ class TabMixin(GridMixin):
|
||||||
if not settings_manager.is_trace_debug():
|
if not settings_manager.is_trace_debug():
|
||||||
self.fm_controller.save_state()
|
self.fm_controller.save_state()
|
||||||
|
|
||||||
|
wid, tid = None, None
|
||||||
|
window = None
|
||||||
|
tab = None
|
||||||
|
|
||||||
|
|
||||||
def on_tab_switch_update(self, notebook, content = None, index = None):
|
def on_tab_switch_update(self, notebook, content = None, index = None):
|
||||||
self.selected_files.clear()
|
self.selected_files.clear()
|
||||||
wid, tid = content.get_children()[0].get_name().split("|")
|
wid, tid = content.get_children()[0].tab.get_name().split("|")
|
||||||
self.fm_controller.set_wid_and_tid(wid, tid)
|
self.fm_controller.set_wid_and_tid(wid, tid)
|
||||||
self.set_path_text(wid, tid)
|
self.set_path_text(wid, tid)
|
||||||
self.set_window_title()
|
self.set_window_title()
|
||||||
|
|
||||||
|
wid, tid = None, None
|
||||||
|
|
||||||
def get_id_from_tab_box(self, tab_box):
|
def get_id_from_tab_box(self, tab_box):
|
||||||
return tab_box.get_children()[2].get_text()
|
return tab_box.tab.get_id()
|
||||||
|
|
||||||
def get_tab_label(self, notebook, icon_grid):
|
def get_tab_label(self, notebook, icon_grid):
|
||||||
return notebook.get_tab_label(icon_grid.get_parent()).get_children()[0]
|
return notebook.get_tab_label(icon_grid.get_parent()).get_children()[0]
|
||||||
|
@ -141,6 +174,8 @@ class TabMixin(GridMixin):
|
||||||
state.tab.load_directory()
|
state.tab.load_directory()
|
||||||
self.load_store(state.tab, state.store)
|
self.load_store(state.tab, state.store)
|
||||||
|
|
||||||
|
state = None
|
||||||
|
|
||||||
def update_tab(self, tab_label, tab, store, wid, tid):
|
def update_tab(self, tab_label, tab, store, wid, tid):
|
||||||
self.load_store(tab, store)
|
self.load_store(tab, store)
|
||||||
self.set_path_text(wid, tid)
|
self.set_path_text(wid, tid)
|
||||||
|
@ -181,16 +216,38 @@ class TabMixin(GridMixin):
|
||||||
if isinstance(focused_obj, Gtk.Entry):
|
if isinstance(focused_obj, Gtk.Entry):
|
||||||
self.process_path_menu(widget, tab, dir)
|
self.process_path_menu(widget, tab, dir)
|
||||||
|
|
||||||
|
action = None
|
||||||
|
store = None
|
||||||
|
|
||||||
if path.endswith(".") or path == dir:
|
if path.endswith(".") or path == dir:
|
||||||
|
tab_label = None
|
||||||
|
notebook = None
|
||||||
|
wid, tid = None, None
|
||||||
|
path = None
|
||||||
|
tab = None
|
||||||
return
|
return
|
||||||
|
|
||||||
if not tab.set_path(path):
|
if not tab.set_path(path):
|
||||||
|
tab_label = None
|
||||||
|
notebook = None
|
||||||
|
wid, tid = None, None
|
||||||
|
path = None
|
||||||
|
tab = None
|
||||||
return
|
return
|
||||||
|
|
||||||
icon_grid = self.get_icon_grid_from_notebook(notebook, f"{wid}|{tid}")
|
icon_grid = self.get_icon_grid_from_notebook(notebook, f"{wid}|{tid}")
|
||||||
icon_grid.clear_and_set_new_store()
|
icon_grid.clear_and_set_new_store()
|
||||||
self.update_tab(tab_label, tab, store, wid, tid)
|
self.update_tab(tab_label, tab, store, wid, tid)
|
||||||
|
|
||||||
|
action = None
|
||||||
|
wid, tid = None, None
|
||||||
|
notebook = None
|
||||||
|
store, tab_label = None, None
|
||||||
|
path = None
|
||||||
|
tab = None
|
||||||
|
icon_grid = None
|
||||||
|
|
||||||
|
|
||||||
def process_path_menu(self, gtk_entry, tab, dir):
|
def process_path_menu(self, gtk_entry, tab, dir):
|
||||||
path_menu_buttons = self.builder.get_object("path_menu_buttons")
|
path_menu_buttons = self.builder.get_object("path_menu_buttons")
|
||||||
query = gtk_entry.get_text().replace(dir, "")
|
query = gtk_entry.get_text().replace(dir, "")
|
||||||
|
@ -207,6 +264,10 @@ class TabMixin(GridMixin):
|
||||||
path_menu_buttons.add(button)
|
path_menu_buttons.add(button)
|
||||||
show_path_menu = True
|
show_path_menu = True
|
||||||
|
|
||||||
|
path_menu_buttons = None
|
||||||
|
query = None
|
||||||
|
files = None
|
||||||
|
|
||||||
if not show_path_menu:
|
if not show_path_menu:
|
||||||
event_system.emit("hide_path_menu")
|
event_system.emit("hide_path_menu")
|
||||||
else:
|
else:
|
||||||
|
@ -225,6 +286,8 @@ class TabMixin(GridMixin):
|
||||||
button.grab_focus()
|
button.grab_focus()
|
||||||
button.clicked()
|
button.clicked()
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def set_path_entry(self, button = None, eve = None):
|
def set_path_entry(self, button = None, eve = None):
|
||||||
self.path_auto_filled = True
|
self.path_auto_filled = True
|
||||||
state = self.get_current_state()
|
state = self.get_current_state()
|
||||||
|
@ -236,9 +299,16 @@ class TabMixin(GridMixin):
|
||||||
path_entry.set_position(-1)
|
path_entry.set_position(-1)
|
||||||
event_system.emit("hide_path_menu")
|
event_system.emit("hide_path_menu")
|
||||||
|
|
||||||
|
state = None
|
||||||
|
path = None
|
||||||
|
path_entry = None
|
||||||
|
|
||||||
def show_hide_hidden_files(self):
|
def show_hide_hidden_files(self):
|
||||||
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
||||||
tab.set_hiding_hidden(not tab.is_hiding_hidden())
|
tab.set_hiding_hidden(not tab.is_hiding_hidden())
|
||||||
tab.load_directory()
|
tab.load_directory()
|
||||||
self.builder.get_object("refresh_tab").released()
|
self.builder.get_object("refresh_tab").released()
|
||||||
|
|
||||||
|
wid, tid = None, None
|
||||||
|
tab = None
|
||||||
|
|
|
@ -42,10 +42,17 @@ class WindowMixin(TabMixin):
|
||||||
event_system.emit("set_window_title", (dir,))
|
event_system.emit("set_window_title", (dir,))
|
||||||
self.set_bottom_labels(tab)
|
self.set_bottom_labels(tab)
|
||||||
|
|
||||||
|
wid, tid = None, None
|
||||||
|
notebook = None
|
||||||
|
tab = None
|
||||||
|
dir = None
|
||||||
|
|
||||||
def set_path_text(self, wid, tid):
|
def set_path_text(self, wid, tid):
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
||||||
event_system.emit("go_to_path", (tab.get_current_directory(),))
|
event_system.emit("go_to_path", (tab.get_current_directory(),))
|
||||||
|
|
||||||
|
tab = None
|
||||||
|
|
||||||
def grid_set_selected_items(self, icons_grid):
|
def grid_set_selected_items(self, icons_grid):
|
||||||
new_items = icons_grid.get_selected_items()
|
new_items = icons_grid.get_selected_items()
|
||||||
items_size = len(new_items)
|
items_size = len(new_items)
|
||||||
|
@ -160,6 +167,12 @@ class WindowMixin(TabMixin):
|
||||||
if target not in current:
|
if target not in current:
|
||||||
self.fm_controller.set_wid_and_tid(wid, tid)
|
self.fm_controller.set_wid_and_tid(wid, tid)
|
||||||
|
|
||||||
|
current = None
|
||||||
|
target = None
|
||||||
|
wid, tid = None, None
|
||||||
|
store = None
|
||||||
|
path_at_loc = None
|
||||||
|
|
||||||
|
|
||||||
def grid_on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
|
def grid_on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
|
||||||
if info == 80:
|
if info == 80:
|
||||||
|
@ -173,6 +186,10 @@ class WindowMixin(TabMixin):
|
||||||
if from_uri != dest:
|
if from_uri != dest:
|
||||||
event_system.emit("move_files", (uris, dest))
|
event_system.emit("move_files", (uris, dest))
|
||||||
|
|
||||||
|
Gtk.drag_finish(drag_context, True, False, time)
|
||||||
|
return
|
||||||
|
|
||||||
|
Gtk.drag_finish(drag_context, False, False, time)
|
||||||
|
|
||||||
def create_new_tab_notebook(self, widget=None, wid=None, path=None):
|
def create_new_tab_notebook(self, widget=None, wid=None, path=None):
|
||||||
self.create_tab(wid, None, path)
|
self.create_tab(wid, None, path)
|
||||||
|
|
|
@ -75,6 +75,20 @@ class IconGridWidget(Gtk.IconView):
|
||||||
return self.get_model()
|
return self.get_model()
|
||||||
|
|
||||||
def clear_and_set_new_store(self):
|
def clear_and_set_new_store(self):
|
||||||
|
store = self.get_model()
|
||||||
|
if store:
|
||||||
|
iter = store.get_iter_first()
|
||||||
|
while iter:
|
||||||
|
next_iter = store.iter_next(iter)
|
||||||
|
store.unref_node(iter)
|
||||||
|
iter = next_iter
|
||||||
|
|
||||||
|
store.clear()
|
||||||
|
store.run_dispose()
|
||||||
|
store = None
|
||||||
|
|
||||||
self.set_model(None)
|
self.set_model(None)
|
||||||
store = Gtk.ListStore(GdkPixbuf.Pixbuf or GdkPixbuf.PixbufAnimation or None, str or None)
|
store = Gtk.ListStore(GdkPixbuf.Pixbuf or GdkPixbuf.PixbufAnimation or None, str or None)
|
||||||
|
# store = Gtk.ListStore(Gtk.DirectoryList)
|
||||||
self.set_model(store)
|
self.set_model(store)
|
||||||
|
store = None
|
||||||
|
|
|
@ -59,7 +59,7 @@ class IconTreeWidget(Gtk.TreeView):
|
||||||
name = Gtk.CellRendererText()
|
name = Gtk.CellRendererText()
|
||||||
selec = self.get_selection()
|
selec = self.get_selection()
|
||||||
|
|
||||||
self.set_model(store)
|
self.set_model(self._store)
|
||||||
selec.set_mode(3)
|
selec.set_mode(3)
|
||||||
|
|
||||||
column.pack_start(icon, False)
|
column.pack_start(icon, False)
|
||||||
|
|
|
@ -65,19 +65,20 @@ class IOWidget(Gtk.Box):
|
||||||
logger.info(f"Canceling: [{self._action}] of {self._basename} ...")
|
logger.info(f"Canceling: [{self._action}] of {self._basename} ...")
|
||||||
eve.cancel()
|
eve.cancel()
|
||||||
|
|
||||||
def update_progress(self, current, total, eve=None):
|
def update_progress(self, current, total, eve = None):
|
||||||
self.progress.set_fraction(current/total)
|
self.progress.set_fraction(current/total)
|
||||||
|
|
||||||
def finish_callback(self, file, task=None, eve=None):
|
def finish_callback(self, file, task = None, eve = None):
|
||||||
|
if task.had_error():
|
||||||
|
logger.info(f"{self._action} of {self._basename} cancelled/failed...")
|
||||||
|
return
|
||||||
|
|
||||||
if self._action == "move" or self._action == "rename":
|
if self._action == "move" or self._action == "rename":
|
||||||
status = self._file.move_finish(task)
|
status = self._file.move_finish(task)
|
||||||
if self._action == "copy":
|
if self._action == "copy":
|
||||||
status = self._file.copy_finish(task)
|
status = self._file.copy_finish(task)
|
||||||
|
|
||||||
if status:
|
|
||||||
self.delete_self()
|
self.delete_self()
|
||||||
else:
|
|
||||||
logger.info(f"{self._action} of {self._basename} failed...")
|
|
||||||
|
|
||||||
def delete_self(self, widget=None, eve=None):
|
def delete_self(self, widget = None, eve = None):
|
||||||
self.get_parent().remove(self)
|
self.get_parent().remove(self)
|
|
@ -61,7 +61,6 @@ class MessagePopupWidget(Gtk.Popover):
|
||||||
scroll_window.set_hexpand(True)
|
scroll_window.set_hexpand(True)
|
||||||
vbox.set_orientation(Gtk.Orientation.VERTICAL)
|
vbox.set_orientation(Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
self.builder.expose_object(f"message_popup_widget", self)
|
|
||||||
self.builder.expose_object(f"message_text_view", message_text_view)
|
self.builder.expose_object(f"message_text_view", message_text_view)
|
||||||
|
|
||||||
scroll_window.add(message_text_view)
|
scroll_window.add(message_text_view)
|
||||||
|
@ -103,7 +102,7 @@ class MessagePopupWidget(Gtk.Popover):
|
||||||
self.popup()
|
self.popup()
|
||||||
self.hide_message_timeout(seconds)
|
self.hide_message_timeout(seconds)
|
||||||
|
|
||||||
@threaded
|
@daemon_threaded
|
||||||
def hide_message_timeout(self, seconds=3):
|
def hide_message_timeout(self, seconds=3):
|
||||||
time.sleep(seconds)
|
time.sleep(seconds)
|
||||||
GLib.idle_add(event_system.emit, ("hide_messages_popup"))
|
GLib.idle_add(event_system.emit, ("hide_messages_popup"))
|
||||||
|
|
|
@ -29,16 +29,16 @@ class PathMenuPopupWidget(Gtk.Popover):
|
||||||
self.set_relative_to(path_entry)
|
self.set_relative_to(path_entry)
|
||||||
self.set_modal(False)
|
self.set_modal(False)
|
||||||
self.set_position(Gtk.PositionType.BOTTOM)
|
self.set_position(Gtk.PositionType.BOTTOM)
|
||||||
self.set_size_request(240, 420)
|
self.set_size_request(480, 420)
|
||||||
|
|
||||||
def _setup_signals(self):
|
def _setup_signals(self):
|
||||||
event_system.subscribe("show_path_menu", self.show_path_menu)
|
event_system.subscribe("show_path_menu", self.show_path_menu)
|
||||||
event_system.subscribe("hide_path_menu", self.hide_path_menu)
|
event_system.subscribe("hide_path_menu", self.hide_path_menu)
|
||||||
|
|
||||||
def _load_widgets(self):
|
def _load_widgets(self):
|
||||||
path_menu_buttons = Gtk.ButtonBox()
|
|
||||||
scroll_window = Gtk.ScrolledWindow()
|
scroll_window = Gtk.ScrolledWindow()
|
||||||
view_port = Gtk.Viewport()
|
view_port = Gtk.Viewport()
|
||||||
|
path_menu_buttons = Gtk.Box()
|
||||||
|
|
||||||
scroll_window.set_vexpand(True)
|
scroll_window.set_vexpand(True)
|
||||||
scroll_window.set_hexpand(True)
|
scroll_window.set_hexpand(True)
|
||||||
|
@ -47,12 +47,13 @@ class PathMenuPopupWidget(Gtk.Popover):
|
||||||
self.builder.expose_object(f"path_menu_buttons", path_menu_buttons)
|
self.builder.expose_object(f"path_menu_buttons", path_menu_buttons)
|
||||||
view_port.add(path_menu_buttons)
|
view_port.add(path_menu_buttons)
|
||||||
scroll_window.add(view_port)
|
scroll_window.add(view_port)
|
||||||
scroll_window.show_all()
|
|
||||||
self.add(scroll_window)
|
self.add(scroll_window)
|
||||||
|
|
||||||
|
scroll_window.show_all()
|
||||||
|
|
||||||
def show_path_menu(self, widget=None, eve=None):
|
|
||||||
|
def show_path_menu(self, widget = None, eve = None):
|
||||||
self.popup()
|
self.popup()
|
||||||
|
|
||||||
def hide_path_menu(self, widget=None, eve=None):
|
def hide_path_menu(self, widget = None, eve = None):
|
||||||
self.popdown()
|
self.popdown()
|
|
@ -9,14 +9,12 @@ from gi.repository import Gtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TabHeaderWidget(Gtk.Box):
|
class TabHeaderWidget(Gtk.Box):
|
||||||
"""docstring for TabHeaderWidget"""
|
"""docstring for TabHeaderWidget"""
|
||||||
|
|
||||||
def __init__(self, tab, close_tab):
|
def __init__(self, close_tab):
|
||||||
super(TabHeaderWidget, self).__init__()
|
super(TabHeaderWidget, self).__init__()
|
||||||
|
|
||||||
self._tab = tab
|
|
||||||
self._close_tab = close_tab # NOTE: Close method in tab_mixin
|
self._close_tab = close_tab # NOTE: Close method in tab_mixin
|
||||||
|
|
||||||
self._setup_styling()
|
self._setup_styling()
|
||||||
|
@ -32,25 +30,19 @@ class TabHeaderWidget(Gtk.Box):
|
||||||
...
|
...
|
||||||
|
|
||||||
def _load_widgets(self):
|
def _load_widgets(self):
|
||||||
label = Gtk.Label()
|
self.label = Gtk.Label()
|
||||||
tid = Gtk.Label()
|
|
||||||
close = Gtk.Button()
|
close = Gtk.Button()
|
||||||
icon = Gtk.Image(stock=Gtk.STOCK_CLOSE)
|
icon = Gtk.Image(stock=Gtk.STOCK_CLOSE)
|
||||||
|
|
||||||
label.set_label(f"{self._tab.get_end_of_path()}")
|
self.label.set_xalign(0.0)
|
||||||
label.set_width_chars(len(self._tab.get_end_of_path()))
|
self.label.set_margin_left(25)
|
||||||
label.set_xalign(0.0)
|
self.label.set_margin_right(25)
|
||||||
label.set_margin_left(25)
|
self.label.set_hexpand(True)
|
||||||
label.set_margin_right(25)
|
|
||||||
label.set_hexpand(True)
|
|
||||||
tid.set_label(f"{self._tab.get_id()}")
|
|
||||||
|
|
||||||
close.connect("released", self._close_tab)
|
close.connect("released", self._close_tab)
|
||||||
|
|
||||||
close.add(icon)
|
close.add(icon)
|
||||||
self.add(label)
|
self.add(self.label)
|
||||||
self.add(close)
|
self.add(close)
|
||||||
self.add(tid)
|
|
||||||
|
|
||||||
self.show_all()
|
self.show_all()
|
||||||
tid.hide()
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# Python imports
|
# Python imports
|
||||||
import time
|
|
||||||
import signal
|
import signal
|
||||||
|
|
||||||
# Lib imports
|
# Lib imports
|
||||||
|
@ -10,6 +9,7 @@ gi.require_version('Gdk', '3.0')
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from gi.repository import Gdk
|
from gi.repository import Gdk
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
|
from gi.repository import GObject
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from core.controller import Controller
|
from core.controller import Controller
|
||||||
|
@ -24,18 +24,20 @@ class Window(Gtk.ApplicationWindow):
|
||||||
"""docstring for Window."""
|
"""docstring for Window."""
|
||||||
|
|
||||||
def __init__(self, args, unknownargs):
|
def __init__(self, args, unknownargs):
|
||||||
super(Window, self).__init__()
|
GObject.threads_init()
|
||||||
|
|
||||||
self._controller = None
|
super(Window, self).__init__()
|
||||||
settings_manager.set_main_window(self)
|
settings_manager.set_main_window(self)
|
||||||
|
|
||||||
self._set_window_data()
|
self._controller = None
|
||||||
|
|
||||||
self._setup_styling()
|
self._setup_styling()
|
||||||
self._setup_signals()
|
self._setup_signals()
|
||||||
self._subscribe_to_events()
|
self._subscribe_to_events()
|
||||||
|
|
||||||
self._load_widgets(args, unknownargs)
|
self._load_widgets(args, unknownargs)
|
||||||
|
|
||||||
|
self._set_window_data()
|
||||||
|
self._set_size_constraints()
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,6 +68,18 @@ class Window(Gtk.ApplicationWindow):
|
||||||
|
|
||||||
self.add( self._controller.get_core_widget() )
|
self.add( self._controller.get_core_widget() )
|
||||||
|
|
||||||
|
def _set_size_constraints(self):
|
||||||
|
_window_x = settings.config.main_window_x
|
||||||
|
_window_y = settings.config.main_window_y
|
||||||
|
_min_width = settings.config.main_window_min_width
|
||||||
|
_min_height = settings.config.main_window_min_height
|
||||||
|
_width = settings.config.main_window_width
|
||||||
|
_height = settings.config.main_window_height
|
||||||
|
|
||||||
|
self.move(_window_x, _window_y - 28)
|
||||||
|
self.set_size_request(_min_width, _min_height)
|
||||||
|
self.set_default_size(_width, _height)
|
||||||
|
|
||||||
def _set_window_data(self) -> None:
|
def _set_window_data(self) -> None:
|
||||||
screen = self.get_screen()
|
screen = self.get_screen()
|
||||||
visual = screen.get_rgba_visual()
|
visual = screen.get_rgba_visual()
|
||||||
|
@ -73,7 +87,7 @@ class Window(Gtk.ApplicationWindow):
|
||||||
if visual != None and screen.is_composited():
|
if visual != None and screen.is_composited():
|
||||||
self.set_visual(visual)
|
self.set_visual(visual)
|
||||||
self.set_app_paintable(True)
|
self.set_app_paintable(True)
|
||||||
self.connect("draw", self._area_draw)
|
# self.connect("draw", self._area_draw)
|
||||||
|
|
||||||
# bind css file
|
# bind css file
|
||||||
cssProvider = Gtk.CssProvider()
|
cssProvider = Gtk.CssProvider()
|
||||||
|
@ -94,5 +108,18 @@ class Window(Gtk.ApplicationWindow):
|
||||||
|
|
||||||
def _tear_down(self, widget = None, eve = None):
|
def _tear_down(self, widget = None, eve = None):
|
||||||
event_system.emit("shutting_down")
|
event_system.emit("shutting_down")
|
||||||
|
|
||||||
|
size = self.get_size()
|
||||||
|
pos = self.get_position()
|
||||||
|
|
||||||
|
settings_manager.set_main_window_width(size.width)
|
||||||
|
settings_manager.set_main_window_height(size.height)
|
||||||
|
settings_manager.set_main_window_x(pos.root_x)
|
||||||
|
settings_manager.set_main_window_y(pos.root_y)
|
||||||
|
settings_manager.save_settings()
|
||||||
|
|
||||||
settings_manager.clear_pid()
|
settings_manager.clear_pid()
|
||||||
Gtk.main_quit()
|
Gtk.main_quit()
|
||||||
|
|
||||||
|
def main(self):
|
||||||
|
Gtk.main()
|
||||||
|
|
|
@ -15,7 +15,7 @@ class ManifestProcessorException(Exception):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots = True)
|
||||||
class PluginInfo:
|
class PluginInfo:
|
||||||
path: str = None
|
path: str = None
|
||||||
name: str = None
|
name: str = None
|
||||||
|
@ -24,23 +24,28 @@ class PluginInfo:
|
||||||
support: str = None
|
support: str = None
|
||||||
requests:{} = None
|
requests:{} = None
|
||||||
reference: type = None
|
reference: type = None
|
||||||
|
pre_launch: bool = False
|
||||||
|
|
||||||
|
|
||||||
class ManifestProcessor:
|
class ManifestProcessor:
|
||||||
def __init__(self, path, builder):
|
def __init__(self, path, builder):
|
||||||
manifest = join(path, "manifest.json")
|
manifest_pth = join(path, "manifest.json")
|
||||||
if not os.path.exists(manifest):
|
if not os.path.exists(manifest_pth):
|
||||||
raise ManifestProcessorException("Invalid Plugin Structure: Plugin doesn't have 'manifest.json'. Aboarting load...")
|
raise ManifestProcessorException("Invalid Plugin Structure: Plugin doesn't have 'manifest.json'. Aboarting load...")
|
||||||
|
|
||||||
self._path = path
|
self._path = path
|
||||||
self._builder = builder
|
self._builder = builder
|
||||||
with open(manifest) as f:
|
with open(manifest_pth) as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
self._manifest = data["manifest"]
|
self._manifest = data["manifest"]
|
||||||
self._plugin = self.collect_info()
|
self._plugin = self.collect_info()
|
||||||
|
|
||||||
|
def is_pre_launch(self) -> bool:
|
||||||
|
return self._plugin.pre_launch
|
||||||
|
|
||||||
def collect_info(self) -> PluginInfo:
|
def collect_info(self) -> PluginInfo:
|
||||||
plugin = PluginInfo()
|
plugin = PluginInfo()
|
||||||
|
|
||||||
plugin.path = self._path
|
plugin.path = self._path
|
||||||
plugin.name = self._manifest["name"]
|
plugin.name = self._manifest["name"]
|
||||||
plugin.author = self._manifest["author"]
|
plugin.author = self._manifest["author"]
|
||||||
|
@ -48,14 +53,16 @@ class ManifestProcessor:
|
||||||
plugin.support = self._manifest["support"]
|
plugin.support = self._manifest["support"]
|
||||||
plugin.requests = self._manifest["requests"]
|
plugin.requests = self._manifest["requests"]
|
||||||
|
|
||||||
|
if "pre_launch" in self._manifest.keys():
|
||||||
|
plugin.pre_launch = True if self._manifest["pre_launch"] == "true" else False
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
|
|
||||||
def get_loading_data(self):
|
def get_loading_data(self):
|
||||||
loading_data = {}
|
loading_data = {}
|
||||||
requests = self._plugin.requests
|
requests = self._plugin.requests
|
||||||
keys = requests.keys()
|
|
||||||
|
|
||||||
if "ui_target" in keys:
|
if "ui_target" in requests:
|
||||||
if requests["ui_target"] in [
|
if requests["ui_target"] in [
|
||||||
"none", "other", "main_Window", "main_menu_bar",
|
"none", "other", "main_Window", "main_menu_bar",
|
||||||
"main_menu_bttn_box_bar", "path_menu_bar", "plugin_control_list",
|
"main_menu_bttn_box_bar", "path_menu_bar", "plugin_control_list",
|
||||||
|
@ -63,7 +70,7 @@ class ManifestProcessor:
|
||||||
"window_2", "window_3", "window_4"
|
"window_2", "window_3", "window_4"
|
||||||
]:
|
]:
|
||||||
if requests["ui_target"] == "other":
|
if requests["ui_target"] == "other":
|
||||||
if "ui_target_id" in keys:
|
if "ui_target_id" in requests:
|
||||||
loading_data["ui_target"] = self._builder.get_object(requests["ui_target_id"])
|
loading_data["ui_target"] = self._builder.get_object(requests["ui_target_id"])
|
||||||
if loading_data["ui_target"] == None:
|
if loading_data["ui_target"] == None:
|
||||||
raise ManifestProcessorException('Invalid "ui_target_id" given in requests. Must have one if setting "ui_target" to "other"...')
|
raise ManifestProcessorException('Invalid "ui_target_id" given in requests. Must have one if setting "ui_target" to "other"...')
|
||||||
|
@ -74,11 +81,11 @@ class ManifestProcessor:
|
||||||
else:
|
else:
|
||||||
raise ManifestProcessorException('Unknown "ui_target" given in requests.')
|
raise ManifestProcessorException('Unknown "ui_target" given in requests.')
|
||||||
|
|
||||||
if "pass_fm_events" in keys:
|
if "pass_fm_events" in requests:
|
||||||
if requests["pass_fm_events"] in ["true"]:
|
if requests["pass_fm_events"] in ["true"]:
|
||||||
loading_data["pass_fm_events"] = True
|
loading_data["pass_fm_events"] = True
|
||||||
|
|
||||||
if "pass_ui_objects" in keys:
|
if "pass_ui_objects" in requests:
|
||||||
if len(requests["pass_ui_objects"]) > 0:
|
if len(requests["pass_ui_objects"]) > 0:
|
||||||
loading_data["pass_ui_objects"] = []
|
loading_data["pass_ui_objects"] = []
|
||||||
for ui_id in requests["pass_ui_objects"]:
|
for ui_id in requests["pass_ui_objects"]:
|
||||||
|
@ -87,7 +94,7 @@ class ManifestProcessor:
|
||||||
except ManifestProcessorException as e:
|
except ManifestProcessorException as e:
|
||||||
logger.error(repr(e))
|
logger.error(repr(e))
|
||||||
|
|
||||||
if "bind_keys" in keys:
|
if "bind_keys" in requests:
|
||||||
if isinstance(requests["bind_keys"], list):
|
if isinstance(requests["bind_keys"], list):
|
||||||
loading_data["bind_keys"] = requests["bind_keys"]
|
loading_data["bind_keys"] = requests["bind_keys"]
|
||||||
|
|
||||||
|
|
|
@ -36,41 +36,76 @@ class PluginsController:
|
||||||
|
|
||||||
self._plugins_dir_watcher = None
|
self._plugins_dir_watcher = None
|
||||||
self._plugin_collection = []
|
self._plugin_collection = []
|
||||||
|
self._plugin_manifests = {}
|
||||||
|
|
||||||
|
self._load_manifests()
|
||||||
|
|
||||||
|
|
||||||
def launch_plugins(self) -> None:
|
def _load_manifests(self):
|
||||||
|
logger.info(f"Loading manifests...")
|
||||||
|
|
||||||
|
for path, folder in [[join(self._plugins_path, item), item] if os.path.isdir(join(self._plugins_path, item)) else None for item in os.listdir(self._plugins_path)]:
|
||||||
|
manifest = ManifestProcessor(path, self._builder)
|
||||||
|
self._plugin_manifests[path] = {
|
||||||
|
"path": path,
|
||||||
|
"folder": folder,
|
||||||
|
"manifest": manifest
|
||||||
|
}
|
||||||
|
|
||||||
self._set_plugins_watcher()
|
self._set_plugins_watcher()
|
||||||
self.load_plugins()
|
|
||||||
|
|
||||||
def _set_plugins_watcher(self) -> None:
|
def _set_plugins_watcher(self) -> None:
|
||||||
self._plugins_dir_watcher = Gio.File.new_for_path(self._plugins_path) \
|
self._plugins_dir_watcher = Gio.File.new_for_path(self._plugins_path) \
|
||||||
.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, Gio.Cancellable())
|
.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, Gio.Cancellable())
|
||||||
self._plugins_dir_watcher.connect("changed", self._on_plugins_changed, ())
|
self._plugins_dir_watcher.connect("changed", self._on_plugins_changed, ())
|
||||||
|
|
||||||
def _on_plugins_changed(self, file_monitor, file, other_file=None, eve_type=None, data=None):
|
def _on_plugins_changed(self, file_monitor, file, other_file = None, eve_type = None, data = None):
|
||||||
if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED,
|
if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED,
|
||||||
Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN,
|
Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN,
|
||||||
Gio.FileMonitorEvent.MOVED_OUT]:
|
Gio.FileMonitorEvent.MOVED_OUT]:
|
||||||
self.reload_plugins(file)
|
self.reload_plugins(file)
|
||||||
|
|
||||||
@daemon_threaded
|
def pre_launch_plugins(self) -> None:
|
||||||
def load_plugins(self, file: str = None) -> None:
|
logger.info(f"Loading pre-launch plugins...")
|
||||||
logger.info(f"Loading plugins...")
|
plugin_manifests: {} = {}
|
||||||
|
|
||||||
|
for key in self._plugin_manifests:
|
||||||
|
target_manifest = self._plugin_manifests[key]["manifest"]
|
||||||
|
if target_manifest.is_pre_launch():
|
||||||
|
plugin_manifests[key] = self._plugin_manifests[key]
|
||||||
|
|
||||||
|
self._load_plugins(plugin_manifests, is_pre_launch = True)
|
||||||
|
|
||||||
|
def post_launch_plugins(self) -> None:
|
||||||
|
logger.info(f"Loading post-launch plugins...")
|
||||||
|
plugin_manifests: {} = {}
|
||||||
|
|
||||||
|
for key in self._plugin_manifests:
|
||||||
|
target_manifest = self._plugin_manifests[key]["manifest"]
|
||||||
|
if not target_manifest.is_pre_launch():
|
||||||
|
plugin_manifests[key] = self._plugin_manifests[key]
|
||||||
|
|
||||||
|
self._load_plugins(plugin_manifests)
|
||||||
|
|
||||||
|
def _load_plugins(self, plugin_manifests: {} = {}, is_pre_launch: bool = False) -> None:
|
||||||
parent_path = os.getcwd()
|
parent_path = os.getcwd()
|
||||||
|
|
||||||
for path, folder in [[join(self._plugins_path, item), item] if os.path.isdir(join(self._plugins_path, item)) else None for item in os.listdir(self._plugins_path)]:
|
for key in plugin_manifests:
|
||||||
|
target_manifest = plugin_manifests[key]
|
||||||
|
path, folder, manifest = target_manifest["path"], target_manifest["folder"], target_manifest["manifest"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
target = join(path, "plugin.py")
|
target = join(path, "plugin.py")
|
||||||
manifest = ManifestProcessor(path, self._builder)
|
|
||||||
|
|
||||||
if not os.path.exists(target):
|
if not os.path.exists(target):
|
||||||
raise FileNotFoundError("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...")
|
raise FileNotFoundError("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...")
|
||||||
|
|
||||||
plugin, loading_data = manifest.get_loading_data()
|
plugin, loading_data = manifest.get_loading_data()
|
||||||
module = self.load_plugin_module(path, folder, target)
|
module = self.load_plugin_module(path, folder, target)
|
||||||
|
|
||||||
|
if is_pre_launch:
|
||||||
|
self.execute_plugin(module, plugin, loading_data)
|
||||||
|
else:
|
||||||
GLib.idle_add(self.execute_plugin, *(module, plugin, loading_data))
|
GLib.idle_add(self.execute_plugin, *(module, plugin, loading_data))
|
||||||
# self.execute_plugin(module, plugin, loading_data)
|
|
||||||
except InvalidPluginException as e:
|
except InvalidPluginException as e:
|
||||||
logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !")
|
logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !")
|
||||||
logger.debug("Trace: ", traceback.print_exc())
|
logger.debug("Trace: ", traceback.print_exc())
|
||||||
|
@ -100,25 +135,26 @@ class PluginsController:
|
||||||
|
|
||||||
def execute_plugin(self, module: type, plugin: PluginInfo, loading_data: []):
|
def execute_plugin(self, module: type, plugin: PluginInfo, loading_data: []):
|
||||||
plugin.reference = module.Plugin()
|
plugin.reference = module.Plugin()
|
||||||
keys = loading_data.keys()
|
|
||||||
|
|
||||||
if "ui_target" in keys:
|
if "ui_target" in loading_data:
|
||||||
loading_data["ui_target"].add( plugin.reference.generate_reference_ui_element() )
|
loading_data["ui_target"].add( plugin.reference.generate_reference_ui_element() )
|
||||||
loading_data["ui_target"].show_all()
|
loading_data["ui_target"].show_all()
|
||||||
|
|
||||||
if "pass_ui_objects" in keys:
|
if "pass_ui_objects" in loading_data:
|
||||||
plugin.reference.set_ui_object_collection( loading_data["pass_ui_objects"] )
|
plugin.reference.set_ui_object_collection( loading_data["pass_ui_objects"] )
|
||||||
|
|
||||||
if "pass_fm_events" in keys:
|
if "pass_fm_events" in loading_data:
|
||||||
plugin.reference.set_fm_event_system(event_system)
|
plugin.reference.set_fm_event_system(event_system)
|
||||||
plugin.reference.subscribe_to_events()
|
plugin.reference.subscribe_to_events()
|
||||||
|
|
||||||
if "bind_keys" in keys:
|
if "bind_keys" in loading_data:
|
||||||
keybindings.append_bindings( loading_data["bind_keys"] )
|
keybindings.append_bindings( loading_data["bind_keys"] )
|
||||||
|
|
||||||
plugin.reference.run()
|
plugin.reference.run()
|
||||||
self._plugin_collection.append(plugin)
|
self._plugin_collection.append(plugin)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def reload_plugins(self, file: str = None) -> None:
|
def reload_plugins(self, file: str = None) -> None:
|
||||||
logger.info(f"Reloading plugins...")
|
logger.info(f"Reloading plugins...")
|
||||||
parent_path = os.getcwd()
|
parent_path = os.getcwd()
|
||||||
|
|
|
@ -14,7 +14,6 @@ from random import randint
|
||||||
from .utils.settings import Settings
|
from .utils.settings import Settings
|
||||||
from .utils.launcher import Launcher
|
from .utils.launcher import Launcher
|
||||||
from .utils.filehandler import FileHandler
|
from .utils.filehandler import FileHandler
|
||||||
from .icons.icon import Icon
|
|
||||||
from .path import Path
|
from .path import Path
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,9 +39,8 @@ except Exception as e:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Tab(Settings, FileHandler, Launcher, Icon, Path):
|
class Tab(Settings, FileHandler, Launcher, Path):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.logger = None
|
|
||||||
self._id_length: int = 10
|
self._id_length: int = 10
|
||||||
|
|
||||||
self._id: str = ""
|
self._id: str = ""
|
||||||
|
@ -168,33 +166,6 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_video_icons(self) -> list:
|
|
||||||
data = []
|
|
||||||
dir = self.get_current_directory()
|
|
||||||
for file in self._vids:
|
|
||||||
img_hash, hash_img_path = self.create_video_thumbnail(full_path=f"{dir}/{file}", returnHashInstead=True)
|
|
||||||
data.append([img_hash, hash_img_path])
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_pixbuf_icon_str_combo(self):
|
|
||||||
data = []
|
|
||||||
dir = self.get_current_directory()
|
|
||||||
for file in self._files:
|
|
||||||
icon = self.create_icon(dir, file).get_pixbuf()
|
|
||||||
data.append([icon, file])
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_gtk_icon_str_combo(self) -> list:
|
|
||||||
data = []
|
|
||||||
dir = self.get_current_directory()
|
|
||||||
for file in self._files:
|
|
||||||
icon = self.create_icon(dir, file)
|
|
||||||
data.append([icon, file[0]])
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_current_directory(self) -> str:
|
def get_current_directory(self) -> str:
|
||||||
return self.get_path()
|
return self.get_path()
|
||||||
|
|
||||||
|
@ -264,7 +235,7 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path):
|
||||||
return int(text) if text.isdigit() else text
|
return int(text) if text.isdigit() else text
|
||||||
|
|
||||||
def _natural_keys(self, text):
|
def _natural_keys(self, text):
|
||||||
return [ self._atoi(c) for c in re.split('(\d+)',text) ]
|
return [ self._atoi(c) for c in re.split(r'(\d+)', text) ]
|
||||||
|
|
||||||
def _hash_text(self, text) -> str:
|
def _hash_text(self, text) -> str:
|
||||||
return hashlib.sha256(str.encode(text)).hexdigest()[:18]
|
return hashlib.sha256(str.encode(text)).hexdigest()[:18]
|
||||||
|
@ -289,3 +260,7 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path):
|
||||||
|
|
||||||
def _set_error_message(self, text: str):
|
def _set_error_message(self, text: str):
|
||||||
self.error_message = text
|
self.error_message = text
|
||||||
|
|
||||||
|
|
||||||
|
def create_icon(self, dir, file):
|
||||||
|
return event_system.emit_and_await("create-thumbnail", (dir, file,))
|
||||||
|
|
|
@ -41,11 +41,11 @@ class Launcher:
|
||||||
|
|
||||||
def execute(self, command, start_dir=os.getenv("HOME"), use_shell=False):
|
def execute(self, command, start_dir=os.getenv("HOME"), use_shell=False):
|
||||||
try:
|
try:
|
||||||
self.logger.debug(command)
|
logger.debug(command)
|
||||||
subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=True, stdout=None, stderr=None, close_fds=True)
|
subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=True, stdout=None, stderr=None, close_fds=True)
|
||||||
except ShellFMLauncherException as e:
|
except ShellFMLauncherException as e:
|
||||||
self.logger.error(f"Couldn't execute: {command}")
|
logger.error(f"Couldn't execute: {command}")
|
||||||
self.logger.error(e)
|
logger.error(e)
|
||||||
|
|
||||||
# TODO: Return std(out/in/err) handlers along with subprocess instead of sinking to null
|
# TODO: Return std(out/in/err) handlers along with subprocess instead of sinking to null
|
||||||
def execute_and_return_thread_handler(self, command, start_dir=os.getenv("HOME"), use_shell=False):
|
def execute_and_return_thread_handler(self, command, start_dir=os.getenv("HOME"), use_shell=False):
|
||||||
|
@ -53,8 +53,8 @@ class Launcher:
|
||||||
DEVNULL = open(os.devnull, 'w')
|
DEVNULL = open(os.devnull, 'w')
|
||||||
return subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=False, stdout=DEVNULL, stderr=DEVNULL, close_fds=False)
|
return subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=False, stdout=DEVNULL, stderr=DEVNULL, close_fds=False)
|
||||||
except ShellFMLauncherException as e:
|
except ShellFMLauncherException as e:
|
||||||
self.logger.error(f"Couldn't execute and return thread: {command}")
|
logger.error(f"Couldn't execute and return thread: {command}")
|
||||||
self.logger.error(e)
|
logger.error(e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@threaded
|
@threaded
|
||||||
|
@ -63,7 +63,7 @@ class Launcher:
|
||||||
|
|
||||||
def remux_video(self, hash, file):
|
def remux_video(self, hash, file):
|
||||||
remux_vid_pth = "{self.REMUX_FOLDER}/{hash}.mp4"
|
remux_vid_pth = "{self.REMUX_FOLDER}/{hash}.mp4"
|
||||||
self.logger.debug(remux_vid_pth)
|
logger.debug(remux_vid_pth)
|
||||||
|
|
||||||
if not os.path.isfile(remux_vid_pth):
|
if not os.path.isfile(remux_vid_pth):
|
||||||
self.check_remux_space()
|
self.check_remux_space()
|
||||||
|
@ -83,8 +83,8 @@ class Launcher:
|
||||||
proc = subprocess.Popen(command)
|
proc = subprocess.Popen(command)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
except ShellFMLauncherException as e:
|
except ShellFMLauncherException as e:
|
||||||
self.logger.error(message)
|
logger.error(message)
|
||||||
self.logger.error(e)
|
logger.error(e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -94,7 +94,7 @@ class Launcher:
|
||||||
try:
|
try:
|
||||||
limit = int(limit)
|
limit = int(limit)
|
||||||
except ShellFMLauncherException as e:
|
except ShellFMLauncherException as e:
|
||||||
self.logger.debug(e)
|
logger.debug(e)
|
||||||
return
|
return
|
||||||
|
|
||||||
usage = self.get_remux_folder_usage(self.REMUX_FOLDER)
|
usage = self.get_remux_folder_usage(self.REMUX_FOLDER)
|
||||||
|
|
|
@ -14,8 +14,6 @@ class ShellFMSettingsException(Exception):
|
||||||
|
|
||||||
|
|
||||||
class Settings:
|
class Settings:
|
||||||
logger = None
|
|
||||||
|
|
||||||
# NOTE: app_name should be defined using python 'builtins'
|
# NOTE: app_name should be defined using python 'builtins'
|
||||||
app_name_exists = False
|
app_name_exists = False
|
||||||
try:
|
try:
|
||||||
|
@ -31,45 +29,13 @@ class Settings:
|
||||||
CONFIG_FILE = f"{CONFIG_PATH}/settings.json"
|
CONFIG_FILE = f"{CONFIG_PATH}/settings.json"
|
||||||
HIDE_HIDDEN_FILES = True
|
HIDE_HIDDEN_FILES = True
|
||||||
|
|
||||||
DEFAULT_ICONS = f"{CONFIG_PATH}/icons"
|
|
||||||
DEFAULT_ICON = f"{DEFAULT_ICONS}/text.png"
|
|
||||||
FFMPG_THUMBNLR = f"{CONFIG_PATH}/ffmpegthumbnailer" # Thumbnail generator binary
|
|
||||||
BLENDER_THUMBNLR = f"{CONFIG_PATH}/blender-thumbnailer" # Blender thumbnail generator binary
|
|
||||||
REMUX_FOLDER = f"{USER_HOME}/.remuxs" # Remuxed files folder
|
REMUX_FOLDER = f"{USER_HOME}/.remuxs" # Remuxed files folder
|
||||||
|
|
||||||
ICON_DIRS = ["/usr/share/icons", f"{USER_HOME}/.icons" "/usr/share/pixmaps"]
|
|
||||||
BASE_THUMBS_PTH = f"{USER_HOME}/.thumbnails"
|
|
||||||
ABS_THUMBS_PTH = f"{BASE_THUMBS_PTH}/normal"
|
|
||||||
STEAM_ICONS_PTH = f"{BASE_THUMBS_PTH}/steam_icons"
|
|
||||||
|
|
||||||
if not os.path.exists(CONFIG_PATH) or not os.path.exists(CONFIG_FILE):
|
|
||||||
msg = f"No config file located! Aborting loading ShellFM library...\nExpected: {CONFIG_FILE}"
|
|
||||||
raise ShellFMSettingsException(msg)
|
|
||||||
|
|
||||||
if not path.isdir(REMUX_FOLDER):
|
|
||||||
os.mkdir(REMUX_FOLDER)
|
|
||||||
|
|
||||||
if not path.isdir(BASE_THUMBS_PTH):
|
|
||||||
os.mkdir(BASE_THUMBS_PTH)
|
|
||||||
|
|
||||||
if not path.isdir(ABS_THUMBS_PTH):
|
|
||||||
os.mkdir(ABS_THUMBS_PTH)
|
|
||||||
|
|
||||||
if not path.isdir(STEAM_ICONS_PTH):
|
|
||||||
os.mkdir(STEAM_ICONS_PTH)
|
|
||||||
|
|
||||||
if not os.path.exists(DEFAULT_ICONS):
|
|
||||||
DEFAULT_ICONS = f"{USR_APP_CONTEXT}/icons"
|
|
||||||
DEFAULT_ICON = f"{DEFAULT_ICONS}/text.png"
|
|
||||||
|
|
||||||
with open(CONFIG_FILE) as f:
|
with open(CONFIG_FILE) as f:
|
||||||
settings = json.load(f)
|
settings = json.load(f)
|
||||||
config = settings["config"]
|
config = settings["config"]
|
||||||
|
|
||||||
subpath = config["base_of_home"]
|
subpath = config["base_of_home"]
|
||||||
STEAM_CDN_URL = config["steam_cdn_url"]
|
|
||||||
FFMPG_THUMBNLR = FFMPG_THUMBNLR if config["thumbnailer_path"] == "" else config["thumbnailer_path"]
|
|
||||||
BLENDER_THUMBNLR = BLENDER_THUMBNLR if config["blender_thumbnailer_path"] == "" else config["blender_thumbnailer_path"]
|
|
||||||
HIDE_HIDDEN_FILES = True if config["hide_hidden_files"] in ["true", ""] else False
|
HIDE_HIDDEN_FILES = True if config["hide_hidden_files"] in ["true", ""] else False
|
||||||
go_past_home = True if config["go_past_home"] in ["true", ""] else False
|
go_past_home = True if config["go_past_home"] in ["true", ""] else False
|
||||||
lock_folder = False if config["lock_folder"] in ["false", ""] else True
|
lock_folder = False if config["lock_folder"] in ["false", ""] else True
|
||||||
|
@ -83,9 +49,6 @@ class Settings:
|
||||||
code_app = config["code_app"]
|
code_app = config["code_app"]
|
||||||
text_app = config["text_app"]
|
text_app = config["text_app"]
|
||||||
terminal_app = config["terminal_app"]
|
terminal_app = config["terminal_app"]
|
||||||
container_icon_wh = config["container_icon_wh"]
|
|
||||||
video_icon_wh = config["video_icon_wh"]
|
|
||||||
sys_icon_wh = config["sys_icon_wh"]
|
|
||||||
file_manager_app = config["file_manager_app"]
|
file_manager_app = config["file_manager_app"]
|
||||||
remux_folder_max_disk_usage = config["remux_folder_max_disk_usage"]
|
remux_folder_max_disk_usage = config["remux_folder_max_disk_usage"]
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ def debug_signal_handler(signal, frame):
|
||||||
rpdb2.start_embedded_debugger("foobar", True, True)
|
rpdb2.start_embedded_debugger("foobar", True, True)
|
||||||
rpdb2.setbreak(depth=1)
|
rpdb2.setbreak(depth=1)
|
||||||
return
|
return
|
||||||
except StandardError:
|
except Exception:
|
||||||
...
|
...
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -26,7 +26,7 @@ def debug_signal_handler(signal, frame):
|
||||||
logger.debug("\n\nStarting embedded rconsole debugger...\n\n")
|
logger.debug("\n\nStarting embedded rconsole debugger...\n\n")
|
||||||
rconsole.spawn_server()
|
rconsole.spawn_server()
|
||||||
return
|
return
|
||||||
except StandardError as ex:
|
except Exception as ex:
|
||||||
...
|
...
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -34,7 +34,15 @@ def debug_signal_handler(signal, frame):
|
||||||
logger.debug("\n\nStarting PuDB debugger...\n\n")
|
logger.debug("\n\nStarting PuDB debugger...\n\n")
|
||||||
set_trace(paused = True)
|
set_trace(paused = True)
|
||||||
return
|
return
|
||||||
except StandardError as ex:
|
except Exception as ex:
|
||||||
|
...
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ipdb
|
||||||
|
logger.debug("\n\nStarting IPDB debugger...\n\n")
|
||||||
|
ipdb.set_trace()
|
||||||
|
return
|
||||||
|
except Exception as ex:
|
||||||
...
|
...
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -42,11 +50,11 @@ def debug_signal_handler(signal, frame):
|
||||||
logger.debug("\n\nStarting embedded PDB debugger...\n\n")
|
logger.debug("\n\nStarting embedded PDB debugger...\n\n")
|
||||||
pdb.Pdb(skip=['gi.*']).set_trace()
|
pdb.Pdb(skip=['gi.*']).set_trace()
|
||||||
return
|
return
|
||||||
except StandardError as ex:
|
except Exception as ex:
|
||||||
...
|
...
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import code
|
import code
|
||||||
code.interact()
|
code.interact()
|
||||||
except StandardError as ex:
|
except Exception as ex:
|
||||||
logger.debug(f"{ex}, returning to normal program flow...")
|
logger.debug(f"{ex}, returning to normal program flow...")
|
|
@ -51,15 +51,20 @@ class IPCServer(Singleton):
|
||||||
listener = Listener((self._ipc_address, self._ipc_port))
|
listener = Listener((self._ipc_address, self._ipc_port))
|
||||||
|
|
||||||
self.is_ipc_alive = True
|
self.is_ipc_alive = True
|
||||||
self._run_ipc_loop(listener)
|
# self._run_ipc_loop(listener)
|
||||||
|
GLib.Thread("", self._run_ipc_loop, listener)
|
||||||
|
|
||||||
@daemon_threaded
|
# @daemon_threaded
|
||||||
def _run_ipc_loop(self, listener) -> None:
|
def _run_ipc_loop(self, listener) -> None:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
conn = listener.accept()
|
conn = listener.accept()
|
||||||
start_time = time.perf_counter()
|
start_time = time.perf_counter()
|
||||||
|
|
||||||
GLib.idle_add(self._handle_ipc_message, *(conn, start_time,))
|
GLib.idle_add(self._handle_ipc_message, *(conn, start_time,))
|
||||||
|
|
||||||
|
conn = None
|
||||||
|
start_time = None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug( repr(e) )
|
logger.debug( repr(e) )
|
||||||
|
|
||||||
|
@ -73,22 +78,29 @@ class IPCServer(Singleton):
|
||||||
if "FILE|" in msg:
|
if "FILE|" in msg:
|
||||||
file = msg.split("FILE|")[1].strip()
|
file = msg.split("FILE|")[1].strip()
|
||||||
if file:
|
if file:
|
||||||
event_system.emit("handle_file_from_ipc", file)
|
event_system.emit_and_await("handle_file_from_ipc", file)
|
||||||
|
|
||||||
|
msg = None
|
||||||
|
file = None
|
||||||
conn.close()
|
conn.close()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
if msg in ['close connection', 'close server']:
|
if msg in ['close connection', 'close server']:
|
||||||
|
msg = None
|
||||||
conn.close()
|
conn.close()
|
||||||
break
|
break
|
||||||
|
|
||||||
# NOTE: Not perfect but insures we don't lock up the connection for too long.
|
# NOTE: Not perfect but insures we don't lock up the connection for too long.
|
||||||
end_time = time.perf_counter()
|
end_time = time.perf_counter()
|
||||||
if (end_time - start_time) > self._ipc_timeout:
|
if (end_time - start_time) > self._ipc_timeout:
|
||||||
|
msg = None
|
||||||
|
end_time = None
|
||||||
conn.close()
|
conn.close()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def send_ipc_message(self, message: str = "Empty Data...") -> None:
|
def send_ipc_message(self, message: str = "Empty Data...") -> None:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -146,6 +146,13 @@ class SettingsManager(StartCheckMixin, Singleton):
|
||||||
def is_trace_debug(self) -> bool: return self._trace_debug
|
def is_trace_debug(self) -> bool: return self._trace_debug
|
||||||
def is_debug(self) -> bool: return self._debug
|
def is_debug(self) -> bool: return self._debug
|
||||||
|
|
||||||
|
def set_main_window_x(self, x = 0): self.settings.config.main_window_x = x
|
||||||
|
def set_main_window_y(self, y = 0): self.settings.config.main_window_y = y
|
||||||
|
def set_main_window_width(self, width = 800): self.settings.config.main_window_width = width
|
||||||
|
def set_main_window_height(self, height = 600): self.settings.config.main_window_height = height
|
||||||
|
def set_main_window_min_width(self, width = 720): self.settings.config.main_window_min_width = width
|
||||||
|
def set_main_window_min_height(self, height = 480): self.settings.config.main_window_min_height = height
|
||||||
|
|
||||||
def set_trace_debug(self, trace_debug: bool):
|
def set_trace_debug(self, trace_debug: bool):
|
||||||
self._trace_debug = trace_debug
|
self._trace_debug = trace_debug
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,13 @@ class Config:
|
||||||
sys_icon_wh: list = field(default_factory=lambda: [56, 56])
|
sys_icon_wh: list = field(default_factory=lambda: [56, 56])
|
||||||
steam_cdn_url: str = "https://steamcdn-a.akamaihd.net/steam/apps/"
|
steam_cdn_url: str = "https://steamcdn-a.akamaihd.net/steam/apps/"
|
||||||
remux_folder_max_disk_usage: str = "8589934592"
|
remux_folder_max_disk_usage: str = "8589934592"
|
||||||
|
make_transparent: int = 0
|
||||||
|
main_window_x: int = 721
|
||||||
|
main_window_y: int = 465
|
||||||
|
main_window_min_width: int = 720
|
||||||
|
main_window_min_height: int = 480
|
||||||
|
main_window_width: int = 800
|
||||||
|
main_window_height: int = 600
|
||||||
application_dirs: list = field(default_factory=lambda: [
|
application_dirs: list = field(default_factory=lambda: [
|
||||||
"/usr/share/applications",
|
"/usr/share/applications",
|
||||||
f"{settings_manager.get_home_path()}/.local/share/applications"
|
f"{settings_manager.get_home_path()}/.local/share/applications"
|
||||||
|
|
|
@ -11,36 +11,48 @@ import inspect
|
||||||
|
|
||||||
|
|
||||||
class StartCheckMixin:
|
class StartCheckMixin:
|
||||||
def is_dirty_start(self) -> bool: return self._dirty_start
|
def is_dirty_start(self) -> bool:
|
||||||
def clear_pid(self): self._clean_pid()
|
return self._dirty_start
|
||||||
|
|
||||||
|
def clear_pid(self):
|
||||||
|
if not self.is_trace_debug():
|
||||||
|
self._clean_pid()
|
||||||
|
|
||||||
def do_dirty_start_check(self):
|
def do_dirty_start_check(self):
|
||||||
if not os.path.exists(self._PID_FILE):
|
if self.is_trace_debug():
|
||||||
self._write_new_pid()
|
pid = os.getpid()
|
||||||
else:
|
self._print_pid(pid)
|
||||||
with open(self._PID_FILE, "r") as _pid:
|
return
|
||||||
pid = _pid.readline().strip()
|
|
||||||
|
if os.path.exists(self._PID_FILE):
|
||||||
|
with open(self._PID_FILE, "r") as f:
|
||||||
|
pid = f.readline().strip()
|
||||||
if pid not in ("", None):
|
if pid not in ("", None):
|
||||||
self._check_alive_status(int(pid))
|
if self.is_pid_alive( int(pid) ):
|
||||||
else:
|
print("PID file exists and PID is alive... Letting downstream errors (sans debug args) handle app closure propigation.")
|
||||||
|
return
|
||||||
|
|
||||||
self._write_new_pid()
|
self._write_new_pid()
|
||||||
|
|
||||||
""" Check For the existence of a unix pid. """
|
""" Check For the existence of a unix pid. """
|
||||||
def _check_alive_status(self, pid):
|
def is_pid_alive(self, pid):
|
||||||
print(f"PID Found: {pid}")
|
print(f"PID Found: {pid}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.kill(pid, 0)
|
os.kill(pid, 0)
|
||||||
except OSError:
|
except OSError:
|
||||||
print(f"{app_name} is starting dirty...")
|
print(f"{app_name} PID file exists but PID is irrelevant; starting dirty...")
|
||||||
self._dirty_start = True
|
self._dirty_start = True
|
||||||
self._write_new_pid()
|
return False
|
||||||
return
|
|
||||||
|
|
||||||
print("PID is alive... Let downstream errors (sans debug args) handle app closure propigation.")
|
return True
|
||||||
|
|
||||||
def _write_new_pid(self):
|
def _write_new_pid(self):
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
self._write_pid(pid)
|
self._write_pid(pid)
|
||||||
|
self._print_pid(pid)
|
||||||
|
|
||||||
|
def _print_pid(self, pid):
|
||||||
print(f"{app_name} PID: {pid}")
|
print(f"{app_name} PID: {pid}")
|
||||||
|
|
||||||
def _clean_pid(self):
|
def _clean_pid(self):
|
||||||
|
|
|
@ -20,6 +20,10 @@ function main() {
|
||||||
files[$size]="${target}"
|
files[$size]="${target}"
|
||||||
done
|
done
|
||||||
|
|
||||||
python /opt/solarfm.zip "${files[@]}"
|
export G_SLICE=always-malloc
|
||||||
|
export G_DEBUG=gc-friendly
|
||||||
|
export GOBJECT_DEBUG=instance-count
|
||||||
|
export GSK_RENDERER=cairo
|
||||||
|
python /opt/solarfm.zip "$@"
|
||||||
}
|
}
|
||||||
main "$@";
|
main "$@";
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
#!/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() {
|
|
||||||
call_path=`pwd`
|
|
||||||
cd "${call_path}"
|
|
||||||
echo "Working Dir: " $(pwd)
|
|
||||||
|
|
||||||
python /opt/solarfm.zip "$@"
|
|
||||||
}
|
|
||||||
main "$@";
|
|
|
@ -2,6 +2,7 @@
|
||||||
"Open Actions": {
|
"Open Actions": {
|
||||||
"Open": ["STOCK_OPEN", "open"],
|
"Open": ["STOCK_OPEN", "open"],
|
||||||
"Open With": ["STOCK_OPEN", "open_with"],
|
"Open With": ["STOCK_OPEN", "open_with"],
|
||||||
|
"Open 2 Tab": ["STOCK_OPEN", "open_2_new_tab"],
|
||||||
"Execute": ["STOCK_EXECUTE", "execute"],
|
"Execute": ["STOCK_EXECUTE", "execute"],
|
||||||
"Execute in Terminal": ["STOCK_EXECUTE", "execute_in_terminal"]
|
"Execute in Terminal": ["STOCK_EXECUTE", "execute_in_terminal"]
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
"config": {
|
"config": {
|
||||||
"base_of_home": "",
|
"base_of_home": "",
|
||||||
"hide_hidden_files": "true",
|
"hide_hidden_files": "true",
|
||||||
"thumbnailer_path": "ffmpegthumbnailer",
|
|
||||||
"blender_thumbnailer_path": "",
|
|
||||||
"go_past_home": "true",
|
"go_past_home": "true",
|
||||||
"lock_folder": "false",
|
"lock_folder": "false",
|
||||||
"locked_folders": "venv::::flasks",
|
"locked_folders": "venv::::flasks",
|
||||||
|
@ -16,12 +14,18 @@
|
||||||
"code_app": "newton",
|
"code_app": "newton",
|
||||||
"text_app": "mousepad",
|
"text_app": "mousepad",
|
||||||
"terminal_app": "terminator",
|
"terminal_app": "terminator",
|
||||||
"container_icon_wh": [128, 128],
|
|
||||||
"video_icon_wh": [128, 64],
|
|
||||||
"sys_icon_wh": [56, 56],
|
|
||||||
"file_manager_app": "solarfm",
|
"file_manager_app": "solarfm",
|
||||||
"steam_cdn_url": "https://steamcdn-a.akamaihd.net/steam/apps/",
|
"remux_folder_max_disk_usage": "8589934592",
|
||||||
"remux_folder_max_disk_usage": "8589934592"
|
"make_transparent":0,
|
||||||
|
"main_window_x":721,
|
||||||
|
"main_window_y":465,
|
||||||
|
"main_window_min_width":720,
|
||||||
|
"main_window_min_height":480,
|
||||||
|
"main_window_width":800,
|
||||||
|
"main_window_height":600,
|
||||||
|
"application_dirs":[
|
||||||
|
"/usr/share/applications"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"meshs": [".dae", ".fbx", ".gltf", ".obj", ".stl"],
|
"meshs": [".dae", ".fbx", ".gltf", ".obj", ".stl"],
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="border-width">5</property>
|
<property name="border-width">5</property>
|
||||||
<property name="window-position">center-on-parent</property>
|
<property name="window-position">center-on-parent</property>
|
||||||
<property name="icon">../icons/solarfm.png</property>
|
<property name="icon">../icons/solarfm-64x64.png</property>
|
||||||
<property name="type-hint">dialog</property>
|
<property name="type-hint">dialog</property>
|
||||||
<property name="skip-taskbar-hint">True</property>
|
<property name="skip-taskbar-hint">True</property>
|
||||||
<property name="skip-pager-hint">True</property>
|
<property name="skip-pager-hint">True</property>
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
<property name="copyright" translatable="yes">Copyright (C) 2021 GPL2</property>
|
<property name="copyright" translatable="yes">Copyright (C) 2021 GPL2</property>
|
||||||
<property name="comments" translatable="yes">by ITDominator</property>
|
<property name="comments" translatable="yes">by ITDominator</property>
|
||||||
<property name="website">https://code.itdominator.com/itdominator/SolarFM</property>
|
<property name="website">https://code.itdominator.com/itdominator/SolarFM</property>
|
||||||
|
<property name="website-label" translatable="yes">ITDominator</property>
|
||||||
<property name="license" translatable="yes">SolarFM - Copyright (C) 2021 ITDominator GPL2
|
<property name="license" translatable="yes">SolarFM - Copyright (C) 2021 ITDominator GPL2
|
||||||
|
|
||||||
|
|
||||||
|
@ -367,7 +368,9 @@ Public License instead of this License.
|
||||||
|
|
||||||
|
|
||||||
SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspection.</property>
|
SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspection.</property>
|
||||||
<property name="translator-credits" translatable="yes" comments="Please replace this line with your own names, one name per line. ">translator-credits</property>
|
<property name="documenters">...</property>
|
||||||
|
<property name="translator-credits" translatable="yes" comments="Please replace this line with your own names, one name per line. ">...</property>
|
||||||
|
<property name="artists">...</property>
|
||||||
<property name="logo">../icons/solarfm-64x64.png</property>
|
<property name="logo">../icons/solarfm-64x64.png</property>
|
||||||
<property name="wrap-license">True</property>
|
<property name="wrap-license">True</property>
|
||||||
<property name="license-type">custom</property>
|
<property name="license-type">custom</property>
|
||||||
|
|
Loading…
Reference in New Issue