diff --git a/.project b/.project new file mode 100644 index 0000000..1c6dd94 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + nemo + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + diff --git a/.pydevproject b/.pydevproject new file mode 100644 index 0000000..037bd25 --- /dev/null +++ b/.pydevproject @@ -0,0 +1,8 @@ + + + +/${PROJECT_DIR_NAME} + +python 2.7 +Default + diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..676fca1 Binary files /dev/null and b/icon.png differ diff --git a/nemo.py b/nemo.py new file mode 100755 index 0000000..390ced1 --- /dev/null +++ b/nemo.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# +# generated by wxGlade 0.6.8 on Sun Mar 9 14:56:37 2014 +# + +application_name = u'NeMo' +application_version = u'0.1.0' + +import json +import os +import subprocess +import wx + +# begin wxGlade: dependencies +import gettext +# end wxGlade + +# begin wxGlade: extracode +# end wxGlade + + +class NeMo(wx.Frame): + def __init__(self, *args, **kwds): + # begin wxGlade: NeMo.__init__ + kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE + wx.Frame.__init__(self, *args, **kwds) + self.SetSize((900, 480)) + self.text_ctrl_stdout = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY) + self.text_ctrl_stderr = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY) + + self.__set_properties() + self.__do_layout() + # end wxGlade + + def __set_properties(self): + # begin wxGlade: NeMo.__set_properties + self.SetTitle(_("NeMo")) + self.text_ctrl_stdout.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) + self.text_ctrl_stderr.SetForegroundColour(wx.Colour(255, 0, 0)) + self.text_ctrl_stderr.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) + # end wxGlade + self.SetTitle(_("%s - V%s" % (application_name, application_version))) + + def __do_layout(self): + # begin wxGlade: NeMo.__do_layout + main_sizer = wx.BoxSizer(wx.HORIZONTAL) + output_sizer = wx.BoxSizer(wx.VERTICAL) + stderr_sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("stderr")), wx.HORIZONTAL) + stdout_sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("stdout")), wx.HORIZONTAL) + action_sizer = wx.BoxSizer(wx.VERTICAL) + action_sizer.Add((0, 0), 0, 0, 0) + action_sizer.Add((0, 0), 0, 0, 0) + main_sizer.Add(action_sizer, 1, wx.EXPAND, 0) + stdout_sizer.Add(self.text_ctrl_stdout, 1, wx.EXPAND, 0) + output_sizer.Add(stdout_sizer, 1, wx.EXPAND, 0) + stderr_sizer.Add(self.text_ctrl_stderr, 1, wx.EXPAND, 0) + output_sizer.Add(stderr_sizer, 1, wx.EXPAND, 0) + main_sizer.Add(output_sizer, 3, wx.EXPAND, 0) + self.SetSizer(main_sizer) + self.Layout() + self.Centre() + # end wxGlade + self.action_sizer = action_sizer + + def AddStaBu(self, stabu): + self.action_sizer.Add(stabu, 0, wx.EXPAND, 0) + self.Layout() +# end of class NeMo + + +class StaBu(wx.BoxSizer): + SSH_FS = 'ssh' + WEB_DAV = 'dav' + FTP_FS = 'ftp' + + def __init__(self, wx_frame, stdout, stderr, **kwargs): + self.wx_frame = wx_frame + self.stdout = stdout + self.stderr = stderr + + for key in ['name', 'prot', 'remote_host', 'remote_path', 'local_path', 'user', 'port']: + setattr(self, key, kwargs[key]) + for key, value in [('password', None), ]: + setattr(self, key, kwargs.get(key, value)) + + wx.BoxSizer.__init__(self, wx.HORIZONTAL) + self.button = wx.Button(wx_frame, wx.ID_ANY, _(self.name)) + self.panel = wx.Panel(wx_frame, wx.ID_ANY) + if kwargs.get('hidden', False): + self.button.Hide() + self.panel.Hide() + elif kwargs.get('disabled', False): + self.button.Disable() + self.Add(self.button, 3, 0, 0) + self.Add(self.panel, 1, wx.EXPAND, 0) + wx_frame.Bind(wx.EVT_BUTTON, self.mount_pressed, self.button) + self.panel.Bind(wx.EVT_LEFT_UP, self.open_pressed) + wx_frame.Bind(wx.EVT_IDLE, self.panel_update, None) + wx_frame.AddStaBu(self) + + def set_password(self): + if self.password == None: + box = wx.PasswordEntryDialog(self.wx_frame, 'Please enter password for %(name)s:' % self.__dict__, 'Password') + if box.ShowModal() == wx.ID_OK: + self.password = box.GetValue() + box.Destroy() + + def error_msg(self, msg): + self.stderr.write('nemo-file (%s): ' % self.name) + self.stderr.write(msg) + + def panel_update(self, event): + if self.is_mounted(): + #self.button_open.SetBackgroundColour('green') + self.panel.SetBackgroundColour('green') + else: + #self.button_open.SetBackgroundColour('red') + self.panel.SetBackgroundColour('red') + event.Skip() + + def mount_pressed(self, event): + if self.is_mounted(): + self.umount() + else: + self.mount() + event.Skip() + + def open_pressed(self, event): + for prog in ['nautilus', 'dolphin', 'nemo', 'thunar']: + try: + subprocess.Popen([prog, self.local_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except OSError: + pass + else: + break + event.Skip() + + def check_prot(self): + test_cmd = self.mount_cmd(True) + if test_cmd == None: + self.__ok__ = False + self.error_msg('Protocol not supported!\n') + #TODO: try to execute test_cmd to get info that progs are installed + + def is_mounted(self): + p = subprocess.Popen(["mount"], stdout=subprocess.PIPE) + out, err = p.communicate() + self.stderr.write(err or '') + if self.local_path in out.decode('utf-8'): + return True + else: + return False + + def umount(self): + self.stdout.write('umounting %s\n' % (self.local_path)) + p = subprocess.Popen(['fusermount', '-u', self.local_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + if not self.is_mounted(): + self.stdout.write('SUCCESS.\n') + else: + self.stdout.write('FAILED.\n') + self.stdout.write(out or '') + self.stderr.write(err or '') + + def mount(self): + cmd = self.mount_cmd() + self.stdout.write('mounting %(name)s to %(local_path)s\n' % self.__dict__) + if cmd != None: + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + out = out.decode('utf-8') + err = err.decode('utf-8') + if self.password != None: + out = out.replace(self.password, u'*****') + err = err.replace(self.password, u'*****') + if self.is_mounted(): + self.stdout.write('SUCCESS.\n') + else: + self.stdout.write('FAILED.\n') + self.password = None + self.stdout.write(out or '') + self.stderr.write(err or '') + else: + self.stdout.write('FAILED.\n') + + def mount_cmd(self, test_cmd=False): + if self.prot == self.SSH_FS: + if test_cmd: + return ['sshfs', '--version'] + return ['sshfs', '%(user)s@%(remote_host)s:%(remote_path)s' % self.__dict__, self.local_path] + elif self.prot == self.WEB_DAV: + if test_cmd: + return ['wdfs', '--version'] + self.set_password() + return ['wdfs', '-o', 'accept_sslcert,username=%(user)s,password=%(password)s' % self.__dict__, self.remote_host + self.remote_path, self.local_path] + elif self.prot == self.FTP_FS: + if test_cmd: + return ['curlftpfs', '--version'] + self.set_password() + return ['curlftpfs', 'ftp://%(user)s:%(password)s@%(remote_host)s:%(port)s%(remote_path)s' % self.__dict__, self.local_path] + else: + return None + + +if __name__ == "__main__": + gettext.install("app") # replace with the appropriate catalog name + app = wx.App(0) + nemo_frame = NeMo(None, wx.ID_ANY, "") + # + + with open(os.path.abspath(os.path.join(os.path.expanduser('~'), '.nemo.json')), 'r') as fh: + config = json.load(fh) + for entry in config: + StaBu(nemo_frame, nemo_frame.text_ctrl_stdout, nemo_frame.text_ctrl_stderr, **entry) + # + app.SetTopWindow(nemo_frame) + nemo_frame.Show() + app.MainLoop() diff --git a/nemo.wxg b/nemo.wxg new file mode 100644 index 0000000..b6e801a --- /dev/null +++ b/nemo.wxg @@ -0,0 +1,83 @@ + + + + + + + NeMo + 1 + 900, 480 + + wxHORIZONTAL + + wxEXPAND + 0 + + + wxVERTICAL + + + + + + wxEXPAND + 0 + + + wxVERTICAL + + wxEXPAND + 0 + + + wxHORIZONTAL + + + wxEXPAND + 0 + + + + + 10 + default + + bold + 0 + + + + + + + + wxEXPAND + 0 + + + wxHORIZONTAL + + + wxEXPAND + 0 + + + #ff0000 + + + 10 + default + + bold + 0 + + + + + + + + + + +