]> SALOME platform Git repositories - tools/install.git/commitdiff
Salome HOME
Improve Installation Wizard:
authorvsr <vsr@opencascade.com>
Thu, 9 Mar 2006 09:03:07 +0000 (09:03 +0000)
committervsr <vsr@opencascade.com>
Thu, 9 Mar 2006 09:03:07 +0000 (09:03 +0000)
- introduce new runInstall script (support command line options in POSIX standard);
- provide version information
- provide About dialog box
- pass 'targe directory' and 'temp directory' parameters from command line to the GUI IW

bin/SALOME_InstallWizard
runInstall
src/InstallWizard.cpp
src/InstallWizard.h
src/Makefile
src/SALOME_InstallWizard.cxx
src/SALOME_InstallWizard.hxx
src/globals.h
src/icons.cxx
src/icons.h
src/main.cxx

index dba449bf19f9266ff0fa1d25cbb1ed2fde09a840..b11011676bbde12f94264969ae8d1fa35bc2fb2a 100755 (executable)
Binary files a/bin/SALOME_InstallWizard and b/bin/SALOME_InstallWizard differ
index d5cf9148407153c8befed7a075e9c8ffe17b5b29..8472253292766371dd5ec51a0d31aa59767c2dd4 100755 (executable)
 #!/usr/bin/env python
 
+"""
+Installation Wizard launching script.
+"""
+
+__version__ = "1.0.1"
+
+# --- avoid "deprecation" warnings --- #
 import warnings
 warnings.filterwarnings("ignore", "", DeprecationWarning)
 
+# --- imports --- #
 import xmllib
 import sys, os, string, re
+import types
+import random
 
-#==============================================================
-# get_help_info
-#==============================================================
-def get_help_info() :
-    str = "\nSALOME Installation Wizard\n\n"
-    str = str + "\tUsage : \n\tInstall [-g|b] [-f <xml-file>] [-t <target-dir>] [-tmp <tmp-dir>]\n"
-    str = str + "\n"
-    str = str + " -g              Runs the Installation Wizard in the GUI mode.\n"
-    str = str + "                 In this case only <xmlfile> is taken into account \n"
-    str = str + "                 from the parameters list. This key is used by default.\n"
-    str = str + "\n"
-    str = str + " -b              Runs the Installation Wizard in the batch mode.\n"
-    str = str + "                 All the found parameters are taken in to account.\n"
-    str = str + "\n"
-    str = str + " -f <xml-file>   The configuration file to be parsed by the Installation Wizard.\n" 
-    str = str + "                 If this parameter is missed then the script tries to define\n"
-    str = str + "                 the Red Hat version and use the corresponding xml. For example,\n"
-    str = str + "                 for Red Hat 8.0 config_RedHat_8.0.xml file is supposed to be used\n"
-    str = str + "                 by default. If the appropriate xml file is not found, the config.xml\n"
-    str = str + "                 is used by default.\n"
-    str = str + "\n"
-    str = str + " -t <target-dir> The target directory the products to be installed to.\n"
-    str = str + "                 This parameter overloads the target directory described in the\n"
-    str = str + "                 configuration file.\n"
-    str = str + "\n"
-    str = str + " -tmp <tmp-dir>  The directory which should be used for the temporary files.\n"
-    str = str + "                 This parameter overloads the temporary directory described in the\n"
-    str = str + "                 configuration file.\n"
-    str = str + "\n"
-    str = str + " -h              Prints this help information.\n" 
-    return str
-
-#==============================================================
-# message finction
-#==============================================================
-def message(msg):
-    print ">>>", msg
-    
-#==============================================================
-# error_exit
-#==============================================================
-def error_exit (str = ""):
-    import sys
-    if len(str): res = "\n" + str + "\n"
-    else : res = ""
-    print res + \
-          get_help_info() 
-    sys.exit(1);
-
-
-#==============================================================
-# Cheks whether the passed parameter is a key.
-#==============================================================
-def is_key ( val ):
-    import re
-    if val is not None : 
-       return re.match(r'^-[a-zA-Z]', val)
-    return 0
-
-#==============================================================
-# From the list of parameters extracts value following 'key' 
-#==============================================================
-def extract_parameter ( key, args ) :
-    import sys
-    length = len(args);
-    if ( length == 0 ) :  return None
-   
-    found = 0;
-
-    for i in range(0, length-1):
-        if  args[i] == key : 
-            if ( is_key ( args[i+1]) ) :
-               print " No value after key ", key
-                sys.exit(1);
-           
-           value = args[i+1]
-            if ( i < length - 2  and is_key ( args[i+2] ) == 0 ) : #control that only one value follows key 
-                                                              #(not a list). In this case params are correct.
-               print "Too much values after key ", key
-                sys.exit(1);
-           
-           found = 1; break;
-    
-    if (found) : 
-       return  value 
-    
-    return None
-
-
-#===============================================================
-# Extracts list of values following specified 'key' from 'args[]'
-#===============================================================
-def extract_list (key, args) : 
-
-    lenght = len(args)
-    if ( args is None or lenght == 0 ):
-       error_exit()
-
-    list=[]
-    found = 0
-
-    for i in  range(0, length) :
-        if args[i] == key  : 
-            if (is_key ( args[i+1]))  : 
-                error_exit();
-           
-            for i in range (i+1, lenght):
-                if is_key(args[i]) : break
-                list.append(args[i])
-            found =1; break
-     
-    return list; #empty list is returned if no values after key
-
-
-#==============================================================
-# Method find the $key in the list and return 1 if success
-# and 0 otherwise. 
-#==============================================================
-def find_key (key, argv) :
+# --- global variables --- #
+opt_parser = None
+root_path  = None
 
-    if (not is_key(key)) : return 0
+# --- XML tags definition --- #
+__TAG__SOURCES__    = "install sources"
+__TAG__BINARIES__   = "install binaries"
+__TAG__NATIVE__     = "use native"
+__TAG__PREINSTALL__ = "not install"
 
-    for simbol in  argv :
-        if simbol == key:
-            return 1 
-    return 0
+#------------------------------------------------------------------#
+#                                                                  #
+#                 COMMAND LINE ARGUMENTS PARSER                    #
+#                                                                  #
+#------------------------------------------------------------------#
 
-#==============================================================
-# Parse the list of parameters
-#==============================================================
-def parse_parameters (args) :
-
-    if find_key('-h', args) :
-        print get_help_info();
-        import sys
-        sys.exit(0)
-        
-    xmlfile = extract_parameter("-f", args)
-    target_dir =  extract_parameter("-t", args)
-    tmp_dir = extract_parameter("-tmp", args)
-    if find_key('-b', args):
-        is_gui = 0
-    else : is_gui = 1
-    return [xmlfile, target_dir,  tmp_dir, is_gui]
-
-
-#=================================================================
-# Checks boolean value: yes/no, true/false, 1/0
-#=================================================================
-def check_bool(val):
-    return str(val).strip() in ["yes","true", "1"]
+#===================================================================
+# class OptBaseError : base parse error
+#===================================================================
+class OptBaseError(Exception):
+    """
+    Base option parsing exception class
+    """
+    def __init__(self, msg):
+        self.msg = msg
+    def __str__ (self):
+        return self.msg
 
-#=================================================================
-# The first algorithm to create the dependencies list by their level
-#=================================================================
-def get_next_level(list, products):
-    
-    import re
-    result = []
-    expr = "(" + list[0].name
-    for i in range(1, len(list)):
-        expr = expr + "|"+ list[i].name
-    
-    expr = expr + ")$"
-    #expr=re.compile(expr)
+#===================================================================
+# class OptError : bad option error
+#===================================================================
+class OptError(OptBaseError):
+    """
+    Bad option exception class
+    """
+    def __init__ (self, msg, option):
+        self.msg = msg
+        self.option = option
+    def __str__ (self):
+        if self.option:
+            opt_prs = "<unknown>"
+            if self.option.short_opt and self.option.long_opt:
+                opt_prs = "%s/%s"%(self.option.short_opt,self.option.long_opt)
+            elif self.option.short_opt:
+                opt_prs = "%s"%(self.option.short_opt)
+            elif self.option.long_opt:
+                opt_prs = "%s"%(self.option.long_opt)
+            return "option %s: %s"%(opt_prs, self.msg)
+        return self.msg
 
-    for product in products:
-        deps = re.sub(r'\s+', "", product.dependencies)
-        if re.search(expr,  deps):
-            result.append(product)
+#===================================================================
+# class ArgError : bad option argument error
+#===================================================================
+class ArgError(OptBaseError):
+    """
+    Bad argument exception class
+    """
+    pass
 
-    return result
+#===================================================================
+# class ValError : bad command line parameter error
+#===================================================================
+class ValError(OptBaseError):
+    """
+    Bad parameter exception class
+    """
+    pass
 
