diff options
Diffstat (limited to 'gmm-gtk')
-rwxr-xr-x | gmm-gtk | 293 |
1 files changed, 293 insertions, 0 deletions
@@ -0,0 +1,293 @@ +#!/usr/bin/env python3 +# vim: set et ts=4 sts=4 sw=4: +# Startdate: 2024-11-20-4 16:31 +# Title: Graphical Mount Manager in Gtk3 +# Purpose: Easily mount iso files and easily manage these mounted files and mount points, basically like acetoneiso +# Reference: +# logout-manager-gtk +# https://python-gtk-3-tutorial.readthedocs.io/en/latest/dialogs.html +# https://python-gtk-3-tutorial.readthedocs.io/en/latest/menus.html also shows popup (right click) menu, and toolbars not used here +# https://www.programcreek.com/python/example/1399/gtk.FileChooserDialog gtk2 example which took a little work to update to gtk3; minor things like FileChooserAction.CANCEL or similar. +# https://gist.github.com/mi4code/d53b81ed6353275e9bbeedfb7b5fd990 +# https://github.com/sam-m888/python-gtk3-tutorial/blob/master/aboutdialog.rst +# disused references: +# https://python-gtk-3-tutorial.readthedocs.io/en/latest/application.html#application +# WORKHERE: need to define sudo rules + +import gi, os, sys, subprocess +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GLib, Gdk +from gi.repository.GdkPixbuf import Pixbuf + +sys.path.append("/usr/share/gmm") +sys.path.append(".") +import gmm_lib as gmm +from gmm_lib import debuglev, ferror, appname + +# GRAPHICAL APP +# gtk complains that this is deprecated, but they make every good thing "deprecated." +MENU_INFO = """ +<ui> + <menubar name='MenuBar'> + <menu action='FileMenu'> + <menuitem action='FileMount' /> + <menuitem action='FileSettings' /> + <separator /> + <menuitem action='FileQuit' /> + </menu> + <menu action='HelpMenu'> + <menuitem action='HelpAbout' /> + </menu> + </menubar> +</ui> +""" + +class SettingsDialog(Gtk.Dialog): + # ref https://python-gtk-3-tutorial.readthedocs.io/en/latest/dialogs.html#custom-dialogs + def __init__(self, parent): + super().__init__(title="Settings", transient_for=parent,flags=0) + self.add_buttons( + Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + self.set_default_size(150,50) + lbl_mounts_dir = Gtk.Label(label="Mounts directory") + self.ent_mounts_dir = Gtk.Entry(text=parent.dialog_mounts_dir) + if parent.gmmapp.mounts: + self.ent_mounts_dir.set_sensitive(False) + box = self.get_content_area() + box.add(lbl_mounts_dir) + box.add(self.ent_mounts_dir) + self.show_all() + +class MainWindow(Gtk.ApplicationWindow): + def __init__(self, gmmapp, *args, **kwargs): + super().__init__(title=gmm.appname_full,*args, **kwargs) + self.gmmapp = gmmapp + self.liststore = Gtk.ListStore(str,str) + self.set_icon_name(gmm.icon_name) + # menu + action_group = Gtk.ActionGroup(name="actions") + self.add_file_menu_actions(action_group) + self.add_help_menu_actions(action_group) + uimanager = self.create_ui_manager() + uimanager.insert_action_group(action_group) + self.menubar = uimanager.get_widget("/MenuBar") + # main layout + self.grid = Gtk.Grid() + self.add(self.grid) + self.grid.add(self.menubar) + renderer = Gtk.CellRendererText() + #column = Gtk.TreeViewColumn("Source",renderer,text=0,weight=1) + self.ml = Gtk.TreeView(model=self.liststore) + self.ml.append_column(Gtk.TreeViewColumn("Source",renderer,text=0)) + self.ml.append_column(Gtk.TreeViewColumn("Mount point",renderer,text=1)) + self.ml.connect("row-activated", self.func_double_click_entry) + self.current_selection = None + selection = self.ml.get_selection() + selection.connect("changed", self.func_tree_selection_changed) + self.grid.attach(self.ml, 0,1, 4, 1) + self.unmount_btn = Gtk.Button.new_with_mnemonic(label="_Unmount") + self.unmount_btn.connect("clicked", self.func_unmount_current_selection) + self.grid.attach_next_to(self.unmount_btn,self.ml,Gtk.PositionType.BOTTOM,1,1) + # xfe needs the Gdk.DragAction.MOVE flag + self.ml.drag_dest_set( + Gtk.DestDefaults.ALL, + [ + Gtk.TargetEntry.new("text/uri-list", 0, 0), + ], + Gdk.DragAction.COPY | Gdk.DragAction.MOVE | Gdk.DragAction.ASK | Gdk.DragAction.LINK + ) + self.ml.connect("drag-data-received", self.on_drop) + # initial load + self.refresh_form() + + def on_drop(self, context, x, y, data, info, time, o1 = None): + #print("Dropped file(s):", uris) + # mimetype, will probably just be uri-list + mimetype = None + try: + mimetype = info.get_data_type() + except: + pass + # raw string with \r\n, not useful because get_uris exists + #urilist = info.get_data() + uris = [] + try: + uris = info.get_uris() + except: + pass + if debuglev(9): + ferror(f"DEBUG: on_drop got mimetype {mimetype}") + ferror(f"DEBUG: on_drop got uris {uris}") + for filename in [i.replace("file://","") for i in uris]: + self.mount_iso_to_default(filename) + + def add_file_menu_actions(self, action_group): + action_filemenu = Gtk.Action(name="FileMenu", label="_File") + action_group.add_action(action_filemenu) + #action_fileopenmenu = Gtk.Action(name="FileOpen", stock_id=Gtk.STOCK_OPEN) + #action_group.add_action(action_fileopenmenu) + + action_mount = Gtk.Action( + name="FileMount", + label="_Mount iso...", + tooltip="Mount existing disc image", + stock_id=Gtk.STOCK_OPEN + ) + action_mount.connect("activate", self.on_menu_file_mount) + action_group.add_action_with_accel(action_mount, None) + + action_settings = Gtk.Action( + name="FileSettings", + label="_Settings...", + tooltip="Open settings dialog", + stock_id=Gtk.STOCK_PREFERENCES + ) + action_settings.connect("activate", self.on_menu_file_settings) + action_group.add_action_with_accel(action_settings, None) + + action_filequit = Gtk.Action(name="FileQuit", stock_id=Gtk.STOCK_QUIT) + action_filequit.connect("activate", self.on_menu_file_quit) + action_group.add_action(action_filequit) + + def add_help_menu_actions(self, action_group): + action_helpmenu = Gtk.Action(name="HelpMenu", label="_Help") + action_group.add_action(action_helpmenu) + action_helpaboutmenu = Gtk.Action(name="HelpAbout", stock_id=Gtk.STOCK_ABOUT) + action_helpaboutmenu.connect("activate", self.on_menu_help_about) + action_group.add_action(action_helpaboutmenu) + + def create_ui_manager(self): + uimanager = Gtk.UIManager() + uimanager.add_ui_from_string(MENU_INFO) + accelgroup = uimanager.get_accel_group() + self.add_accel_group(accelgroup) + return uimanager + + def on_menu_file_mount(self, widget): + #print("STUB Do something with file mount....") + chooser = Gtk.FileChooserDialog( + title="Mount iso file", + action=Gtk.FileChooserAction.OPEN, + buttons=( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, + Gtk.ResponseType.OK, + ) + ) + chooser.set_default_response(Gtk.ResponseType.OK) + filter = Gtk.FileFilter() + filter.set_name("Disc images") + filter.add_pattern("*.iso") + chooser.add_filter(filter) + filter = Gtk.FileFilter() + filter.set_name("All files") + filter.add_pattern("*") + chooser.add_filter(filter) + if chooser.run() == Gtk.ResponseType.OK: + filename = chooser.get_filename() + chooser.destroy() + #self.open_file(filename) + if debuglev(4): + ferror(f"From file chooser, got file {filename}") + self.mount_iso_to_default(filename) + else: + if debuglev(4): + ferror(f"File chooser returned nothing.") + chooser.destroy() + + + def mount_iso_to_default(self, filename = None, o2 = None): + if debuglev(1): + #ferror(f"Mounting file {filename}") + ferror(f"Will try to mount {filename}") + if filename: + self.gmmapp.mount_iso_to_default(filename) + self.refresh_form() + + def on_menu_file_settings(self, widget): + #print("STUB Do something with file settings....") + dialog = SettingsDialog(self) + response = dialog.run() + print(f"got settings response {response}") + if response == Gtk.ResponseType.OK: + self.gmmapp.config.set(appname,"mounts_dir",str(dialog.ent_mounts_dir.get_text())) + self.gmmapp.save_config(self.gmmapp.conffile) + # delete the dialog + dialog.destroy() + # reload config and entire app + self.refresh_form() + + def on_menu_file_quit(self, widget): + Gtk.main_quit() + + def on_menu_help_about(self, widget): + about_dialog = Gtk.AboutDialog( + program_name = gmm.appname_full, + icon_name = gmm.icon_name, + logo_icon_name = gmm.icon_name, + transient_for=self, modal=True, + copyright = "© 2024", + license_type = Gtk.License.GPL_3_0_ONLY, + authors = gmm.authors, + version = gmm.appversion, + ) + #license = Gtk.License.GPL_3_0_ONLY, + # present() will not let you close with the Close button. + #about_dialog.present() + _ = about_dialog.run() + about_dialog.destroy() + + def refresh_form(self): + # compare all config settings to see if they are different + self.gmmapp.load_config(self.gmmapp.conffile) # this populates object "self.gmmapp.config" + self.dialog_mounts_dir = self.gmmapp.config[appname]["mounts_dir"] + self.gmmapp.mounts = self.gmmapp.list_mounts() + self.liststore.clear() + for i in self.gmmapp.mounts: + print(f"DEBUG: looping over {i}") + self.liststore.append([i["source"],i["mountpoint"]]) + + def func_double_click_entry(self, tree_view, path, column): + if debuglev(9): + ferror(f"DEBUG: double-click {tree_view},{path},{column}") + path = self.current_selection + if path: + if debuglev(1): + ferror(f"Running xdg-open {path}") + subprocess.Popen(["xdg-open",path]) + else: + if debuglev(4): + ferror(f"INFO: No item selected to open, continuing...") + pass + + def func_tree_selection_changed(self, selection): + model, treeiter = selection.get_selected() + if treeiter is not None: + # you have to know which column has the mountpoint. It is column 1 in gmm. + try: + self.current_selection = str(model[treeiter][1]) + except Exception as e: + print(f"WARNING: cannot seem to save string from {model[treeiter]}") + + def func_unmount_current_selection(self, o1 = None): + if debuglev(9): + ferror(f"DEBUG: unmount button {o1}") + #ferror(f"please unmount, {self.current_selection}!") + path = self.current_selection + if path: + self.gmmapp.unmount_iso_to_path(path,"") + self.refresh_form() + elif debuglev(4): + ferror(f"INFO: Nothing selected to unmount, continuing...") + +if "__main__" == __name__: + app = gmm.Gmm(gmm.args) + app.cli_main() + if app.show_gui: + # MAIN GRAPICAL APP + win = MainWindow(app) + win.connect("destroy", Gtk.main_quit) + win.show_all() + Gtk.main() |