pax_global_header 0000666 0000000 0000000 00000000064 14706556777 0014542 g ustar 00root root 0000000 0000000 52 comment=78d45e5b5093711e0e692f8ecf9af207417eb791
nwg-clipman-0.2.4/ 0000775 0000000 0000000 00000000000 14706556777 0013761 5 ustar 00root root 0000000 0000000 nwg-clipman-0.2.4/.github/ 0000775 0000000 0000000 00000000000 14706556777 0015321 5 ustar 00root root 0000000 0000000 nwg-clipman-0.2.4/.github/FUNDING.yml 0000664 0000000 0000000 00000000041 14706556777 0017131 0 ustar 00root root 0000000 0000000 github: nwg-piotr
liberapay: nwg
nwg-clipman-0.2.4/.gitignore 0000664 0000000 0000000 00000000124 14706556777 0015746 0 ustar 00root root 0000000 0000000 /.idea
/venv
/nwg_clipman.egg-info/
/build/
/dist/
/nwg_clipman/__pycache__
/result
nwg-clipman-0.2.4/LICENSE 0000664 0000000 0000000 00000002055 14706556777 0014770 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.4/README.md 0000664 0000000 0000000 00000004756 14706556777 0015254 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.4/install.sh 0000775 0000000 0000000 00000001656 14706556777 0015776 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
# Before running this script, make sure you have python-build, python-installer,
# python-wheel and python-setuptools installed.
PROGRAM_NAME="nwg-clipman"
MODULE_NAME="nwg_clipman"
SITE_PACKAGES="$(python3 -c "import sysconfig; print(sysconfig.get_paths()['purelib'])")"
PATTERN="$SITE_PACKAGES/$MODULE_NAME*"
# Remove from site_packages
for path in $PATTERN; do
if [ -e "$path" ]; then
echo "Removing $path"
rm -r "$path"
fi
done
# Remove launcher script
if [ -f "/usr/bin/$PROGRAM_NAME" ]; then
echo "Removing /usr/bin/$PROGRAM_NAME"
rm "/usr/bin/nwg-clipman"
fi
python -m build --wheel --no-isolation
python -m installer dist/*.whl
install -Dm 644 -t "/usr/share/pixmaps" "$PROGRAM_NAME.svg"
install -Dm 644 -t "/usr/share/applications" "$PROGRAM_NAME.desktop"
install -Dm 644 -t "/usr/share/licenses/$PROGRAM_NAME" LICENSE
install -Dm 644 -t "/usr/share/doc/$PROGRAM_NAME" README.md
nwg-clipman-0.2.4/nwg-clipman.desktop 0000664 0000000 0000000 00000000554 14706556777 0017574 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.4/nwg-clipman.svg 0000664 0000000 0000000 00000011536 14706556777 0016724 0 ustar 00root root 0000000 0000000
nwg-clipman-0.2.4/nwg_clipman/ 0000775 0000000 0000000 00000000000 14706556777 0016257 5 ustar 00root root 0000000 0000000 nwg-clipman-0.2.4/nwg_clipman/__about__.py 0000664 0000000 0000000 00000000314 14706556777 0020535 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.4/nwg_clipman/__init__.py 0000664 0000000 0000000 00000000000 14706556777 0020356 0 ustar 00root root 0000000 0000000 nwg-clipman-0.2.4/nwg_clipman/langs/ 0000775 0000000 0000000 00000000000 14706556777 0017363 5 ustar 00root root 0000000 0000000 nwg-clipman-0.2.4/nwg_clipman/langs/de_AT.json 0000664 0000000 0000000 00000000327 14706556777 0021234 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.4/nwg_clipman/langs/de_DE.json 0000664 0000000 0000000 00000000327 14706556777 0021220 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.4/nwg_clipman/langs/en_US.json 0000664 0000000 0000000 00000000274 14706556777 0021272 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.4/nwg_clipman/langs/es_AR.json 0000664 0000000 0000000 00000000316 14706556777 0021247 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.4/nwg_clipman/langs/es_ES.json 0000664 0000000 0000000 00000000316 14706556777 0021254 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.4/nwg_clipman/langs/fr_FR.json 0000664 0000000 0000000 00000000316 14706556777 0021254 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.4/nwg_clipman/langs/it_IT.json 0000664 0000000 0000000 00000000324 14706556777 0021265 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.4/nwg_clipman/langs/pl_PL.json 0000664 0000000 0000000 00000000315 14706556777 0021263 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.4/nwg_clipman/langs/pt_BR.json 0000664 0000000 0000000 00000000330 14706556777 0021260 0 ustar 00root root 0000000 0000000 {
"clear": "Limpar",
"clear-clipboard-history": "Limpar histórico da área de transferência",
"close": "Fechar",
"copy": "Copiar",
"preview": "Prévia",
"preview-unavailable": "Prévia indisponível"
}
nwg-clipman-0.2.4/nwg_clipman/langs/pt_PT.json 0000664 0000000 0000000 00000000344 14706556777 0021305 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.4/nwg_clipman/langs/ru_RU.json 0000664 0000000 0000000 00000000543 14706556777 0021314 0 ustar 00root root 0000000 0000000 {
"clear": "Прозрачный",
"clear-clipboard-history": "Очистить историю буфера обмена",
"close": "Закрывать",
"copy": "Копировать",
"preview": "Предварительный просмотр",
"preview-unavailable": "Предварительный просмотр недоступен"
} nwg-clipman-0.2.4/nwg_clipman/main.py 0000664 0000000 0000000 00000034017 14706556777 0017562 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:
print(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.json"))
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", "{}.json".format(lang))
if os.path.isfile(loc_file):
# localized vocabulary
loc = load_json(loc_file)
if not loc:
eprint(f"Failed loading translation '{loc_file}'")
else:
print(f"Loaded translation file: '{loc_file}'")
for key in loc:
voc[key] = loc[key]
else:
eprint(f"Translation into {lang} not found")
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_modal(True)
self.set_destroy_with_parent(True)
self.connect("key-release-event", self.handle_keyboard)
GtkLayerShell.init_for_window(self)
GtkLayerShell.set_layer(self, GtkLayerShell.Layer.OVERLAY)
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:
try:
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)
except IndexError:
eprint(f"Error parsing line: {line}")
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.4/nwg_clipman/tools.py 0000664 0000000 0000000 00000003545 14706556777 0020000 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.4/setup.py 0000664 0000000 0000000 00000001231 14706556777 0015470 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.4',
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'
]
}
)
nwg-clipman-0.2.4/uninstall.sh 0000775 0000000 0000000 00000001345 14706556777 0016334 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
PROGRAM_NAME="nwg-clipman"
MODULE_NAME="nwg_clipman"
SITE_PACKAGES="$(python3 -c "import sysconfig; print(sysconfig.get_paths()['purelib'])")"
PATTERN="$SITE_PACKAGES/$MODULE_NAME*"
# Remove from site_packages
for path in $PATTERN; do
if [ -e "$path" ]; then
echo "Removing $path"
rm -r "$path"
fi
done
# Remove launcher script
if [ -f "/usr/bin/$PROGRAM_NAME" ]; then
echo "Removing /usr/bin/$PROGRAM_NAME"
rm "/usr/bin/nwg-clipman"
fi
echo "Removing icon, .desktop file, license and readme"
rm -f "/usr/share/pixmaps/$PROGRAM_NAME.svg"
rm -f "/usr/share/applications/$PROGRAM_NAME.desktop"
rm -f "/usr/share/licenses/$PROGRAM_NAME/LICENSE"
rm -f "/usr/share/doc/$PROGRAM_NAME/README.md"