pax_global_header 0000666 0000000 0000000 00000000064 14555211677 0014527 g ustar 00root root 0000000 0000000 52 comment=7c4934a712ca962c98db59bc22560fb27ef9a6d5
nwg-clipman-0.2.1/ 0000775 0000000 0000000 00000000000 14555211677 0013743 5 ustar 00root root 0000000 0000000 nwg-clipman-0.2.1/.gitignore 0000664 0000000 0000000 00000000124 14555211677 0015730 0 ustar 00root root 0000000 0000000 /.idea
/venv
/nwg_clipman.egg-info/
/build/
/dist/
/nwg_clipman/__pycache__
/result
nwg-clipman-0.2.1/LICENSE 0000664 0000000 0000000 00000002055 14555211677 0014752 0 ustar 00root root 0000000 0000000 MIT License
Copyright (c) 2024 Piotr Miller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
nwg-clipman-0.2.1/README.md 0000664 0000000 0000000 00000004756 14555211677 0015236 0 ustar 00root root 0000000 0000000
nwg-clipman
This program is a part of the [nwg-shell](https://nwg-piotr.github.io/nwg-shell) project.
**Nwg-clipman** is a GTK3-based GUI for Senan Kelly's [cliphist](https://github.com/sentriz/cliphist). It provides access to previously copied items, as well
as management of the clipboard history from a window opened on gtk-layer-shell. The program is intended for use with
sway, Hyprland and other wlroots-based Wayland compositors.
## Features
- image & text history item preview;
- searching clipboard history by a textual phrase;
- deleting selected history item;
- clearing clipboard history.
## Dependencies
- python >= 3.6;
- python-gobject;
- gtk3;
- gtk-layer-shell;
- wl-clipboard;
- cliphist;
- xdg-utils.
## Installation
[](https://repology.org/project/nwg-clipman/versions)
The program may be installed by cloning this repository and executing the `install.sh` script (make sure you installed
dependencies first). Then you need to edit your compositor config file, to enable storing clipboard history to cliphist.
Example for sway:
```text
exec wl-paste --type text --watch cliphist store
exec wl-paste --type image --watch cliphist store
```
Example for Hyprland:
```text
exec-once = wl-paste --type text --watch cliphist store
exec-once = wl-paste --type image --watch cliphist store
```
You may omit the second line if you don't want images to be remembered.
Then create a key binding to launch nwg-clipman:
Example for sway:
```text
bindsym Mod1+C exec nwg-clipman
```
Example for Hyprland:
```text
bind = ALT, C, exec, nwg-clipman
```
## Options
```text
❯ nwg-clipman -h
usage: nwg-clipman [-h] [-v] [-n] [-w]
options:
-h, --help show this help message and exit
-v, --version display Version information
-n, --numbers show item Numbers in the list
-w, --window run in regular Window, w/o layer shell
```
## Hints
- To see numbers in the cliboard history use the `nwg-clipman -n` command.
- If you'd like the window to open normally, not on the layer shell, use the `nwg-clipman -w` command.
- You may clear the search entry / close the program window with the `Esc` key.
nwg-clipman-0.2.1/install.sh 0000775 0000000 0000000 00000000223 14555211677 0015745 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
python3 setup.py install --optimize=1
cp nwg-clipman.svg /usr/share/pixmaps/
cp nwg-clipman.desktop /usr/share/applications/
nwg-clipman-0.2.1/nwg-clipman.desktop 0000664 0000000 0000000 00000000554 14555211677 0017556 0 ustar 00root root 0000000 0000000 [Desktop Entry]
Type=Application
Name=Clipboard manager
Name[pl]=Menedżer schowka
GenericName=nwg-shell clipboard manager
GenericName[pl]=Menedżer schowka nwg-shell
Comment=Clipboard history search and management tool
Comment[pl]=Narzędzie do wyszukiwania i zarządzania historią schowka
Exec=nwg-clipman
Icon=nwg-clipman
Terminal=false
Categories=Utility; nwg-clipman-0.2.1/nwg-clipman.svg 0000664 0000000 0000000 00000011536 14555211677 0016706 0 ustar 00root root 0000000 0000000
nwg-clipman-0.2.1/nwg_clipman/ 0000775 0000000 0000000 00000000000 14555211677 0016241 5 ustar 00root root 0000000 0000000 nwg-clipman-0.2.1/nwg_clipman/__about__.py 0000664 0000000 0000000 00000000314 14555211677 0020517 0 ustar 00root root 0000000 0000000 try:
from importlib import metadata
except ImportError:
import importlib_metadata as metadata
try:
__version__ = metadata.version("nwg-clipman")
except Exception:
__version__ = "unknown"
nwg-clipman-0.2.1/nwg_clipman/__init__.py 0000664 0000000 0000000 00000000000 14555211677 0020340 0 ustar 00root root 0000000 0000000 nwg-clipman-0.2.1/nwg_clipman/langs/ 0000775 0000000 0000000 00000000000 14555211677 0017345 5 ustar 00root root 0000000 0000000 nwg-clipman-0.2.1/nwg_clipman/langs/de_AT 0000664 0000000 0000000 00000000327 14555211677 0020246 0 ustar 00root root 0000000 0000000 {
"clear": "Klar",
"clear-clipboard-history": "Verlauf der Zwischenablage löschen",
"close": "Schließen",
"copy": "Kopieren",
"preview": "Vorschau",
"preview-unavailable": "Vorschau nicht verfügbar"
} nwg-clipman-0.2.1/nwg_clipman/langs/de_DE 0000664 0000000 0000000 00000000327 14555211677 0020232 0 ustar 00root root 0000000 0000000 {
"clear": "Klar",
"clear-clipboard-history": "Verlauf der Zwischenablage löschen",
"close": "Schließen",
"copy": "Kopieren",
"preview": "Vorschau",
"preview-unavailable": "Vorschau nicht verfügbar"
} nwg-clipman-0.2.1/nwg_clipman/langs/en_US 0000664 0000000 0000000 00000000274 14555211677 0020304 0 ustar 00root root 0000000 0000000 {
"clear": "Clear",
"clear-clipboard-history": "Clear clipboard history",
"close": "Close",
"copy": "Copy",
"preview": "Preview",
"preview-unavailable": "Preview unavailable"
} nwg-clipman-0.2.1/nwg_clipman/langs/es_AR 0000664 0000000 0000000 00000000316 14555211677 0020261 0 ustar 00root root 0000000 0000000 {
"clear": "Claro",
"clear-clipboard-history": "Borrar historial del portapapeles",
"close": "Cerca",
"copy": "Copiar",
"preview": "Avance",
"preview-unavailable": "Vista previa no disponible"
} nwg-clipman-0.2.1/nwg_clipman/langs/es_ES 0000664 0000000 0000000 00000000316 14555211677 0020266 0 ustar 00root root 0000000 0000000 {
"clear": "Claro",
"clear-clipboard-history": "Borrar historial del portapapeles",
"close": "Cerca",
"copy": "Copiar",
"preview": "Avance",
"preview-unavailable": "Vista previa no disponible"
} nwg-clipman-0.2.1/nwg_clipman/langs/fr_FR 0000664 0000000 0000000 00000000316 14555211677 0020266 0 ustar 00root root 0000000 0000000 {
"clear": "Clair",
"clear-clipboard-history": "Effacer l'historique du presse-papiers",
"close": "Fermer",
"copy": "Copie",
"preview": "Aperçu",
"preview-unavailable": "Aperçu indisponible"
} nwg-clipman-0.2.1/nwg_clipman/langs/it_IT 0000664 0000000 0000000 00000000324 14555211677 0020277 0 ustar 00root root 0000000 0000000 {
"clear": "Chiaro",
"clear-clipboard-history": "Cancella la cronologia degli appunti",
"close": "Vicino",
"copy": "Copia",
"preview": "Anteprima",
"preview-unavailable": "Anteprima non disponibile"
} nwg-clipman-0.2.1/nwg_clipman/langs/pl_PL 0000664 0000000 0000000 00000000315 14555211677 0020275 0 ustar 00root root 0000000 0000000 {
"clear": "Wyczyść",
"clear-clipboard-history": "Wyczyścić historię schowka",
"close": "Zamknij",
"copy": "Kopiuj",
"preview": "Podgląd",
"preview-unavailable": "Podgląd niedostępny"
} nwg-clipman-0.2.1/nwg_clipman/langs/pt_BR 0000664 0000000 0000000 00000000344 14555211677 0020277 0 ustar 00root root 0000000 0000000 {
"clear": "Claro",
"clear-clipboard-history": "Limpar histórico da área de transferência",
"close": "Fechar",
"copy": "Cópia",
"preview": "Visualização",
"preview-unavailable": "Visualização indisponível"
} nwg-clipman-0.2.1/nwg_clipman/langs/pt_PT 0000664 0000000 0000000 00000000344 14555211677 0020317 0 ustar 00root root 0000000 0000000 {
"clear": "Claro",
"clear-clipboard-history": "Limpar histórico da área de transferência",
"close": "Fechar",
"copy": "Cópia",
"preview": "Visualização",
"preview-unavailable": "Visualização indisponível"
} nwg-clipman-0.2.1/nwg_clipman/langs/ru_RU 0000664 0000000 0000000 00000000543 14555211677 0020326 0 ustar 00root root 0000000 0000000 {
"clear": "Прозрачный",
"clear-clipboard-history": "Очистить историю буфера обмена",
"close": "Закрывать",
"copy": "Копировать",
"preview": "Предварительный просмотр",
"preview-unavailable": "Предварительный просмотр недоступен"
} nwg-clipman-0.2.1/nwg_clipman/main.py 0000664 0000000 0000000 00000033306 14555211677 0017544 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
nwg-shell clipboard manager (GUI for cliphist by Senan Kelly)
Copyright (c) 2024 Piotr Miller
e-mail: nwg.piotr@gmail.com
Project: https://github.com/nwg-piotr/nwg-clipman
License: MIT
"""
import argparse
import os.path
import signal
import sys
import gi
gi.require_version('Gtk', '3.0')
try:
gi.require_version('GtkLayerShell', '0.1')
except ValueError:
raise RuntimeError('\n\n' +
'If you haven\'t installed GTK Layer Shell, you need to point Python to the\n' +
'library by setting GI_TYPELIB_PATH and LD_LIBRARY_PATH to /src/.\n' +
'For example you might need to run:\n\n' +
'GI_TYPELIB_PATH=build/src LD_LIBRARY_PATH=build/src python3 ' + ' '.join(sys.argv))
from nwg_clipman.tools import *
from nwg_clipman.__about__ import __version__
from gi.repository import Gtk, Gdk, GtkLayerShell, GdkPixbuf, Pango
dir_name = os.path.dirname(__file__)
pid = os.getpid()
args = None
voc = {}
tmp_file = os.path.join(temp_dir(), "clipman.dump")
window = None
search_entry = None
flowbox_wrapper = None
flowbox = None
preview_frame = None
btn_copy = None
selected_item = None
if not is_command("cliphist") or not is_command("wl-copy"):
# die if dependencies check failed
eprint("Dependencies (cliphist, wl-clipboard) check failed, terminating")
sys.exit(1)
def list_cliphist():
# query cliphist
try:
output = subprocess.check_output("cliphist list", shell=True).decode('utf-8', errors="ignore").splitlines()
return output
except subprocess.CalledProcessError:
return []
def signal_handler(sig, frame):
# gentle handle termination
desc = {2: "SIGINT", 15: "SIGTERM"}
if sig == 2 or sig == 15:
eprint("Terminated with {}".format(desc[sig]))
Gtk.main_quit()
def terminate_old_instance():
pid_file = os.path.join(temp_dir(), "nwg-clipman-pid")
if os.path.isfile(pid_file):
try:
old_pid = int(load_text_file(pid_file))
if old_pid != pid:
eprint(f"Attempting to kill the old instance in case it's still running, pid: {old_pid}")
os.kill(old_pid, signal.SIGINT)
except:
pass
# save new pid
save_string(str(pid), pid_file)
def handle_keyboard(win, event):
# on Esc key first search entry if not empty, else terminate
global search_entry
if event.type == Gdk.EventType.KEY_RELEASE and event.keyval == Gdk.KEY_Escape:
if search_entry.get_text():
search_entry.set_text("")
else:
Gtk.main_quit()
def load_vocabulary():
# translate UI
global voc
# basic vocabulary (en_US)
voc = load_json(os.path.join(dir_name, "langs", "en_US"))
if not voc:
eprint("Failed loading vocabulary, terminating")
sys.exit(1)
# check "interface-locale" forced in nwg-shell data file - if forced, and the file exists
shell_data = load_shell_data()
lang = os.getenv("LANG")
if lang is None:
lang = "en_US"
else:
lang = lang.split(".")[0] if not shell_data["interface-locale"] else shell_data["interface-locale"]
# translate if translation available
if lang != "en_US":
loc_file = os.path.join(dir_name, "langs", "{}".format(lang))
if os.path.isfile(loc_file):
# localized vocabulary
loc = load_json(loc_file)
if not loc:
eprint("Failed loading translation into '{}'".format(lang))
else:
for key in loc:
voc[key] = loc[key]
def on_enter_notify_event(widget, event):
# highlight item
widget.set_state_flags(Gtk.StateFlags.DROP_ACTIVE, clear=False)
widget.set_state_flags(Gtk.StateFlags.SELECTED, clear=False)
def on_leave_notify_event(widget, event):
# clear highlight
widget.unset_state_flags(Gtk.StateFlags.DROP_ACTIVE)
widget.unset_state_flags(Gtk.StateFlags.SELECTED)
def flowbox_filter(_search_entry):
# filter flowbox visibility by search_entry content
def filter_func(fb_child, _text):
if _text in fb_child.get_name():
return True
else:
return False
text = _search_entry.get_text()
flowbox.set_filter_func(filter_func, text)
def on_child_activated(fb, child):
# on flowbox item clicked
global selected_item
selected_item = child.get_name()
name = bytes(child.get_name(), 'utf-8')
subprocess.run(f"cliphist decode > {tmp_file}", shell=True, input=name)
preview()
def preview():
# create preview frame content
pixbuf = None
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(tmp_file, 256, 256)
except Exception as e:
pass
for child in preview_frame.get_children():
child.destroy()
preview_frame.set_label(voc["preview"])
if pixbuf:
scrolled = Gtk.ScrolledWindow()
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
scrolled.set_min_content_height(256)
preview_frame.add(scrolled)
image = Gtk.Image.new_from_pixbuf(pixbuf)
scrolled.add(image)
else:
scrolled = Gtk.ScrolledWindow()
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
scrolled.set_min_content_height(256)
preview_frame.add(scrolled)
text = load_text_file(tmp_file)
if not text:
text = voc["preview-unavailable"]
label = Gtk.Label.new(text)
label.set_max_width_chars(80)
label.set_line_wrap(True)
label.set_line_wrap_mode(Pango.WrapMode.CHAR)
scrolled.add(label)
scrolled.set_property("name", "preview-window")
btn_copy.set_sensitive(True)
preview_frame.show_all()
def on_del_button(btn, name):
# delete entry from cliphist
eprint(f"Delete '{name}'")
name = bytes(name, 'utf-8')
subprocess.run("cliphist delete", shell=True, input=name)
search_entry.set_text("")
build_flowbox()
def on_copy_button(btn):
eprint(f"Copying: '{selected_item}'")
name = bytes(selected_item, 'utf-8')
subprocess.run("cliphist decode | wl-copy", shell=True, input=name)
Gtk.main_quit()
def on_wipe_button(btn):
win = ConfirmationWindow()
class ConfirmationWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
self.set_transient_for(window)
self.set_modal(True)
self.set_destroy_with_parent(True)
self.connect("key-release-event", self.handle_keyboard)
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
vbox.set_property("name", "warning")
self.add(vbox)
lbl = Gtk.Label.new(f'{voc["clear-clipboard-history"]}?')
vbox.pack_start(lbl, False, False, 6)
hbox = Gtk.Box.new(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
vbox.pack_start(hbox, False, False, 6)
btn = Gtk.Button.new_with_label(voc["clear"])
btn.connect("clicked", self.clear_history)
hbox.pack_start(btn, False, False, 0)
btn = Gtk.Button.new_with_label(voc["close"])
btn.connect("clicked", self.quit)
hbox.pack_start(btn, False, False, 0)
self.show_all()
def quit(self, btn):
self.destroy()
def handle_keyboard(self, win, event):
if event.type == Gdk.EventType.KEY_RELEASE and event.keyval == Gdk.KEY_Escape:
self.destroy()
def clear_history(self, btn):
eprint("Wipe cliphist")
subprocess.run("cliphist wipe", shell=True)
Gtk.main_quit()
class FlowboxItem(Gtk.Box):
def __init__(self, parts):
Gtk.EventBox.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
if args.numbers:
label = Gtk.Label.new(parts[0])
self.add(label)
eb = Gtk.EventBox()
self.pack_start(eb, True, True, 0)
name = parts[1]
label = Gtk.Label.new(name)
label.set_property("halign", Gtk.Align.START)
eb.add(label)
button = Gtk.Button.new_from_icon_name("edit-clear", Gtk.IconSize.MENU)
button.set_property("name", "del-btn")
button.set_property("margin-right", 9)
button.connect("clicked", on_del_button, "\t".join(parts))
self.pack_end(button, False, False, 0)
eb.connect('enter-notify-event', on_enter_notify_event)
eb.connect('leave-notify-event', on_leave_notify_event)
def build_flowbox():
global flowbox_wrapper
global flowbox
# destroy flowbox wrapper content, if any
for item in flowbox_wrapper.get_children():
item.destroy()
# build from scratch
scrolled = Gtk.ScrolledWindow()
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
scrolled.set_min_content_height(300)
scrolled.set_propagate_natural_height(True)
flowbox_wrapper.add(scrolled)
flowbox = Gtk.FlowBox()
flowbox.set_selection_mode(Gtk.SelectionMode.NONE)
flowbox.connect("child-activated", on_child_activated)
flowbox.set_valign(Gtk.Align.START)
flowbox.set_max_children_per_line(1)
scrolled.add(flowbox)
# query cliphist
clip_hist = list_cliphist()
for line in clip_hist:
parts = line.split("\t")
_name = parts[1]
item = FlowboxItem(parts)
child = Gtk.FlowBoxChild()
# we will be filtering by _name
child.set_name(line)
child.add(item)
flowbox.add(child)
flowbox_wrapper.show_all()
def main():
# handle signals
catchable_sigs = set(signal.Signals) - {signal.SIGKILL, signal.SIGSTOP}
for sig in catchable_sigs:
signal.signal(sig, signal_handler)
# arguments
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--version", action="version",
version="%(prog)s version {}".format(__version__),
help="display Version information")
parser.add_argument("-n", "--numbers", action="store_true", help="show item Numbers in the list")
parser.add_argument("-w", "--window", action="store_true", help="run in regular Window, w/o layer shell")
global args
args = parser.parse_args()
# kill running instance, if any
terminate_old_instance()
global search_entry
# UI strings localization
load_vocabulary()
global window
window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
if not args.window:
# attach to gtk-layer-shell
GtkLayerShell.init_for_window(window)
GtkLayerShell.set_layer(window, GtkLayerShell.Layer.TOP)
GtkLayerShell.set_exclusive_zone(window, 0)
GtkLayerShell.set_keyboard_mode(window, GtkLayerShell.KeyboardMode.ON_DEMAND)
window.connect('destroy', Gtk.main_quit)
window.connect("key-release-event", handle_keyboard)
vbox = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=6)
vbox.set_property("name", "main-wrapper")
window.add(vbox)
hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
vbox.pack_start(hbox, False, False, 0)
# search entry
search_entry = Gtk.SearchEntry()
search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "edit-clear-symbolic")
search_entry.set_property("hexpand", True)
search_entry.set_property("margin", 12)
search_entry.set_size_request(700, 0)
search_entry.connect('search_changed', flowbox_filter)
hbox.pack_start(search_entry, False, True, 0)
# "Clear" button next to search entry
btn = Gtk.Button.new_with_label(voc["clear"])
btn.set_property("valign", Gtk.Align.CENTER)
btn.connect("clicked", on_wipe_button)
hbox.pack_start(btn, False, False, 6)
# wrapper for the flowbox
global flowbox_wrapper
flowbox_wrapper = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
flowbox_wrapper.set_property("margin-left", 12)
flowbox_wrapper.set_property("margin-right", 12)
vbox.pack_start(flowbox_wrapper, False, False, 0)
# clear flowbox wrapper, build content
build_flowbox()
hbox = Gtk.Box.new(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
hbox.set_property("margin", 12)
vbox.pack_end(hbox, False, False, 0)
global preview_frame
preview_frame = Gtk.Frame.new(voc["preview"])
hbox.pack_start(preview_frame, True, True, 0)
# temporary placeholder for future content preview
placeholder_box = Gtk.Box.new(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
preview_frame.add(placeholder_box)
placeholder_box.set_size_request(0, 256)
preview_frame.show_all()
# "Clear" and "Close" buttons
ibox = Gtk.Box.new(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
ibox.set_homogeneous(True)
hbox.pack_end(ibox, False, False, 0)
# "Copy" button
global btn_copy
btn_copy = Gtk.Button.new_with_label(voc["copy"])
btn_copy.set_property("valign", Gtk.Align.END)
btn_copy.connect("clicked", on_copy_button)
btn_copy.set_sensitive(False)
ibox.pack_start(btn_copy, True, True, 0)
# "Close" button
btn = Gtk.Button.new_with_label(voc["close"])
btn.set_property("valign", Gtk.Align.END)
btn.connect("clicked", Gtk.main_quit)
ibox.pack_start(btn, True, True, 0)
window.show_all()
# customize styling
screen = Gdk.Screen.get_default()
provider = Gtk.CssProvider()
style_context = Gtk.StyleContext()
style_context.add_provider_for_screen(screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
css = b"""
#main-wrapper { background-color: rgba(0, 0, 0, 0.1) }
#preview-window { padding: 0 6px 0 6px }
#del-btn { background: none; border: none; margin: 0; padding: 0 }
#del-btn:hover { background-color: rgba(255, 255, 255, 0.1) }
#warning { border: solid 1px; padding: 24px; margin: 6px}
"""
provider.load_from_data(css)
Gtk.main()
if __name__ == "__main__":
sys.exit(main())
nwg-clipman-0.2.1/nwg_clipman/tools.py 0000664 0000000 0000000 00000003545 14555211677 0017762 0 ustar 00root root 0000000 0000000 import json
import os
import subprocess
import sys
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def is_command(cmd):
cmd = cmd.split()[0]
cmd = "command -v {}".format(cmd)
try:
is_cmd = subprocess.check_output(
cmd, shell=True).decode("utf-8").strip()
if is_cmd:
return True
except subprocess.CalledProcessError:
return False
def temp_dir():
if os.getenv("TMPDIR"):
return os.getenv("TMPDIR")
elif os.getenv("TEMP"):
return os.getenv("TEMP")
elif os.getenv("TMP"):
return os.getenv("TMP")
return "/tmp"
def load_text_file(path):
try:
with open(path, 'r') as file:
data = file.read()
return data
except Exception as e:
return None
def save_string(string, file):
try:
file = open(file, "wt")
file.write(string)
file.close()
except Exception as e:
print(f"Error writing '{file}': {e}")
def load_json(path):
try:
with open(path, 'r') as f:
return json.load(f)
except Exception as e:
print("Error loading json: {}".format(e))
return None
def get_shell_data_dir():
data_dir = ""
home = os.getenv("HOME")
xdg_data_home = os.getenv("XDG_DATA_HOME")
if xdg_data_home:
data_dir = os.path.join(xdg_data_home, "nwg-shell/")
else:
if home:
data_dir = os.path.join(home, ".local/share/nwg-shell/")
return data_dir
def load_shell_data():
shell_data_file = os.path.join(get_shell_data_dir(), "data")
shell_data = load_json(shell_data_file) if os.path.isfile(shell_data_file) else {}
defaults = {
"interface-locale": ""
}
for key in defaults:
if key not in shell_data:
shell_data[key] = defaults[key]
return shell_data
nwg-clipman-0.2.1/setup.py 0000664 0000000 0000000 00000001231 14555211677 0015452 0 ustar 00root root 0000000 0000000 import os
from setuptools import setup, find_packages
def read(f_name):
return open(os.path.join(os.path.dirname(__file__), f_name)).read()
setup(
name='nwg-clipman',
version='0.2.1',
description='nwg-shell clipboard manager',
packages=find_packages(),
include_package_data=True,
package_data={
"": ["langs/*"]
},
url='https://github.com/nwg-piotr/nwg-clipman',
license='MIT',
author='Piotr Miller',
author_email='nwg.piotr@gmail.com',
python_requires='>=3.6.0',
install_requires=[],
entry_points={
'gui_scripts': [
'nwg-clipman = nwg_clipman.main:main'
]
}
)