# -*- coding: UTF8 -*-

# Python module pysdm.py
# Autogenerated from pysdm.glade
# Generated on Thu May 11 20:01:12 2006

# Warning: Do not modify any context comment such as #--
# They are required to keep user's code

import os
import gtk
import re
import thread

import constants
import posix
from options_ui import *
from blkid import *
from fstab import *
from sysfs import *

from SimpleGladeApp import SimpleGladeApp
from SimpleGladeApp import bindtextdomain

app_name = "pysdm"
app_version = "0.3"

glade_dir = ""
locale_dir = ""

import gettext
gettext.install(app_name)
#bindtextdomain(app_name, locale_dir)

PARTITIONS = "/proc/partitions"
FSTAB = "/etc/fstab"
DEFAULT_MOUNT = "/media"
UDEV_DIR = "/etc/udev/"
UDEV_RULES = UDEV_DIR + "user.rules"

class Mainwindow(SimpleGladeApp):

    def __init__(self, path = os.path.join (constants.data_dir, "pysdm.glade"),
                 root="mainWindow",
                 domain=app_name, **kwargs):
        path = os.path.join(glade_dir, path)
        SimpleGladeApp.__init__(self, path, root, domain, **kwargs)

    #-- Mainwindow.new {
    def new(self):
        self.main_widget.set_title(_("Storage Device Manager"))
        #Load fstab data
        self.fstab = Fstab(FSTAB)
        self.urules = UdevFile(UDEV_RULES)
        self.rules = []
        self.blocks = {}

        self.current_FS = None

        #Initializes partition tree
        ptree = self.get_widget("partitiontree")
        column = gtk.TreeViewColumn(_("Partition List"), gtk.CellRendererText(), text=0)
        ptree.append_column(column)
        self.refresh_partitions(self, None)

        #Initializes rules view
        rulesv = self.get_widget("rules_treeview")
        column = gtk.TreeViewColumn(_("Rules"), gtk.CellRendererText(), text=0)
        rulesv.append_column(column)

        self.get_widget("frame3").set_sensitive(False)
    #-- Mainwindow.new }

    #-- Mainwindow custom methods {
    def refresh_panel(self, partition):
        """
        Update General Information for the partition given

        partition:
            The name of the partition in /dev tree
        """
        filesystem = self.fstab.getFS(partition, None)
        name = re.split('/', filesystem.file)
        name = name[len(name)-1]

        name_entry = self.get_widget("name_entry")
        mountpoint_entry = self.get_widget("mountpoint_entry")
        type_entry = self.get_widget("type_entry")

        self.get_widget("main_notebook").set_sensitive(True)
        self.get_widget("frame3").set_sensitive(True)

        #Root filesystem cannot be deleted
        if filesystem.file == "/":
            self.get_widget("remove_fs_button").set_sensitive(False)
        else:
            self.get_widget("remove_fs_button").set_sensitive(True)

        if filesystem.file == "/" or cmp(filesystem.file, "none")==0:
            self.get_widget("name_entry").set_sensitive(False)
            self.get_widget("openmountpoint").set_sensitive(False)
            self.get_widget("mount_button").set_sensitive(False)
        else:
            self.get_widget("name_entry").set_sensitive(True)
            self.get_widget("openmountpoint").set_sensitive(True)
            self.get_widget("mount_button").set_sensitive(True)
        
        mount_label = self.get_widget("mount_button").get_child().get_child().get_children()[1]
        if(filesystem.is_mounted()):
            mount_label.set_text(_("Unmount"))
        else:
            mount_label.set_text(_("Mount"))

        name_entry.set_text(name)
        mountpoint_entry.set_text(filesystem.file)
        type_entry.set_text(filesystem.vfstype)

        self.refresh_udev_panel(partition)

    def refresh_udev_panel(self, partition):
        block = self.blocks[partition]
        for att_name in block.attributes:
            if block.attributes[att_name]!=None:
                self.get_widget("sys_" + att_name + "_label").set_text(block.attributes[att_name])
            else:
                self.get_widget("sys_" + att_name + "_label").set_text(_("Unknown"))      
    
    def get_options(self):
        options_str = self.get_widget("options_entry").get_text()
        if len(options_str)==0: options_str = "defaults"
        return re.compile("[,\s]*").split(options_str)

    def set_options(self, options_array):
        if len(options_array)==0: options_array=["defaults"]
        options_str = options_array[0]
        for option in options_array[1:]:
            options_str = options_str + "," + option
        self.get_widget("options_entry").set_text(options_str)
        self.current_FS.mntops = options_array

    def refresh_rules(self, rules_array):
        rtree = self.get_widget("rules_treeview")

        model = gtk.TreeStore(str)
        self.rules = []

        for rule in rules_array:
            row = rule.__repr__()
            row = (row[:len(row)-1],)
            iter = model.append(None, row)
            self.rules.append(rule)
        rtree.set_model(model)

    def auto_configure(self, partition):
        """
        Autoconfigures a new partition

        partition:
            The name of the partition in /dev tree to autoconfigure

        """
        vfstype = get_vfstype(partition)
        name = re.split('/', partition)
        name = name[len(name)-1]
        self.fstab.addFS(Filesystem(partition, DEFAULT_MOUNT + "/" + name, vfstype, ["defaults"], "0", "0"))


    def get_partitions(self):
        """
        Find the partitions in the system
        """

        self.blocks = {}
        partitions = []
        partitions_file = open(PARTITIONS, "r")
        lines = partitions_file.readlines()
        for line in lines[2:]:
            partition = re.split('\s+', line)[4]
            device = re.compile("([a-z]+)([0-9]+)?").match(partition).groups()[0]

            if partition == device:
                block_path = "/sys/block/" + device
            else:
                block_path = "/sys/block/" + device + "/" + partition

            try:
                os.stat(block_path)
                #os.stat("/dev/" + partition)
                partitions.append(partition)
                block = Block(partition, device)

                self.blocks[block.name] = block
                self.blocks[block.dev] = block
            except OSError:
                pass
            except AttributeError:
                print "Not suitable dev for " + block.name

        return partitions
    #-- Mainwindow custom methods }

    #-- Mainwindow.refresh_partitions {
    def refresh_partitions(self, widget, *args):
        """
        Updates partition tree
        """

        ptree = self.get_widget("partitiontree")
        partitions = self.get_partitions()
        model = gtk.TreeStore(str)
        lastdevice = partitions[0]
        iter = model.append(None, (partitions[0],))

        for partition in partitions[1:]:
            device = re.compile("([a-z]+)([0-9]+)?").match(partition).groups()[0]
            if lastdevice != device:
                lastdevice = partition
                iter = model.append(None, (partition,))
            else:
                if is_mountable(self.blocks[partition].dev):
                    model.append(iter, (partition,))

        ptree.set_model(model) 
    #-- Mainwindow.refresh_partitions }

    #-- Mainwindow.on_partitiontree_cursor_changed {
    def on_partitiontree_cursor_changed(self, widget, *args):
        ptree = self.get_widget("partitiontree")
        selection = ptree.get_selection()
        model, selected = ptree.get_selection().get_selected()

        #User has selected a device
        if len(ptree.get_selection().get_selected_rows()[1][0])==1: 
            return

        partition = self.blocks[model.get_value(selected, 0)].dev

        #If exists a configuration in FSTAB
        if self.fstab.getFS(partition, None) != None:
            self.current_FS = self.fstab.getFS(partition, None)
            self.refresh_panel(partition)
            self.set_options(self.current_FS.mntops)
        else:
            ask_configure = gtk.Dialog(_("Configure now?"), self.main_widget, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
            ask_configure.vbox.pack_start(gtk.Label("\n" + partition + _(" hasn't been configured.\nDo you want to configure it now?\n")))
            ask_configure.show_all()
            result = ask_configure.run()
            ask_configure.destroy()
            if result == gtk.RESPONSE_ACCEPT:
                self.auto_configure(partition)
                self.current_FS = self.fstab.getFS(partition, None)
                self.refresh_panel(partition)
                self.set_options(self.current_FS.mntops)
            else:
                self.current_FS = Filesystem(partition, "", "", "", "", "")
                self.get_widget("main_notebook").set_sensitive(True)
                self.get_widget("frame3").set_sensitive(False)
                self.refresh_udev_panel(partition)

        self.get_widget("status_bar").push(0, self.current_FS.spec)
        self.refresh_rules(self.urules.get_matching_rules(self.blocks[partition]))

    #-- Mainwindow.on_partitiontree_cursor_changed }

    #-- Mainwindow.on_name_entry_changed {
    def on_name_entry_changed(self, widget, *args):
        if cmp(self.current_FS.file, "none")==0:
            return
        path = re.split("/", self.current_FS.file)
        npath = ""
        for folder in path[1:len(path)-1]:
            npath = npath + "/" + folder
        
        npath = npath + "/" +  widget.get_text()
        self.get_widget("mountpoint_entry").set_text(npath)
        
        self.current_FS.file = npath
    #-- Mainwindow.on_name_entry_changed }

    #-- Mainwindow.on_openmountpoint_clicked {
    def on_openmountpoint_clicked(self, widget, *args):
        filechooser = gtk.FileChooserDialog(_("Select a file for mountpoint..."), self.main_widget, action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
        filechooser.set_modal(True)
        filechooser.set_current_folder(DEFAULT_MOUNT)
        filechooser.show()
        response = filechooser.run()
        filechooser.hide()
        if response == gtk.RESPONSE_ACCEPT:
            self.get_widget("mountpoint_entry").set_text(filechooser.get_filename())
            self.current_FS.file = filechooser.get_filename()
        
            name = re.split('/', self.current_FS.file)
            name = name[len(name)-1]
            self.get_widget("name_entry").set_text(name)

        filechooser.destroy()
    #-- Mainwindow.on_openmountpoint_clicked }

    #-- Mainwindow.on_options_entry_changed {
    def on_options_entry_changed(self, widget, *args):
        self.current_FS.mntops = self.get_options()
    #-- Mainwindow.on_options_entry_changed }

    #-- Mainwindow.on_defaults_button_clicked {
    def on_defaults_button_clicked(self, widget, *args):
        self.set_options(["defaults"])
    #-- Mainwindow.on_defaults_button_clicked }

    #-- Mainwindow.on_open_options_clicked {
    def on_open_options_clicked(self, widget, *args):
        dialog = option_dialog(self.current_FS.vfstype, title=_("Select options"), flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
        dialog.set_value(self.current_FS.mntops)
        if dialog.run() == gtk.RESPONSE_ACCEPT:
            self.set_options(dialog.get_value())
        dialog.destroy()
    #-- Mainwindow.on_open_options_clicked }

    #-- Mainwindow.on_mount_button_clicked {
    def on_mount_button_clicked(self, widget, *args):
        try:
            os.makedirs(self.current_FS.file)
        except OSError:
            pass

        if self.current_FS.is_mounted():
            mount_ret = self.current_FS.umount()

            if mount_ret!=0:
                if mount_ret==256:
                    error_message = _("The file system is busy.\nCan't be unmounted")
                else:
                    error_message = _("The file system can't be mounted\nmount returned error:") + str(mount_ret)
                dialog = gtk.Dialog(_("Mount error"), self.main_widget, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
                dialog.vbox.set_border_width(10)
                dialog.vbox.pack_start(gtk.Label(error_message))
                dialog.show_all()
                dialog.run()
                dialog.destroy()
        else:
            mount_ret = self.current_FS.mount()

        mount_label = self.get_widget("mount_button").get_child().get_child().get_children()[1]
        if(self.current_FS.is_mounted()):
            mount_label.set_text(_("Unmount"))
        else:
            mount_label.set_text(_("Mount"))
            

    #-- Mainwindow.on_mount_button_clicked }

    #-- Mainwindow.on_remove_fs_button_clicked {
    def on_remove_fs_button_clicked(self, widget, *args):
        dialog = gtk.MessageDialog(self.main_widget, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK_CANCEL, _("This action will delete file system configuration\nAre you sure?"))
        dialog.show_all()
        if dialog.run()==gtk.RESPONSE_OK:
            self.fstab.delFS(self.current_FS.spec, self.current_FS.file)
            self.get_widget("frame3").set_sensitive(False)
        dialog.destroy()
    #-- Mainwindow.on_remove_fs_button_clicked }

    #-- Mainwindow.on_rules_treeview_cursor_changed {
    def on_rules_treeview_cursor_changed(self, widget, *args):
        pass
    #-- Mainwindow.on_rules_treeview_cursor_changed }

    #-- Mainwindow.on_udev_up_button_clicked {
    def on_udev_up_button_clicked(self, widget, *args):
        try:
            selected = self.get_widget("rules_treeview").get_selection().get_selected_rows()[1][0][0]
        except IndexError:
            return
        rule1 = selected
        rule2 = selected - 1
        if rule2 < 0: return
        self.urules.switch_rule(self.rules[rule1].line, self.rules[rule2].line)
        self.refresh_rules(self.urules.get_matching_rules(self.blocks[self.current_FS.spec]))
    #-- Mainwindow.on_udev_up_button_clicked }

    #-- Mainwindow.on_udev_down_button_clicked {
    def on_udev_down_button_clicked(self, widget, *args):
        try:
            selected = self.get_widget("rules_treeview").get_selection().get_selected_rows()[1][0][0]
        except IndexError:
            return
        rule1 = selected
        rule2 = selected + 1
        if rule2 > len(self.rules): return
        self.urules.switch_rule(self.rules[rule1].line, self.rules[rule2].line)
        self.refresh_rules(self.urules.get_matching_rules(self.blocks[self.current_FS.spec]))

    #-- Mainwindow.on_udev_down_button_clicked }

    #-- Mainwindow.on_udev_edit_button_clicked {
    def on_udev_edit_button_clicked(self, widget, *args):
        try:
            selected = self.get_widget("rules_treeview").get_selection().get_selected_rows()[1][0][0]
        except IndexError:
            return
        rule_line = self.rules[selected].line
        rules = self.urules.rules[rule_line]
        block = self.blocks[self.current_FS.spec]

        dialog = RulesMakerDialog(block, rules, title=_("New rule..."), flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
        dialog.show_all()
        if dialog.run() == gtk.RESPONSE_ACCEPT:
            self.urules.set_rule(rule_line, dialog.get_rule())
        dialog.destroy()

        self.refresh_rules(self.urules.get_matching_rules(block))
    #-- Mainwindow.on_udev_edit_button_clicked }

    #-- Mainwindow.on_udev_delete_button_clicked {
    def on_udev_delete_button_clicked(self, widget, *args):
        try:
            selected = self.get_widget("rules_treeview").get_selection().get_selected_rows()[1][0][0]
        except IndexError:
            return
        dialog = gtk.MessageDialog(self.main_widget, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK_CANCEL, _("Remove selected udev rule?"))
        dialog.show_all()
        if dialog.run()==gtk.RESPONSE_OK:
            rule_line = self.rules[selected].line
            self.urules.del_rule(rule_line)
            self.refresh_rules(self.urules.get_matching_rules(self.blocks[self.current_FS.spec]))
        dialog.destroy()
    #-- Mainwindow.on_udev_delete_button_clicked }

    #-- Mainwindow.on_udev_new_button_clicked {
    def on_udev_new_button_clicked(self, widget, *args):
        dialog = RulesMakerDialog(self.blocks[self.current_FS.spec], title=_("New rule..."), flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
        dialog.show_all()
        if dialog.run() == gtk.RESPONSE_ACCEPT:
            self.urules.add_rule(dialog.get_rule())
        dialog.destroy()
        self.refresh_rules(self.urules.get_matching_rules(self.blocks[self.current_FS.spec]))
    #-- Mainwindow.on_udev_new_button_clicked }

    #-- Mainwindow.on_apply_clicked {
    def on_apply_clicked(self, widget, *args):
        os.rename(FSTAB, FSTAB + ".BAK")
        for filesystem in self.fstab.filesystems:
            if type(filesystem) == str: continue
            if cmp(filesystem.file,"none")!=0:
                try:
                    os.makedirs(filesystem.file)
                except OSError:
                    pass
        self.fstab.toFile(FSTAB)
        self.urules.write()
    #-- Mainwindow.on_apply_clicked }


#-- main {

def main():
	if posix.geteuid() != 0:
		dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, _("You should have root privileges to run this program"))
		dialog.show_all()
		dialog.run()
	else:
		main_window = Mainwindow()
		main_window.run()

if __name__ == "__main__":
    main()

#-- main }