+#===================================================================
+# class ArgOption : command line option
+#===================================================================
+class ArgOption:
+    """
+    Option class
+    """
+    attrs   = ["short_opt", "long_opt", "dest", "action", "type", "default", "metavar", "help"]
+    actions = ["store", "store_true", "store_false"]
+    types   = ["string", "int", "float", "bool"]
+    def __init__(self, *args, **kwargs):
+        # set defaults
+        for attr in self.attrs: setattr(self, attr, None)
+        # parse arguments
+        for i in range(len(args)):
+            if i > len(self.attrs)-1:
+                msg = "Wrong number of parameters is given (maximum is %d)" % len(self.attrs)
+                raise OptBaseError(msg)
+            setattr(self, self.attrs[i], args[i])
+        for arg in kwargs:
+            if arg not in self.attrs:
+                msg = "Invalid argument: %s" % arg
+                raise OptBaseError(msg)
+            setattr(self, arg, kwargs[arg])
+        # check short option key
+        if self.short_opt and \
+               not re.match("^-[a-zA-Z]$",self.short_opt):
+            msg  = "invalid short option key; "
+            msg += "should be of the form -x (x is letter)"
+            raise OptError(msg, self)
+        # check long option key
+        if self.long_opt and \
+               not re.match("^--[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*$",self.long_opt):
+            msg  = "invalid long option key; "
+            msg += "should be of the form --word[[-word]...] "
+            msg += "(word is letters and digits sequence)"
+            raise OptError(msg, self)
+        # check that at least one option key is defined
+        if not self.short_opt and not self.long_opt:
+            msg  = "invalid option; neither short nor long option key is defined"
+            raise OptError(msg, self)
+        # destination
+        if not self.dest and self.long_opt:
+            self.dest = self.long_opt[2:].replace('-','_')
+        if not self.dest and self.short_opt:
+            self.dest = self.short_opt[1:]
+        # action
+        if not self.action:
+            self.action = "store"
+        if self.action not in self.actions:
+            msg  = "invalid action: %s" % self.action
+            raise OptError(msg, self)
+        # type
+        if not self.type:
+            if self.action in ["store_true","store_false"]: self.type = "bool"
+            else: self.type = "string"
+        if self.type not in self.types:
+            msg  = "invalid type: %s" % self.type
+            raise OptError(msg, self)
+        if self.action in ["store_true","store_false"] and self.type != "bool":
+            msg  = "invalid type: %s : should be 'bool' or None" % self.type
+            raise OptError(msg, self)
+        # default
+        if self.default:
+            try:
+                if self.type == "string": self.default = str(self.default)
+                if self.type == "int":    self.default = int(self.default)
+                if self.type == "float":  self.default = float(self.default)
+                if self.type == "bool":   self.default = boolean(self.default)
+            except :
+                msg  = "invalid default value type: should be %s" % self.type
+                raise OptError(msg, self)
+            pass
+        # metavar
+        if not self.metavar:
+            self.metavar = self.dest.upper()
+        # help
+        if not self.help:
+            self.help = ""
+        pass
 
-def create_levels(prods):
-    import copy
-    
-    products = copy.deepcopy(prods)
+    def to_string(self):
+        """
+        Returns string representation of the option
+        """
+        opts = []
+        opt = self.short_opt
+        if opt and self.action == "store" and self.metavar: opt += " %s" % self.metavar
+        if opt: opts.append(opt)
+        opt = self.long_opt
+        if opt and self.action == "store" and self.metavar: opt += "=%s" % self.metavar
+        if opt: opts.append(opt)
+        return (", ").join(opts)
     
-    result = {}
-    import re
-    #1. find the products with empty lists of dependencies
-    list = []
-    for product in products:
-        if len(re.sub(r'\s', "", product.dependencies)) == 0 :
-            list.append(product)
-
-    if len(list) == 0 :
-        raise RuntimeError, "Products that depend on nothing are not found"
-
-    # remove the first level products from the common list of products
-    for product in list :
-        products.remove(product)
-
-    ind = 0; 
-    result[0] = list
-
-    while (len(products)) :
-        res = get_next_level(list, products)
-        if len(res) == 0 :
-            raise RuntimeError, "Empty list of products is found"
-
-        for product in res :
-            products.remove(product)
+#===================================================================
+# class Values : resulting option values
+#===================================================================
+class Values:
+    """
+    Values class
+    """
+    def __init__(self):
+        pass
+        
+#===================================================================
+# class ArgParser : command line arguments parser
+#===================================================================
+class ArgParser:
+    """
+    Arguments parser class
+    """
+    def __init__(self):
+        self.options = []
+        pass
 
-        ind = ind +1
-        result[ind] =  res
-        list = res
-            
-    str = ""
-    for i in result.keys():
-        for product in  result[i]:
-            str = str + product.name + " "
+    def add_option(self, *args, **kwargs):
+        """Register an option"""
+        o = ArgOption(*args, **kwargs)
+        self._check_option(o)
+        self.options.append(o)
+        pass
 
-    return str;
+    def parse_args(self, args = None):
+        """Parse an arguments"""
+        if not args: args = sys.argv[1:]
+        values = Values()
+        for o in self.options:
+            if o.default:
+                setattr(values, o.dest, o.default)
+            elif not hasattr(values,o.dest):
+                setattr(values, o.dest, None)
+        try:
+            (values, args) = self._process_args(values, args)
+        except (ArgError, ValError), e:
+            self._error(e.msg)
+
+        return (values, args)
             
-#=================================================================
-# The second algorithm
-#=================================================================
-def get_dependencies_set(prods) :
-    import copy
-    import re
-
-    products = copy.deepcopy(prods)
-    deps = ""
-    list = []
-
-    while (len(products)) :
-
-        tmplist = []
-        #find the products with empty list of dependencies
-        for product in products:
-            product.dependencies = re.sub(r'\s+$', "", product.dependencies)
-            product.dependencies = re.sub(r'^\s+', "", product.dependencies)
-           
-            if len(product.dependencies) == 0 :
-               tmplist.append(product); 
-               deps = deps + " " + product.name
-
-        list.append(tmplist)
+    def print_usage(self):
+        """Print usage"""
+        print "usage: %s [options]" % os.path.basename(sys.argv[0])
+        pass
 
-        #remove the products names from other products dependencies
-        for item in tmplist:
-            products.remove(item)
+    def print_help(self):
+        """Print help"""
+        self.print_usage()
+        print ""
+        olen = 0
+        _maxwidth, _indent = 79, 2
+        if len(self.options):
+            for option in self.options:
+                if olen < len(option.to_string()): olen = len(option.to_string())
+            print "options:"
+            for option in self.options:
+                strings = []
+                for hs in option.help.split("\n"):
+                    s = ""
+                    for w in hs.split():
+                        if len("%s %s" % (s,w)) > _maxwidth:
+                            strings.append(s.strip()); s = ""
+                        s = "%s %s" % (s,w)
+                    if s.strip(): strings.append(s.strip())
+                if not strings: strings[:0] = [""]
+                print "%s%s%s" % (option.to_string(), " "*(_indent+olen-len(option.to_string())), strings[0])
+                for i in range(1, len(strings)):
+                    print "%s%s" % (" "*(olen+_indent), strings[i])
+        pass
+    
+    def _check_option(self, option):
+        o = self._get_option(option.short_opt)
+        if not o: o = self._get_option(option.long_opt)
+        if o:
+            msg = "option conflicts with previously defined option(s)"
+            raise OptError(msg, option)
+        pass
 
-            regexpr1 = "((^|,(\s+)?)"+item.name+"$|^"+item.name+"(\s+)?,(\s+)?)"
-            regexpr2 = ",(\s+)?"+item.name+"(\s+)?,(\s+)?"
+    def _get_option(self, opt_key):
+        if opt_key:
+            for o in self.options:
+                if opt_key in [o.short_opt, o.long_opt]: return o
+        return None
+        
+    def _error(self, msg):
+        self.print_usage()
+        sys.exit("\n%s: error: %s\n" % (os.path.basename(sys.argv[0]), msg))
+        pass
 
-            for product in products:
-                product.dependencies = re.sub(r'\s+$', "", product.dependencies)
-                product.dependencies = re.sub(r'^\s+', "", product.dependencies)
+    def _check_value(self, option, value):
+        o = self._get_option(option)
+        try:
+            if o.type == "string": return str(value)
+            if o.type == "int":    return int(value)
+            if o.type == "float":  return float(value)
+            if o.type == "bool":   return boolean(value)
+        except:
+            msg  = "invalid value type for option %s: %s; " % (option, value)
+            msg += "should be %s" % o.type
+            raise ValError(msg)
+        raise OptBaseError("unknown error")
+
+    def _process_args(self, values, args):
+        res_args = []
+        cur_opt = None
+        rargs   = []
+        for index in range(len(args)):
+            a = args[index]
+            if cur_opt and cur_opt[1].action == "store":
+                setattr(values, cur_opt[1].dest, self._check_value(cur_opt[0], a))
+                cur_opt = None
+                continue
+            if a == "-":
+                rargs = args[index+1:]
+                break
+            elif re.match("^-[a-zA-Z].*", a):
+                for i in range(1,len(a)):
+                    if cur_opt and cur_opt[1].action == "store":
+                        setattr(values, cur_opt[1].dest, self._check_value(cur_opt[0], a[i:]))
+                        cur_opt = None
+                        break
+                    o = self._get_option("-%s"%a[i])
+                    if not o:
+                        raise ArgError("no such option: -%s"%a[i])
+                    if o.action == "store_true":
+                        setattr(values, o.dest, True)
+                    elif o.action == "store_false":
+                        setattr(values, o.dest, False)
+                    else:
+                        cur_opt = ("-%s"%a[i], o)
+                pass
+            elif re.match("^--[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*", a):
+                oname  = ("%s="%a).split("=")[0]
+                ovalue = ("%s="%a).split("=")[1]
+                o = self._get_option(oname)
+                if not o:
+                    raise ArgError("no such option: %s" % oname)
+                if o.action == "store_true":
+                    if ovalue:
+                        raise ValError("option %s does not take a value" % oname)
+                    setattr(values, o.dest, True)
+                elif o.action == "store_false":
+                    if ovalue:
+                        raise ValError("option %s does not take a value" % oname)
+                    setattr(values, o.dest, False)
+                else:
+                    if ovalue:
+                        setattr(values, o.dest, self._check_value(oname, ovalue))
+                    else:
+                        cur_opt = (oname, o)
+                pass
+            elif a.startswith("-"):
+                raise ArgError("bad formatted option: %s" % a)
+            else:
+                rargs = args[index:]
+                break
+        if cur_opt and cur_opt[1].action == "store":
+            raise ValError("option %s requires value" % cur_opt[0])
+        return (values, rargs)
+
+#------------------------------------------------------------------#
+#                                                                  #
+#                 XML CONFIGURATION FILES PARSER                   #
+#                                                                  #
+#------------------------------------------------------------------#
 
