From: robk-tahoe Date: Wed, 20 Feb 2008 01:56:59 +0000 (-0700) Subject: mac: added 'mount filesystem' action to the mac gui X-Git-Url: https://git.rkrishnan.org/pf/content/en/footer/FOOURL?a=commitdiff_plain;h=899d6027229450da56d08e03ed67b8caec59d643;p=tahoe-lafs%2Ftahoe-lafs.git mac: added 'mount filesystem' action to the mac gui this adds an action to the dock menu and to the file menu (when visible) "Mount Filesystem". This action opens a windows offering the user an opportunity to select from any of the named *.cap files in their .tahoe/private directory, and choose a corresponding mount point to mount that at. it launches the .app binary as a subprocess with the corresponding command line arguments to launch the 'tahoe fuse' functionality to mount that file system. if a NAME.icns file is present in .tahoe/private alonside the chosen NAME.cap, then that icon will be used when the filesystem is mounted. this is highly unlikely to work when running from source, since it uses introspection on sys.executable to find the relavent binary to launch in order to get the right built .app's 'tahoe fuse' functionality. it is also relatively likely that the code currently checked in, hence linked into the build, will have as yet unresolved library dependencies. it's quite unlikely to work on 10.5 with macfuse 1.3.1 at the moment. --- diff --git a/src/allmydata/gui/macapp.py b/src/allmydata/gui/macapp.py index d8eee3e7..e8f10577 100644 --- a/src/allmydata/gui/macapp.py +++ b/src/allmydata/gui/macapp.py @@ -2,6 +2,7 @@ import operator import os import stat +import subprocess import sys import thread import threading @@ -16,6 +17,7 @@ from twisted.python import log, logfile import allmydata from allmydata import client from allmydata.gui.confwiz import ConfWizApp, ACCOUNT_PAGE, DEFAULT_SERVER_URL +from allmydata.uri import NewDirectoryURI import amdicon @@ -170,6 +172,7 @@ def DisplayTraceback(message): WEBOPEN_ID = wx.NewId() ACCOUNT_PAGE_ID = wx.NewId() +MOUNT_ID = wx.NewId() class SplashFrame(wx.Frame): def __init__(self): @@ -217,10 +220,145 @@ class SplashPanel(wx.Panel): self.SetAutoLayout(True) +class MountFrame(wx.Frame): + def __init__(self, app): + wx.Frame.__init__(self, None, -1, 'Allmydata Tahoe Mount Filesystem') + + self.SetSizeHints(100, 100, 600, 800) + self.SetIcon(amdicon.getIcon()) + self.Bind(wx.EVT_CLOSE, self.on_close) + + background = wx.Panel(self, -1) + background.parent = self + self.mount_panel = MountPanel(background, self.on_close, app) + sizer = wx.BoxSizer(wx.VERTICAL) + background_sizer = wx.BoxSizer(wx.VERTICAL) + background_sizer.Add(self.mount_panel, 1, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 26) + background.SetSizer(background_sizer) + sizer.Add(background, 0, wx.EXPAND | wx.ALL, 0) + self.SetSizer(sizer) + self.SetAutoLayout(True) + self.Fit() + self.Layout() + + def on_close(self, event): + self.Show(False) + +class MountPanel(wx.Panel): + def __init__(self, parent, on_close, app): + wx.Panel.__init__(self, parent, -1) + self.parent = parent + self.app = app + + self.sizer = wx.BoxSizer(wx.VERTICAL) + + self.caps = self.find_dir_caps() + + self.label = wx.StaticText(self, -1, 'Allmydata Tahoe Mount Filesystem') + self.mnt_label = wx.StaticText(self, -1, 'Mount') + self.cap_choice = wx.Choice(self, -1, (120, 64), choices=self.caps.keys()) + root_dir = self.cap_choice.FindString('root_dir') + if root_dir != -1: + self.cap_choice.SetSelection(root_dir) + self.at_label = wx.StaticText(self, -1, 'at') + self.mountpoint = wx.TextCtrl(self, -1, 'choose a mount dir', size=(256,22)) + self.mnt_browse = wx.Button(self, -1, 'Browse') + mount_sizer = wx.BoxSizer(wx.HORIZONTAL) + mount_sizer.Add(self.mnt_label, 0, wx.ALL, 4) + mount_sizer.Add(self.cap_choice, 0, wx.ALL, 4) + mount_sizer.Add(self.at_label, 0, wx.ALL, 4) + mount_sizer.Add(self.mountpoint, 0, wx.ALL, 4) + mount_sizer.Add(self.mnt_browse, 0, wx.ALL, 4) + self.mount = wx.Button(self, -1, 'Mount') + self.Bind(wx.EVT_BUTTON, self.on_mount, self.mount) + #self.Bind(wx.EVT_CHOICE, self.on_choice, self.cap_choice) + self.Bind(wx.EVT_BUTTON, self.on_mnt_browse, self.mnt_browse) + self.sizer.Add(self.label, 0, wx.CENTER | wx.ALL, 2) + self.sizer.Add(wx.Size(28,28), 1, wx.EXPAND | wx.ALL, 2) + self.sizer.Add(mount_sizer, 0, wx.EXPAND | wx.ALL, 0) + self.sizer.Add(wx.Size(28,28), 1, wx.EXPAND | wx.ALL, 2) + self.sizer.Add(self.mount, 0, wx.CENTER | wx.ALL, 2) + self.SetSizer(self.sizer) + self.SetAutoLayout(True) + + def find_dir_caps(self): + priv_dir = os.path.join(self.app.basedir, 'private') + fs = os.listdir(priv_dir) + caps = {} + for f in fs: + if not f.endswith('.cap'): + continue + try: + log.msg('reading: %r' % (f,)) + fh = file(os.path.join(priv_dir, f), 'rb') + cap = fh.read().strip() + fh.close() + uri = NewDirectoryURI.init_from_string(cap) + caps[f[:-4]] = cap + except: + log.msg('failed to read dir cap from "%s"' % (f,)) + log.err() + return caps + + #def on_choice(self, event): + #choice = event.GetString() + #log.msg('chose dir: %s' % (choice,)) + + def on_mount(self, event): + mountpoint = str(self.mountpoint.GetValue()) + if not os.path.isdir(mountpoint): + wx.MessageBox(u'"%s" is not a directory' % (mountpoint,)) + else: + cap_name = self.cap_choice.GetStringSelection() + self.do_mount(cap_name, mountpoint) + + def on_mnt_browse(self, event): + dlg = wx.DirDialog(self, "Choose a Mountpoint Directory:", + style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON) + if dlg.ShowModal() == wx.ID_OK: + mountpoint = dlg.GetPath() + self.mountpoint.SetValue(mountpoint) + dlg.Destroy() + + def do_mount(self, cap_name, mountpoint): + log.msg('do_mount(%r, %r)' % (cap_name, mountpoint)) + log.msg('sys.exec = %r' % (sys.executable,)) + if not sys.executable.endswith('Tahoe.app/Contents/MacOS/python'): + log.msg("can't find tahoe.app: sys.executable = %r" % (sys.executable,)) + wx.MessageBox("Can't determine location of Allmydata Tahoe.app") + self.parent.parent.Show(False) + return + bin_path = sys.executable[:-6] + 'Allmydata Tahoe' + log.msg('%r exists: %r' % (bin_path, os.path.exists(bin_path),)) + + icns_path = os.path.join(self.app.basedir, 'private', cap_name+'.icns') + if os.path.exists(icns_path): + icon_arg = ['-ovolicon=%s' % (icns_path,)] + else: + icon_arg = [] + + command = [bin_path, 'fuse', cap_name] + icon_arg + [mountpoint] + log.msg('spawning command %r' % (command,)) + proc = subprocess.Popen(command, + cwd=self.app.basedir, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + log.msg('spawned process, pid %s' % (proc.pid,)) + wx.FutureCall(4096, self.check_mount, proc) + self.parent.parent.Show(False) + + def check_mount(self, proc): + message = [ 'pid: %s' % (proc.pid,), + 'ret: %s' % (proc.returncode,), + 'stdout:\n%s' % (proc.stdout.read(),), + 'stderr:\n%s' % (proc.stderr.read(),), + ] + log.msg('\n'.join(['spawned process:'] + message)) + class MacGuiApp(wx.App): def __init__(self, app): - wx.App.__init__(self) self.app = app + wx.App.__init__(self) def OnInit(self): try: @@ -230,6 +368,8 @@ class MacGuiApp(wx.App): wx.FutureCall(4096, self.on_timer, None) + self.mount_frame = MountFrame(self.app) + self.setup_dock_icon() menubar = self.setup_app_menu(self.frame) self.frame.SetMenuBar(menubar) @@ -254,6 +394,8 @@ class MacGuiApp(wx.App): frame.Bind(wx.EVT_MENU, self.on_webopen, item) item = file_menu.Append(ACCOUNT_PAGE_ID, text='Open Account Page') frame.Bind(wx.EVT_MENU, self.on_account_page, item) + item = file_menu.Append(MOUNT_ID, text='Mount Filesystem') + frame.Bind(wx.EVT_MENU, self.on_mount, item) item = file_menu.Append(wx.ID_ABOUT, text='About') frame.Bind(wx.EVT_MENU, self.on_about, item) item = file_menu.Append(wx.ID_EXIT, text='Quit') @@ -269,6 +411,8 @@ class MacGuiApp(wx.App): self.tbicon.Bind(wx.EVT_MENU, self.on_webopen, item) item = dock_menu.Append(ACCOUNT_PAGE_ID, text='Open Account Page') self.tbicon.Bind(wx.EVT_MENU, self.on_account_page, item) + item = dock_menu.Append(MOUNT_ID, text='Mount Filesystem') + self.tbicon.Bind(wx.EVT_MENU, self.on_mount, item) self.tbicon.PopupMenu(dock_menu) def on_about(self, event): @@ -283,3 +427,6 @@ class MacGuiApp(wx.App): def on_account_page(self, event): webbrowser.open(DEFAULT_SERVER_URL + ACCOUNT_PAGE) + def on_mount(self, event): + self.mount_frame.Show(True) +