Renaming to SolarFM

This commit is contained in:
itdominator 2021-11-28 16:01:29 -06:00
parent 98feeabfa4
commit 4f18844fd2
181 changed files with 8751 additions and 2452 deletions

View File

@ -1,15 +1,22 @@
# PyFM
# SolarFM
# PyFM
PyFM is a Gtk + Python file manager.
# SolarFM
SolarFM is a Gtk + Python file manager.
# Notes
```sudo apt-get install python3 wget steamcmd```
# TODO
<ul>
<li>Add prompt guards for actions.</li>
<li>Add path bar search dropdown.</li>
<li>Add "execute" and "execute in terminal" context options.</li>
<li>Add "go to trash" and "delete all trash" options.</li>
<li>Add save button for "show errors" dropdown.</li>
<li>Add simpleish plugin system to run bash/python scripts.</li>
<li>Add DnD context awareness for over folder drop.</li>
</ul>
# Images
![1 PyFM showing different directories. ](images/pic1.png)
![1 PyFM themed dark and made transparent. ](images/pic2.png)
![1 SolarFM showing different directories. ](images/pic1.png)
![1 SolarFM themed dark and made transparent. ](images/pic2.png)

1
bin/REMOVE.txt Normal file
View File

@ -0,0 +1 @@
Remove me...

Binary file not shown.

View File

@ -1,11 +0,0 @@
#!/bin/bash
#postrm (script executed after uninstalling the package)
#set -e
if [ -f /bin/pytop ]; then
rm /bin/pytop
fi
if [ -d /opt/Pytop ]; then
rm -rf /opt/Pytop
fi

Binary file not shown.

View File

@ -1,36 +0,0 @@
#!/usr/bin/python3
# Gtk Imports
import gi, faulthandler
gi.require_version('Gtk', '3.0')
gi.require_version('WebKit2', '4.0')
from gi.repository import Gtk as gtk
from gi.repository import Gdk as gdk
from gi.repository import WebKit2 as webkit
# Python imports
from utils import Settings, Events
gdk.threads_init()
class Main:
def __init__(self):
faulthandler.enable()
webkit.WebView() # Needed for glade file to load...
self.builder = gtk.Builder()
self.settings = Settings()
self.settings.attachBuilder(self.builder)
self.builder.connect_signals(Events(self.settings))
window = self.settings.createWindow()
window.fullscreen()
window.show_all()
if __name__ == "__main__":
try:
main = Main()
gtk.main()
except Exception as e:
print(e)

View File

@ -1,305 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="webkit2gtk" version="2.12"/>
<object class="GtkFileFilter" id="Folders">
<mime-types>
<mime-type>inode/directory</mime-type>
</mime-types>
</object>
<object class="GtkImage" id="webDropDown">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Show Mini Webbrowser</property>
<property name="stock">gtk-go-down</property>
<property name="icon_size">3</property>
</object>
<object class="GtkWindow" id="Window">
<property name="can_focus">False</property>
<property name="default_width">800</property>
<property name="default_height">600</property>
<property name="type_hint">desktop</property>
<property name="decorated">False</property>
<property name="gravity">center</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkBox" id="box1">
<property name="width_request">256</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="popOutBttn">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">webDropDown</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="showWebview" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFileChooserButton" id="selectedDirDialog">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action">select-folder</property>
<property name="filter">Folders</property>
<property name="title" translatable="yes">Directory Chooser</property>
<signal name="file-set" handler="setIconViewDir" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSearchEntry" id="searDir">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkIconView" id="Desktop">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin">6</property>
<property name="selection_mode">multiple</property>
<property name="columns">6</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkPopover" id="iconControlsWindow">
<property name="can_focus">False</property>
<property name="relative_to">popOutBttn</property>
<property name="position">bottom</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkEntry" id="iconRenameInput">
<property name="width_request">300</property>
<property name="height_request">26</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_stock">gtk-edit</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton">
<property name="label">gtk-copy</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label">gtk-cut</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label">gtk-paste</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label">gtk-delete</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="margin_left">65</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkPopover" id="webViewer">
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="relative_to">popOutBttn</property>
<property name="position">bottom</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton">
<property name="label">gtk-home</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="loadHome" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label">gtk-refresh</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="refreshPage" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSearchEntry" id="webviewSearch">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<signal name="key-release-event" handler="runSearchWebview" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="WebKitWebView" id="webview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="load-changed" handler="setUrlBar" swapped="no"/>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -1,139 +0,0 @@
# Gtk Imports
import gi, cairo, os
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk as gtk
from gi.repository import Gdk as gdk
class Settings:
def __init__(self):
self.builder = None
self.hideHiddenFiles = True
self.GTK_ORIENTATION = 1 # HORIZONTAL (0) VERTICAL (1)
self.THUMB_GENERATOR = "ffmpegthumbnailer"
self.DEFAULTCOLOR = gdk.RGBA(0.0, 0.0, 0.0, 0.0) # ~#00000000
self.MOUSEOVERCOLOR = gdk.RGBA(0.0, 0.9, 1.0, 0.64) # ~#00e8ff
self.SELECTEDCOLOR = gdk.RGBA(0.4, 0.5, 0.1, 0.84)
self.ColumnSize = 8
self.usrHome = os.path.expanduser('~')
self.desktopPath = self.usrHome + "/Desktop"
self.webHome = 'http://webfm.com/'
self.iconContainerWxH = [128, 128]
self.systemIconImageWxH = [72, 72]
self.viIconWxH = [256, 128]
self.vidsExtensionList = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv', '.mpeg', '.mp4', '.webm')
self.imagesExtensionList = ('.png', '.jpg', '.jpeg', '.gif', '.ico', '.tga')
def attachBuilder(self, builder):
self.builder = builder
self.builder.add_from_file("resources/PyTop.glade")
def createWindow(self):
# Get window and connect signals
window = self.builder.get_object("Window")
window.connect("delete-event", gtk.main_quit)
self.setWindowData(window)
return window
def setWindowData(self, window):
screen = window.get_screen()
visual = screen.get_rgba_visual()
if visual != None and screen.is_composited():
window.set_visual(visual)
# bind css file
cssProvider = gtk.CssProvider()
cssProvider.load_from_path('resources/stylesheet.css')
screen = gdk.Screen.get_default()
styleContext = gtk.StyleContext()
styleContext.add_provider_for_screen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER)
window.set_app_paintable(True)
monitors = self.getMonitorData(screen)
window.resize(monitors[0].width, monitors[0].height)
def getMonitorData(self, screen):
monitors = []
for m in range(screen.get_n_monitors()):
monitors.append(screen.get_monitor_geometry(m))
for monitor in monitors:
print(str(monitor.width) + "x" + str(monitor.height) + "+" + str(monitor.x) + "+" + str(monitor.y))
return monitors
def returnBuilder(self): return self.builder
def returnUserHome(self): return self.usrHome
def returnDesktopPath(self): return self.usrHome + "/Desktop"
def returnIconImagePos(self): return self.GTK_ORIENTATION
def getThumbnailGenerator(self): return self.THUMB_GENERATOR
def returnColumnSize(self): return self.ColumnSize
def returnContainerWH(self): return self.iconContainerWxH
def returnSystemIconImageWH(self): return self.systemIconImageWxH
def returnVIIconWH(self): return self.viIconWxH
def returnWebHome(self): return self.webHome
def isHideHiddenFiles(self): return self.hideHiddenFiles
def returnVidsExtensionList(self): return self.vidsExtensionList
def returnImagesExtensionList(self): return self.imagesExtensionList
def setDefaultWebviewSettings(self, widget, settings=None):
# Usability
settings.set_property('enable-fullscreen', True)
settings.set_property('print-backgrounds', True)
settings.set_property('enable-frame-flattening', False)
settings.set_property('enable-plugins', True)
settings.set_property('enable-java', False)
settings.set_property('enable-resizable-text-areas', True)
settings.set_property('zoom-text-only', False)
settings.set_property('enable-smooth-scrolling', True)
settings.set_property('enable-back-forward-navigation-gestures', False)
settings.set_property('media-playback-requires-user-gesture', False)
settings.set_property('enable-tabs-to-links', True)
settings.set_property('enable-caret-browsing', False)
# Security
settings.set_property('user-agent','Mozilla/5.0 (X11; Generic; Linux x86-64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Safari/605.1.15')
settings.set_property('enable-private-browsing', False)
settings.set_property('enable-xss-auditor', True)
settings.set_property('enable-hyperlink-auditing', False)
settings.set_property('enable-site-specific-quirks', True)
settings.set_property('enable-offline-web-application-cache', True)
settings.set_property('enable-page-cache', True)
settings.set_property('allow-modal-dialogs', False)
settings.set_property('enable-html5-local-storage', True)
settings.set_property('enable-html5-database', True)
settings.set_property('allow-file-access-from-file-urls', False)
settings.set_property('allow-universal-access-from-file-urls', False)
settings.set_property('enable-dns-prefetching', False)
# Media stuff
# settings.set_property('hardware-acceleration-policy', 'on-demand')
settings.set_property('enable-webgl', False)
settings.set_property('enable-webaudio', True)
settings.set_property('enable-accelerated-2d-canvas', True)
settings.set_property('auto-load-images', True)
settings.set_property('enable-media-capabilities', True)
settings.set_property('enable-media-stream', True)
settings.set_property('enable-mediasource', True)
settings.set_property('enable-encrypted-media', True)
settings.set_property('media-playback-allows-inline', True)
# JS
settings.set_property('enable-javascript', True)
settings.set_property('enable-javascript-markup', True)
settings.set_property('javascript-can-access-clipboard', False)
settings.set_property('javascript-can-open-windows-automatically', False)
# Debugging
settings.set_property('enable-developer-extras', False)
settings.set_property('enable-write-console-messages-to-stdout', False)
settings.set_property('draw-compositing-indicators', False)
settings.set_property('enable-mock-capture-devices', False)
settings.set_property('enable-spatial-navigation', False)

View File

@ -5,4 +5,4 @@ Priority: optional
Architecture: amd64
Depends: ffmpegthumbnailer (>= 2.0.10-0.1)
Maintainer: Maxim Stewart <1itdominator@gmail.com>
Description: Pytop is a custom desktop GUI.
Description: SolarFM is a Gtk + Python file manager.

View File

@ -0,0 +1,11 @@
#!/bin/bash
#postrm (script executed after uninstalling the package)
#set -e
if [ -f /bin/solarfm ]; then
rm /bin/solarfm
fi
if [ -d /opt/SolarFM ]; then
rm -rf /opt/SolarFM
fi

Binary file not shown.

View File

@ -20,7 +20,7 @@ from __init__ import Main
if __name__ == "__main__":
try:
setproctitle('PyFM')
setproctitle('solarfm')
faulthandler.enable() # For better debug info
parser = argparse.ArgumentParser()
# Add long and short arguments

View File