-                product.dependencies = re.sub(regexpr1, "", product.dependencies)
-                product.dependencies = re.sub(regexpr2, ",", product.dependencies)
+#===================================================================
+# class Config : general configuration options : version, OS, etc...
+#===================================================================
+class Config :
+    """
+    General configuration file options:
+    - Install Wizard window caption
+    - SALOME platform version
+    - Copyright and libcense info
+    - Target Linux OS version
+    """
+    def __init__(self,
+                 theVersion   = None,
+                 theCaption   = None,
+                 theCopyright = None,
+                 theLicense   = None,
+                 theOS        = None):
+        self.version   = strip(theVersion)
+        self.caption   = strip(theCaption)
+        self.copyright = strip(theCopyright)
+        self.license   = strip(theLicense)
+        self.os        = strip(theOS)
 
-    return deps 
-    
-#=================================================================
-# The third algorithm (same as SALOME_InstallWizard.cxx uses)
-#=================================================================
-def get_dependencies(prods) :
-    list = []
-    for product in prods:
-        if check_bool(product.disable): continue
+#===================================================================
+# class Path : default target, temporary directories options
+#===================================================================
+class Path :
+    """
+    Path options:
+    - default target directory
+    - default temporary directory
+    """
+    def __init__(self,
+                 theTargetdir = None,
+                 theTmpdir    = None):
+        self.targetdir = strip(theTargetdir)
+        self.tmpdir    = strip(theTmpdir)
         
-        deps = product.dependencies.split(",")
-        for dep in deps:
-            if dep and not dep in list:
-                list.append( dep )
-                
-        if product and not product in list:
-            list.append( product.name )
-            
-    return " ".join( list )
-
 #==============================================================
-# Creates dir, returns the part of path that existed early.
-# Access may be defined.
+# class Product : pre-requisite product options
 #==============================================================
-def create_dir (directory, access = 0777):
-    import string, os
-    dirs = string.split(directory, "/")
-    existing = ""; dir = ""
-    root = ""
-    for item in dirs:
-        if len(item) == 0:  continue
-        dir = dir + "/"+item
-        if os.path.exists(dir):
-            existing = dir
-        else:
-            os.mkdir(dir, access )
-            #root= existing + "/"+item
-            if dir == existing + "/"+item :
-                root = dir
-            #else : root = existing
-    
-    return root
-
-#==============================================================
-# class Product 
-#==============================================================
-
 class Product :
