diff --git a/src/Javascript/amazon-calculate-spending.js b/src/Javascript/amazon-calculate-spending.js new file mode 100644 index 0000000..1790409 --- /dev/null +++ b/src/Javascript/amazon-calculate-spending.js @@ -0,0 +1,15 @@ +// Open the Orders page in Amazon. +// Run the script for each page you wish to calculate total of. + +elms = document.querySelectorAll("#ordersContainer")[0].querySelectorAll(".a-row .a-size-base"); +total = 0; + +for (let i = 0; i < elms.length ; i++) { + value = elms[i].innerText; + if (value.includes("$")) { + total += parseInt(value.replace("$", ""), 10); + console.log(value); + } +} + +console.log(total); diff --git a/src/Javascript/bulk-delete-youtube-videos.js b/src/Javascript/bulk-delete-youtube-videos.js new file mode 100644 index 0000000..a18ed05 --- /dev/null +++ b/src/Javascript/bulk-delete-youtube-videos.js @@ -0,0 +1,39 @@ +pos = 7 // Position to delete from. Note: 0 is the 1st video of the list; 1 is the 2nd, etc. +end = 5 // How many videos to delete? + +// :: UI Timeouts :: (1000 equals 1 second.) +// Note: Your computer is a toaster or potato? Can't render UI changes quickly? +// Bump these up till the proper prompts show, then up the Interval Timer. +t1 = 800 +t2 = 800 +t3 = 800 + +// :: Interval Timer :: (1000 equals 1 second.) +// Note: Slow internet or bumped up the UI Timeouts? +// If slow internet, incrimint this by 1 second till things delete. +// If you bumped uo or down the UI Timeouts, add them up and add ~2000. +wait = 4000 + + +i = 1 // Index to keep track of deletions. Don't change! +interval = setInterval(function () { + if (i == end) + clearInterval(interval) + + elm = document.getElementsByClassName('style-scope ytcp-video-list-cell-video open-menu-button')[pos]; + + elm.click(); + setTimeout(function () { + document.getElementById('text-item-4').click(); + }, t1); + + setTimeout(function () { + document.getElementById('delete-confirm-checkbox').click(); + }, t2); + + setTimeout(function () { + document.getElementById('delete-confirm-button').click(); + }, t3); + + i += 1 +}, wait); diff --git a/src/Javascript/google-scripts-api-examples.js b/src/Javascript/google-scripts-api-examples.js new file mode 100644 index 0000000..241bc2e --- /dev/null +++ b/src/Javascript/google-scripts-api-examples.js @@ -0,0 +1,39 @@ +Reference Material: + https://developers.google.com/apps-script + + + + +# ---- Functions ---- # + +function createSheetsFromRow() { + // getActiveSpreadsheet actually returns the whole SpreadSheet object whereas getActiveSheet returns a single "Sheet" or null object. + let spreadSheet = SpreadsheetApp.getActiveSpreadsheet(); // The "container" that holds all your Sheet objects + let activeSheet = SpreadsheetApp.getActiveSheet(); // The Sheet that is active in your SpreadSheet object + let sheetData = activeSheet.getDataRange().getValues(); // Get the active Sheet's data... aka Rows and Columns + + for (var i in sheetData) { + if (i == 0) { continue; } // Keep from adding first row as sheet + let row = sheetData[i]; // Get row's data + let newSheetName = row[2]; // New sheet name is derived from 3rd column (2nd index) which is Agency column + let newSheet = spreadSheet.getSheetByName(newSheetName); + if (newSheet === null) { // If sheet doesn't exists, we create it + newSheet = spreadSheet.insertSheet(newSheetName); + } + newSheet.appendRow(row); + } +} + +function deleteAllSheetsButForFirst() { + let spreadSheet = SpreadsheetApp.getActiveSpreadsheet(); + let sheets = spreadSheet.getSheets(); + + for (var i in sheets) { + if (i == 0) { continue; } // Keep from deleting first sheet + let sheet = sheets[i]; + spreadSheet.deleteSheet(sheet); + } +} + + + diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/ChronosBrowser.py b/src/Python/Scripts/GTK/chronos-webkit2-browser/ChronosBrowser.py new file mode 100755 index 0000000..f6b3baf --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/ChronosBrowser.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 + +# Gtk imports +import gi, faulthandler, signal +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 +from gi.repository import GLib + +# Python imports +import inspect, faulthandler + +# Application imports +from utils import Settings +from signal_classes import CrossClassSignals, WebviewSignals + + +class Main: + def __init__(self): + GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, gtk.main_quit) + faulthandler.enable() # For better debug info + webkit.WebView() # Need one initialized for webview to work from glade file + + builder = gtk.Builder() + settings = Settings() + settings.attachBuilder(builder) + + # Gets the methods from the classes and sets to handler. + # Then, builder connects to any signals it needs. + classes = [CrossClassSignals(settings), + WebviewSignals(settings)] + + handlers = {} + for c in classes: + methods = inspect.getmembers(c, predicate=inspect.ismethod) + handlers.update(methods) + + builder.connect_signals(handlers) + window = settings.createWindow() + window.show_all() + + +if __name__ == "__main__": + try: + main = Main() + gtk.main() + except Exception as e: + print(e) diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/ChronosBrowser.sh b/src/Python/Scripts/GTK/chronos-webkit2-browser/ChronosBrowser.sh new file mode 100755 index 0000000..f102198 --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/ChronosBrowser.sh @@ -0,0 +1,12 @@ +#!/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 ./ChronosBrowser.py + python3 ./ChronosBrowser.py +} +main $@; diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/factory/WebviewFactory.py b/src/Python/Scripts/GTK/chronos-webkit2-browser/factory/WebviewFactory.py new file mode 100644 index 0000000..2ee1dcb --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/factory/WebviewFactory.py @@ -0,0 +1,39 @@ +# Gtk imports +import gi +gi.require_version('WebKit2', '4.0') + +from gi.repository import WebKit2 as webkit + + +# Python imports + +# Application imports + + +class WebviewFactory: + def __init__(self): + self.index = -1 + self.view_collection = [] + + def createWebview(self): + view = webkit.WebView() + self.view_collection.append(view) + self.index += 1 + return view + + def deleteWebview(self, index): + if (len(self.view_collection) - 1) > 0: + self.view_collection.pop(index) + self.index -= 1 + return 0 + else: + return 1 + + def get_index(self, index): + val = index + if val >= 0 and val < len(self.view_collection): + self.index = index + return self.view_collection[self.index] + + def returnNotebookSize(self,): + return len(self.view_collection) diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/factory/__init__.py b/src/Python/Scripts/GTK/chronos-webkit2-browser/factory/__init__.py new file mode 100644 index 0000000..6b58560 --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/factory/__init__.py @@ -0,0 +1 @@ +from factory.WebviewFactory import WebviewFactory diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/requirements.txt b/src/Python/Scripts/GTK/chronos-webkit2-browser/requirements.txt new file mode 100644 index 0000000..66c1b01 --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/requirements.txt @@ -0,0 +1 @@ +sudo apt-get -y install python3 pygobject3-devel webkitgtk3-devel ubuntu-restricted-extras gstreamer1.0-gtk3 \ No newline at end of file diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/resources/ChronosBrowser.glade b/src/Python/Scripts/GTK/chronos-webkit2-browser/resources/ChronosBrowser.glade new file mode 100644 index 0000000..4b7e184 --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/resources/ChronosBrowser.glade @@ -0,0 +1,164 @@ + + + + + + True + False + gtk-add + 3 + + + True + False + gtk-delete + + + True + False + Home + gtk-home + 3 + + + True + False + gtk-refresh + 3 + + + False + Chronos Browser + center + 800 + 600 + center + + + + + + True + False + vertical + + + True + False + + + True + True + True + homeImage + True + + + + False + True + 0 + + + + + True + True + True + Refresh... + refreshImage + True + + + + False + True + 2 + + + + + True + True + Search... + edit-find-symbolic + False + False + + + + True + True + 3 + + + + + False + True + 0 + + + + + True + True + False + True + True + + + + + + + + + + + + + + + + + + + + + + True + True + True + deleteImage + True + + + + False + + + + + True + True + True + addImage + True + + + + False + + + + + True + True + 1 + + + + + + diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/resources/stylesheet.css b/src/Python/Scripts/GTK/chronos-webkit2-browser/resources/stylesheet.css new file mode 100644 index 0000000..fb2a684 --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/resources/stylesheet.css @@ -0,0 +1,3 @@ +window { + +} diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/signal_classes/CrossClassSignals.py b/src/Python/Scripts/GTK/chronos-webkit2-browser/signal_classes/CrossClassSignals.py new file mode 100644 index 0000000..08614af --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/signal_classes/CrossClassSignals.py @@ -0,0 +1,15 @@ +# Gtk imports + +# Python imports + +# Application imports + + +class CrossClassSignals: + def __init__(self, settings): + self.settings = settings + self.builder = self.settings.returnBuilder() + + + def closePopup(self, widget, data=None): + widget.hide() diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/signal_classes/WebviewSignals.py b/src/Python/Scripts/GTK/chronos-webkit2-browser/signal_classes/WebviewSignals.py new file mode 100644 index 0000000..9cbce1b --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/signal_classes/WebviewSignals.py @@ -0,0 +1,98 @@ +# Gtk imports +import gi +gi.require_version('Gtk', '3.0') + +from gi.repository import Gtk as gtk + +# Python imports + +# Application imports +from factory import WebviewFactory + + +class WebviewSignals: + def __init__(self, settings): + self.webviewFactory = WebviewFactory() + self.settings = settings + builder = settings.returnBuilder() + + self.home_page = settings.returnWebHome() + self.webview_search = builder.get_object("webview_search") + self.notebook = builder.get_object("notebook") + self.page = None + self.index = 0 + self.labelLen = settings.returnLabelLen() + + self.addTab() + self.page = self.notebook.get_nth_page(0) + + + def addTab(self, widget=None, uri=None): + self.current_webview = self.createWebview() + self.addToNotebook(self.current_webview, uri) + + def delTab(self, widget): + state = self.webviewFactory.deleteWebview(self.index) + if state == 0: + self.notebook.remove_page(self.index) + + def selecte_view(self, widget, page, index): + self.current_webview = self.webviewFactory.get_index(index) + self.webview_search.set_text(self.current_webview.get_uri()) + self.page = page + self.index = index + + def createWebview(self): + webview = self.webviewFactory.createWebview() + webview.connect("create", self.webviewCreateSignal) + self.set_webview_settings(webview) + webview.load_uri(self.home_page) + webview.connect("load-changed", self.setUrlBar) + return webview + + def runSearchWebview(self, widget, data=None): + query = widget.get_text().strip() + if data.keyval == 65293: # If enter key pressed + if "http://" in query or "https://" in query or "file://" in query: + self.current_webview.load_uri(query) + else: + query = '+'.join(query.split()) + query = "http://www.google.com/search?q=" + query + self.current_webview.load_uri(query) + + self.notebook.get_tab_label(self.page).set_text(query) + + def refreshPage(self, widget, data=None): + self.current_webview.load_uri(self.current_webview.get_uri()) + + def loadHome(self, widget): + self.current_webview.load_uri(self.home_page) + + def setUrlBar(self, widget, data=None): + uri = widget.get_uri() + self.webview_search.set_text(uri) + label = uri[0: self.labelLen] + "..." + self.notebook.get_tab_label(self.page).set_text(label) + + def webviewCreateSignal(self, widget, eve): + uri = eve.get_request() + type = eve.get_navigation_type() + isRedirect = eve.is_redirect() + + if isRedirect == False and type == 5: # Open in new tab + self.addTab(None, uri.get_uri()) + + + def addToNotebook(self, view, uri=None): + webview_box = gtk.Box() + webview_box.pack_start(view, expand = True, fill = True, padding = 0) + webview_box.show_all() + + if uri: + self.notebook.append_page(webview_box, gtk.Label(uri)) + view.load_uri(uri) + else: + self.notebook.append_page(webview_box, gtk.Label(view.get_uri())) + + def set_webview_settings(self, view): + self.settings.setDefaultWebviewSettings(view, view.get_settings()) diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/signal_classes/__init__.py b/src/Python/Scripts/GTK/chronos-webkit2-browser/signal_classes/__init__.py new file mode 100644 index 0000000..e82d0ea --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/signal_classes/__init__.py @@ -0,0 +1,2 @@ +from signal_classes.CrossClassSignals import CrossClassSignals +from signal_classes.WebviewSignals import WebviewSignals diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/utils/Settings.py b/src/Python/Scripts/GTK/chronos-webkit2-browser/utils/Settings.py new file mode 100644 index 0000000..3fc8229 --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/utils/Settings.py @@ -0,0 +1,125 @@ +# Gtk imports +import gi, cairo +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 + +# Python imports +import os + +# Application imports + + +class Settings: + def __init__(self): + self.builder = None + + 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.webHome = 'http://webfm.com/' + self.labelLength = 25 - 3 + 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/ChronosBrowser.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) + + 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 returnWebHome(self): return self.webHome + def returnVidsExtensionList(self): return self.vidsExtensionList + def returnImagesExtensionList(self): return self.imagesExtensionList + def returnLabelLen(self): return self.labelLength + + 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', True) + settings.set_property('enable-html5-local-storage', True) + settings.set_property('enable-html5-database', True) + settings.set_property('allow-file-access-from-file-urls', True) + settings.set_property('allow-universal-access-from-file-urls', False) + settings.set_property('enable-dns-prefetching', False) + + # Media stuff + settings.set_hardware_acceleration_policy(0) + # settings.set_property('hardware-acceleration-policy', 'on-demand') + settings.set_property('enable-webgl', True) + 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) diff --git a/src/Python/Scripts/GTK/chronos-webkit2-browser/utils/__init__.py b/src/Python/Scripts/GTK/chronos-webkit2-browser/utils/__init__.py new file mode 100644 index 0000000..27b4e23 --- /dev/null +++ b/src/Python/Scripts/GTK/chronos-webkit2-browser/utils/__init__.py @@ -0,0 +1 @@ +from utils.Settings import Settings diff --git a/src/Python/Scripts/GTK/dark-overlay/pyDarkness.desktop b/src/Python/Scripts/GTK/dark-overlay/pyDarkness.desktop new file mode 100755 index 0000000..3853b35 --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/pyDarkness.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=pyDarkness +GenericName=Darken a region or add highlighting over an area. +Comment=Darken a region or add highlighting over an area. +Exec=/home/abaddon/Portable_Apps/local_bin/DarkOverlay/src/darkness +Icon=/home/abaddon/Portable_Apps/local_bin/DarkOverlay/src/resources/darkness.png +Type=Application +StartupNotify=true +Categories=Utility; +MimeType=text/plain; +Terminal=false diff --git a/src/Python/Scripts/GTK/dark-overlay/requirements.txt b/src/Python/Scripts/GTK/dark-overlay/requirements.txt new file mode 100644 index 0000000..e89fc2a --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/requirements.txt @@ -0,0 +1,6 @@ +EasyProcess==0.2.7 +Pillow==6.1.0 +pycairo==1.18.1 +PyGObject==3.34.0 +pyscreenshot==0.5.1 +setproctitle==1.1.10 diff --git a/src/Python/Scripts/GTK/dark-overlay/src/__init__.py b/src/Python/Scripts/GTK/dark-overlay/src/__init__.py new file mode 100644 index 0000000..45e0c34 --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/src/__init__.py @@ -0,0 +1,29 @@ +# Python imports +import inspect + + +# Gtk imports + + +# Application imports +from utils import Settings +from signal_classes import CrossClassSignals + + +class Main: + def __init__(self, args): + settings = Settings() + builder = settings.returnBuilder() + + # Gets the methods from the classes and sets to handler. + # Then, builder connects to any signals it needs. + classes = [CrossClassSignals(settings)] + + handlers = {} + for c in classes: + methods = inspect.getmembers(c, predicate=inspect.ismethod) + handlers.update(methods) + + builder.connect_signals(handlers) + window = settings.createWindow() + window.show() diff --git a/src/Python/Scripts/GTK/dark-overlay/src/__main__.py b/src/Python/Scripts/GTK/dark-overlay/src/__main__.py new file mode 100644 index 0000000..de20660 --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/src/__main__.py @@ -0,0 +1,32 @@ +#!/usr/bin/python3 + + +# Python imports +import argparse +from setproctitle import setproctitle + +# Gtk imports +import gi, faulthandler, signal +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk as gtk +from gi.repository import GLib + +# Application imports +from __init__ import Main + + +if __name__ == "__main__": + try: + setproctitle('DarkOverlay') + GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, gtk.main_quit) + faulthandler.enable() # For better debug info + parser = argparse.ArgumentParser() + # Add long and short arguments + parser.add_argument("--file", "-f", default="firefox", help="JUST SOME FILE ARG.") + + # Read arguments (If any...) + args = parser.parse_args() + main = Main(args) + gtk.main() + except Exception as e: + print( repr(e) ) diff --git a/src/Python/Scripts/GTK/dark-overlay/src/darkness b/src/Python/Scripts/GTK/dark-overlay/src/darkness new file mode 100755 index 0000000..b8f0580 --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/src/darkness @@ -0,0 +1,13 @@ +#!/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() { + SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + source "/home/abaddon/Portable_Apps/py-venvs/pycornia-venv/bin/activate" + python "${SCRIPTPATH}" +} +main $@; diff --git a/src/Python/Scripts/GTK/dark-overlay/src/resources/Main_Window.glade b/src/Python/Scripts/GTK/dark-overlay/src/resources/Main_Window.glade new file mode 100644 index 0000000..ae68179 --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/src/resources/Main_Window.glade @@ -0,0 +1,172 @@ + + + + + + 320 + 220 + False + center + 600 + 480 + darkness.png + True + False + center + True + False + + + + + + True + False + vertical + + + True + False + + + False + True + 0 + + + + + True + True + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK + True + + + + + + + + True + False + + + + + + + True + True + 1 + + + + + + + + + + + + + + 0.01 + 1 + 0.75 + 0.01 + 10 + + + + 320 + 220 + False + separator1 + none + + + True + False + vertical + True + + + True + True + True + True + rgb(0,0,0) + + + + False + True + 0 + + + + + True + False + 10 + 10 + True + + + True + False + 10 + 10 + Opacity + + + False + True + 0 + + + + + True + True + opacityAdjustment + 0.01 + 2 + 0.75 + + + False + True + 1 + + + + + False + True + 1 + + + + + gtk-quit + True + True + True + True + True + + + + False + True + 4 + + + + + + diff --git a/src/Python/Scripts/GTK/dark-overlay/src/resources/darkness.png b/src/Python/Scripts/GTK/dark-overlay/src/resources/darkness.png new file mode 100644 index 0000000..9f8f2cc Binary files /dev/null and b/src/Python/Scripts/GTK/dark-overlay/src/resources/darkness.png differ diff --git a/src/Python/Scripts/GTK/dark-overlay/src/resources/stylesheet.css b/src/Python/Scripts/GTK/dark-overlay/src/resources/stylesheet.css new file mode 100644 index 0000000..5ad1098 --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/src/resources/stylesheet.css @@ -0,0 +1,3 @@ +.regionWindow { + background-color: rgba(0, 0, 0, 0.0); +} diff --git a/src/Python/Scripts/GTK/dark-overlay/src/signal_classes/CrossClassSignals.py b/src/Python/Scripts/GTK/dark-overlay/src/signal_classes/CrossClassSignals.py new file mode 100644 index 0000000..2802ae5 --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/src/signal_classes/CrossClassSignals.py @@ -0,0 +1,187 @@ +# Python imports +import threading, subprocess, os, cairo + + +# Gtk imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk as gtk +from gi.repository import Gdk as gdk + + +# Application imports + + +def threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs).start() + return wrapper + + +class MouseButton: + LEFT_BUTTON = 1 + MIDDLE_BUTTON = 2 + RIGHT_BUTTON = 3 + +class StateController: + isDragging = False + isCtrlDown = False + + +class CrossClassSignals: + def __init__(self, settings): + self.settings = settings + self.builder = self.settings.returnBuilder() + + self.window = self.builder.get_object("Main_Window") + self.drawArea = self.builder.get_object("drawArea") + self.brushColorProp = self.builder.get_object("brushColorProp") + self.wh = self.builder.get_object("wh") + self.xy = self.builder.get_object("xy") + + self.opacityVal = 0.75 + self.states = StateController() + self.area = None + self.states.isCtrlDown = False + self.states.isMouseHeld = False + self.doDrawBackground = True + self.surface = None + self.brush = None + self.aw = None # Draw area width + self.ah = None # Draw area height + rgba = self.brushColorProp.get_rgba() + self.brushColorVal = [rgba.red, rgba.green, rgba.blue, self.opacityVal] + self.startCoords = [0.0, 0.0] + self.w1 = 0.0 + self.h1 = 0.0 + + self.window.set_keep_above(True) + + + + + def onConfigure(self, area, eve, data = None): + self.area = area + aw = area.get_allocated_width() + ah = area.get_allocated_height() + self.aw = aw + self.ah = ah + self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, aw, ah) + self.brush = cairo.Context(self.surface) + + self.drawBackground(self.brush, aw, ah) + return False + + + def onDraw(self, area, brush): + if self.surface is not None: + brush.set_source_surface(self.surface, 0.0, 0.0) + brush.paint() + else: + print("No surface info...") + + return False + + + # Draw background white + def drawBackground(self, brush, aw, ah): + rgba = self.brushColorVal + brush.rectangle(0, 0, aw, ah) # x, y, width, height + brush.set_source_rgba(rgba[0], rgba[1], rgba[2], rgba[3]) # x, y, width, height + + if not self.doDrawBackground: # If transparent or white + self.brush.set_operator(0); + + brush.fill() + self.brush.set_operator(1); # reset the brush after filling bg... + + + def onColorSet(self, widget): + rgba = widget.get_rgba() + self.brushColorVal = [rgba.red, rgba.green, rgba.blue, self.opacityVal] + self.drawArea.queue_draw() + self.drawArea.emit("configure_event", gdk.Event()) + + + def onOpacityChange(self, widget): + self.opacityVal = widget.get_value() + self.brushColorVal[3] = self.opacityVal + self.drawArea.queue_draw() + self.drawArea.emit("configure_event", gdk.Event()) + + + def popupMenu(self, widget, eve): + self.states.isMouseHeld = False + if eve.type == gdk.EventType.BUTTON_RELEASE and eve.button == MouseButton.RIGHT_BUTTON: + self.builder.get_object("rClickMenu").popup() + + + def getStartCoords(self, widget, eve): + if eve.type == gdk.EventType.BUTTON_PRESS and eve.button == MouseButton.LEFT_BUTTON: + self.startCoords = [eve.x, eve.y] # Used for delta calculations + self.w1 = self.window.get_size()[0] # Ref window width + self.h1 = self.window.get_size()[1] # Ref window height + self.states.isMouseHeld = True # State check for when updating width 'n height + + + def keyActionToggle(self, widget, eve): + key_id = gdk.keyval_name(eve.keyval).upper() + if key_id in ["CONTROL_R", "CONTROL_L"]: + self.states.isCtrlDown = True + + def endKeyActionToggle(self, widget, eve): + key_id = gdk.keyval_name(eve.keyval).upper() + if key_id in ["CONTROL_R", "CONTROL_L"]: + self.states.isCtrlDown = False + + + def onMotion(self, widget, eve): + if self.states.isMouseHeld: + if self.states.isCtrlDown is False: + px1 = self.window.get_position()[0] # Ref window x + py1 = self.window.get_position()[1] # Ref window y + + # Getting deltas of movement inner to draw event box + x1 = self.startCoords[0] + y1 = self.startCoords[1] + x2 = eve.x + y2 = eve.y + px = 0 + py = 0 + + # Calculate that to actual posion change + if x2 > x1: # Is growing + px = px1 + (x2 - x1) + else: # Is shrinking + px = px1 - (x1 - x2) + + if y2 > y1: # Is growing + py = py1 + (y2 - y1) + else: # Is shrinking + py = py1 - (y1 - y2) + + self.window.move(px, py) + + if self.states.isCtrlDown: + x1 = self.startCoords[0] + y1 = self.startCoords[1] + x2 = eve.x + y2 = eve.y + w = 0 + h = 0 + + if x2 > x1: # Is growing + w = self.w1 + (x2 - x1) + else: # Is shrinking + w = self.w1 - (x1 - x2) + + if y2 > y1: # Is growing + h = self.h1 + (y2 - y1) + else: # Is shrinking + h = self.h1 - (y1 - y2) + + self.window.resize(w, h) + + + def close_app(self, widget): + gtk.main_quit() diff --git a/src/Python/Scripts/GTK/dark-overlay/src/signal_classes/__init__.py b/src/Python/Scripts/GTK/dark-overlay/src/signal_classes/__init__.py new file mode 100644 index 0000000..2c626b0 --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/src/signal_classes/__init__.py @@ -0,0 +1 @@ +from .CrossClassSignals import CrossClassSignals diff --git a/src/Python/Scripts/GTK/dark-overlay/src/utils/Settings.py b/src/Python/Scripts/GTK/dark-overlay/src/utils/Settings.py new file mode 100644 index 0000000..c20aefe --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/src/utils/Settings.py @@ -0,0 +1,71 @@ +# Gtk imports +import gi, cairo +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 + +# Python imports +import os + +# Application imports + + +class Settings: + def __init__(self): + self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/" + self.builder = gtk.Builder() + self.builder.add_from_file(self.SCRIPT_PTH + "../resources/Main_Window.glade") + + # 'Filters' + self.images = ('.png', '.jpg', '.jpeg', '.gif') + HOME_PATH = os.path.expanduser('~') + self.SCREENSHOTS_DIR = HOME_PATH + "/" + ".screenshots" + + if not os.path.isdir(self.SCREENSHOTS_DIR): + os.mkdir(self.SCREENSHOTS_DIR) + + + def createWindow(self): + # Get window and connect signals + window = self.builder.get_object("Main_Window") + window.connect("delete-event", gtk.main_quit) + self.setWindowData(window) + 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(self.SCRIPT_PTH + '../resources/stylesheet.css') + screen = gdk.Screen.get_default() + styleContext = gtk.StyleContext() + styleContext.add_provider_for_screen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER) + + def getMonitorData(self): + screen = self.builder.get_object("Main_Window").get_screen() + wdth = screen.get_width() + hght = screen.get_height() + mon0 = gdk.Rectangle() + mon0.width = wdth + mon0.height = hght + monitors = [] + + monitors.append(mon0) + for m in range(screen.get_n_monitors()): + monitors.append(screen.get_monitor_geometry(m)) + + return monitors + + + def returnBuilder(self): return self.builder + def returnScreenshotsDir(self): return self.SCREENSHOTS_DIR + + # Filter returns + def returnImagesFilter(self): return self.images diff --git a/src/Python/Scripts/GTK/dark-overlay/src/utils/__init__.py b/src/Python/Scripts/GTK/dark-overlay/src/utils/__init__.py new file mode 100644 index 0000000..e2cf696 --- /dev/null +++ b/src/Python/Scripts/GTK/dark-overlay/src/utils/__init__.py @@ -0,0 +1 @@ +from .Settings import Settings diff --git a/src/Python/Scripts/GTK/edit-path/__main__.py b/src/Python/Scripts/GTK/edit-path/__main__.py new file mode 100755 index 0000000..7085258 --- /dev/null +++ b/src/Python/Scripts/GTK/edit-path/__main__.py @@ -0,0 +1,151 @@ +#!/usr/bin/python3 + +# GTK Imports +import gi, faulthandler, signal +gi.require_version('Gtk', '3.0') + +from gi.repository import Gtk as gtk +from gi.repository import GLib + + +# Python Imports +import os, threading, time + +from setproctitle import setproctitle + + +def threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs).start() + + return wrapper + + + +class Main: + def __init__(self): + setproctitle('PATH Edit Tool') + GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, gtk.main_quit) + faulthandler.enable() # For better debug info + + self.HOME = os.path.expanduser('~') + PREFERED_BASH_PATH = self.HOME + "/" + ".bash_paths" + SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/" + GLADE_FILE = "main.glade" + + self.builder = gtk.Builder() + self.builder.add_from_file(SCRIPT_PTH + GLADE_FILE) + self.builder.connect_signals(self) + + self.pathTreeView = self.builder.get_object("pathTreeView") + self.messageWidget = self.builder.get_object("messageWidget") + self.messageLabel = self.builder.get_object("messageLabel") + + self.pathListStore = gtk.ListStore(str) + self.success = "#88cc27" + self.warning = "#ffa800" + self.error = "#ff0000" + self.selected = None + self.bashrcPath = "" + + if os.path.isfile(PREFERED_BASH_PATH): + self.bashrcPath = PREFERED_BASH_PATH + else: + self.bashrcPath = self.HOME + "/" + ".bashrc" + + self.setupTreeview() + self.loadPaths() + + window = self.builder.get_object("Main_Window") + window.connect("delete-event", gtk.main_quit) + window.show() + + + + def addEntry(self, widget): + toAddPathTxt = self.builder.get_object("toAddPathEntry").get_text().strip() + + if os.path.isdir(toAddPathTxt): + self.pathListStore.append([toAddPathTxt]) + else: + self.displayMessage(self.warning, "Not a directory!") + + def deleteEntry(self, widget): + self.pathListStore.remove(self.selected) + + def saveToBashrc(self, widget): + try: + paths = list() + iter = self.pathListStore.get_iter_first() + while iter != None: + pth = self.pathListStore.get_value(iter, 0) + pth = pth.replace(self.HOME, "$HOME") + paths.append(pth) + # paths.append(self.pathListStore.get_value(iter, 0)) + iter = self.pathListStore.iter_next(iter) + + + toExport = "export PATH=\"" + ':'.join(paths) + "\"\n\n" + file = open(self.bashrcPath, mode='r') + for line in file: + if "export PATH=" in line: + continue + else: + toExport += line + + file.close() + file = open(self.bashrcPath, mode='w') + file.write(toExport) + file.close() + self.displayMessage(self.success, "Successfully saved file!") + except Exception as e: + self.displayMessage(self.error, "Opening/Writing to file failed!") + print("Opening/Writing to file failed with the following:\n\n") + print(e) + + + def setSelected(self, user_data): + selected = user_data.get_selected()[1] + if selected: + self.selected = selected + + def loadPaths(self): + pathsStr = os.getenv("PATH") + + # If path exists in bashrc replace default selection... + file = open(self.bashrcPath, mode='r') + for line in file: + if "export PATH=" in line: + part = line.replace("export PATH=", "") + cleaned = part.replace("\"", "") + pathsStr = cleaned.strip() + + # Split string into list/tuple and add parts to store + paths = pathsStr.split(":") + for path in paths: + self.pathListStore.append([path]) + + + def setupTreeview(self): + renderer = gtk.CellRendererText() + pathColumn = gtk.TreeViewColumn(title="Paths", cell_renderer=renderer, text=0) + self.pathTreeView.append_column(pathColumn) + self.pathTreeView.set_model(self.pathListStore) + + + def displayMessage(self, type, text): + markup = "" + text + "" + self.messageLabel.set_markup(markup) + self.messageWidget.popup() + self.hideMessageTimed() + + @threaded + def hideMessageTimed(self): + time.sleep(3) + GLib.idle_add(self.messageWidget.popdown) + + + +if __name__ == '__main__': + main = Main() + gtk.main() diff --git a/src/Python/Scripts/GTK/edit-path/main.glade b/src/Python/Scripts/GTK/edit-path/main.glade new file mode 100644 index 0000000..9853404 --- /dev/null +++ b/src/Python/Scripts/GTK/edit-path/main.glade @@ -0,0 +1,162 @@ + + + + + + True + False + gtk-delete + + + True + False + gtk-add + + + False + Edit PATH App + center + 480 + 560 + applications-accessories + center + + + + + + True + False + vertical + + + True + False + + + False + True + 0 + + + + + True + True + in + + + True + False + + + True + True + + + + + + + + + + + + True + True + 1 + + + + + True + False + + + True + True + Path... + + + True + True + 0 + + + + + True + True + True + Add path... + plusImage + True + + + + False + True + 1 + + + + + True + True + True + Delete... + delImage + True + + + + False + True + 2 + + + + + False + True + 2 + + + + + gtk-save + True + True + True + Save... + True + True + + + + False + True + 3 + + + + + + + 320 + False + separator1 + bottom + + + True + False + center + + + + + + + diff --git a/src/Python/Scripts/bulk-rename/__init__.py b/src/Python/Scripts/bulk-rename/__init__.py new file mode 100644 index 0000000..c7dcd65 --- /dev/null +++ b/src/Python/Scripts/bulk-rename/__init__.py @@ -0,0 +1,53 @@ +# Python Imports +import os +import sys + +# Lib Imports +import typer + + +# Application Imports + + +app = typer.Typer() + + +@app.command() +def makeTitleCase(file: str): + if os.path.isdir(file) : + for f in os.listdir(file): + os.rename(f, f.title()) + else: + os.rename(file, file.title()) + + +@app.command() +def removeFromName(fsub: str, file: str): + if os.path.isdir(file) : + for f in os.listdir(file): + os.rename(f, f.replace(fsub, '')) + else: + os.rename(file, file.replace(fsub, '')) + +@app.command() +def removeFromToName(fsub: str, tsub: str, file: str): + if os.path.isdir(file) : + for f in os.listdir(file): + startIndex = f.index(fsub) + 1 + endIndex = f.index(tsub) + toRemove = f[startIndex:endIndex] + os.rename(f, f.replace(toRemove, '')) + else: + startIndex = file.index(fsub) + 1 + endIndex = file.index(tsub) + toRemove = file[startIndex:endIndex] + os.rename(file, file.replace(toRemove, '')) + + +@app.command() +def replaceInName(fsub: str, tsub: str, file: str): + if os.path.isdir(file) : + for f in os.listdir(file): + os.rename(f, f.replace(fsub, tsub)) + else: + os.rename(file, file.replace(fsub, tsub)) diff --git a/src/Python/Scripts/bulk-rename/__main__.py b/src/Python/Scripts/bulk-rename/__main__.py new file mode 100644 index 0000000..9919b99 --- /dev/null +++ b/src/Python/Scripts/bulk-rename/__main__.py @@ -0,0 +1,7 @@ +#!/usr/sbin/python + +from __init__ import app + + +if __name__ == "__main__": + app() diff --git a/src/Python/Scripts/smart-device-manager.py b/src/Python/Scripts/smart-device-manager.py new file mode 100644 index 0000000..30b52ca --- /dev/null +++ b/src/Python/Scripts/smart-device-manager.py @@ -0,0 +1,73 @@ +# Python imports +import json +import asyncio +import time + +# Lib imports +from kasa import Discover + +# Application imports + + +class SmartDeviceManager: + def __init__(self): + self.BASE_IP = "192.168.0." + self.IPs = ["12", "13", "14", "15", "16", "17"] + self.devices = self.retrieve_devices() + + + def retrieve_devices(self): + devices = [] + for ip in self.IPs: + device = self.get_device(self.BASE_IP + ip) + if device: + print("Device: {} is available...".format(device.alias)) + print("{}".format(device.hw_info)) + devices.append(device) + + return devices + + def set_device_on(self, device): + asyncio.run(device.turn_on()) + + def set_device_off(self, device): + asyncio.run(device.turn_off()) + + def set_led_on(self, device): + asyncio.run(device.set_led(True)) + + def set_led_off(self, device): + asyncio.run(device.set_led(False)) + + def set_device_alias(self, device, alias): + asyncio.run(device.set_alias(alias)) + + def update_info(self, device): + try: + asyncio.run(device.update()) + except Exception as e: + pass + + def get_device(self, addr): + try: + device = asyncio.run( Discover.discover_single(addr) ) + self.update_info(device) + return device + except Exception as e: + print(repr(e)) + return None + + def pulsate(self, device, rate = 1): + state = "on" + while True: + if state == "on": + state = "off" + self.set_device_on(device) + else: + state = "on" + self.set_device_off(device) + + time.sleep(rate) + +if __name__ == '__main__': + SmartDeviceManager() diff --git a/src/Python/Scripts/smart-window-position.py b/src/Python/Scripts/smart-window-position.py new file mode 100644 index 0000000..a400c57 --- /dev/null +++ b/src/Python/Scripts/smart-window-position.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python3 + +# Python Imports +import sys +import enum +import argparse + +# Lib Imports +from Xlib import display +from Xlib.ext import randr, xinerama + + +try: + from . import __version__ +except ImportError: + __version__ = "N/A" + + +class Position(enum.Enum): + TOP = enum.auto() + BOTTOM = enum.auto() + LEFT = enum.auto() + RIGHT = enum.auto() + + def __str__(self): + return self.name.lower() + + @staticmethod + def from_str(pos): + try: + return Position[pos.upper()] + except KeyError: + raise ValueError(f"value '{pos}' not part of the Pos enum") + + +class Shape: + def __init__(self, major, minor): + if max(major, minor) > 1.0 or min(major, minor) < 0.0: + raise ValueError(f"Shape out of range [0,1]: major={major}, minor={minor}") + self.major = major + self.minor = minor + + +class Monitor: + def __init__(self, name, x, y, width, height): + self.name = name + self.x = x + self.y = y + self.width = width + self.height = height + + +DEFAULTS = { + "name": "Smart Position", + "shape": (1.0, 0.4), + "position": str(Position.LEFT), +} + + +class SmartPositioner: + def __init__(self, name: str, shape: Shape, pos: Position, positioner_argv: list = None): + self.positioner_argv = positioner_argv + self.display = display.Display() + self.screen = self.display.screen() + self.root = self.screen.root + + # self.fakeWindow = self.screen.root.create_window(0, 0, 1, 1, 1, self.screen.root_depth) + + self.name = name + self.shape = shape + self.pos = pos + self.id = None + self.monitors = [] + + self.printXineramaVersion() + self.generateMonitorList() + self.getActiveMonitor() + + + def on_keybind(self, _, be): + pass + # if be.binding.command == f"nop {self.name}": + # self.toggle() + + + def _get_instance(self): + pass + # tree = self.i3.get_tree() + # if self.id is None: + # matches = tree.find_instanced(self.name) + # instance = {m.window_instance: m for m in matches}.get(self.name, None) + # if instance is not None: + # self.id = instance.id + # else: + # instance = tree.find_by_id(self.id) + # return instance + + def toggle(self): + scrSize = self.getScreenSize() + mPos = self.getMousePosition() + self.display.flush() # Needed in some instances to prompt XWindows + + # kitty = self._get_instance() + # if kitty is None: + # self.spawn() + # else: + # focused_ws = self._get_focused_workspace() + # if focused_ws is None: + # print("no focused workspaces; ignoring toggle request") + # return + # if kitty.workspace().name == focused_ws.name: # kitty present on current WS; hide + # self.i3.command(f"[con_id={self.id}] floating enable, move scratchpad") + # else: + # self.fetch(focused_ws) + + + def getMousePosition(self): + data = self.screen.root.query_pointer()._data + return data["root_x"], data["root_y"] + + def getActiveMonitor(self): + pos = self.getMousePosition() + for mon in self.monitors: + pass + + def getScreenSize(self): + w = self.display.screen().width_in_pixels + h = self.display.screen().height_in_pixels + return w, h + + + def printXineramaVersion(self): + xinerama_version = self.display.xinerama_query_version() + print('Found XINERAMA version %s.%s' % ( + xinerama_version.major_version, + xinerama_version.minor_version, + ), file=sys.stderr) + + def generateMonitorList(self): + print('Available screens:') + screens = xinerama.query_screens(self.root).screens + i = 1 + for (idx, screen) in enumerate(screens): + x, y, w, h = screen.x, screen.y, screen.width, screen.height + print('screen %d: %s' % (idx, screen)) + self.monitors.append( Monitor( "Monitor " + str(i), x, y, w, x ) ) + i += 1 + + # resources = randr.get_screen_resources(self.root) + # for output in resources.outputs: + # params = self.display.xrandr_get_output_info(output, resources.config_timestamp) + # if not params.crtc: + # continue + + # crtc = self.display.xrandr_get_crtc_info(params.crtc, resources.config_timestamp) + # self.monitors.append( Monitor(params.name, crtc.width, crtc.height) ) + + + def spawn(self): + pass + # cmd_base = f"exec --no-startup-id kitty --name {self.name}" + # if self.positioner_argv is None: + # cmd = cmd_base + # else: + # argv = " ".join(self.positioner_argv) + # cmd = f"{cmd_base} {argv}" + # self.i3.command(cmd) + + def on_spawned(self, _, we): + pass + # if we.container.window_instance == self.name: + # self.id = we.container.id + # self.i3.command(f"[con_id={we.container.id}] " + # "floating enable, " + # "border none, " + # "move scratchpad") + # self.fetch(self._get_focused_workspace()) + + def on_moved(self, _, we): + pass + # # Con is floating wrapper; the Kitty window/container is a child + # is_kitty = we.container.find_by_id(self.id) + # if not is_kitty: + # return + # focused_ws = self._get_focused_workspace() + # if focused_ws is None: + # return + # kitty = self._get_instance() # need "fresh" instance to capture destination WS + # kitty_ws = kitty.workspace() + # if (kitty_ws is None or kitty_ws.name == focused_ws.name or + # kitty_ws.name == "__i3_scratch"): # FIXME: fragile way to check if hidden? + # return + # self.fetch(kitty_ws, retrieve=False) + + def _get_focused_workspace(self): + pass + # focused_workspaces = [w for w in self.i3.get_workspaces() if w.focused] + # if not len(focused_workspaces): + # return None + # return focused_workspaces[0] + + def fetch(self, ws, retrieve=True): + pass + # if self.id is None: + # raise RuntimeError("Kitty instance ID not yet assigned") + # + # if self.pos in (Position.TOP, Position.BOTTOM): + # width = round(ws.rect.width * self.shape.major) + # height = round(ws.rect.height * self.shape.minor) + # x = ws.rect.x + # y = ws.rect.y if self.pos is Position.TOP else ws.rect.y + ws.rect.height - height + # else: # LEFT || RIGHT + # width = round(ws.rect.width * self.shape.minor) + # height = round(ws.rect.height * self.shape.major) + # x = ws.rect.x if self.pos is Position.LEFT else ws.rect.x + ws.rect.width - width + # y = ws.rect.y + # + # self.i3.command(f"[con_id={self.id}] " + # f"resize set {width}px {height}px, " + # f"{', move scratchpad, scratchpad show' if retrieve else ''}" + # f"move absolute position {x}px {y}px") + + @staticmethod + def on_shutdown(_, se): + exit(0) + + +def _split_args(args): + try: + split = args.index("--") + return args[:split], args[split + 1:] + except ValueError: + return args, None + + +def _simple_fraction(arg): + arg = float(arg) + if not 0 <= arg <= 1: + raise argparse.ArgumentError("Argument needs to be a simple fraction, within" + "[0, 1]") + return arg + + +def _parse_args(argv, defaults): + ap = argparse.ArgumentParser( + description="SmartPositioner: Window position wrapper for programs. " + "Arguments following '--' are forwarded to the program instance") + ap.set_defaults(**defaults) + ap.add_argument("-v", "--version", + action="version", + version=f"%(prog)s {__version__}", + help="show %(prog)s's version number and exit") + ap.add_argument("-n", "--name", + help="name/tag connecting a Kitti3 bindsym with a Kitty instance. " + "Forwarded to Kitty on spawn and scanned for on i3 binding " + "events") + ap.add_argument("-p", "--position", + type=Position.from_str, + choices=list(Position), + help="Along which edge of the screen to align the Kitty window") + ap.add_argument("-s", "--shape", + type=_simple_fraction, + nargs=2, + help="shape of the terminal window major and minor dimensions as a " + "fraction [0, 1] of the screen size (note: i3bar is accounted " + "for such that a 1.0 1.0 shaped terminal would not overlap it)") + + args = ap.parse_args(argv) + return args + + + + +def cli(): + argv_positioner3, argv_positioner = _split_args(sys.argv[1:]) + args = _parse_args(argv_positioner3, DEFAULTS) + + positioner = SmartPositioner( + name=args.name, + shape=Shape(*args.shape), + pos=args.position, + positioner_argv=argv_positioner, + ) + # positioner.loop() + + +if __name__ == "__main__": + cli() diff --git a/src/Scripts/Generate Java Project/insert_base_Java_data.sh b/src/Shell/Generate Java Project/insert_base_Java_data.sh similarity index 100% rename from src/Scripts/Generate Java Project/insert_base_Java_data.sh rename to src/Shell/Generate Java Project/insert_base_Java_data.sh diff --git a/src/Scripts/Generate Java Project/insert_script_data.sh b/src/Shell/Generate Java Project/insert_script_data.sh similarity index 100% rename from src/Scripts/Generate Java Project/insert_script_data.sh rename to src/Shell/Generate Java Project/insert_script_data.sh diff --git a/src/Scripts/Generate Java Project/make_java_project.sh b/src/Shell/Generate Java Project/make_java_project.sh similarity index 100% rename from src/Scripts/Generate Java Project/make_java_project.sh rename to src/Shell/Generate Java Project/make_java_project.sh diff --git a/src/Scripts/Retriev GBA Roms/gba_rom_dlr.sh b/src/Shell/Retriev GBA Roms/gba_rom_dlr.sh similarity index 100% rename from src/Scripts/Retriev GBA Roms/gba_rom_dlr.sh rename to src/Shell/Retriev GBA Roms/gba_rom_dlr.sh diff --git a/src/Scripts/Retriev GBA Roms/get_rom_list.sh b/src/Shell/Retriev GBA Roms/get_rom_list.sh similarity index 100% rename from src/Scripts/Retriev GBA Roms/get_rom_list.sh rename to src/Shell/Retriev GBA Roms/get_rom_list.sh diff --git a/src/Scripts/Utils/a-z_folder_maker.sh b/src/Shell/Utils/a-z_folder_maker.sh similarity index 100% rename from src/Scripts/Utils/a-z_folder_maker.sh rename to src/Shell/Utils/a-z_folder_maker.sh diff --git a/src/Scripts/Utils/clean_start_menu.sh b/src/Shell/Utils/clean_start_menu.sh similarity index 100% rename from src/Scripts/Utils/clean_start_menu.sh rename to src/Shell/Utils/clean_start_menu.sh diff --git a/src/Scripts/Utils/clear_pycache_dirs.sh b/src/Shell/Utils/clear_pycache_dirs.sh similarity index 100% rename from src/Scripts/Utils/clear_pycache_dirs.sh rename to src/Shell/Utils/clear_pycache_dirs.sh diff --git a/src/Scripts/Utils/compress.sh b/src/Shell/Utils/compress.sh similarity index 100% rename from src/Scripts/Utils/compress.sh rename to src/Shell/Utils/compress.sh diff --git a/src/Scripts/Utils/get_missing_gpg_keys.sh b/src/Shell/Utils/get_missing_gpg_keys.sh similarity index 100% rename from src/Scripts/Utils/get_missing_gpg_keys.sh rename to src/Shell/Utils/get_missing_gpg_keys.sh diff --git a/src/Scripts/Utils/get_mynoise_src.sh b/src/Shell/Utils/get_mynoise_src.sh similarity index 100% rename from src/Scripts/Utils/get_mynoise_src.sh rename to src/Shell/Utils/get_mynoise_src.sh diff --git a/src/Scripts/Utils/marathon_game_launcher.sh b/src/Shell/Utils/marathon_game_launcher.sh similarity index 100% rename from src/Scripts/Utils/marathon_game_launcher.sh rename to src/Shell/Utils/marathon_game_launcher.sh diff --git a/src/Scripts/Utils/quick_hash_extractor.sh b/src/Shell/Utils/quick_hash_extractor.sh similarity index 100% rename from src/Scripts/Utils/quick_hash_extractor.sh rename to src/Shell/Utils/quick_hash_extractor.sh diff --git a/src/Scripts/Utils/sound_fix_ubuntu.sh b/src/Shell/Utils/sound_fix_ubuntu.sh similarity index 100% rename from src/Scripts/Utils/sound_fix_ubuntu.sh rename to src/Shell/Utils/sound_fix_ubuntu.sh diff --git a/src/Scripts/automated_dreamscene_switcher.sh b/src/Shell/automated_dreamscene_switcher.sh similarity index 100% rename from src/Scripts/automated_dreamscene_switcher.sh rename to src/Shell/automated_dreamscene_switcher.sh diff --git a/src/Scripts/cisco_anytime_vpn_setup.sh b/src/Shell/cisco_anytime_vpn_setup.sh similarity index 100% rename from src/Scripts/cisco_anytime_vpn_setup.sh rename to src/Shell/cisco_anytime_vpn_setup.sh diff --git a/src/Scripts/create_virtualbox_HDD_vmdk.sh b/src/Shell/create_virtualbox_HDD_vmdk.sh similarity index 100% rename from src/Scripts/create_virtualbox_HDD_vmdk.sh rename to src/Shell/create_virtualbox_HDD_vmdk.sh diff --git a/src/Scripts/install_development_packages.sh b/src/Shell/install_development_packages.sh similarity index 100% rename from src/Scripts/install_development_packages.sh rename to src/Shell/install_development_packages.sh diff --git a/src/Scripts/media_list_backup_maker.sh b/src/Shell/media_list_backup_maker.sh similarity index 100% rename from src/Scripts/media_list_backup_maker.sh rename to src/Shell/media_list_backup_maker.sh diff --git a/src/Scripts/profile_backup.sh b/src/Shell/profile_backup.sh similarity index 100% rename from src/Scripts/profile_backup.sh rename to src/Shell/profile_backup.sh diff --git a/src/Scripts/qemu_setup.sh b/src/Shell/qemu_setup.sh similarity index 100% rename from src/Scripts/qemu_setup.sh rename to src/Shell/qemu_setup.sh diff --git a/src/Scripts/system_admin.sh b/src/Shell/system_admin.sh similarity index 100% rename from src/Scripts/system_admin.sh rename to src/Shell/system_admin.sh diff --git a/src/Scripts/system_info.sh b/src/Shell/system_info.sh similarity index 100% rename from src/Scripts/system_info.sh rename to src/Shell/system_info.sh diff --git a/src/Scripts/vnc_servers.sh b/src/Shell/vnc_servers.sh similarity index 100% rename from src/Scripts/vnc_servers.sh rename to src/Shell/vnc_servers.sh diff --git a/src/Scripts/xbox_controller.sh b/src/Shell/xbox_controller.sh similarity index 100% rename from src/Scripts/xbox_controller.sh rename to src/Shell/xbox_controller.sh