@ -6,15 +6,15 @@
<property name="can-focus">False</property>
<property name="border-width">5</property>
<property name="window-position">center-on-parent</property>
<property name="icon">pyfm.png</property>
<property name="icon">solarfm.png</property>
<property name="type-hint">dialog</property>
<property name="gravity">center</property>
<property name="program-name">PyFM</property>
<property name="program-name">SolarFM</property>
<property name="version">0.0.1</property>
<property name="copyright" translatable="yes">Copyright (C) 2021 GPL2</property>
<property name="comments" translatable="yes">by ITDominator</property>
<property name="website">https://code.itdominator.com/itdominator/PyFM</property>
<property name="license" translatable="yes">PyFM - Copyright (C) 2021 ITDominator GPL2
<property name="website">https://code.itdominator.com/itdominator/SolarFM</property>
<property name="license" translatable="yes">SolarFM - Copyright (C) 2021 ITDominator GPL2
GNU GENERAL PUBLIC LICENSE
@ -361,10 +361,9 @@ Public License instead of this License.
ITDominator &lt;1itdominator@gmail.com&gt;
PyFM 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="logo">pyfm-64x64.png</property>
<property name="logo">solarfm-64x64.png</property>
<property name="wrap-license">True</property>
<property name="license-type">custom</property>
<child internal-child="vbox">
@ -798,7 +797,7 @@ PyFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspecti
<property name="window-position">center</property>
<property name="default-width">1670</property>
<property name="default-height">830</property>
<property name="icon">pyfm.png</property>
<property name="icon">solarfm.png</property>
<property name="gravity">center</property>
<signal name="key-press-event" handler="global_key_press_controller" swapped="no"/>
<signal name="key-release-event" handler="global_key_release_controller" swapped="no"/>

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -17,7 +17,7 @@ def threaded(fn):
class WindowController:
def __init__(self):
USER_HOME = path.expanduser('~')
CONFIG_PATH = USER_HOME + "/.config/pyfm"
CONFIG_PATH = USER_HOME + "/.config/solarfm"
self.session_file = CONFIG_PATH + "/session.json"
self._event_sleep_time = 1

View File

@ -14,7 +14,7 @@ class Settings:
logger = None
USER_HOME = path.expanduser('~')
CONFIG_PATH = USER_HOME + "/.config/pyfm"
CONFIG_PATH = USER_HOME + "/.config/solarfm"
CONFIG_FILE = CONFIG_PATH + "/settings.json"
HIDE_HIDDEN_FILES = True

View File

@ -17,7 +17,7 @@ class DBusControllerMixin:
@threaded
def create_ipc_server(self):
listener = Listener(('127.0.0.1', 4848), authkey=b'pyfm-ipc')
listener = Listener(('127.0.0.1', 4848), authkey=b'solarfm-ipc')
self.is_ipc_alive = True
while event_system.keep_ipc_alive:
conn = listener.accept()
@ -56,7 +56,7 @@ class DBusControllerMixin:
def send_ipc_message(self, message="Empty Data..."):
try:
conn = Client(('127.0.0.1', 4848), authkey=b'pyfm-ipc')
conn = Client(('127.0.0.1', 4848), authkey=b'solar-ipc')
conn.send(message)
conn.send('close connection')
except Exception as e:

View File

@ -40,7 +40,7 @@ class KeyboardSignalsMixin:
self.tear_down()
if (self.ctrlDown and keyname == "slash") or keyname == "home":
self.builder.get_object("go_home").released()
if self.ctrlDown and keyname == "r":
if (self.ctrlDown and keyname == "r") or keyname == "f5":
self.builder.get_object("refresh_view").released()
if (self.ctrlDown and keyname == "up") or (self.ctrlDown and keyname == "u"):
self.builder.get_object("go_up").released()

View File

@ -63,7 +63,7 @@ class WindowMixin(TabMixin):
ctx = notebook.get_style_context()
ctx.add_class("notebook-selected-focus")
self.window.set_title("PyFM ~ " + dir)
self.window.set_title("SolarFM ~ " + dir)
self.set_bottom_labels(view)
def set_path_text(self, wid, tid):

View File

@ -13,6 +13,6 @@ function main() {
echo "Working Dir: " $(pwd)
source "/home/abaddon/Portable_Apps/py-venvs/flask-apps-venv/venv/bin/activate"
python ../pyfm "$@"
python ../solarfm "$@"
}
main "$@";

View File

@ -1,7 +1,7 @@
Pytop is copyright 2019 Maxim Stewart.
Pytop is currently developed by ITDominator <1itdominator@gmail.com>.
SolarFM is copyright 2021 Maxim Stewart.
SolarFM is currently developed by ITDominator <1itdominator@gmail.com>.
License: GPLv2+
License: GPLv2
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -1,331 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkDialog" id="dlg">
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="title" translatable="yes">Choose Application</property>
<property name="default-height">420</property>
<property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">12</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="cancelbutton">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">False</property>
<property name="use-stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="okbutton">
<property name="label">gtk-ok</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="has-default">True</property>
<property name="receives-default">False</property>
<property name="use-stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_command">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="xpad">4</property>
<property name="ypad">4</property>
<property name="label" translatable="yes">Please choose an application or enter a command:</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">1</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox5">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">6</property>
<child>
<object class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="xpad">4</property>
<property name="ypad">0</property>
<property name="label" translatable="yes">File Type:</property>
<property name="yalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="file_type">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="selectable">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">2</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
<property name="can-focus">True</property>
<signal name="switch-page" handler="on_notebook_switch_page" object="app_chooser_dlg" swapped="yes"/>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTreeView" id="recommended_apps">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="has-focus">True</property>
<property name="headers-visible">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
</object>
</child>
</object>
</child>
<child type="tab">
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Associated Apps</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="tab-fill">False</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTreeView" id="all_apps">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="headers-visible">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">A_ll Apps</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab-fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox_command">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">4</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Command:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">cmdline</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="cmdline">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="activates-default">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="browse_btn">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<signal name="clicked" handler="on_browse_btn_clicked" object="app_chooser_dlg" swapped="yes"/>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<child>
<object class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">2</property>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="stock">gtk-open</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Browse</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="open_in_terminal">
<property name="label" translatable="yes">Opened in Terminal</property>
<property name="sensitive">False</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="set_default">
<property name="label" translatable="yes">_Set as default application for this file type</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-6">cancelbutton</action-widget>
<action-widget response="-5">okbutton</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -1,858 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkDialog" id="dlg">
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="title" translatable="yes">File Properties</property>
<property name="window-position">center-on-parent</property>
<property name="default-width">360</property>
<property name="type-hint">dialog</property>
<property name="gravity">center</property>
<signal name="response" handler="on_filePropertiesDlg_response" swapped="no"/>
<child internal-child="vbox">
<object class="GtkBox" id="dialog_vbox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">12</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog_action_area">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">False</property>
<property name="use-stock">True</property>
<signal name="clicked" handler="gtk_widget_destroy" object="filePropertiesDlg" swapped="yes"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ok_button">
<property name="label">gtk-ok</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">False</property>
<property name="use-stock">True</property>
<signal name="clicked" handler="gtk_widget_destroy" object="filePropertiesDlg" swapped="yes"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="border-width">6</property>
<child>
<object class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="top-padding">6</property>
<property name="bottom-padding">6</property>
<property name="left-padding">12</property>
<child>
<object class="GtkTable" id="general_table">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">4</property>
<property name="n-rows">9</property>
<property name="n-columns">2</property>
<property name="column-spacing">12</property>
<property name="row-spacing">6</property>
<child>
<object class="GtkLabel" id="label_filename">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;File _Name:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">file_name</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="file_name">
<property name="visible">True</property>
<property name="can-focus">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label20">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;_Location:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">location</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">1</property>
<property name="bottom-attach">2</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="location">
<property name="visible">True</property>
<property name="can-focus">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">1</property>
<property name="bottom-attach">2</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_target">
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Link _Target:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">target</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">2</property>
<property name="bottom-attach">3</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="target">
<property name="can-focus">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">2</property>
<property name="bottom-attach">3</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Type:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
</object>
<packing>
<property name="top-attach">3</property>
<property name="bottom-attach">4</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="mime_type">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="selectable">True</property>
<property name="ellipsize">end</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">3</property>
<property name="bottom-attach">4</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="open_with_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Opens _With:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">open_with</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">4</property>
<property name="bottom-attach">5</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkComboBox" id="open_with">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">4</property>
<property name="bottom-attach">5</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Total Size:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">5</property>
<property name="bottom-attach">6</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="total_size">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">5</property>
<property name="bottom-attach">6</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label21">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Size On Disk:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">6</property>
<property name="bottom-attach">7</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="size_on_disk">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">6</property>
<property name="bottom-attach">7</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_count">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Count:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">7</property>
<property name="bottom-attach">8</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="count">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">7</property>
<property name="bottom-attach">8</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;_Modified:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">mtime</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">8</property>
<property name="bottom-attach">9</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="mtime">
<property name="visible">True</property>
<property name="can-focus">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">8</property>
<property name="bottom-attach">9</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;_Accessed:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">atime</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">9</property>
<property name="bottom-attach">10</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="atime">
<property name="visible">True</property>
<property name="can-focus">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">9</property>
<property name="bottom-attach">10</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="tab">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Info</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="tab-fill">False</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="top-padding">6</property>
<property name="bottom-padding">6</property>
<property name="left-padding">12</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkTable" id="table3">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">2</property>
<property name="n-rows">2</property>
<property name="n-columns">2</property>
<property name="column-spacing">12</property>
<property name="row-spacing">6</property>
<child>
<object class="GtkLabel" id="owner_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;_Owner:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">owner</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="group_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;_Group:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">group</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">1</property>
<property name="bottom-attach">2</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="owner">
<property name="visible">True</property>
<property name="can-focus">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="group">
<property name="visible">True</property>
<property name="can-focus">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">1</property>
<property name="bottom-attach">2</property>
<property name="y-options"/>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator1">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkTable" id="table2">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">4</property>
<property name="n-rows">3</property>
<property name="n-columns">6</property>
<property name="column-spacing">12</property>
<property name="row-spacing">6</property>
<child>
<object class="GtkLabel" id="label17">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Owner:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label18">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Group:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">1</property>
<property name="bottom-attach">2</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label19">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Other:&lt;/b&gt;</property>
<property name="use-markup">True</property>
<property name="use-underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="top-attach">2</property>
<property name="bottom-attach">3</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="owner_r">
<property name="label" translatable="yes">Read</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="group_r">
<property name="label" translatable="yes">Read</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">1</property>
<property name="bottom-attach">2</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="others_r">
<property name="label" translatable="yes">Read</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="right-attach">2</property>
<property name="top-attach">2</property>
<property name="bottom-attach">3</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="owner_w">
<property name="label" translatable="yes">Write</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">2</property>
<property name="right-attach">3</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="group_w">
<property name="label" translatable="yes">Write</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">2</property>
<property name="right-attach">3</property>
<property name="top-attach">1</property>
<property name="bottom-attach">2</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="others_w">
<property name="label" translatable="yes">Write</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">2</property>
<property name="right-attach">3</property>
<property name="top-attach">2</property>
<property name="bottom-attach">3</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="owner_x">
<property name="label" translatable="yes">Execute</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">3</property>
<property name="right-attach">4</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="group_x">
<property name="label" translatable="yes">Execute</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">3</property>
<property name="right-attach">4</property>
<property name="top-attach">1</property>
<property name="bottom-attach">2</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="others_x">
<property name="label" translatable="yes">Execute</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">3</property>
<property name="right-attach">4</property>
<property name="top-attach">2</property>
<property name="bottom-attach">3</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="set_uid">
<property name="label" translatable="yes" comments="This is used in file attribute">Set UID</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">5</property>
<property name="right-attach">6</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="set_gid">
<property name="label" translatable="yes" comments="This is used in file attribute">Set GID</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">5</property>
<property name="right-attach">6</property>
<property name="top-attach">1</property>
<property name="bottom-attach">2</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="sticky">
<property name="label" translatable="yes" comments="This is used in file attribute">Sticky</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="border-width">2</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">5</property>
<property name="right-attach">6</property>
<property name="top-attach">2</property>
<property name="bottom-attach">3</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options"/>
</packing>
</child>
<child>
<object class="GtkVSeparator" id="vseparator1">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="left-attach">4</property>
<property name="right-attach">5</property>
<property name="bottom-attach">3</property>
<property name="x-options">GTK_FILL</property>
<property name="y-options">GTK_FILL</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator_recurse">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="recursive">
<property name="label" translatable="yes">Recursive (apply changes to folders and their contents)</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Permissions</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab-fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-6">cancel_button</action-widget>
<action-widget response="-5">ok_button</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -1,12 +0,0 @@
#!/bin/bash
# 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() {
# GTK_DEBUG=interactive python3 ./PyFM.py
python3 ./PyFM.py
}
main $@;