-    def __init__(self, theName,
+    """
+    Product options:
+    - name, version
+    - supported installation modes and the default one
+    - dependencies
+    - required disk space
+    - installation script
+    - etc...
+    """
+    def __init__(self,
+                 theName,
                  theVersion            = None,
                  theInstall            = None,
                  theSupportred         = None,
-                 theDisable            = None,
                  theDependencies       = None,
                  theInstalldiskspace   = None,
                  theTemporarydiskspace = None,
                  theScript             = None,
                  thePickUpEnvironment  = None):
-        
-
-        self.name               = theName
-        self.version            = theVersion
-        self.install            = theInstall
-        self.supported          = theSupportred
-        self.disable            = theDisable
-        self.dependencies       = theDependencies
-        self.installdiskspace   = theInstalldiskspace
-        self.temporarydiskspace = theTemporarydiskspace
-        self.script             = theScript
-        self.pickupEnv          = thePickUpEnvironment
+        self.name               = strip(theName)
+        self.version            = strip(theVersion)
+        self.install            = strip(theInstall)
+        self.supported          = strip(theSupportred)
+        self.dependencies       = strip(theDependencies)
+        self.installdiskspace   = strip(theInstalldiskspace)
+        self.temporarydiskspace = strip(theTemporarydiskspace)
+        self.script             = strip(theScript)
+        self.pickupEnv          = strip(thePickUpEnvironment)
 
 #===================================================================
-# class Config
-#===================================================================
-class Config :
-    def __init__(self, theVersion='', theCaption='', theCopyright='', theLicense='', theOS=''):
-        self.version   = theVersion
-        self.caption   = theCaption
-        self.copyright = theCopyright
-        self.license   = theLicense
-        self.os        = theOS
-
-
-#===================================================================
-# class Path
-#===================================================================
-class Path :
-    def __init__(self, theTargetdir=".", theTmpdir="."):
-        self.targetdir = theTargetdir
-        self.tmpdir    = theTmpdir
-
-        
-#===================================================================
-# class ConfigParser
+# class ConfigParser : XML files parser implementation
 #===================================================================
 class ConfigParser(xmllib.XMLParser):
+    """
+    XML configuration files parser
+    """
     def __init__(self):
         xmllib.XMLParser.__init__(self)
         self.products = []
@@ -368,36 +437,38 @@ class ConfigParser(xmllib.XMLParser):
         self.currentdata.append(data)
         
     def start_product(self, attrs):
-        aProduct = Product(attrs['name'],
-                           attrs['version'],
-                           attrs['install'],
-                           attrs['supported'],
-                           attrs['disable'],
-                           attrs['dependancies'],
-                           attrs['installdiskspace'],
-                           attrs['temporarydiskspace'],
-                           attrs['script'])
-
-        if attrs.has_key( 'pickupenv' ):
-            aProduct.pickupEnv = attrs['pickupenv']
-
+        if not attrs.get('name', '').strip():         return
+        if check_bool(attrs.get('disable', 'false')): return
+        aProduct = Product(attrs.get('name'),
+                           attrs.get('version',            None),
+                           attrs.get('install',            None),
+                           attrs.get('supported',          None),
+                           attrs.get('dependancies',       None),
+                           attrs.get('installdiskspace',   None),
+                           attrs.get('temporarydiskspace', None),
+                           attrs.get('script',             None),
+                           attrs.get('pickupenv',          None))
         self.products.append(aProduct)
+        pass
 
     def end_product(self):
         pass
 
     def start_config(self, attrs):
-        self.config = Config(attrs['version'],
-                             attrs['caption'],
-                             attrs['copyright'],
-                             attrs['license'],
-                             attrs['os'])
+        self.config = Config(attrs.get('version',   None),
+                             attrs.get('caption',   None),
+                             attrs.get('copyright', None),
+                             attrs.get('license',   None),
+                             attrs.get('os',        None))
+        pass
+    
     def end_config(self):
         pass
 
     def start_path (self, attrs):
-        self.path = Path(attrs['targetdir'],
-                         attrs['tempdir'])
+        self.path = Path(attrs.get('targetdir', None),
+                         attrs.get('tempdir',   None))
+        pass
         
     def end_path(self):
         pass
@@ -408,84 +479,388 @@ class ConfigParser(xmllib.XMLParser):
                 return product
         return None
 
+#------------------------------------------------------------------#
+#                                                                  #
+#                         SERVICE FUNCTIONS                        #
+#                                                                  #
+#------------------------------------------------------------------#
+
+#==============================================================
+# message: prints diagnostic information
+#==============================================================
+def message(msg):
+    """
+    Prints diagnostic information.
+    """
+    if msg.strip():
+        print ">>>", msg
+    pass
+
+#==============================================================
+# warning: prints warning
+#==============================================================
+def warning(msg):
+    """
+    Prints warning.
+    """
+    if msg.strip():
+        print ""
+        print msg
+        print ""
+    pass
+
+#==============================================================
+# error_exit : prints (optionally) error string, then prints
+#              help information and quits
+#==============================================================
+def error_exit(msg = "", print_help = True):
+    """
+    Prints (optionally) error string,
+    then prints help information and quits.
+    """
+    # print error message
+    if len(msg.strip()):
+        print ""
+        print msg
+        print ""
+    # print help information
+    if print_help:
+        global opt_parser
+        if opt_parser:
+            opt_parser.print_help() 
+            print ""
+    # cleaning 
+    clean_all()
+    # quit
+    sys.exit(1);
+    pass
+
+#==============================================================
+# boolean : Converts string to boolean value.
+#==============================================================
+def boolean(val):
+    """
+    Converts string to boolean value if possible.
+    Raises exception if wrong string is used.
+    """
+    if isinstance(val, types.StringType):
+        if val.strip().lower()   in ["true",  "yes", "ok"]     : return True
+        elif val.strip().lower() in ["false", "no",  "cancel"] : return False
+        else: raise TypeError("invalid boolean value")
+    return bool(val)
+
+#=================================================================
+# check_bool : checks boolean value: yes/no, true/false, 1/0
+#=================================================================
+def check_bool(val):
+    """
+    Checks boolean value.
+    """
+    try:
+        return boolean(val)
+    except:
+        pass
+    return False
+
+#==============================================================
+# clean_all : performs system cleaning before exiting
+#==============================================================
+def clean_all():
+    """
+    Performs system cleaning before exiting.
+    """
+    global root_path
+    remove_dir(root_path)
+    pass
+
+#==============================================================
+# parse_parameters : parses command line arguments
+#==============================================================
+def parse_parameters():
+    """
+    Parses command line arguments.
+    """
+    global opt_parser
+    opt_parser = ArgParser()
+    
+    help_str  = "Runs the Installation Wizard in the GUI mode [default].\n"
+    opt_parser.add_option("-g",
+                          "--gui",
+                          action="store_true",
+                          dest="gui",
+                          default=True,
+                          help=help_str)
+    help_str  = "Runs the Installation Wizard in the TUI mode."
+    opt_parser.add_option("-b",
+                          "--batch",
+                          action="store_false",
+                          dest="gui",
+                          help=help_str)
+    help_str  = "The configuration xml file.\n"
+    help_str += "If this parameter is missing, then the program tries to define the "
+    help_str += "Linux platform and use the corresponding xml file. For example, "
+    help_str += "for Red Hat 8.0 config_RedHat_8.0.xml file is used in this case. "
+    help_str += "If program fails to define target Linux platform or the corresponding "
+    help_str += "xml file is not provided with the Installation Wizard, then default "
+    help_str += "config.xml file is used."
+    opt_parser.add_option("-f",
+                          "--file",
+                          action="store",
+                          dest="xmlfile",
+                          metavar="FILE",
+                          help=help_str)
+    help_str  = "The target directory the products to be installed to.\n"
+    help_str += "When used this parameter overrides the default target directory "
+    help_str += "defined in the configuration xml file."
+    opt_parser.add_option("-d",
+                          "--target",
+                          action="store",
+                          dest="target_dir",
+                          metavar="DIR",
+                          help=help_str)
+    help_str  = "The directory to be used for temporary files.\n"
+    help_str += "When used this parameter overrides the default temporary directory "
+    help_str += "defined in the configuration xml file."
+    opt_parser.add_option("-t",
+                          "--tmp",
+                          action="store",
+                          dest="tmp_dir",
+                          metavar="DIR",
+                          help=help_str)
+    help_str  = "Prints version information and quits."
+    opt_parser.add_option("-v",
+                          "--version",
+                          action="store_true",
+                          help=help_str)
+    help_str  = "Prints this help and quits."
+    opt_parser.add_option("-h",
+                          "--help",
+                          action="store_true",
+                          help=help_str)
+    (options, args) = opt_parser.parse_args()
+    if options.help:
+        # print help info and quit
+        print "\nSALOME Installation Wizard\n"
+        opt_parser.print_help()
+        print ""
+        sys.exit(0)
+    if options.version:
+        # print version info and quit
+        print ""
+        cmd = "./bin/SALOME_InstallWizard --version"
+        os.system(cmd)
+        print ""
+        sys.exit(0)
+    return [options.xmlfile, options.target_dir, options.tmp_dir, options.gui]
+
+#=================================================================
+# strip : removes spaces at the beginning and at the end of the 
+#         <param> if it is of string type
+#=================================================================
+def strip(param):
+    """
+    Removes spaces at the beginning and at the end
+    of the given parameter.
+    """
+    if type(param) == types.StringType:
+        return param.strip()
+    return param
+    
+#=================================================================
+# get_dependencies : extract products dependencies
+#=================================================================
+def get_dependencies(prods):
+    """
+    Gets full list of pre-requisite products.
+    """
+    list = []
+    for product in prods:
+        deps = product.dependencies.split(",")
+        for dep in deps:
+            if dep and not dep in list:
+                list.append( dep )
+                
+        if product and not product in list:
+            list.append( product.name )
+            
+    return " ".join( list )
+
+#==============================================================
+# create_dir : creates a directory with (optional) permissions,
+#              returns the part of path that existed before
+#              directory creation; exits with error if access
+#              is denied
+#==============================================================
+def create_dir(directory, access = 0777):
+    """
+    Creates a directory with (optional) permissions,
+    returns the part of path that existed before
+    directory creation; exits with error if access
+    is denied.
+    """
+    dirs = string.split(directory, "/")
+    existing = "";
+    dir = ""
+    root = ""
+    for subdir in dirs:
+        if len(subdir) == 0:  continue
+        dir = "%s/%s"%(dir, subdir)
+        if os.path.exists(dir):
+            existing = dir
+        else:
+            try:
+                os.mkdir(dir, access)
+            except:
+                error_exit("Can't create directory: %s.\nAccess is denied."%directory)
+            if dir == "%s/%s"%(existing, subdir):
+                root = dir
+    return root
+
+#==============================================================
+# substituteVars : performes environment variables substistution
+#                  the given string; if varibale is not defined
+#                  it is substituted by the empty string
+#==============================================================
+def substituteVars(str):
+    """
+    Performes environment variables substistution.
+    """
+    str = os.path.expanduser(str)
+    str = os.path.expandvars(str)
+    return str
+
 #================================================================
-# get the path using file name
+# get_program_path : gets program's directory path
+#                    (and performs 'cd' command there) 
 #================================================================
-def get_current_path(file_name):
-    path = "."; where = string.rfind(file_name,'/');
-    if (where != -1):
-        path = (file_name)[: where]
-        os.chdir(path);
-    path = os.getcwd() + "/"
-    return path
+def get_program_path():
+    """
+    Returns the program directory path
+    (and make this directory current).
+    """
+    path = os.path.dirname(sys.argv[0])
+    if path:
+        os.chdir(path)
+    return os.getcwd()
 
 #================================================================
-# checks dir existing 
+# check_dir : checks directory existence
 #================================================================
 def check_dir(dir):
-    if (os.path.islink(dir)) :
-        native_dir = os.readlink(dir)
-        if not os.path.exists(native_dir) :
-            print "Invalid link " + dir + ". The directory " + native_dir + " a link points to does not exist."
-            return 0 # problem
-    else :
+    """
+    Checks directory existence.
+    """
+    if (os.path.islink(dir)):
+        realpath = os.path.realpath(dir)
+        if not os.path.exists(realpath):
+            msg = "Invalid link %s.\nThe directory %s a link points to does not exist. Stopped..."%(dir,realpath)
+            error_exit(msg, False)
+    else:
         if not os.path.exists(dir):
-            print "Directory " + dir + " does not exist"
-            return 0
-    return 1
+            msg = "Directory %s does not exist. Stopped..."%dir
+            error_exit(msg, False)
+    pass
 
 #===============================================================
-# Checks the disk space. Exit from interpreter if there is no
-# enough disk space.
+# check_disk_space : checks the disk space;
+#                    quits if there is no enough disk space
 #===============================================================
-def check_disk_space(products, script_dir, target_dir, tmp_dir):
-    import re, string, os
+def check_disk_space(products, scripts_dir, target_dir, tmp_dir):
+    """
+    Checks if there is enough disk space to install products.
+    Quits with error if there is no enough disk space.
+    """
     install_space = 0
     temporary_space = 0
-    for product in products :
-        product.install = re.sub(r'^\s+', "", product.install)
-        product.install = re.sub(r'\s+$', "", product.install)
-        
-        if check_bool(product.disable) or product.install == "use native" or product.install == "not install":
+    for product in products:
+        if product.install in [__TAG__NATIVE__, __TAG__PREINSTALL__]:
             continue
-        spaces = string.split( product.installdiskspace,',')
+        spaces = string.split(product.installdiskspace, ',')
         prod_space = spaces[0]
-        if (len(spaces) == 2 ) and (product.install == "install binaries") :
+        if (len(spaces) > 1 ) and (product.install == __TAG__SOURCES__):
             prod_space = spaces[1]
         install_space = install_space + string.atoi(prod_space)
-        temporary_space = temporary_space + string.atoi(product.temporarydiskspace)
-    res = os.system(scripts_dir + "checkSize.sh" + " " + target_dir + " " + str(install_space))
+        if product.install == __TAG__SOURCES__:
+            temporary_space = max(temporary_space, string.atoi(product.temporarydiskspace))
+
+    res = os.system("%s/%s %s %d"%(scripts_dir, "checkSize.sh", target_dir, install_space))
     if res:
-        print "There is no enough space to install the products."
-        return 0
+        msg = "There is no enough space to install the products. Stopped..."
+        error_exit(msg, False)
     
-    res = os.system(scripts_dir + "checkSize.sh" + " " + tmp_dir + " " + str(temporary_space))
+    res = os.system("%s/%s %s %d"%(scripts_dir, "checkSize.sh", tmp_dir, temporary_space))
     if res:
-        print "There is no enough space for tmp directory."
-        return 0
-    
-    return 1
+        msg = "There is no enough space for temporary directory. Stopped..."
+        error_exit(msg, False)
+    pass
  
 #===============================================================
-# Removes temporary directory
+# remove_dir : removes temporary directory
 #===============================================================
-def remove_dir( rem_path = "" ):
-    if len( rem_path ) and os.path.exists( rem_path ):
-        os.system( "rm -rf " + rem_path )
+def remove_dir(path):
+    """
+    Removes temporary directory.
+    """
+    if path and os.path.exists(path):
+        os.system("rm -rf " + path)
     pass
+
+#==============================================================
+# has_binaries : returns True if some product is installed from
+#                binaries
+#===============================================================
+def has_binaries(products):
+    """
+    Returns True if some product is installed in 'binaries' mode.
+    """
+    for product in products:
+        if product.install == __TAG__BINARIES__:
+            return True
+    return False
+
+#==============================================================
+# has_sources : returns True if some product is installed from
+#               sources
+#===============================================================
+def has_sources(products):
+    """
+    Returns True if some product is installed in 'sources' mode.
+    """
+    for product in products:
+        if product.install == __TAG__SOURCES__:
+            return True
+    return False
+
+#==============================================================
+# get_tmp_dir : gets temporary directory name
+#===============================================================
+def get_tmp_dir(dir):
+    """
+    Gets temporary directory path.
+    """
+    max_attempts = 100
+    dir_prefix="INSTALLWORK"
+    range_bottom = 0; range_top = 999999
+    for i in xrange(max_attempts):
+        tmp_dir = "%s/%s%d"%(dir, dir_prefix, random.randint(range_bottom,range_top))
+        if not os.path.exists( tmp_dir ):
+            return tmp_dir
+    return "%s/%s%d"%(dir, dir_prefix, random.randint(range_bottom,range_top))
     
-#================================================================
-# main
-#================================================================
+#------------------------------------------------------------------#
+#                                                                  #
+#                    EXECUTION STARTS HERE                         #
+#                                                                  #
+#------------------------------------------------------------------#
     
 if __name__ == "__main__":
-    
-    cur_dir = get_current_path(sys.argv[0])
-   
-    [xml_file, target_dir, tmp_dir, is_gui]  = parse_parameters(sys.argv)
+    # get program dir
+    cur_dir = get_program_path()
+    # parse command line
+    [xml_file, target_dir, tmp_dir, is_gui] = parse_parameters()
 
-    # define xml file -----------------
-    if (xml_file is None) :
+    # define xml file to be used
+    if (xml_file is None):
         plt_name = ""
         plt_ver  = ""
         xml_file_name = "config.xml"
@@ -504,45 +879,47 @@ if __name__ == "__main__":
             plt_name = "Debian"
             plt_ver = open("/etc/debian_version").readline().strip()
         _xml_file_name = "config_%s_%s.xml"%(plt_name, plt_ver)
-        if plt_name and plt_ver and os.path.exists(cur_dir + _xml_file_name):
+        if plt_name and plt_ver and os.path.exists("%s/%s"%(cur_dir, _xml_file_name)):
             xml_file_name = _xml_file_name
         else:
-            print ""
-            print "Not supported Linux platform!"
-            print "Trying to use default configuration!"
-            print ""
+            msg  = "Not supported Linux platform!\n"
+            msg += "Trying to use default configuration file!"
+            warning(msg)
 
-        xml_file = cur_dir +  xml_file_name
+        xml_file = "%s/%s"%(cur_dir, xml_file_name)
 
     if not xml_file or not os.path.exists(xml_file):
-        msg  = "Configuration file %s is not found!"%xml_file
-        msg += "\nTry to run with -f <xmlfile> option."
+        msg = "Configuration file %s is not found!"%xml_file
         error_exit(msg)
 
     if not os.access(xml_file, os.R_OK):
-        print "There is no read access for %s file!"%xml_file
-        sys.exit(1)
+        msg = "There is no read access for %s file!"%xml_file
+        error_exit(msg)
 
     #---- GUI ----------------
+
     if is_gui : 
         env = os.environ
         if not env.has_key("PATH") :
             env["PATH"] = ""
         if not env.has_key("LD_LIBRARY_PATH") :
-            env["LD_LIBRARY_PATH"] ""
+            env["LD_LIBRARY_PATH"] = ""
 
-        env["LD_LIBRARY_PATH"] =  ".:" +  env["LD_LIBRARY_PATH"]
-        env["PATH"] = ".:"+ env["PATH"]
+        env["LD_LIBRARY_PATH"] =  ".:" + env["LD_LIBRARY_PATH"]
+        env["PATH"] = ".:" + env["PATH"]
 
-        sys.exit(os.system("cd " + cur_dir + "; ./bin/SALOME_InstallWizard " + xml_file +"&"))
-        
-        
+        cmd = "./bin/SALOME_InstallWizard --file %s"%xml_file
+        if target_dir is not None:
+            cmd += " --target %s"%target_dir
+        if tmp_dir is not None:
+            cmd += " --tmp %s"%tmp_dir
+        cmd += "&"
+        sys.exit(os.system(cmd))
 
     #-----  TUI ---------------------
 
-    #print xml_file, target_dir, tmp_dir, is_gui
-
-    message("Parsing xml config file: " + xml_file)
+    # parse XML file -----------
+    message("Parsing XML configuration file: %s"%xml_file)
     filehandle = open(xml_file)
     data = filehandle.read()
     filehandle.close()
@@ -550,103 +927,114 @@ if __name__ == "__main__":
     parser.feed(data)
     parser.close()
 
-    # definitions :
-    # map
-    what_to_do = { "install sources"  : "install_source",
-                   "install binaries" : "install_binary",
-                   "use native"       : "try_native",
-                   "not install"      : "try_preinstalled"}
-    # define tmp dir  -----------
-    if tmp_dir is None:
-        tmp_dir = parser.path.tmpdir
-    if tmp_dir is None or tmp_dir == "":
-        tmp_dir = "/tmp"
-    import random
-    tmp_dir = tmp_dir + "/INSTALLWORK" + str(random.randint(10000,100000))
-    root_path = ""
-    if not os.path.exists(tmp_dir):
-        message("Creating temporary directory: " + tmp_dir); root_path = create_dir(tmp_dir, 0755) ; 
-    if not os.path.exists(tmp_dir):
-        error_exit("Invalid temporary directory " + tmp_dir + ". Use -tmp key to set directory or correct xml file\n\n")
-
-    if not os.access(tmp_dir, os.W_OK) :
-        str = "There is no write permissions for directory " + tmp_dir + ". Use -tmp key to set temporary directory or correct xml file"
-        error_exit(str)
-        
-    # define target dir  --------
+    # actions map
+    what_to_do = { __TAG__SOURCES__    : "install_source",
+                   __TAG__BINARIES__   : "install_binary",
+                   __TAG__NATIVE__     : "try_native",
+                   __TAG__PREINSTALL__ : "try_preinstalled"}
+    # source directory map
+    bin_dir = ""
+    if parser.config.os:
+        bin_dir += "/%s"%parser.config.os
+    subdir = { __TAG__SOURCES__    : "SOURCES",
+               __TAG__BINARIES__   : "BINARIES" + bin_dir,
+               __TAG__NATIVE__     : "",
+               __TAG__PREINSTALL__ : ""}
+
+    # check scripts directory -----------
+    scripts_dir = "%s/%s"%(cur_dir, "config_files")
+    check_dir(scripts_dir)
+
+    # check products archives directories -----------
+    has_bin = has_binaries(parser.products)
+    has_src = has_sources(parser.products)
+    source_dir = "%s/%s"%(cur_dir, "Products")
+
+    if has_src or has_bin:
+        check_dir(source_dir)
+
+    if has_src:
+        check_dir("%s/%s"%(source_dir,subdir[__TAG__SOURCES__]))
+
+    if has_bin:
+        check_dir("%s/%s"%(source_dir,subdir[__TAG__BINARIES__]))
+
+    # check/create target dir -----------
     if target_dir is None:
         target_dir = parser.path.targetdir
+    target_dir = substituteVars(target_dir)
+
+    message("Creating target directory: " + target_dir)
+    create_dir(target_dir, 0755)
 
     if not os.path.exists(target_dir):
-        message("Creating target directory: " + target_dir); create_dir(target_dir, 0755)
-    if not os.path.exists(target_dir):
-        error_exit("Invalid target directory " + target_dir + ". Use -t key to set directory or correct xml file\n\n")
+        error_exit("Invalid target directory: " + target_dir)
 
     if not os.access(target_dir, os.W_OK) :
-        str = "There is no write permissions for directory " + target_dir + ". Use -t key to set target directory or correct xml file."
-        error_exit(str)
-
-    # define products dir ------------
-    source_dir =  cur_dir + "Products" ; 
-    if not check_dir(source_dir):
-        remove_dir(root_path)
-        sys.exit(1)
-       
-    subdir = {"install sources"  : "SOURCES",
-              "install binaries" : "BINARIES/"+parser.config.os,
-              "use native"       : "",
-              "not install"      : ""}
-
-
-    #  define scripts dir ------------
-    scripts_dir = cur_dir + "config_files/"
-    if not check_dir(scripts_dir):
-        remove_dir(root_path)
-        sys.exit(1)
-    os.chdir(scripts_dir)
+        error_exit("There is no write permissions for the directory: " + target_dir)
 
-    #list_of_dep =  create_levels(parser.products)
-    #list_of_dep =  get_dependencies_set(parser.products)
-    list_of_dep =  get_dependencies(parser.products)
+    # check/create temporary dir -----------
+    if tmp_dir is None:
+        tmp_dir = parser.path.tmpdir
+    if not tmp_dir:
+        tmp_dir = "/tmp"
+    tmp_dir = substituteVars(tmp_dir)
+    tmp_dir = get_tmp_dir(tmp_dir)
 
-    message("Checking available disk space")
-    if check_disk_space(parser.products, scripts_dir, target_dir, tmp_dir) :
+    message("Creating temporary directory: " + tmp_dir)
+    root_path = create_dir(tmp_dir, 0755)
+   
+    if not os.path.exists(tmp_dir):
+        error_exit("Invalid temporary directory: " + tmp_dir)
 
-        message("Starting...")
-        # install products
-        for product in parser.products :
+    if not os.access(tmp_dir, os.W_OK) :
+        error_exit("There is no write permissions for the directory: " + tmp_dir)
+        
+    # check available disk space -----------
+    message("Checking available disk space")
+    check_disk_space(parser.products, scripts_dir, target_dir, tmp_dir)
 
-            if check_bool(product.disable): continue
+    # change current directory -----------
+    os.chdir(scripts_dir)
 
-            message("Processing " + product.name + "...")
-            cmd = scripts_dir +  product.script + " " + \
-                  what_to_do[product.install]+ " " + \
-                  tmp_dir + " " + \
-                  source_dir + "/" + subdir[product.install] + " " + \
-                  target_dir + " " + \
-                  '"' + list_of_dep + '"' + " " + \
-                  product.name
+    # get dependencies list -----------
+    list_of_dep = get_dependencies(parser.products)
 
+    # starting -----------
+    message("Starting ...")
+    
+    # install products -----------
+    for product in parser.products:
+        message("... processing %s ..."%product.name)
+        cmd = '%s/%s %s %s %s/%s %s "%s" %s'%(scripts_dir,
+                                              product.script,
+                                              what_to_do[product.install],
+                                              tmp_dir,
+                                              source_dir,
+                                              subdir[product.install],
+                                              target_dir,
+                                              list_of_dep,
+                                              product.name)
+        res = os.system(cmd)
+
+    # pickup environment -----------
+    message("Creating environment files")
+    for product in parser.products :
+        if check_bool(product.pickupEnv):
+            cmd = '%s/%s pickup_env %s %s/%s %s "%s" %s'%(scripts_dir,
+                                                          product.script,
+                                                          tmp_dir,
+                                                          source_dir,
+                                                          subdir[product.install],
+                                                          target_dir,
+                                                          list_of_dep,
+                                                          product.name)
             res = os.system(cmd)
-            #if res : break; # try_preinstalled can return 1
-
-        # pickup environment
-        message("Creating environment files")
-        for product in parser.products :
-
-            if check_bool(product.disable): continue
-
-            if check_bool(product.pickupEnv):
-                cmd = scripts_dir +  product.script + " " + \
-                      "pickup_env " + \
-                      tmp_dir + " " + \
-                      source_dir + "/" + subdir[product.install] + " " + \
-                      target_dir + " " + \
-                      '"' + list_of_dep + '"' + " " + \
-                      product.name
-                
-                res = os.system(cmd)
 
+    # clean temporary directory -----------
     message("Cleaning temporary directory")
-    remove_dir(root_path)
+    clean_all()
+    
+    # finishing -----------
     message("Finished!")
+    pass
index f2628e384605fcca61dca97f12b07a28c234ec33..d4bd4da508824be8ebbccc78a47bdf427883564a 100644 (file)
@@ -39,6 +39,7 @@
 
 #include <qlayout.h>
 #include <qpushbutton.h>
+#include <qtoolbutton.h>
 #include <qcursor.h>
 #include <qlabel.h>
 #include <qwidgetstack.h>
@@ -95,6 +96,7 @@ public:
   QPushButton * cancelButton;
   QPushButton * helpButton;
   QFrame * hbar1, * hbar2;
+  QToolButton * aboutButton;
   
 #ifndef QT_NO_ACCEL
   QAccel * accel;
@@ -128,11 +130,13 @@ InstallWizard::InstallWizard( QWidget *parent, const char *name, bool modal,
   d->ws = new QWidgetStack( this, "qt_widgetstack" );
   d->pages.setAutoDelete( TRUE );
   d->titleBox = new QHBox( this, "title box" );
+  d->aboutButton = new QToolButton( d->titleBox, "about button");
+  d->aboutButton->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) );
+  d->aboutButton->setAutoRaise( true );
   d->title = new QLabel( d->titleBox, "title label" );
   d->logoBox = new QHBox( d->titleBox, "logo box" );
   d->logoBox->setSpacing( 2 );
   d->titleBox->setStretchFactor( d->title, 10 );
