#!/usr/bin/python

# kernel -- Kernel Configuration Tool
# Copyright (C) 1996 Red Hat, Inc
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


import string

from Tkinter import *
from rhtkinter import *
from buttonbar import *
from rhutil import *
from rhdialog import Dialog
from listbox import *
from Conf import *

import rhdialog
import os
from sys import exit
import posixpath
import regex
import regsub
import glob

VERSION = "0.4"
COPYRIGHT = "Copyright (C) 1996 Red Hat Software\nRedistributable under the terms of the GNU General Public License"

print "Red Hat Linux kernelcfg", VERSION
print COPYRIGHT


class MFListbox(MultifieldListbox):
    def insert(self, tuple, where = 'end'):
	which = 0
	for item in tuple:
	    self.listBoxes[which].insert(where, item)
	    which = which + 1

class LabelledEntry(RHFrame):
    def bind(self, key, action):
	self.E.bind(key, action)
    def __init__(self, Master, text, textvar, width):
	RHFrame.__init__(self, Master)
	self.L = Label(self, {'text':text, 'width':width, 'anchor':'w'})
	self.E = Entry(self, {'textvar':textvar})
	self.L.pack({'side':'left'})
	self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})

class LabelledLabel(RHFrame):
    def __init__(self, Master, text, vartext, width):
	RHFrame.__init__(self, Master)
	self.L = Label(self, {'text':text, 'width':width, 'anchor':'w'})
	self.E = Label(self, {'text':vartext, 'anchor':'w'})
	self.L.pack({'side':'left'})
	self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
    def settext(self, vartext):
	self.E.config({'text':vartext})

class relay:
    def __init__(self, commandlist, arg = None):
	self.commandlist = commandlist
	self.args = arg
    def cb(self):
	for command in self.commandlist:
	    if command:
		command(self.args)

class EasyMenu(RHFrame):
    def bind(self, key, action):
	self.E.bind(key, action)
    def __init__(self, Master, text, width, menulist):
	# menulist is [(label, command),...]; command may be ''
	# When command (if it exists) is called, it will be passed
	# one argument containing label
	RHFrame.__init__(self, Master)
	self.value = ''
	MB = Menubutton(self, {'text':text, 'relief':'raised'})
	M = Menu(MB, {'tearoff':'0'})
	for tup in menulist:
	    runcommand = relay([self.setvalue, tup[1]], tup[0])
	    M.add_command({'label':tup[0], 'command':runcommand.cb})
	MB.config({'menu':M})
	MB.pack({'side':'left'})
    def setvalue(self, value):
	self.value = value
    def getvalue(self):
	return self.value

class MenuEntry(EasyMenu):
    def __init__(self, Master, text, width, menulist, defaultvalue=''):
	EasyMenu.__init__(self, Master, text, width, menulist)
	self.L = Label(self, {'anchor':'w', 'width':width, 'relief':'sunken'})
	self.L.pack({'side':'left'})
	self.setvalue(defaultvalue)
    def setvalue(self, value):
	EasyMenu.setvalue(self, value)
	self.L.config({'text':value})

# use MenuEntryDialog like this:
# foo = MenuEntryDialog(Master, title, label, menulabel, menulist, defaultvalue).getvalue()
class MenuEntryDialog:
    def __init__(self, Master, title, label, menulabel, menulist, defaultvalue=''):
	self.T = Toplevel()
	self.T.title(title)
	F = RHFrame(self.T)
	Label(F, {'text':label, 'anchor':'w'}).pack({'side':'top'})
	self.M = MenuEntry(F, menulabel, '20', menulist, defaultvalue)
	self.M.pack({'side':'top'})
	BB = ButtonBar(F)
	BB.setOrientation('horizontal')
	BB.addButton('Ok', self.T.destroy)
	BB.addButton('Cancel', self.kill)
	BB.pack({'side':'bottom'})
	F.pack({'side':'top', 'expand':'1', 'fill':'both'})
	self.T.update()
	self.T.grab_set()
	self.T.wait_window(self.T)
    def kill(self):
	self.M.setvalue('')
	self.T.destroy()
    def getvalue(self):
	return self.M.getvalue()

class MenuEntryListDialog:
    def __init__(self, Master, title, label, menulist):
	# menulist is [ [title, [(label, command),...], defaultvalue], ...]
	#             1 2       3                    3              2     1
	self.T = Toplevel()
	self.T.title(title)
	self.menus = {}
	F = RHFrame(self.T)
	Label(F, {'text':label, 'anchor':'w'}).pack({'side':'top'})
	for menu in menulist:
	    # menu is [title, [(label, command),...], defaultvalue]
	    self.menus[menu[0]] = MenuEntry(F, menu[0], '20', menu[1], menu[2])
	    self.menus[menu[0]].pack({'side':'top'})
	BB = ButtonBar(F)
	BB.setOrientation('horizontal')
	BB.addButton('Ok', self.T.destroy)
	BB.addButton('Cancel', self.kill)
	BB.pack({'side':'bottom'})
	F.pack({'side':'top', 'expand':'1', 'fill':'both'})
	self.T.update()
	self.T.grab_set()
	self.T.wait_window(self.T)
    def kill(self):
	for menu in self.menus.keys():
	    self.menus[menu].setvalue('')
	self.T.destroy()
    def getvalue(self):
	dict = {}
	for menu in self.menus.keys():
	    dict[menu] = self.menus[menu].getvalue()
	return dict