View File

@ -1,88 +0,0 @@
viewport,
treeview,
treeview > header,
notebook > stack,
notebook > header {
background-color: rgba(0, 0, 0, 0.24);
}
notebook > header {
background-color: rgba(0, 0, 0, 0.24);
border-color: rgba(0, 232, 255, 0.64);
}
box,
iconview {
background-color: rgba(0, 0, 0, 0.2);
background: rgba(0, 0, 0, 0.2);
}
treeview,
treeview.view {
background: rgba(0, 0, 0, 0.2);
background-color: rgba(0, 0, 0, 0.2);
}
cell {
margin: 0em;
padding: 0em;
/* float: left; */
}
cell:focus {
outline-style: solid;
outline-color: rgba(0, 232, 255, 0.64);
}
/* Ivonview and children default color */
.view {
background-color: rgba(0, 0, 0, 0.22);
color: #ebebeb;
}
/* Hover over color when not selected */
.view:hover {
box-shadow: inset 0 0 0 9999px alpha(rgba(0, 232, 255, 0.64), 0.54);
}
/* Handles the icon selection hover and selected hover color. */
.view:selected,
.view:selected:hover {
box-shadow: inset 0 0 0 9999px rgba(15, 134, 13, 0.49);
}
/* Rubberband coloring */
.rubberband,
rubberband,
flowbox rubberband,
treeview.view rubberband,
.content-view rubberband,
.content-view .rubberband,
XfdesktopIconView.view .rubberband {
border: 1px solid #6c6c6c;
background-color: rgba(21, 158, 167, 0.57);
}
XfdesktopIconView.view:active {
background-color: rgba(172, 102, 21, 1);
}
XfdesktopIconView.view {
border-radius: 4px;
background-color: transparent;
color: white;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
}
XfdesktopIconView.view:active {
box-shadow: none;
text-shadow: none;
}
XfdesktopIconView.view .rubberband {
border-radius: 0;
}

View File

@ -1,79 +0,0 @@
import os, gi
gi.require_version('Gdk', '3.0')
from gi.repository import Gdk
from gi.repository import GObject
class Dragging:
def __init__(self):
# higher values make movement more performant
# lower values make movement smoother
self.SENSITIVITY = 1
self.desktop = None
self.EvMask = Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON1_MOTION_MASK
self.offsetx = 0
self.offsety = 0
self.px = 0
self.py = 0
self.maxx = 0
self.maxy = 0
def connectEvents(self, desktop, widget):
self.desktop = desktop
widget.set_events(self.EvMask)
widget.connect("button_press_event", self.press_event)
widget.connect("motion_notify_event", self.draggingEvent)
widget.show()
def press_event(self, w, event):
if event.button == 1:
p = w.get_parent()
# offset == distance of parent widget from edge of screen ...
self.offsetx, self.offsety = p.get_window().get_position()
# plus distance from pointer to edge of widget
self.offsetx += event.x
self.offsety += event.y
# self.maxx, self.maxy both relative to the parent
# note that we're rounding down now so that these max values don't get
# rounded upward later and push the widget off the edge of its parent.
self.maxx = self.RoundDownToMultiple(p.get_allocation().width - w.get_allocation().width, self.SENSITIVITY)
self.maxy = self.RoundDownToMultiple(p.get_allocation().height - w.get_allocation().height, self.SENSITIVITY)
def draggingEvent(self, widget, event):
# x_root,x_root relative to screen
# x,y relative to parent (fixed widget)
# self.px,self.py stores previous values of x,y
# get starting values for x,y
x = event.x_root - self.offsetx
y = event.y_root - self.offsety
# make sure the potential coordinates x,y:
# 1) will not push any part of the widget outside of its parent container
# 2) is a multiple of self.SENSITIVITY
x = self.RoundToNearestMultiple(self.Max(self.Min(x, self.maxx), 0), self.SENSITIVITY)
y = self.RoundToNearestMultiple(self.Max(self.Min(y, self.maxy), 0), self.SENSITIVITY)
if x != self.px or y != self.py:
self.px = x
self.py = y
self.desktop.move(widget, x, y)
def Min(self, a, b):
if b < a:
return b
return a
def Max(self, a, b):
if b > a:
return b
return a
def RoundDownToMultiple(self, i, m):
return i/m*m
def RoundToNearestMultiple(self, i, m):
if i % m > m / 2:
return (i/m+1)*m
return i/m*m

View File

@ -1,72 +0,0 @@
# Gtk Imports
# Python imports
from .Grid import Grid
from .Dragging import Dragging
class Events:
def __init__(self, settings):
self.settings = settings
self.builder = self.settings.returnBuilder()
self.desktop = self.builder.get_object("Desktop")
self.webview = self.builder.get_object("webview")
self.desktopPath = self.settings.returnDesktopPath()
self.settings.setDefaultWebviewSettings(self.webview, self.webview.get_settings())
self.webview.load_uri(self.settings.returnWebHome())
# Add filter to allow only folders to be selected
selectedDirDialog = self.builder.get_object("selectedDirDialog")
filefilter = self.builder.get_object("Folders")
selectedDirDialog.add_filter(filefilter)
selectedDirDialog.set_filename(self.desktopPath)
self.grid = None
self.setIconViewDir(selectedDirDialog)
def setIconViewDir(self, widget, data=None):
newPath = widget.get_filename()
Grid(self.desktop, self.settings, newPath)
# File control events
def createFile(self):
pass
def updateFile(self, widget, data=None):
newName = widget.get_text().strip()
if data and data.keyval == 65293: # Enter key event
self.grid.updateFile(newName)
elif data == None: # Save button 'event'
self.grid.updateFile(newName)
def deleteFile(self, widget, data=None):
self.grid.deleteFile()
def copyFile(self):
pass
def cutFile(self):
pass
def pasteFile(self):
pass
# Webview events
def showWebview(self, widget):
self.builder.get_object("webViewer").popup()
def loadHome(self, widget):
self.webview.load_uri(self.settings.returnWebHome())
def runSearchWebview(self, widget, data=None):
if data.keyval == 65293:
self.webview.load_uri(widget.get_text().strip())
def refreshPage(self, widget, data=None):
self.webview.load_uri(self.webview.get_uri())
def setUrlBar(self, widget, data=None):
self.builder.get_object("webviewSearch").set_text(widget.get_uri())

View File

@ -1,93 +0,0 @@
import os, shutil, subprocess, threading
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class FileHandler:
def __init__(self):
# 'Filters'
self.office = ('.doc', '.docx', '.xls', '.xlsx', '.xlt', '.xltx' '.xlm', '.ppt', 'pptx', '.pps', '.ppsx', '.odt', '.rtf')
self.vids = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv', '.mpeg', '.mp4', '.webm')
self.txt = ('.txt', '.text', '.sh', '.cfg', '.conf')
self.music = ('.psf', '.mp3', '.ogg' , '.flac')
self.images = ('.png', '.jpg', '.jpeg', '.gif')
self.pdf = ('.pdf')
# Args
self.MEDIAPLAYER = "mpv";
self.IMGVIEWER = "mirage";
self.MUSICPLAYER = "/opt/deadbeef/bin/deadbeef";
self.OFFICEPROG = "libreoffice";
self.TEXTVIEWER = "leafpad";
self.PDFVIEWER = "evince";
self.FILEMANAGER = "spacefm";
self.MPLAYER_WH = " -xy 1600 -geometry 50%:50% ";
self.MPV_WH = " -geometry 50%:50% ";
@threaded
def openFile(self, file):
print("Opening: " + file)
if file.lower().endswith(self.vids):
subprocess.Popen([self.MEDIAPLAYER, self.MPV_WH, file])
elif file.lower().endswith(self.music):
subprocess.Popen([self.MUSICPLAYER, file])
elif file.lower().endswith(self.images):
subprocess.Popen([self.IMGVIEWER, file])
elif file.lower().endswith(self.txt):
subprocess.Popen([self.TEXTVIEWER, file])
elif file.lower().endswith(self.pdf):
subprocess.Popen([self.PDFVIEWER, file])
elif file.lower().endswith(self.office):
subprocess.Popen([self.OFFICEPROG, file])
else:
subprocess.Popen(['xdg-open', file])
def createFile(self, newFileName):
pass
def updateFile(self, oldFileName, newFileName):
try:
print("Renaming...")
print(oldFileName + " --> " + newFileName)
os.rename(oldFileName, newFileName)
return 0
except Exception as e:
print("An error occured renaming the file:")
print(e)
return 1
def deleteFile(self, toDeleteFile):
try:
print("Deleting...")
print(toDeleteFile)
if os.path.exists(toDeleteFile):
if os.path.isfile(toDeleteFile):
os.remove(toDeleteFile)
elif os.path.isdir(toDeleteFile):
shutil.rmtree(toDeleteFile)
else:
print("An error occured deleting the file:")
return 1
else:
print("The folder/file does not exist")
return 1
except Exception as e:
print("An error occured deleting the file:")
print(e)
return 1
return 0
def copyFile(self):
pass
def cutFile(self):
pass
def pasteFile(self):
pass

View File