-  
   // create in nice tab order
   d->nextButton = new QPushButton( this, "next" );
   d->finishButton = new QPushButton( this, "finish" );
@@ -168,6 +172,8 @@ InstallWizard::InstallWizard( QWidget *parent, const char *name, bool modal,
     this, SLOT(reject()) );
   connect( d->helpButton, SIGNAL(clicked()),
     this, SLOT(help()) );
+  connect( d->aboutButton, SIGNAL(clicked()),
+    this, SIGNAL(aboutClicked()) );
   
 #ifndef QT_NO_ACCEL
   d->accel = new QAccel( this, "arrow-key accel" );
@@ -176,6 +182,8 @@ InstallWizard::InstallWizard( QWidget *parent, const char *name, bool modal,
   d->nextAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Right );
   d->accel->connectItem( d->nextAccel, this, SIGNAL(nextClicked()) );
 #endif
+
+  showAboutBtn( false );
 }
 
 
@@ -416,7 +424,9 @@ void InstallWizard::help()
   emit helpClicked();
 }
 
-
+/*!
+  Enables/disables <Back> button
+ */
 void InstallWizard::setBackEnabled( bool enable )
 {
   d->backButton->setEnabled( enable );
@@ -425,7 +435,9 @@ void InstallWizard::setBackEnabled( bool enable )
 #endif
 }
 
