#!/usr/bin/python3
import gi, hashlib, threading
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, Pango

class App(Gtk.Window):
    def __init__(self):
        super().__init__(title='Hash Tool')
        self.set_default_size(540, 340)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
        vbox.set_margin_top(12); vbox.set_margin_bottom(12)
        vbox.set_margin_start(12); vbox.set_margin_end(12)
        self.add(vbox)
        # input
        hb = Gtk.Box(spacing=6)
        self.text_entry = Gtk.Entry()
        self.text_entry.set_placeholder_text('Enter text to hash...')
        hb.pack_start(self.text_entry, True, True, 0)
        file_btn = Gtk.Button(label='Hash File...')
        file_btn.connect('clicked', self.hash_file)
        hb.pack_start(file_btn, False, False, 0)
        vbox.pack_start(hb, False, False, 0)
        # algorithm buttons
        algo_box = Gtk.Box(spacing=6)
        for algo in ['MD5','SHA1','SHA256','SHA512']:
            btn = Gtk.Button(label=algo)
            btn.connect('clicked', self.compute, algo.lower().replace('-',''))
            algo_box.pack_start(btn, True, True, 0)
        vbox.pack_start(algo_box, False, False, 0)
        # result
        vbox.add(Gtk.Label(label='Hash result:', xalign=0))
        result_box = Gtk.Box(spacing=6)
        self.result = Gtk.Entry(); self.result.set_editable(False)
        self.result.override_font(Pango.FontDescription('Monospace 10'))
        result_box.pack_start(self.result, True, True, 0)
        copy_btn = Gtk.Button(label='Copy')
        copy_btn.connect('clicked', self.copy_hash)
        result_box.pack_start(copy_btn, False, False, 0)
        vbox.pack_start(result_box, False, False, 0)
        self.info_lbl = Gtk.Label(label='', xalign=0)
        vbox.pack_start(self.info_lbl, False, False, 0)
        # all hashes
        frame = Gtk.Frame(label='All Hashes')
        fbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        fbox.set_margin_top(6); fbox.set_margin_bottom(6)
        fbox.set_margin_start(6); fbox.set_margin_end(6)
        self.all_labels = {}
        for algo in ['md5','sha1','sha256','sha512']:
            hrow = Gtk.Box(spacing=6)
            hrow.add(Gtk.Label(label=f'{algo.upper()}:', xalign=0, width_chars=8))
            lbl = Gtk.Label(label='—', xalign=0, selectable=True)
            lbl.override_font(Pango.FontDescription('Monospace 9'))
            hrow.pack_start(lbl, True, True, 0)
            fbox.pack_start(hrow, False, False, 0)
            self.all_labels[algo] = lbl
        frame.add(fbox)
        vbox.pack_start(frame, True, True, 0)
    def compute(self, _, algo):
        data = self.text_entry.get_text().encode('utf-8')
        self._hash_data(data, algo, 'text')
    def _hash_data(self, data, primary_algo, source):
        for algo in ['md5','sha1','sha256','sha512']:
            h = hashlib.new(algo, data).hexdigest()
            self.all_labels[algo].set_text(h)
        h = hashlib.new(primary_algo, data).hexdigest()
        self.result.set_text(h)
        self.info_lbl.set_text(f'{primary_algo.upper()}  |  {len(data)} bytes  |  source: {source}')
    def hash_file(self, *_):
        dlg = Gtk.FileChooserDialog(title='Select File', parent=self,
            action=Gtk.FileChooserAction.OPEN)
        dlg.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                        Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
        if dlg.run() == Gtk.ResponseType.OK:
            path = dlg.get_filename()
            threading.Thread(target=self._hash_file_thread, args=(path,), daemon=True).start()
        dlg.destroy()
    def _hash_file_thread(self, path):
        with open(path, 'rb') as f: data = f.read()
        GLib.idle_add(self._hash_data, data, 'sha256', f'file:{path}')
    def copy_hash(self, *_):
        clipboard = Gtk.Clipboard.get(self.get_display().get_default_screen().get_display().get_clipboard() if False else self.get_clipboard(self.get_display().get_default_screen().intern_atom('CLIPBOARD', False)))
        clipboard.set_text(self.result.get_text(), -1)

win = App()
win.connect('destroy', Gtk.main_quit)
win.show_all()
Gtk.main()