@ -1,214 +0,0 @@
# Gtk Imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk as gtk
from gi.repository import Gdk as gdk
from gi.repository import GLib as glib
from gi.repository import GdkPixbuf
# Python imports
import os, threading, time
from os.path import isdir, isfile, join
from os import listdir
from .Icon import Icon
from .FileHandler import FileHandler
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class Grid:
def __init__(self, desktop, settings, newPath):
self.desktop = desktop
self.settings = settings
self.filehandler = FileHandler()
self.store = gtk.ListStore(GdkPixbuf.Pixbuf, str)
self.usrHome = settings.returnUserHome()
self.builder = settings.returnBuilder()
self.ColumnSize = settings.returnColumnSize()
self.currentPath = ""
self.selectedFile = ""
self.desktop.set_model(self.store)
self.desktop.set_pixbuf_column(0)
self.desktop.set_text_column(1)
self.desktop.connect("item-activated", self.iconLeftClickEventManager)
self.desktop.connect("button_press_event", self.iconRightClickEventManager, (self.desktop,))
self.desktop.connect("selection-changed", self.setIconSelectionArray, (self.desktop,))
self.vidsList = settings.returnVidsExtensionList()
self.imagesList = settings.returnImagesExtensionList()
self.gtkLock = False # Thread checks for gtkLock
self.threadLock = False # Gtk checks for thread lock
self.helperThread = None # Helper thread object
self.toWorkPool = [] # Thread fills pool and gtk empties it
self.copyCutArry = []
self.setIconViewDir(newPath)
def setIconViewDir(self, path):
self.store.clear()
self.currentPath = path
dirPaths = ['.', '..']
vids = []
images = []
desktop = []
files = []
for f in listdir(path):
file = join(path, f)
if self.settings.isHideHiddenFiles():
if f.startswith('.'):
continue
if isfile(file):
if file.lower().endswith(self.vidsList):
vids.append(f)
elif file.lower().endswith(self.imagesList):
images.append(f)
elif file.lower().endswith((".desktop",)):
desktop.append(f)
else:
files.append(f)
else:
dirPaths.append(f)
dirPaths.sort()
vids.sort()
images.sort()
desktop.sort()
files.sort()
files = dirPaths + vids + images + desktop + files
if self.helperThread:
self.helperThread.terminate()
self.helperThread = None
# Run helper thread...
self.threadLock = True
self.helperThread = threading.Thread(target=self.generateDirectoryGridIcon, args=(path, files)).start()
glib.idle_add(self.addToGrid, (file,)) # This must stay in the main thread b/c
# gtk isn't thread safe/aware So, we
# make a sad lil thread hot potato 'game'
# out of this process.
# @threaded
def generateDirectoryGridIcon(self, dirPath, files):
# NOTE: We'll be passing pixbuf after retreval to keep Icon.py file more
# universaly usable. We can just remove get_pixbuf to get a gtk.Image type
for file in files:
image = Icon(self.settings).createIcon(dirPath, file)
self.toWorkPool.append([image.get_pixbuf(), file])
self.threadLock = False
self.gtkLock = True
def addToGrid(self, args):
# NOTE: Returning true tells gtk to check again in the future when idle.
# False ends checks and "continues normal flow"
files = args[0]
if len(self.toWorkPool) > 0:
for dataSet in self.toWorkPool:
self.store.append(dataSet)
if len(self.store) == len(files): # Confirm processed all files and cleanup
self.gtkLock = False
self.threadLock = False
self.toWorkPool.clear()
return False
# Check again when idle; If nothing else is updating, this function
# gets called immediatly. So, we play hot potato by passing lock to Thread
else:
self.toWorkPool.clear()
self.gtkLock = False
self.threadLock = True
time.sleep(.005) # Fixes refresh and up icon not being added.
return True
def setIconSelectionArray(self, widget, data=None):
pass
# os.system('cls||clear')
# print(data)
def iconLeftClickEventManager(self, widget, item):
try:
model = widget.get_model()
fileName = model[item][1]
dir = self.currentPath
file = dir + "/" + fileName
if fileName == ".":
self.setIconViewDir(dir)
elif fileName == "..":
parentDir = os.path.abspath(os.path.join(dir, os.pardir))
self.currentPath = parentDir
self.setIconViewDir(parentDir)
elif isdir(file):
self.currentPath = file
self.setIconViewDir(self.currentPath)
elif isfile(file):
self.filehandler.openFile(file)
except Exception as e:
print(e)
def iconRightClickEventManager(self, widget, eve, params):
try:
if eve.type == gdk.EventType.BUTTON_PRESS and eve.button == 3:
popover = self.builder.get_object("iconControlsWindow")
popover.show_all()
popover.popup()
# # NOTE: Need to change name of listview box...
# children = widget.get_children()[0].get_children()
# fileName = children[1].get_text()
# dir = self.currentPath
# file = dir + "/" + fileName
#
# input = self.builder.get_object("iconRenameInput")
# popover = self.builder.get_object("iconControlsWindow")
# self.selectedFile = file # Used for return to caller
#
# input.set_text(fileName)
# popover.set_relative_to(widget)
# popover.set_position(gtk.PositionType.RIGHT)
# popover.show_all()
# popover.popup()
except Exception as e:
print(e)
# Passthrough file control events
def createFile(arg):
pass
def updateFile(self, file):
newName = self.currentPath + "/" + file
status = self.filehandler.updateFile(self.selectedFile, newName)
if status == 0:
self.selectedFile = newName
self.setIconViewDir(self.currentPath)
def deleteFile(self):
status = self.filehandler.deleteFile(self.selectedFile)
if status == 0:
self.selectedFile = ""
self.setIconViewDir(self.currentPath)
def copyFile(self):
pass
def cutFile(self):
pass
def pasteFile(self):
pass

View File

@ -1,167 +0,0 @@
# Gtk Imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk as gtk
from gi.repository import Gio as gio
from gi.repository import GdkPixbuf
from xdg.DesktopEntry import DesktopEntry
# Python Imports
import os, subprocess, hashlib, threading
from os.path import isdir, isfile, join
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class Icon:
def __init__(self, settings):
self.settings = settings
self.thubnailGen = settings.getThumbnailGenerator()
self.vidsList = settings.returnVidsExtensionList()
self.imagesList = settings.returnImagesExtensionList()
self.GTK_ORIENTATION = settings.returnIconImagePos()
self.usrHome = settings.returnUserHome()
self.iconContainerWH = settings.returnContainerWH()
self.systemIconImageWH = settings.returnSystemIconImageWH()
self.viIconWH = settings.returnVIIconWH()
def createIcon(self, dir, file):
fullPath = dir + "/" + file
return self.getIconImage(file, fullPath)
def getIconImage(self, file, fullPath):
try:
thumbnl = None
# Video thumbnail
if file.lower().endswith(self.vidsList):
fileHash = hashlib.sha256(str.encode(fullPath)).hexdigest()
hashImgPth = self.usrHome + "/.thumbnails/normal/" + fileHash + ".png"
if isfile(hashImgPth) == False:
self.generateVideoThumbnail(fullPath, hashImgPth)
thumbnl = self.createIconImageBuffer(hashImgPth, self.viIconWH)
# Image Icon
elif file.lower().endswith(self.imagesList):
thumbnl = self.createIconImageBuffer(fullPath, self.viIconWH)
# .desktop file parsing
elif fullPath.lower().endswith( ('.desktop',) ):
thumbnl = self.parseDesktopFiles(fullPath)
# System icons
else:
thumbnl = self.getSystemThumbnail(fullPath, self.systemIconImageWH[0])
if thumbnl == None: # If no icon, try stock file icon...
thumbnl = gtk.Image.new_from_icon_name("gtk-file", gtk.IconSize.LARGE_TOOLBAR)
if thumbnl == None: # If no icon whatsoever, return internal default
thumbnl = gtk.Image.new_from_file("resources/icons/bin.png")
return thumbnl
except Exception as e:
print(e)
return gtk.Image.new_from_file("resources/icons/bin.png")
def parseDesktopFiles(self, fullPath):
try:
xdgObj = DesktopEntry(fullPath)
icon = xdgObj.getIcon()
iconsDirs = "/usr/share/icons"
altIconPath = ""
if "steam" in icon:
steamIconsDir = self.usrHome + "/.thumbnails/steam_icons/"
name = xdgObj.getName()
fileHash = hashlib.sha256(str.encode(name)).hexdigest()
if isdir(steamIconsDir) == False:
os.mkdir(steamIconsDir)
hashImgPth = steamIconsDir + fileHash + ".jpg"
if isfile(hashImgPth) == True:
# Use video sizes since headers are bigger
return self.createIconImageBuffer(hashImgPth, self.viIconWH)
execStr = xdgObj.getExec()
parts = execStr.split("steam://rungameid/")
id = parts[len(parts) - 1]
# NOTE: Can try this logic instead...
# if command exists use it instead of header image
# if "steamcmd app_info_print id":
# proc = subprocess.Popen(["steamcmd", "app_info_print", id])
# proc.wait()
# else:
# use the bottom logic
imageLink = "https://steamcdn-a.akamaihd.net/steam/apps/" + id + "/header.jpg"
proc = subprocess.Popen(["wget", "-O", hashImgPth, imageLink])
proc.wait()
# Use video sizes since headers are bigger
return self.createIconImageBuffer(hashImgPth, self.viIconWH)
elif os.path.exists(icon):
return self.createIconImageBuffer(icon, self.systemIconImageWH)
else:
for (dirpath, dirnames, filenames) in os.walk(iconsDirs):
for file in filenames:
appNM = "application-x-" + icon
if appNM in file:
altIconPath = dirpath + "/" + file
break
return self.createIconImageBuffer(altIconPath, self.systemIconImageWH)
except Exception as e:
print(e)
return None
def getSystemThumbnail(self, filename, size):
try:
iconPath = None
if os.path.exists(filename):
file = gio.File.new_for_path(filename)
info = file.query_info('standard::icon' , 0 , gio.Cancellable())
icon = info.get_icon().get_names()[0]
iconTheme = gtk.IconTheme.get_default()
iconFile = iconTheme.lookup_icon(icon , size , 0)
if iconFile != None:
iconPath = iconFile.get_filename()
return self.createIconImageBuffer(iconPath, self.systemIconImageWH)
else:
return None
else:
return None
except Exception as e:
print(e)
return None
def createIconImageBuffer(self, path, wxh):
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(path, wxh[0], wxh[1], False)
except Exception as e:
return None
return gtk.Image.new_from_pixbuf(pixbuf)
def generateVideoThumbnail(self, fullPath, hashImgPth):
try:
proc = subprocess.Popen([self.thubnailGen, "-t", "65%", "-s", "300", "-c", "jpg", "-i", fullPath, "-o", hashImgPth])
proc.wait()
except Exception as e:
print(e)

View File

@ -1,6 +0,0 @@
from utils.Dragging import Dragging
from utils.Settings import Settings
from utils.Events import Events
from utils.Grid import Grid
from utils.Icon import Icon
from utils.FileHandler import FileHandler

View File