-
+/*!
+  Enables/disables <Next> button
+ */
 void InstallWizard::setNextEnabled( bool enable )
 {
   d->nextButton->setEnabled( enable );
@@ -434,13 +446,14 @@ void InstallWizard::setNextEnabled( bool enable )
 #endif
 }
 
-
+/*!
+  Enables/disables <Help> button
+ */
 void InstallWizard::setHelpEnabled( bool enable )
 {
   d->helpButton->setEnabled( enable );
 }
 
-
 /*!
 \fn void InstallWizard::setFinish( QWidget *, bool )
 \obsolete
@@ -914,6 +927,22 @@ void InstallWizard::removeLogos()
   delete children;
 }
 
+/*!
+Show/hide "About" button
+*/
+void InstallWizard::showAboutBtn( bool show )
+{
+  show ? d->aboutButton->show() : d->aboutButton->hide();
+}
+
+/*!
+Set icon for "About" button
+*/
+void InstallWizard::setAboutIcon( const QPixmap& px )
+{
+  d->aboutButton->setIconSet( px );
+}
+
 /*!
 Posts validation event
 */
index 8b36597894b3ab9e91682c7c4c9e8f251eae02fe..180ccab42b06a2566b5841d536b0444fe99b677d 100644 (file)
@@ -78,13 +78,16 @@ public:
   QWidget * currentPage() const;
   
   QWidget* page( int ) const;
-  QWidget* page( const QString& title ) const;
+  QWidget* page( const QString& ) const;
   int pageCount() const;
   int indexOf( QWidget* ) const;
 
-  void addLogo( const QPixmap& pm );
+  void addLogo( const QPixmap& );
   void removeLogos();
   
+  void showAboutBtn( bool );
+  void setAboutIcon( const QPixmap& );
+  
   virtual bool appropriate( QWidget * ) const;
   virtual void setAppropriate( QWidget *, bool );
   
@@ -115,6 +118,7 @@ signals:
   void nextClicked();
   void backClicked();
   void helpClicked();
+  void aboutClicked();
   void selected( const QString& );
   
 protected:
index b9c1406905a8293fb53e3b78539bc3c9a38f3425..078d0b87b8fe1e5d036b79e99e21979d85685bca 100644 (file)
@@ -1,6 +1,6 @@
 #############################################################################
 # Makefile for building: ../bin/SALOME_InstallWizard
-# Generated by qmake (1.03a) on: Fri Jul  1 17:48:15 2005
+# Generated by qmake (1.03a) on: Tue Mar  7 14:46:50 2006
 # Project:  SALOME_INSTALL.pro
 # Template: app
 # Command: $(QMAKE) SALOME_INSTALL.pro
@@ -12,8 +12,8 @@ CC       = gcc
 CXX      = g++
 LEX      = flex
 YACC     = yacc