class ModBox(RHFrame):
    def __init__(self, Master):
	self.master = Master
	self.master.title('Kernel Configurator')
	RHFrame.__init__(self, Master)
	self.MB = MFListbox(self, [('Type', 8, 0), ('Module', 8, 0), ('Arguments', 40,1)])
	self.MB.bind('<Double-Button-1>', self.editEntry)
	self.BB = ButtonBar(self)
	self.BB.setOrientation('horizontal')
	self.BB.addButton('Add', self.addEntry)
	self.BB.addButton('Edit', self.editEntry)
	self.BB.addButton('Remove', self.removeEntry)
	self.BB.addButton('Restart kerneld', self.kickkerneld)
	self.BB.addButton('Quit', self.quit)
	self.BB.pack({'side':'bottom'})
	self.MB.pack({'side':'top', 'expand':'yes', 'fill':'both'})
	self.cm = ConfModules()
	try:
	    self.mi = ConfModInfo()
	except FileMissing:
	    # Probably missing the symlink.  Look for a source file.
	    try:
		self.mi = ConfModInfo('/boot/module-info-'+os.uname()[2])
	    except:
		Dialog('Missing file', 'I am sorry, but your /boot/module-info-'+os.uname()[2]+' file is missing, and I cannot do without it.',
		       'warning', 0, ['Exit'])
		self.quit()
	except:
	    Dialog('Missing file', 'I am sorry, but your /boot/module-info file is missing, and I cannot do without it.',
		   'warning', 0, ['Exit'])
	    self.quit()
	for type in self.cm.keys():
	    if self.cm[type].has_key('options') and self.cm[type]['options']:
		self.MB.insert([type, self.cm[type]['alias'],
				self.cm.joinoptlist(self.cm[type]['options'])])
	    else:
		self.MB.insert([type, self.cm[type]['alias'], ''])
	self.modtypes = []
	for mod in self.mi.keys():
	    if self.mi[mod].has_key('typealias'):
		if not self.mi[mod]['typealias'] in self.modtypes:
		    self.modtypes.append(self.mi[mod]['typealias'])
	    else:
		if not self.mi[mod]['type'] in self.modtypes:
		    self.modtypes.append(self.mi[mod]['type'])
	self.completions = {
	    'eth':['eth0', 'eth1', 'eth2', 'eth3', 'eth4', 'eth5', 'eth6', 'eth7'],
	    'tr':['tr0', 'tr1'],
	    'arc':['arc0', 'arc1', 'arc2', 'arc3', 'arc4', 'arc5', 'arc6', 'arc7'],
	    'wic':['wic0', 'wic1', 'wic2']
	}
    def kickkerneld(self):
	os.system('killall kerneld; kerneld&')
    def addEntry(self):
	self.edit()
	if self.save:
	    # not cancelled
	    if self.cm[self.type].has_key('options') and self.cm[self.type]['options']:
		self.MB.insert([self.type, self.cm[self.type]['alias'],
				self.cm.joinoptlist(self.cm[self.type]['options'])])
	    else:
		self.MB.insert([self.type, self.cm[self.type]['alias'], ''])
	    self.MB.selectLine('end')
	    self.cm.write()
	    # FIXME: ask if user wants to restart kerneld?
    def editEntry(self, event=None):
	if not self.MB.getSelectedItems():
	    return
	index = string.atoi(self.MB.curselection()[0])
	self.edit(index)
	if self.save:
	    # not cancelled
	    self.MB.delete(index)
	    if self.cm[self.type].has_key('options') and self.cm[self.type]['options']:
		self.MB.insert([self.type, self.cm[self.type]['alias'],
				self.cm.joinoptlist(self.cm[self.type]['options'])])
	    else:
		self.MB.insert([self.type, self.cm[self.type]['alias'], ''])
	    self.MB.selectLine(index)
	    self.cm.write()
	    # FIXME: ask if user wants to restart kerneld?
    def removeEntry(self):
	if not self.MB.getSelectedItems():
	    return
	if Dialog('Remove?', 'Delete current module?',
		  'question', 0, ['Delete', 'Cancel']).num:
	    return
	index = string.atoi(self.MB.curselection()[0])
	del self.cm[self.MB.getItems(index)[0]]
	self.MB.delete(index)
	self.MB.selectLine(index)
	self.cm.write()
	# FIXME: ask if user wants to restart kerneld?
    def edit(self, index = -1):
	self.save = 0
	self.saveindex = index
	self.type = ''
	self.name = ''
	if index >= 0:
	    self.type = self.MB.getItems(index)[0]
	    self.name = self.cm[self.type]['alias']
	else:
	    mods = []
	    for mod in self.modtypes:
		mods.append((mod, ''))
	    self.type = MenuEntryDialog(self.master, 'Choose Module Type',
					'Module Type', 'Module Types',
					mods, 'eth').getvalue()
	    if not self.type:
		# user cancelled
		return

	    completionlist = []
	    if self.type in self.completions.keys():
		# self.type needs numbers added.
		completionlist = self.completions[self.type]
	    menulist = []
	    if completionlist:
		itemlist = []
		for item in completionlist:
		    itemlist.append((item, ''))
		menulist.append(['Which module type?', itemlist, itemlist[0][0]])
	    itemlist = []
	    for module in self.mi.keys():
		if self.mi[module].has_key('typealias'):
		    if not cmp(self.mi[module]['typealias'], self.type):
			itemlist.append((module, ''))
		else:
		    if not cmp(self.mi[module]['type'], self.type):
			itemlist.append((module, ''))
	    if len(itemlist) == 1:
		self.name = itemlist[0][0]
	    if completionlist:
		# require user to choose an entry
		self.type = ''
	    if not self.name:
		menulist.append(['Which module?', itemlist, ''])
	    if menulist:
		dict = MenuEntryListDialog(self.master, 'Define Module',
			'Module Definition', menulist).getvalue()
		if not self.type:
		    self.type = dict['Which module type?']
		if not self.name:
		    self.name = dict['Which module?']
	    else:
		self.name = self.type
	    if not self.type or not self.name:
		# user cancelled
		return

	self.T = Toplevel()
	self.T.title('Set Module Options')
	F = RHFrame(self.T)
	# create list of possible module options
	self.arg = {}
	if self.mi[self.name] and self.mi[self.name].has_key('arguments'):
	    for key in self.mi[self.name]['arguments'].keys():
		self.arg[key] = StringVar(self.T)
		if index >= 0 and self.cm[self.type].has_key('options') and \
		   self.cm[self.type]['options'].has_key(key):
		    self.arg[key].set(self.cm[self.type]['options'][key])
		Label(F, {'text':self.mi[self.name]['arguments'][key][0], 'anchor':'w'}).pack(
		  {'side':'top', 'expand':'1', 'fill':'x'})
		LabelledEntry(F, key+'=', self.arg[key], '10').pack(
		  {'side':'top', 'expand':'1', 'fill':'x'})
	extras = {}
	if self.cm[self.type] and self.cm[self.type].has_key('options') and \
	   self.cm[self.type]['options']:
	    for arg in self.cm[self.type]['options'].keys():
		if self.mi[self.name].has_key('arguments') and \
		   arg not in self.mi[self.name]['arguments'].keys():
		    extras[arg] = self.cm[self.type]['options'][arg]
	else:
	    self.cm[self.type] = {}
	self.extraargs = StringVar(self.T)
	self.extraargs.set(self.cm.joinoptlist(extras))
	if self.mi[self.name].has_key('arguments'):
	    argstring = 'Other arguments:'
	else:
	    argstring = 'Arguments:'
	LabelledEntry(F, argstring, self.extraargs, 20).pack(
	  {'side':'top', 'expand':'1', 'fill':'x'})
	BB = ButtonBar(F)
	BB.setOrientation('horizontal')
	BB.addButton('Done', self.set)
	BB.addButton('Cancel', self.T.destroy)
	BB.pack({'side':'bottom'})
	F.pack({'side':'top', 'expand':'1', 'fill':'both'})
	self.T.update()
	self.T.grab_set()
	self.T.wait_window(self.T)
	self.T.grab_release()
    def set(self):
	self.cm[self.type]['alias'] = self.name
	al = {}
	if self.mi[self.name] and self.mi[self.name].has_key('arguments'):
	    for key in self.mi[self.name]['arguments'].keys():
		arg = self.arg[key].get()
		if arg:
		    al[key] = arg
	dict = self.cm.splitoptlist(regsub.split(self.extraargs.get(),'[\t ]+'))
	for key in dict.keys():
	    if dict[key]:
		al[key] = dict[key]
	self.cm[self.type]['options'] = al
	self.save = 1
	# FIXME: ask if user wants to restart kerneld?
	self.T.destroy()



# magic to keep a root window from appearing
L = Label()
L.tk.call('wm', 'withdraw', '.')
del L

win = ModBox(Toplevel())
win.pack()

win.wait_window(win)