@ -1,6 +0,0 @@
#!/bin/bash
function main() {
gcc -no-pie -s PyFM_exec_bin.cpp -o pyfm
}
main;

View File

@ -1,9 +1,9 @@
from setuptools import setup
setup(
name='pyfm',
name='SolarFM',
version='0.0.1',
packages=['pyfm'],
packages=['solarfm'],
install_requires=[
'setproctitle',
'PyGobject',

View File

@ -13,6 +13,6 @@ function main() {
echo "Working Dir: " $(pwd)
source "/home/abaddon/Portable_Apps/py-venvs/flask-apps-venv/venv/bin/activate"
python ./pyfm
python ./solarfm
}
main "$@";

View File

@ -0,0 +1,66 @@
# Python imports
import builtins
# Gtk imports
# Application imports
from signal_classes.DBusControllerMixin import DBusControllerMixin
class Builtins(DBusControllerMixin):
"""Docstring for __builtins__ extender"""
def __init__(self):
# NOTE: The format used is list of [type, target, data]
# Where data may be any kind of data
self._gui_events = []
self._fm_events = []
self.monitor_events = True
self.keep_ipc_alive = True
self.is_ipc_alive = False
# Makeshift fake "events" type system FIFO
def _pop_gui_event(self):
if len(self._gui_events) > 0:
return self._gui_events.pop(0)
return None
def _pop_fm_event(self):
if len(self._fm_events) > 0:
return self._fm_events.pop(0)
return None
def push_gui_event(self, event):
if len(event) == 3:
self._gui_events.append(event)
return None
raise Exception("Invald event format! Please do: [type, target, data]")
def push_fm_event(self, event):
if len(event) == 3:
self._fm_events.append(event)
return None
raise Exception("Invald event format! Please do: [type, target, data]")
def read_gui_event(self):
return self._gui_events[0]
def read_fm_event(self):
return self._fm_events[0]
def consume_gui_event(self):
return self._pop_gui_event()
def consume_fm_event(self):
return self._pop_fm_event()
# NOTE: Just reminding myself we can add to builtins two different ways...
# __builtins__.update({"event_system": Builtins()})
builtins.event_system = Builtins()
builtins.event_sleep_time = 0.5
builtins.debug = False

View File

@ -0,0 +1,49 @@
# Python imports
import os, inspect, time
# Gtk imports
# Application imports
from utils import Settings
from signal_classes import Controller
from __builtins__ import Builtins
class Main(Builtins):
def __init__(self, args, unknownargs):
event_system.create_ipc_server()
time.sleep(0.5)
if not event_system.is_ipc_alive:
if unknownargs:
for arg in unknownargs:
if os.path.isdir(arg):
message = f"FILE|{arg}"
event_system.send_ipc_message(message)
if args.new_tab and os.path.isdir(args.new_tab):
message = f"FILE|{args.new_tab}"
event_system.send_ipc_message(message)
raise Exception("IPC Server Exists: Will send path(s) to it and close...")
settings = Settings()
settings.createWindow()
controller = Controller(args, unknownargs, settings)
if not controller:
raise Exception("Controller exited and doesn't exist...")
# Gets the methods from the classes and sets to handler.
# Then, builder connects to any signals it needs.
classes = [controller]
handlers = {}
for c in classes:
methods = None
try:
methods = inspect.getmembers(c, predicate=inspect.ismethod)
handlers.update(methods)
except Exception as e:
pass
settings.builder.connect_signals(handlers)

View File

@ -0,0 +1,39 @@
#!/usr/bin/python3
# Python imports
import argparse
from setproctitle import setproctitle
import tracemalloc
tracemalloc.start()
# Gtk imports
import gi, faulthandler, traceback
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from __init__ import Main
if __name__ == "__main__":
try:
setproctitle('solarfm')
faulthandler.enable() # For better debug info
parser = argparse.ArgumentParser()
# Add long and short arguments
parser.add_argument("--new-tab", "-t", default="", help="Open a file into new tab.")
parser.add_argument("--new-window", "-w", default="", help="Open a file into a new window.")
# Read arguments (If any...)
args, unknownargs = parser.parse_known_args()
Main(args, unknownargs)
Gtk.main()
except Exception as e:
print(repr(e))
event_system.keep_ipc_alive = False
if debug:
traceback.print_exc()

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,67 @@
/* Set fm to have transparent window */
box,
iconview,
notebook,
paned,
stack,
scrolledwindow,
treeview.view,
.content-view,
.view {
background: rgba(19, 21, 25, 0.14);
color: rgba(255, 255, 255, 1);
}
notebook > header > tabs > tab:checked {
/* Neon Blue 00e8ff */
background-color: rgba(0, 232, 255, 0.25);
/* Dark Bergundy */
/* background-color: rgba(116, 0, 0, 0.25); */
color: rgba(255, 255, 255, 0.5);
}
#message_view {
font: 16px "Monospace";
}
.notebook-selected-focus {
/* Neon Blue 00e8ff border */
border: 2px solid rgba(0, 232, 255, 0.25);
/* Dark Bergundy */
/* border: 2px solid rgba(116, 0, 0, 0.64); */
}
.view:selected,
.view:selected:hover {
box-shadow: inset 0 0 0 9999px rgba(21, 158, 167, 0.57);
color: rgba(255, 255, 255, 0.5);;
}
/* * {
background: rgba(0, 0, 0, 0.14);
color: rgba(255, 255, 255, 1);
} */
/* * selection {
background-color: rgba(116, 0, 0, 0.65);
color: rgba(255, 255, 255, 0.5);
} */
/* Rubberband coloring */
/* .rubberband,
rubberband,
flowbox rubberband,
treeview.view rubberband,
.content-view rubberband,
.content-view .rubberband,
XfdesktopIconView.view .rubberband {
border: 1px solid #6c6c6c;
background-color: rgba(21, 158, 167, 0.57);
}
XfdesktopIconView.view:active {
background-color: rgba(172, 102, 21, 1);
} */

View File

@ -0,0 +1 @@
from .windows import WindowController

View File

@ -0,0 +1,66 @@
# Python imports
from random import randint
# Lib imports
# Application imports
from .view import View
class Window:
def __init__(self):
self.id_length = 10
self.id = ""
self.name = ""
self.nickname = ""
self.isHidden = False
self.views = []
self.generate_id()
def random_with_N_digits(self, n):
range_start = 10**(n-1)
range_end = (10**n)-1
return randint(range_start, range_end)
def generate_id(self):
self.id = str(self.random_with_N_digits(self.id_length))
def get_window_id(self):
return self.id
def create_view(self):
view = View()
self.views.append(view)
return view
def pop_view(self):
self.views.pop()
def delete_view_by_id(self, vid):
for view in self.views:
if view.id == vid:
self.views.remove(view)
break
def get_view_by_id(self, vid):
for view in self.views:
if view.id == vid:
return view
def get_view_by_index(self, index):
return self.views[index]
def get_views_count(self):
return len(self.views)
def get_all_views(self):
return self.views
def list_files_from_views(self):
for view in self.views:
print(view.files)

View File

@ -0,0 +1,179 @@
# Python imports
import threading, subprocess, time, json
from os import path
# Lib imports
# Application imports
from . import Window
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class WindowController:
def __init__(self):
USER_HOME = path.expanduser('~')
CONFIG_PATH = USER_HOME + "/.config/solarfm"
self.session_file = CONFIG_PATH + "/session.json"
self._event_sleep_time = 1
self.active_window_id = ""
self.active_tab_id = ""
self.windows = []
self.fm_event_observer()
@threaded
def fm_event_observer(self):
while event_system.monitor_events:
time.sleep(event_sleep_time)
event = event_system.consume_fm_event()
if event:
print(event)
def set_active_data(self, wid, tid):
self.active_window_id = str(wid)
self.active_tab_id = str(tid)
def get_active_data(self):
return self.active_window_id, self.active_tab_id
def create_window(self):
window = Window()
window.name = "window_" + window.id
window.nickname = "window_" + str(len(self.windows) + 1)
self.windows.append(window)
return window
def add_view_for_window(self, win_id):
for window in self.windows:
if window.id == win_id:
return window.create_view()
def add_view_for_window_by_name(self, name):
for window in self.windows:
if window.name == name:
return window.create_view()
def add_view_for_window_by_nickname(self, nickname):
for window in self.windows:
if window.nickname == nickname:
return window.create_view()
def pop_window(self):
self.windows.pop()
def delete_window_by_id(self, win_id):
for window in self.windows:
if window.id == win_id:
self.windows.remove(window)
break
def delete_window_by_name(self, name):
for window in self.windows:
if window.name == name:
self.windows.remove(window)
break
def delete_window_by_nickname(self, nickname):
for window in self.windows:
if window.nickname == nickname:
self.windows.remove(window)
break
def get_window_by_id(self, win_id):
for window in self.windows:
if window.id == win_id:
return window
raise(f"No Window by ID {win_id} found!")
def get_window_by_name(self, name):
for window in self.windows:
if window.name == name:
return window
raise(f"No Window by Name {name} found!")
def get_window_by_nickname(self, nickname):
for window in self.windows:
if window.nickname == nickname:
return window
raise(f"No Window by Nickname {nickname} found!")
def get_window_by_index(self, index):
return self.windows[index]
def get_all_windows(self):
return self.windows
def set_window_nickname(self, win_id = None, nickname = ""):
for window in self.windows:
if window.id == win_id:
window.nickname = nickname
def list_windows(self):
print("\n[ ---- Windows ---- ]\n")
for window in self.windows:
print(f"\nID: {window.id}")
print(f"Name: {window.name}")
print(f"Nickname: {window.nickname}")
print(f"Is Hidden: {window.isHidden}")
print(f"View Count: {window.get_views_count()}")
print("\n-------------------------\n")
def list_files_from_views_of_window(self, win_id):
for window in self.windows:
if window.id == win_id:
window.list_files_from_views()
break
def get_views_count(self, win_id):
for window in self.windows:
if window.id == win_id:
return window.get_views_count()
def get_views_from_window(self, win_id):
for window in self.windows:
if window.id == win_id:
return window.get_all_views()
def save_state(self):
windows = []
for window in self.windows:
views = []
for view in window.views:
views.append(view.get_current_directory())
windows.append(
[
{
'window':{
"ID": window.id,
"Name": window.name,
"Nickname": window.nickname,
"isHidden": f"{window.isHidden}",
'views': views
}
}
]
)
with open(self.session_file, 'w') as outfile:
json.dump(windows, outfile, separators=(',', ':'), indent=4)
def load_state(self):
if path.isfile(self.session_file):
with open(self.session_file) as infile:
return json.load(infile)

View File

@ -0,0 +1,2 @@
from .Window import Window
from .WindowController import WindowController

View File

@ -0,0 +1,59 @@
# Python imports
import os
# Lib imports
# Application imports
class Path:
def get_home(self):
return os.path.expanduser("~") + self.subpath
def get_path(self):
return "/" + "/".join(self.path)
def get_path_list(self):
return self.path
def push_to_path(self, dir):
self.path.append(dir)
self.load_directory()
def pop_from_path(self):
self.path.pop()
if not self.go_past_home:
if self.get_home() not in self.get_path():
self.set_to_home()
self.load_directory()
def set_path(self, path):
if path == self.get_path():
return
if os.path.isdir(path):
self.path = list( filter(None, path.replace("\\", "/").split('/')) )
self.load_directory()
return True
return False
def set_path_with_sub_path(self, sub_path):
path = os.path.join(self.get_home(), sub_path)
if path == self.get_path():
return False
if os.path.isdir(path):
self.path = list( filter(None, path.replace("\\", "/").split('/')) )
self.load_directory()
return True
return False
def set_to_home(self):
home = os.path.expanduser("~") + self.subpath
path = list( filter(None, home.replace("\\", "/").split('/')) )
self.path = path
self.load_directory()

View File

@ -0,0 +1,227 @@
# Python imports
import hashlib
import os
from os import listdir
from os.path import isdir, isfile, join
from random import randint
# Lib imports
# Application imports
from .utils import Settings, Launcher, FileHandler
from .icons import Icon
from . import Path
class View(Settings, FileHandler, Launcher, Icon, Path):
def __init__(self):
self. logger = None
self.id_length = 10
self.id = ""
self.wid = None
self.dir_watcher = None
self.hide_hidden = self.HIDE_HIDDEN_FILES
self.files = []
self.dirs = []
self.vids = []
self.images = []
self.desktop = []
self.ungrouped = []
self.hidden = []
self.generate_id()
self.set_to_home()
def random_with_N_digits(self, n):
range_start = 10**(n-1)
range_end = (10**n)-1
return randint(range_start, range_end)
def generate_id(self):
self.id = str(self.random_with_N_digits(self.id_length))
def get_tab_id(self):
return self.id
def set_wid(self, _wid):
self.wid = _wid
def get_wid(self):
return self.wid
def set_dir_watcher(self, watcher):
self.dir_watcher = watcher
def get_dir_watcher(self):
return self.dir_watcher
def load_directory(self):
path = self.get_path()
self.dirs = []
self.vids = []
self.images = []
self.desktop = []
self.ungrouped = []
self.hidden = []
self.files = []
if not isdir(path):
self.set_to_home()
return ""
for f in listdir(path):
file = join(path, f)
if self.hide_hidden:
if f.startswith('.'):
self.hidden.append(f)
continue
if isfile(file):
lowerName = file.lower()
if lowerName.endswith(self.fvideos):
self.vids.append(f)
elif lowerName.endswith(self.fimages):
self.images.append(f)
elif lowerName.endswith((".desktop",)):
self.desktop.append(f)
else:
self.ungrouped.append(f)
else:
self.dirs.append(f)
self.dirs.sort()
self.vids.sort()
self.images.sort()
self.desktop.sort()
self.ungrouped.sort()
self.files = self.dirs + self.vids + self.images + self.desktop + self.ungrouped
def hash_text(self, text):
return hashlib.sha256(str.encode(text)).hexdigest()[:18]
def hash_set(self, arry):
data = []
for arr in arry:
data.append([arr, self.hash_text(arr)])
return data
def is_folder_locked(self, hash):
if self.lock_folder:
path_parts = self.get_path().split('/')
file = self.get_path_part_from_hash(hash)
# Insure chilren folders are locked too.
lockedFolderInPath = False
for folder in self.locked_folders:
if folder in path_parts:
lockedFolderInPath = True
break
return (file in self.locked_folders or lockedFolderInPath)
else:
return False
def get_not_hidden_count(self):
return len(self.files) + \
len(self.dirs) + \
len(self.vids) + \
len(self.images) + \
len(self.desktop) + \
len(self.ungrouped)
def get_hidden_count(self):
return len(self.hidden)
def get_files_count(self):
return len(self.files)
def get_path_part_from_hash(self, hash):
files = self.get_files()
file = None
for f in files:
if hash == f[1]:
file = f[0]
break
return file
def get_files_formatted(self):
files = self.hash_set(self.files),
dirs = self.hash_set(self.dirs),
videos = self.get_videos(),
images = self.hash_set(self.images),
desktops = self.hash_set(self.desktop),
ungrouped = self.hash_set(self.ungrouped)
return {
'path_head': self.get_path(),
'list': {
'files': files,
'dirs': dirs,
'videos': videos,
'images': images,
'desktops': desktops,
'ungrouped': ungrouped
}
}
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):
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):
return self.get_path()
def get_current_sub_path(self):
path = self.get_path()
home = self.get_home() + "/"
return path.replace(home, "")
def get_end_of_path(self):
parts = self.get_current_directory().split("/")
size = len(parts)
return parts[size - 1]
def get_dot_dots(self):
return self.hash_set(['.', '..'])
def get_files(self):
return self.hash_set(self.files)
def get_dirs(self):
return self.hash_set(self.dirs)
def get_videos(self):
return self.hash_set(self.vids)
def get_images(self):
return self.hash_set(self.images)
def get_desktops(self):
return self.hash_set(self.desktop)
def get_ungrouped(self):
return self.hash_set(self.ungrouped)