-CFLAGS   = -pipe -Wall -W -O2 -D_REENTRANT  -DQT_NO_DEBUG -DQT_THREAD_SUPPORT
-CXXFLAGS = -pipe -Wall -W -O2 -D_REENTRANT  -DQT_NO_DEBUG -DQT_THREAD_SUPPORT
+CFLAGS   = -pipe -Wno-deprecated -Wall -W -O2 -D_REENTRANT  -DQT_NO_DEBUG -DQT_THREAD_SUPPORT
+CXXFLAGS = -pipe -Wno-deprecated -Wall -W -O2 -D_REENTRANT  -DQT_NO_DEBUG -DQT_THREAD_SUPPORT
 LEXFLAGS = 
 YACCFLAGS= -d
 INCPATH  = -I$(QTDIR)/include -I$(QTDIR)/mkspecs/default
index 3968afcca35da5b990725dfb1b8ab8429f43657f..36737b440493ebbe8847e78848bf61a1a4dcc872 100644 (file)
@@ -317,6 +317,31 @@ static bool hasSpace( const QString& dir )
   return false;
 }
 
+// ================================================================
+/*!
+ *  makeTitle
+ *  Creates HTML-wrapped title text
+ */
+// ================================================================
+QString makeTitle( const QString& text, const QString& separator = " ", bool fl = true )
+{
+  QStringList words = QStringList::split( separator, text );
+  if ( fl ) {
+    for ( uint i = 0; i < words.count(); i++ )
+      words[i] = QString( "<font color=red>%1</font>" ).arg( words[i].left(1) ) + words[i].mid(1);
+  }
+  else {
+    if ( words.count() > 0 )
+      words[0] = QString( "<font color=red>%1</font>" ).arg( words[0] );
+    if ( words.count() > 1 )
+      words[words.count()-1] = QString( "<font color=red>%1</font>" ).arg( words[words.count()-1] );
+  }
+  QString res = words.join( separator );
+  if ( !res.isEmpty() )
+    res = QString( "<b>%1</b>" ).arg( res );
+  return res;
+}
+
 // ================================================================
 /*!
  *  QMyCheckBox class : custom check box
@@ -330,23 +355,117 @@ public:
   void setState ( ToggleState s ) { QCheckBox::setState( s ); }
 };
 
+// ================================================================
+/*!
+ *  AboutDlg
+ *  "About dialog box.
+ */
+// ================================================================
+class AboutDlg: public QDialog
+{
+public:
+  AboutDlg( SALOME_InstallWizard* parent ) : QDialog( parent, "About dialog box", true )
+  {
+    // caption
+    setCaption( QString( "About %1" ).arg( parent->getIWName() ) );
+    // palette
+    QPalette pal = palette();
+    QColorGroup cg = pal.active();
+    cg.setColor( QColorGroup::Foreground, Qt::darkBlue );
+    cg.setColor( QColorGroup::Background, Qt::white );
+    pal.setActive( cg ); pal.setInactive( cg ); pal.setDisabled( cg );
+    setPalette( pal );
+    // layout
+    QGridLayout* main = new QGridLayout( this, 1, 1, 11, 6 );
+    // image
+    QLabel* logo = new QLabel( this, "logo" );
+    logo->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) );
+    logo->setMinimumSize( 32, 32 ); logo->setMaximumSize( 32, 32 );
+    logo->setPaletteBackgroundColor( QColor( 234, 250, 234 ) );
+    logo->setFrameStyle( QLabel::NoFrame | QLabel::Plain );
+    logo->setPixmap( pixmap( pxAbout ) );
+    logo->setScaledContents( false );
+    logo->setAlignment( QLabel::AlignCenter );
+    // decoration
+    QLabel* decorLeft = new QLabel( this, "decorLeft" );
+    decorLeft->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ) );
+    decorLeft->setMinimumWidth( 32 ); decorLeft->setMaximumWidth( 32 );
+    decorLeft->setPaletteBackgroundColor( QColor( 234, 250, 234 ) );
+    decorLeft->setScaledContents( false );
+    QLabel* decorTop = new QLabel( this, "decorTop" );
+    decorTop->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
+    decorTop->setMinimumHeight( 32 ); decorTop->setMaximumHeight( 32 );
+    decorTop->setPaletteBackgroundColor( QColor( 234, 250, 234 ) );
+    decorTop->setScaledContents( false );
+    // contents
+    QLabel* title = new QLabel( this, "title" );
+    QString tlt = parent->getIWName();
+    title->setText( makeTitle( tlt ) );
+    QLabel* version = new QLabel( this, "version" );
+    version->setText( QString( "<b>Version</b>: %1.%1.%1" ).arg( __IW_VERSION_MAJOR__ ) \
+                     .arg( __IW_VERSION_MINOR__ ) \
+                     .arg( __IW_VERSION_PATCH__ ) );
+    QLabel* copyright = new QLabel( this, "copyright" );
+    copyright->setText( "<b>Copyright</b> &copy; 2004-2006 CEA" );
+    QFont font = title->font();
+    font.setPointSize( (int)( font.pointSize() * 1.8 ) );
+    title->setFont( font );
+    QFrame* line = new QFrame( this, "line" );
+    line->setFrameStyle( QFrame::HLine | QFrame::Sunken );
+    QLabel* url = new QLabel( this, "url" );
+    url->setText( makeTitle( "www.salome-platform.org", ".", false ) );
+    url->setAlignment( AlignRight );
+    font = version->font();
+    font.setPointSize( (int)( font.pointSize() / 1.2 ) );
+    version->setFont( font );
+    copyright->setFont( font );
+    url->setFont( font );
+    // layout
+    main->addWidget( logo, 0, 0 );
+    main->addMultiCellWidget( decorLeft, 1, 5, 0, 0 );
+    main->addWidget( decorTop, 0, 1 );
+    main->addWidget( title, 1, 1 );
+    main->addWidget( version, 2, 1 );
+    main->addWidget( copyright, 3, 1 );
+    main->addWidget( line, 4, 1 );
+    main->addWidget( url, 5, 1 );
+    // resize
+    QFontMetrics fm( title->font() );
+    int width = (int)( fm.width( tlt ) * 1.5 );
+    title->setMinimumWidth( width );
+    setMaximumSize( minimumSize() );
+  }
+  void mousePressEvent( QMouseEvent* )
+  {
+    accept();
+  }
+};
+
 // ================================================================
 /*!
  *  SALOME_InstallWizard::SALOME_InstallWizard
  *  Constructor
  */
 // ================================================================
-SALOME_InstallWizard::SALOME_InstallWizard(QString aXmlFileName)
+SALOME_InstallWizard::SALOME_InstallWizard(const QString& aXmlFileName,
+                                          const QString& aTargetDir,
+                                          const QString& aTmpDir)
      : InstallWizard( qApp->desktop(), "SALOME_InstallWizard", false, 0 ), 
        helpWindow( NULL ), 
        moreMode( false ), 
        previousPage( 0 ), 
        exitConfirmed( false )
 {
-  myIWName = tr( "Installation Wizard" );
-  tmpCreated = QString::null;
-  xmlFileName = aXmlFileName;
-  QFont fnt = font(); fnt.setPointSize( 14 ); fnt.setBold( true );
+  myIWName      = tr( "Installation Wizard" );
+  tmpCreated    = QString::null;
+  xmlFileName   = aXmlFileName;
+  targetDirPath = aTargetDir;
+  tmpDirPath    = aTmpDir;
+
+  // set application font
+  QFont fnt = font();
+  fnt.setPointSize( 14 );
+  fnt.setBold( true );
   setTitleFont( fnt );
 
   // set icon
@@ -364,7 +483,9 @@ SALOME_InstallWizard::SALOME_InstallWizard(QString aXmlFileName)
   setLicense( tr( "All right reserved" ) );
   setOS( "" );
 
-  ___MESSAGE___( "Config. file : " << xmlFileName );
+  ___MESSAGE___( "Configuration file : " << xmlFileName );
+  ___MESSAGE___( "Target directory   : " << targetDirPath );
+  ___MESSAGE___( "Temporary directory: " << tmpDirPath );
 
   // xml reader
   QFile xmlfile(xmlFileName);
@@ -405,8 +526,10 @@ SALOME_InstallWizard::SALOME_InstallWizard(QString aXmlFileName)
   
   // common signals connections
   connect( this, SIGNAL( selected( const QString& ) ),
-                                          this, SLOT( pageChanged( const QString& ) ) );
-  connect( this, SIGNAL( helpClicked() ), this, SLOT( helpClicked() ) );
+                                           this, SLOT( pageChanged( const QString& ) ) );
+  connect( this, SIGNAL( helpClicked() ),  this, SLOT( helpClicked() ) );
+  connect( this, SIGNAL( aboutClicked() ), this, SLOT( onAbout() ) );
+
   // catch signals from launched script
   connect(shellProcess, SIGNAL( readyReadStdout() ), this, SLOT( readFromStdout() ) );
   connect(shellProcess, SIGNAL( readyReadStderr() ), this, SLOT( readFromStderr() ) );
@@ -415,6 +538,10 @@ SALOME_InstallWizard::SALOME_InstallWizard(QString aXmlFileName)
 
   // create validation thread
   myThread = new ProcessThread( this );
+
+  // show about button
+  setAboutIcon( pixmap( pxAbout ) );
+  showAboutBtn( true );
 }
 // ================================================================
 /*!
@@ -656,6 +783,12 @@ void SALOME_InstallWizard::setupProductsPage()
     reader.setContentHandler( handler );
     reader.parse( source );  
   }
+  // take into account command line parameters
+  if ( !targetDirPath.isEmpty() )
+    targetFolder->setText( targetDirPath );
+  if ( !tmpDirPath.isEmpty() )
+    tempFolder->setText( tmpDirPath );
+
   // set first item to be selected
   if ( productsView->childCount() > 0 ) {
     productsView->setSelected( productsView->firstChild(), true );
@@ -1486,6 +1619,19 @@ void SALOME_InstallWizard::onLaunchSalome()
                        QMessageBox::NoButton,
                        QMessageBox::NoButton );
 }
+
+// ================================================================
+/*!
+ *  SALOME_InstallWizard::onAbout
+ *  <About> button slot: shows <About> dialog box
+ */
+// ================================================================
+void SALOME_InstallWizard::onAbout()
+{
+  AboutDlg d( this );
+  d.exec();
+}
+
 // ================================================================
 /*!
  *  SALOME_InstallWizard::findItem
index 37800cdc8b7f6d7a2155ee45cd70ebd844b7728d..ee7af23440509216b1f17813f48cea1419862dcc 100644 (file)
@@ -114,7 +114,9 @@ class SALOME_InstallWizard: public InstallWizard
     
  public:
   // constructor
-  SALOME_InstallWizard(QString aXmlFileName);
+  SALOME_InstallWizard(const QString& aXmlFileName,
+                      const QString& aTargetDir = QString::null,
+                      const QString& aTmpDir    = QString::null);
   // destructor
   virtual ~SALOME_InstallWizard( );
 
@@ -229,6 +231,8 @@ class SALOME_InstallWizard: public InstallWizard
   void onMoreBtn();
   // <Launch Salome> button slot
   void onLaunchSalome();
+  // <About> button slot
+  void onAbout();
 
   // QProcess slots:
   // -->something was written to stdin
@@ -251,6 +255,8 @@ class SALOME_InstallWizard: public InstallWizard
   MapProducts      productsMap;    // products info (name, dependancies, disk space )
   QStringList      toInstall;      // list of products being installed
   QString          xmlFileName;    // xml file
+  QString          targetDirPath;  // target directory
+  QString          tmpDirPath;     // temporary directory
   bool             moreMode;       // advanced mode flag
   QWidget*         previousPage;   // previous page
   QString          tmpCreated;     // created temporary directory
index 160f26eed5e250f59a9d12a101cf8940ec2fab4b..10fffb1e0ab93e92dcf69092452182a799db1c97 100644 (file)
@@ -17,7 +17,7 @@
 
 #define __IW_VERSION_MAJOR__ 1
 #define __IW_VERSION_MINOR__ 0
-#define __IW_VERSION_PATCH__ 0
+#define __IW_VERSION_PATCH__ 1
 
 #define __IW_VERSION__ (__IW_VERSION_MAJOR__*10000 + \
                         __IW_VERSION_MINOR__*100   + \
index 04409e187bba546e524b8679171f4ceb013e5c0e..560e4d4d10030f4a6ca4e9e5ce13f65f15d2cbf5 100644 (file)
@@ -30812,6 +30812,85 @@ static const char* const image_zoom_out[] = {
 "2 2 [.h+h+H.                i+j+k+l+m+n+",
 "                              W o+p+|+q+"};
 
+static const char* const image_about[] = { 
+"16 16 5 1",
+"      c None",
+".     c #5151C1",
+"+     c #1414C1",
+"@     c #7C7CC1",
+"#     c #A5A5C3",
+"                ",
+".+@             ",
+".+@             ",
+".+@             ",
+".+@             ",
+".+@@+@  @+@  @+@",
+".+@.+@  .+@  .+@",
+".+@.+@  .+@  .+@",
+".+@.+@ #.+@ #.+@",
+".+@.+@#.++@#.++@",
+"###.+..+++..++.#",
+"   .++++@++++@# ",
+"   .++.@#++.@#  ",
+"   .+@#  +@#    ",
+"                ",
+"                "};
+
+static const char* const image_about1[] = { 
+"16 16 8 1",
+"      c None",
+".     c #C15151",
+"+     c #C3A5A5",
+"@     c #C11414",
+"#     c #5151C1",
+"$     c #1414C1",
+"%     c #7C7CC1",
+"&     c #A5A5C3",
+"          .+@+. ",
+"          +.@.+ ",
+"#$%       @@@@@ ",
+"#$%       +.@.+ ",
+"#$%       .+@+. ",
+"#$%             ",
+"#$%%$%  %$%  %$%",
+"#$%#$%  #$%  #$%",
+"#$%#$%  #$%  #$%",
+"#$%#$% &#$% &#$%",
+"#$%#$%&#$$%&#$$%",
+"&&&#$##$$$##$$#&",
+"   #$$$$%$$$$%& ",
+"   #$$#%&$$#%&  ",
+"   #$%&  $%&    ",
+"                "};
+
+static const char* const image_about2[] = { 
+"16 16 9 1",
+"      c None",
+".     c #C15151",
+"+     c #C11414",
+"@     c #C17C7C",
+"#     c #C3A5A5",
+"$     c #5151C1",
+"%     c #1414C1",
+"&     c #7C7CC1",
+"*     c #A5A5C3",
+".+@             ",
+".+@             ",
+"###             ",
+"$%&             ",
+"$%&             ",
+"$%&             ",
+"$%&             ",
+"$%&&%&  &%&  &%&",
+"$%&$%&  $%&  $%&",
+"$%&$%&  $%&  $%&",
+"$%&$%& *$%& *$%&",
+"$%&$%&*$%%&*$%%&",
+"***$%$$%%%$$%%$*",
+"   $%%%%&%%%%&* ",
+"   $%%$&*%%$&*  ",
+"   $%&*  %&*    "};
+
 QPixmap pixmap( const int type )
 {
   switch ( type ) {
@@ -30819,6 +30898,8 @@ QPixmap pixmap( const int type )
     return QPixmap( ( const char** )image_SALOME );
   case pxLogo:         // small logo
     return QPixmap( ( const char** )image_logo );
+  case pxAbout:        // about icon
+    return QPixmap( ( const char** )image_about2 );
   case pxIcon:         // title icon
     return QPixmap( ( const char** )image_icon );
   case pxClose:        // help window : close window
index 8e5f201e615061373faca54f0ffcc0bfeed9b927..4a62bb6063f49700d3b119ca9fbc455b9ab087d0 100644 (file)
@@ -12,6 +12,7 @@
 
 enum { pxBigLogo,      // SALOME Logo
        pxLogo,         // small logo
+       pxAbout,        // about icon
        pxIcon,         // title icon
        pxClose,        // help window : close window
        pxHome,         // help window : go home
index 3e1697f5e74ead131ce98f371adea9cc66d80115..b508de0ac113be19fb47a099452f4d4e9cd2f808 100644 (file)
@@ -50,6 +50,11 @@ int main( int argc, char **argv )
   qInstallMsgHandler( MessageOutput );
 
   QString xmlFileName;
+  QString targetDirPath;
+  QString tmpDirPath;
+  bool has_xml    = false;
+  bool has_target = false;
+  bool has_tmp    = false;
   for( int i = 1; i < argc; i++ ) {
     QString a = QString( argv[i] );
     if ( a == "--version" || a == "-v" ) {
@@ -63,9 +68,50 @@ int main( int argc, char **argv )
             ( QT_VERSION       ) & 0xFF );
       return 0;
     }
-    if ( xmlFileName.isEmpty() )
-      xmlFileName = a;
+    else if ( a == "--target" || a == "-d" ) {
+      has_target = true;
+      if ( i < argc-1 && !QString( argv[i+1] ).startsWith("-") ) {
+       targetDirPath = argv[i+1]; 
+       i++;
+      }
+      else {
+       tmpDirPath = QString::null;
+      }
+    }
+    else if ( a == "--tmp" || a == "-t" ) {
+      has_tmp = true;
+      if ( i < argc-1 && !QString( argv[i+1] ).startsWith("-") ) {
+       tmpDirPath = argv[i+1]; 
+       i++;
+      }
+      else {
+       tmpDirPath = QString::null;
+      }
+    }
+    else if ( a == "--file" || a == "-f" ) {
+      has_xml = true;
+      if ( i < argc-1 && !QString( argv[i+1] ).startsWith("-") ) {
+       xmlFileName = argv[i+1]; 
+       i++;
+      }
+      else {
+       xmlFileName = QString::null;
+      }
+    }
+  }
+  if ( has_xml && xmlFileName.isEmpty() ) {
+    printf("Please specify the configuration XML file!\n");
+    return 1;
   }
+  if ( has_target && targetDirPath.isEmpty() ) {
+    printf("Please specify the target directory path!\n");
+    return 1;
+  }
+  if ( has_tmp && tmpDirPath.isEmpty() ) {
+    printf("Please specify the temprary directory path!\n");
+    return 1;
+  }
+
   if ( xmlFileName.isEmpty() )
     xmlFileName = "config.xml";
 
@@ -75,7 +121,7 @@ int main( int argc, char **argv )
   int result = -1;
   QFile xmlfile(xmlFileName);
   if ( xmlfile.exists() ) {
-    SALOME_InstallWizard wizard(xmlFileName);
+    SALOME_InstallWizard wizard(xmlFileName, targetDirPath, tmpDirPath);
     a.setMainWidget( &wizard );
     wizard.show();
     result = a.exec();