View File

@ -0,0 +1,5 @@
from .utils import *
from .icons import *
from .Path import Path
from .View import View

View File

@ -0,0 +1,76 @@
# Python Imports
import os, subprocess, threading, hashlib
from os.path import isfile
# Gtk imports
from gi.repository import GdkPixbuf
# Application imports
from .mixins import *
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class Icon(DesktopIconMixin, VideoIconMixin):
def create_icon(self, dir, file):
full_path = dir + "/" + file
return self.get_icon_image(dir, file, full_path)
def get_icon_image(self, dir, file, full_path):
try:
thumbnl = None
if file.lower().endswith(self.fvideos): # Video icon
thumbnl = self.create_thumbnail(dir, file)
elif file.lower().endswith(self.fimages): # Image Icon
thumbnl = self.create_scaled_image(full_path, self.VIDEO_ICON_WH)
elif full_path.lower().endswith( ('.desktop',) ): # .desktop file parsing
thumbnl = self.parse_desktop_files(full_path)
return thumbnl
except Exception as e:
return None
def create_thumbnail(self, dir, file):
full_path = dir + "/" + file
try:
file_hash = hashlib.sha256(str.encode(full_path)).hexdigest()
hash_img_pth = self.ABS_THUMBS_PTH + "/" + file_hash + ".jpg"
if isfile(hash_img_pth) == False:
self.generate_video_thumbnail(full_path, hash_img_pth)
thumbnl = self.create_scaled_image(hash_img_pth, self.VIDEO_ICON_WH)
if thumbnl == None: # If no icon whatsoever, return internal default
thumbnl = GdkPixbuf.Pixbuf.new_from_file(self.DEFAULT_ICONS + "/video.png")
return thumbnl
except Exception as e:
print("Thumbnail generation issue:")
print( repr(e) )
return GdkPixbuf.Pixbuf.new_from_file(self.DEFAULT_ICONS + "/video.png")
def create_scaled_image(self, path, wxh):
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(path)
scaled_pixbuf = pixbuf.scale_simple(wxh[0], wxh[1], 2) # 2 = BILINEAR and is best by default
return scaled_pixbuf
except Exception as e:
print("Image Scaling Issue:")
print( repr(e) )
return None
def create_from_file(self, path):
try:
return GdkPixbuf.Pixbuf.new_from_file(path)
except Exception as e:
print("Image from file Issue:")
print( repr(e) )
return None
def return_generic_icon(self):
return GdkPixbuf.Pixbuf.new_from_file(self.DEFAULT_ICON)

View File

@ -0,0 +1,4 @@
from .mixins import DesktopIconMixin
from .mixins import VideoIconMixin
from .Icon import Icon

View File

@ -0,0 +1,65 @@
# Python Imports
import os, subprocess, hashlib
from os.path import isfile
# Gtk imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from .xdg.DesktopEntry import DesktopEntry
class DesktopIconMixin:
def parse_desktop_files(self, full_path):
try:
xdgObj = DesktopEntry(full_path)
icon = xdgObj.getIcon()
alt_icon_path = ""
if "steam" in icon:
name = xdgObj.getName()
file_hash = hashlib.sha256(str.encode(name)).hexdigest()
hash_img_pth = self.STEAM_ICONS_PTH + "/" + file_hash + ".jpg"
if isfile(hash_img_pth) == True:
# Use video sizes since headers are bigger
return self.create_scaled_image(hash_img_pth, self.VIDEO_ICON_WH)
exec_str = xdgObj.getExec()
parts = exec_str.split("steam://rungameid/")
id = parts[len(parts) - 1]
imageLink = self.STEAM_BASE_URL + id + "/header.jpg"
proc = subprocess.Popen(["wget", "-O", hash_img_pth, imageLink])
proc.wait()
# Use video thumbnail sizes since headers are bigger
return self.create_scaled_image(hash_img_pth, self.VIDEO_ICON_WH)
elif os.path.exists(icon):
return self.create_scaled_image(icon, self.SYS_ICON_WH)
else:
alt_icon_path = ""
for dir in self.ICON_DIRS:
alt_icon_path = self.traverse_icons_folder(dir, icon)
if alt_icon_path != "":
break
return self.create_scaled_image(alt_icon_path, self.SYS_ICON_WH)
except Exception as e:
print(".desktop icon generation issue:")
print( repr(e) )
return None
def traverse_icons_folder(self, path, icon):
alt_icon_path = ""
for (dirpath, dirnames, filenames) in os.walk(path):
for file in filenames:
appNM = "application-x-" + icon
if icon in file or appNM in file:
alt_icon_path = dirpath + "/" + file
break
return alt_icon_path

View File

@ -0,0 +1,53 @@
# Python Imports
import subprocess
# Gtk imports
# Application imports
class VideoIconMixin:
def generate_video_thumbnail(self, full_path, hash_img_pth):
try:
proc = subprocess.Popen([self.FFMPG_THUMBNLR, "-t", "65%", "-s", "300", "-c", "jpg", "-i", full_path, "-o", hash_img_pth])
proc.wait()
except Exception as e:
self.logger.debug(repr(e))
self.ffprobe_generate_video_thumbnail(full_path, hash_img_pth)
def ffprobe_generate_video_thumbnail(self, full_path, hash_img_pth):
proc = None
try:
# Stream duration
command = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=duration", "-of", "default=noprint_wrappers=1:nokey=1", full_path]
data = subprocess.run(command, stdout=subprocess.PIPE)
duration = data.stdout.decode('utf-8')
# Format (container) duration
if "N/A" in duration:
command = ["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", full_path]
data = subprocess.run(command , stdout=subprocess.PIPE)
duration = data.stdout.decode('utf-8')
# Stream duration type: image2
if "N/A" in duration:
command = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-f", "image2", "-show_entries", "stream=duration", "-of", "default=noprint_wrappers=1:nokey=1", full_path]
data = subprocess.run(command, stdout=subprocess.PIPE)
duration = data.stdout.decode('utf-8')
# Format (container) duration type: image2
if "N/A" in duration:
command = ["ffprobe", "-v", "error", "-f", "image2", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", full_path]
data = subprocess.run(command , stdout=subprocess.PIPE)
duration = data.stdout.decode('utf-8')
# Get frame roughly 35% through video
grabTime = str( int( float( duration.split(".")[0] ) * 0.35) )
command = ["ffmpeg", "-ss", grabTime, "-an", "-i", full_path, "-s", "320x180", "-vframes", "1", hash_img_pth]
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
proc.wait()
except Exception as e:
print("Video thumbnail generation issue in thread:")
print( repr(e) )
self.logger.debug(repr(e))

View File

@ -0,0 +1,4 @@
from . import xdg
from .VideoIconMixin import VideoIconMixin
from .DesktopIconMixin import DesktopIconMixin

View File

@ -0,0 +1,160 @@
"""
This module is based on a rox module (LGPL):
http://cvs.sourceforge.net/viewcvs.py/rox/ROX-Lib2/python/rox/basedir.py?rev=1.9&view=log
The freedesktop.org Base Directory specification provides a way for
applications to locate shared data and configuration:
http://standards.freedesktop.org/basedir-spec/
(based on version 0.6)
This module can be used to load and save from and to these directories.
Typical usage:
from rox import basedir
for dir in basedir.load_config_paths('mydomain.org', 'MyProg', 'Options'):
print "Load settings from", dir
dir = basedir.save_config_path('mydomain.org', 'MyProg')
print >>file(os.path.join(dir, 'Options'), 'w'), "foo=2"
Note: see the rox.Options module for a higher-level API for managing options.
"""
import os, stat
_home = os.path.expanduser('~')
xdg_data_home = os.environ.get('XDG_DATA_HOME') or \
os.path.join(_home, '.local', 'share')
xdg_data_dirs = [xdg_data_home] + \
(os.environ.get('XDG_DATA_DIRS') or '/usr/local/share:/usr/share').split(':')
xdg_config_home = os.environ.get('XDG_CONFIG_HOME') or \
os.path.join(_home, '.config')
xdg_config_dirs = [xdg_config_home] + \
(os.environ.get('XDG_CONFIG_DIRS') or '/etc/xdg').split(':')
xdg_cache_home = os.environ.get('XDG_CACHE_HOME') or \
os.path.join(_home, '.cache')
xdg_data_dirs = [x for x in xdg_data_dirs if x]
xdg_config_dirs = [x for x in xdg_config_dirs if x]
def save_config_path(*resource):
"""Ensure ``$XDG_CONFIG_HOME/<resource>/`` exists, and return its path.
'resource' should normally be the name of your application. Use this
when saving configuration settings.
"""
resource = os.path.join(*resource)
assert not resource.startswith('/')
path = os.path.join(xdg_config_home, resource)
if not os.path.isdir(path):
os.makedirs(path, 0o700)
return path
def save_data_path(*resource):
"""Ensure ``$XDG_DATA_HOME/<resource>/`` exists, and return its path.
'resource' should normally be the name of your application or a shared
resource. Use this when saving or updating application data.
"""
resource = os.path.join(*resource)
assert not resource.startswith('/')
path = os.path.join(xdg_data_home, resource)
if not os.path.isdir(path):
os.makedirs(path)
return path
def save_cache_path(*resource):
"""Ensure ``$XDG_CACHE_HOME/<resource>/`` exists, and return its path.
'resource' should normally be the name of your application or a shared
resource."""
resource = os.path.join(*resource)
assert not resource.startswith('/')
path = os.path.join(xdg_cache_home, resource)
if not os.path.isdir(path):
os.makedirs(path)
return path
def load_config_paths(*resource):
"""Returns an iterator which gives each directory named 'resource' in the
configuration search path. Information provided by earlier directories should
take precedence over later ones, and the user-specific config dir comes
first."""
resource = os.path.join(*resource)
for config_dir in xdg_config_dirs:
path = os.path.join(config_dir, resource)
if os.path.exists(path): yield path
def load_first_config(*resource):
"""Returns the first result from load_config_paths, or None if there is nothing
to load."""
for x in load_config_paths(*resource):
return x
return None
def load_data_paths(*resource):
"""Returns an iterator which gives each directory named 'resource' in the
application data search path. Information provided by earlier directories
should take precedence over later ones."""
resource = os.path.join(*resource)
for data_dir in xdg_data_dirs:
path = os.path.join(data_dir, resource)
if os.path.exists(path): yield path
def get_runtime_dir(strict=True):
"""Returns the value of $XDG_RUNTIME_DIR, a directory path.
This directory is intended for 'user-specific non-essential runtime files
and other file objects (such as sockets, named pipes, ...)', and
'communication and synchronization purposes'.
As of late 2012, only quite new systems set $XDG_RUNTIME_DIR. If it is not
set, with ``strict=True`` (the default), a KeyError is raised. With
``strict=False``, PyXDG will create a fallback under /tmp for the current
user. This fallback does *not* provide the same guarantees as the
specification requires for the runtime directory.
The strict default is deliberately conservative, so that application
developers can make a conscious decision to allow the fallback.
"""
try:
return os.environ['XDG_RUNTIME_DIR']
except KeyError:
if strict:
raise
import getpass
fallback = '/tmp/pyxdg-runtime-dir-fallback-' + getpass.getuser()
create = False
try:
# This must be a real directory, not a symlink, so attackers can't
# point it elsewhere. So we use lstat to check it.
st = os.lstat(fallback)
except OSError as e:
import errno
if e.errno == errno.ENOENT:
create = True
else:
raise
else:
# The fallback must be a directory
if not stat.S_ISDIR(st.st_mode):
os.unlink(fallback)
create = True
# Must be owned by the user and not accessible by anyone else
elif (st.st_uid != os.getuid()) \
or (st.st_mode & (stat.S_IRWXG | stat.S_IRWXO)):
os.rmdir(fallback)
create = True
if create:
os.mkdir(fallback, 0o700)
return fallback

View File

@ -0,0 +1,39 @@
"""
Functions to configure Basic Settings
"""
language = "C"
windowmanager = None
icon_theme = "hicolor"
icon_size = 48
cache_time = 5
root_mode = False
def setWindowManager(wm):
global windowmanager
windowmanager = wm
def setIconTheme(theme):
global icon_theme
icon_theme = theme
import xdg.IconTheme
xdg.IconTheme.themes = []
def setIconSize(size):
global icon_size
icon_size = size
def setCacheTime(time):
global cache_time
cache_time = time
def setLocale(lang):
import locale
lang = locale.normalize(lang)
locale.setlocale(locale.LC_ALL, lang)
import xdg.Locale
xdg.Locale.update(lang)
def setRootMode(boolean):
global root_mode
root_mode = boolean

View File

@ -0,0 +1,435 @@
"""
Complete implementation of the XDG Desktop Entry Specification
http://standards.freedesktop.org/desktop-entry-spec/
Not supported:
- Encoding: Legacy Mixed
- Does not check exec parameters
- Does not check URL's
- Does not completly validate deprecated/kde items
- Does not completly check categories
"""
from .IniFile import IniFile
from . import Locale
from .IniFile import is_ascii
from .Exceptions import ParsingError
from .util import which
import os.path
import re
import warnings
class DesktopEntry(IniFile):
"Class to parse and validate Desktop Entries"
defaultGroup = 'Desktop Entry'
def __init__(self, filename=None):
"""Create a new DesktopEntry.
If filename exists, it will be parsed as a desktop entry file. If not,
or if filename is None, a blank DesktopEntry is created.
"""
self.content = dict()
if filename and os.path.exists(filename):
self.parse(filename)
elif filename:
self.new(filename)
def __str__(self):
return self.getName()
def parse(self, file):
"""Parse a desktop entry file.
This can raise :class:`~xdg.Exceptions.ParsingError`,
:class:`~xdg.Exceptions.DuplicateGroupError` or
:class:`~xdg.Exceptions.DuplicateKeyError`.
"""
IniFile.parse(self, file, ["Desktop Entry", "KDE Desktop Entry"])
def findTryExec(self):
"""Looks in the PATH for the executable given in the TryExec field.
Returns the full path to the executable if it is found, None if not.
Raises :class:`~xdg.Exceptions.NoKeyError` if TryExec is not present.
"""
tryexec = self.get('TryExec', strict=True)
return which(tryexec)
# start standard keys
def getType(self):
return self.get('Type')
def getVersion(self):
"""deprecated, use getVersionString instead """
return self.get('Version', type="numeric")
def getVersionString(self):
return self.get('Version')
def getName(self):
return self.get('Name', locale=True)
def getGenericName(self):
return self.get('GenericName', locale=True)
def getNoDisplay(self):
return self.get('NoDisplay', type="boolean")
def getComment(self):
return self.get('Comment', locale=True)
def getIcon(self):
return self.get('Icon', locale=True)
def getHidden(self):
return self.get('Hidden', type="boolean")
def getOnlyShowIn(self):
return self.get('OnlyShowIn', list=True)
def getNotShowIn(self):
return self.get('NotShowIn', list=True)
def getTryExec(self):
return self.get('TryExec')
def getExec(self):
return self.get('Exec')
def getPath(self):
return self.get('Path')
def getTerminal(self):
return self.get('Terminal', type="boolean")
def getMimeType(self):
"""deprecated, use getMimeTypes instead """
return self.get('MimeType', list=True, type="regex")
def getMimeTypes(self):
return self.get('MimeType', list=True)
def getCategories(self):
return self.get('Categories', list=True)
def getStartupNotify(self):
return self.get('StartupNotify', type="boolean")
def getStartupWMClass(self):
return self.get('StartupWMClass')
def getURL(self):
return self.get('URL')
# end standard keys
# start kde keys
def getServiceTypes(self):
return self.get('ServiceTypes', list=True)
def getDocPath(self):
return self.get('DocPath')
def getKeywords(self):
return self.get('Keywords', list=True, locale=True)
def getInitialPreference(self):
return self.get('InitialPreference')
def getDev(self):
return self.get('Dev')
def getFSType(self):
return self.get('FSType')
def getMountPoint(self):
return self.get('MountPoint')
def getReadonly(self):
return self.get('ReadOnly', type="boolean")
def getUnmountIcon(self):
return self.get('UnmountIcon', locale=True)
# end kde keys
# start deprecated keys
def getMiniIcon(self):
return self.get('MiniIcon', locale=True)
def getTerminalOptions(self):
return self.get('TerminalOptions')
def getDefaultApp(self):
return self.get('DefaultApp')
def getProtocols(self):
return self.get('Protocols', list=True)
def getExtensions(self):
return self.get('Extensions', list=True)
def getBinaryPattern(self):
return self.get('BinaryPattern')
def getMapNotify(self):
return self.get('MapNotify')
def getEncoding(self):
return self.get('Encoding')
def getSwallowTitle(self):
return self.get('SwallowTitle', locale=True)
def getSwallowExec(self):
return self.get('SwallowExec')
def getSortOrder(self):
return self.get('SortOrder', list=True)
def getFilePattern(self):
return self.get('FilePattern', type="regex")
def getActions(self):
return self.get('Actions', list=True)
# end deprecated keys
# desktop entry edit stuff
def new(self, filename):
"""Make this instance into a new, blank desktop entry.
If filename has a .desktop extension, Type is set to Application. If it
has a .directory extension, Type is Directory. Other extensions will
cause :class:`~xdg.Exceptions.ParsingError` to be raised.
"""
if os.path.splitext(filename)[1] == ".desktop":
type = "Application"
elif os.path.splitext(filename)[1] == ".directory":
type = "Directory"
else:
raise ParsingError("Unknown extension", filename)
self.content = dict()
self.addGroup(self.defaultGroup)
self.set("Type", type)
self.filename = filename
# end desktop entry edit stuff
# validation stuff
def checkExtras(self):
# header
if self.defaultGroup == "KDE Desktop Entry":
self.warnings.append('[KDE Desktop Entry]-Header is deprecated')
# file extension
if self.fileExtension == ".kdelnk":
self.warnings.append("File extension .kdelnk is deprecated")
elif self.fileExtension != ".desktop" and self.fileExtension != ".directory":
self.warnings.append('Unknown File extension')
# Type
try:
self.type = self.content[self.defaultGroup]["Type"]
except KeyError:
self.errors.append("Key 'Type' is missing")
# Name
try:
self.name = self.content[self.defaultGroup]["Name"]
except KeyError:
self.errors.append("Key 'Name' is missing")
def checkGroup(self, group):
# check if group header is valid
if not (group == self.defaultGroup \
or re.match("^Desktop Action [a-zA-Z0-9-]+$", group) \
or (re.match("^X-", group) and is_ascii(group))):
self.errors.append("Invalid Group name: %s" % group)
else:
#OnlyShowIn and NotShowIn
if ("OnlyShowIn" in self.content[group]) and ("NotShowIn" in self.content[group]):
self.errors.append("Group may either have OnlyShowIn or NotShowIn, but not both")
def checkKey(self, key, value, group):
# standard keys
if key == "Type":
if value == "ServiceType" or value == "Service" or value == "FSDevice":
self.warnings.append("Type=%s is a KDE extension" % key)
elif value == "MimeType":
self.warnings.append("Type=MimeType is deprecated")
elif not (value == "Application" or value == "Link" or value == "Directory"):
self.errors.append("Value of key 'Type' must be Application, Link or Directory, but is '%s'" % value)
if self.fileExtension == ".directory" and not value == "Directory":
self.warnings.append("File extension is .directory, but Type is '%s'" % value)
elif self.fileExtension == ".desktop" and value == "Directory":
self.warnings.append("Files with Type=Directory should have the extension .directory")
if value == "Application":
if "Exec" not in self.content[group]:
self.warnings.append("Type=Application needs 'Exec' key")
if value == "Link":
if "URL" not in self.content[group]:
self.warnings.append("Type=Link needs 'URL' key")
elif key == "Version":
self.checkValue(key, value)
elif re.match("^Name"+xdg.Locale.regex+"$", key):
pass # locale string
elif re.match("^GenericName"+xdg.Locale.regex+"$", key):
pass # locale string
elif key == "NoDisplay":
self.checkValue(key, value, type="boolean")
elif re.match("^Comment"+xdg.Locale.regex+"$", key):
pass # locale string
elif re.match("^Icon"+xdg.Locale.regex+"$", key):
self.checkValue(key, value)
elif key == "Hidden":
self.checkValue(key, value, type="boolean")
elif key == "OnlyShowIn":
self.checkValue(key, value, list=True)
self.checkOnlyShowIn(value)
elif key == "NotShowIn":
self.checkValue(key, value, list=True)
self.checkOnlyShowIn(value)
elif key == "TryExec":
self.checkValue(key, value)
self.checkType(key, "Application")
elif key == "Exec":
self.checkValue(key, value)
self.checkType(key, "Application")
elif key == "Path":
self.checkValue(key, value)
self.checkType(key, "Application")
elif key == "Terminal":
self.checkValue(key, value, type="boolean")
self.checkType(key, "Application")
elif key == "Actions":
self.checkValue(key, value, list=True)
self.checkType(key, "Application")
elif key == "MimeType":
self.checkValue(key, value, list=True)
self.checkType(key, "Application")
elif key == "Categories":
self.checkValue(key, value)
self.checkType(key, "Application")
self.checkCategories(value)
elif re.match("^Keywords"+xdg.Locale.regex+"$", key):
self.checkValue(key, value, type="localestring", list=True)
self.checkType(key, "Application")
elif key == "StartupNotify":
self.checkValue(key, value, type="boolean")
self.checkType(key, "Application")
elif key == "StartupWMClass":
self.checkType(key, "Application")
elif key == "URL":
self.checkValue(key, value)
self.checkType(key, "URL")
# kde extensions
elif key == "ServiceTypes":
self.checkValue(key, value, list=True)
self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "DocPath":
self.checkValue(key, value)
self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "InitialPreference":
self.checkValue(key, value, type="numeric")
self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "Dev":
self.checkValue(key, value)
self.checkType(key, "FSDevice")
self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "FSType":
self.checkValue(key, value)
self.checkType(key, "FSDevice")
self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "MountPoint":
self.checkValue(key, value)
self.checkType(key, "FSDevice")
self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "ReadOnly":
self.checkValue(key, value, type="boolean")
self.checkType(key, "FSDevice")
self.warnings.append("Key '%s' is a KDE extension" % key)
elif re.match("^UnmountIcon"+xdg.Locale.regex+"$", key):
self.checkValue(key, value)
self.checkType(key, "FSDevice")
self.warnings.append("Key '%s' is a KDE extension" % key)
# deprecated keys
elif key == "Encoding":
self.checkValue(key, value)
self.warnings.append("Key '%s' is deprecated" % key)
elif re.match("^MiniIcon"+xdg.Locale.regex+"$", key):
self.checkValue(key, value)
self.warnings.append("Key '%s' is deprecated" % key)
elif key == "TerminalOptions":
self.checkValue(key, value)
self.warnings.append("Key '%s' is deprecated" % key)
elif key == "DefaultApp":
self.checkValue(key, value)
self.warnings.append("Key '%s' is deprecated" % key)
elif key == "Protocols":
self.checkValue(key, value, list=True)
self.warnings.append("Key '%s' is deprecated" % key)
elif key == "Extensions":
self.checkValue(key, value, list=True)
self.warnings.append("Key '%s' is deprecated" % key)
elif key == "BinaryPattern":
self.checkValue(key, value)
self.warnings.append("Key '%s' is deprecated" % key)
elif key == "MapNotify":
self.checkValue(key, value)
self.warnings.append("Key '%s' is deprecated" % key)
elif re.match("^SwallowTitle"+xdg.Locale.regex+"$", key):
self.warnings.append("Key '%s' is deprecated" % key)
elif key == "SwallowExec":
self.checkValue(key, value)
self.warnings.append("Key '%s' is deprecated" % key)
elif key == "FilePattern":
self.checkValue(key, value, type="regex", list=True)
self.warnings.append("Key '%s' is deprecated" % key)
elif key == "SortOrder":
self.checkValue(key, value, list=True)
self.warnings.append("Key '%s' is deprecated" % key)
# "X-" extensions
elif re.match("^X-[a-zA-Z0-9-]+", key):
pass
else:
self.errors.append("Invalid key: %s" % key)
def checkType(self, key, type):
if not self.getType() == type:
self.errors.append("Key '%s' only allowed in Type=%s" % (key, type))
def checkOnlyShowIn(self, value):
values = self.getList(value)
valid = ["GNOME", "KDE", "LXDE", "MATE", "Razor", "ROX", "TDE", "Unity",
"XFCE", "Old"]
for item in values:
if item not in valid and item[0:2] != "X-":
self.errors.append("'%s' is not a registered OnlyShowIn value" % item);
def checkCategories(self, value):
values = self.getList(value)
main = ["AudioVideo", "Audio", "Video", "Development", "Education", "Game", "Graphics", "Network", "Office", "Science", "Settings", "System", "Utility"]
if not any(item in main for item in values):
self.errors.append("Missing main category")
additional = ['Building', 'Debugger', 'IDE', 'GUIDesigner', 'Profiling', 'RevisionControl', 'Translation', 'Calendar', 'ContactManagement', 'Database', 'Dictionary', 'Chart', 'Email', 'Finance', 'FlowChart', 'PDA', 'ProjectManagement', 'Presentation', 'Spreadsheet', 'WordProcessor', '2DGraphics', 'VectorGraphics', 'RasterGraphics', '3DGraphics', 'Scanning', 'OCR', 'Photography', 'Publishing', 'Viewer', 'TextTools', 'DesktopSettings', 'HardwareSettings', 'Printing', 'PackageManager', 'Dialup', 'InstantMessaging', 'Chat', 'IRCClient', 'Feed', 'FileTransfer', 'HamRadio', 'News', 'P2P', 'RemoteAccess', 'Telephony', 'TelephonyTools', 'VideoConference', 'WebBrowser', 'WebDevelopment', 'Midi', 'Mixer', 'Sequencer', 'Tuner', 'TV', 'AudioVideoEditing', 'Player', 'Recorder', 'DiscBurning', 'ActionGame', 'AdventureGame', 'ArcadeGame', 'BoardGame', 'BlocksGame', 'CardGame', 'KidsGame', 'LogicGame', 'RolePlaying', 'Shooter', 'Simulation', 'SportsGame', 'StrategyGame', 'Art', 'Construction', 'Music', 'Languages', 'ArtificialIntelligence', 'Astronomy', 'Biology', 'Chemistry', 'ComputerScience', 'DataVisualization', 'Economy', 'Electricity', 'Geography', 'Geology', 'Geoscience', 'History', 'Humanities', 'ImageProcessing', 'Literature', 'Maps', 'Math', 'NumericalAnalysis', 'MedicalSoftware', 'Physics', 'Robotics', 'Spirituality', 'Sports', 'ParallelComputing', 'Amusement', 'Archiving', 'Compression', 'Electronics', 'Emulator', 'Engineering', 'FileTools', 'FileManager', 'TerminalEmulator', 'Filesystem', 'Monitor', 'Security', 'Accessibility', 'Calculator', 'Clock', 'TextEditor', 'Documentation', 'Adult', 'Core', 'KDE', 'GNOME', 'XFCE', 'GTK', 'Qt', 'Motif', 'Java', 'ConsoleOnly']
allcategories = additional + main
for item in values:
if item not in allcategories and not item.startswith("X-"):
self.errors.append("'%s' is not a registered Category" % item);
def checkCategorie(self, value):
"""Deprecated alias for checkCategories - only exists for backwards
compatibility.
"""
warnings.warn("checkCategorie is deprecated, use checkCategories",
DeprecationWarning)
return self.checkCategories(value)

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