#
# $Header: /home/rockware/cvs-main/operator/tcl/opAppGroups.tcl,v 1.39 1995/03/14 21:51:04 erez Exp $
# Written by: Erez Strauss (C) All Rights Reserved 1994, 1995.
#
# The Applications Groups handling.
# There are four classes to handle the applications.
# 1.  OpAppGroupItem - give the basic functionality of item in a group.
# 2.  OpAppGroupApp - the application handlig
# 3.  OpAppGroupGrp - The Group of Items handlig.
# 4.  OpAppGroup - The display class, derived from the OpTopLevel.
#
# Each Item has a full name OPagi/name0/name1/...
# the agi stand for Application Group Item.
# each application/group is described by the following fields.
#
#  unique name (without spaces...)
#  Title
#  Description
#  icon name.
#  iconbitmap
#  Keyboard binding
#  Key binding scope info (in the parrant window or global to Operator).
#  Help Message
#  Balloon Message
#
# Specific field for the derived classes.
#
#  Application or group
#    Application
#      working directory.
#      X-Application
#      xterm-command
#      Open new xterm ?
#      Tcl-script
#      Display as stand alone icon ?
#    Group
#      list of AppGroupEnteries
#

# puts OpAppGroupItem ; flush stdout

itcl_class OpAppGroupItem {
    # no inherit
    # base for OpAppGroupApp and OpAppGroupGrp

    constructor config {}
    destructor {}
    method config config {}

    method list_gen {} {
    set l [list -name $name -title $title]
        if {$description != {}} {lappend l  -description $description}
        if {$iconname != {}} {lappend l  -iconname $iconname}
        if {$iconbitmap != {}} {lappend l  -iconbitmap [file rootname [file tail $iconbitmap]]}
        if {$key != {}} {lappend l  -key $key}
        if {$key_scope != {}} {lappend l  -key_scope $key_scope}
        if {$help_msg != {}} {lappend l  -help_msg $help_msg}
        if {$help_bln != {}} {lappend l  -help_bln $help_bln}
        if {$disp_mode != {}} {lappend l  -disp_mode $disp_mode}
        return $l
    }
    method list_set {list} {eval $this config $list}
    # default display - for Icons mode.
    method display {canv {x 0} {y 0}} {
        if {$canvas != {} && $canvas != $canv} {puts {AppGroupItem::display - two different canvases}}
        set canvas $canv
        if {[$canvas find withtag $this] != {}} {
            puts stderr "$this is already defined in $canvas"
        }
        if {$iconname != {} && $iconbitmap == {}} {
            set f [opNameToFile $iconname bitmap]
            if {$f != {}} {set iconbitmap @$f}
        } elseif {$iconbitmap != {}} {
            set f [opNameToFile $iconbitmap bitmap]
            if {$f != {}} {set iconbitmap @$f}
        }
        set item_id [$canvas create icon $x $y -text $title -bitmap $iconbitmap -bo 1 -tags "{$this}"]
    }
    method displayIcons {canv {x 0} {y 0}} {display $canv $x $y}
    # should be updated
    method displayList {canv {x 0} {y 0}} {display $canv $x $y}
    method win_edit {upd} {
        # open new dialog toplevl with entries for the different members
        # OpTopLevel .$this.edit
        OpTopLevel .$this-edit \
            -title "[$this info class] - edit" \
            -pack_option 1 \
            -buttons "
                {Apply . {$this win_update ; .$this-edit delete ; $upd update} {} {} {}}
                {Cancel . {.$this-edit delete} {} {} {}}
            "
        .$this-edit.buttons config -default 0 -padx 6 -pady 6 -relief groove -borderwidth 2
        frame .$this-edit.info.base
        frame .$this-edit.info.derived
        ::pack .$this-edit.info.base    -expand on -fill both
        ::pack .$this-edit.info.derived -expand on -fill both
        set ebase .$this-edit.info.base
        if {$name == {}} {set name [file tail $this]}
        label $ebase.name -text "Name: $name"
        ::pack $ebase.name -side top 
        # $ebase.name.entry delete 0 end
        # $ebase.name.entry insert 0 $name
        opLabeledEntry $ebase.title Title {} 20
        $ebase.title.entry delete 0 end
        $ebase.title.entry insert 0 $title
        opLabeledEntry $ebase.description Description {} 20
        $ebase.description.entry delete 0 end
        $ebase.description.entry insert 0 $description
        opLabeledEntry $ebase.iconname {Icon name} {} 20
        $ebase.iconname.entry delete 0 end
        $ebase.iconname.entry insert 0 $iconname
        opLabeledEntry $ebase.iconbitmap {Icon Bitmap} {} 20
        $ebase.iconbitmap.entry delete 0 end
        $ebase.iconbitmap.entry insert 0 $iconbitmap
        opLabeledEntry $ebase.key {Hot Key} {} 20
        $ebase.key.entry delete 0 end
        $ebase.key.entry insert 0 $key
        opLabeledEntry $ebase.key_scope {Hot Key Active areas} {} 20
        $ebase.key_scope.entry delete 0 end
        $ebase.key_scope.entry insert 0 $key_scope
        opLabeledEntry $ebase.help_msg {Help message} {} 20
        $ebase.help_msg.entry delete 0 end
        $ebase.help_msg.entry insert 0 $help_msg
        opLabeledEntry $ebase.help_bln {Balloon help message} {} 20
        $ebase.help_bln.entry delete 0 end
        $ebase.help_bln.entry insert 0 $help_bln
        opLabeledEntry $ebase.disp_mode {Display mode ??} {} 20
        $ebase.disp_mode.entry delete 0 end
        $ebase.disp_mode.entry insert 0 $disp_mode
        #
        virtual win_edit_specific $ebase ;# Create the entries for the specific derived class.
        #
        set n [winfo children $ebase]
        foreach e $n {::pack $e -side top}
    }
    method win_update {} {# update the item according to the information in the window display.
        set ebase .$this-edit.info.base
        # set name [$ebase.name.entry get]
        set title [$ebase.title.entry get]
        set description [$ebase.description.entry get]
        set iconname [$ebase.iconname.entry get]
        set iconbitmap [$ebase.iconbitmap.entry get]
        set key [$ebase.key.entry get]
        set key_scope [$ebase.key_scope.entry get]
        set help_msg [$ebase.help_msg.entry get]
        set help_bln [$ebase.help_bln.entry get]
        set disp_mode [$ebase.disp_mode.entry get]
        virtual win_update_specific $ebase
    }
    method win_end {} {catch {destroy $this.wd}}
    method is_valid {} {} ;# return 0 or 1

    public name {}
    public title {}
    public description {}
    public iconname {}
    public iconbitmap {} {} ;# if $after_constructor {$canvas itemconfig $item_id -bitmap @$iconbitmap}
    public key {}
    public key_scope {}
    public help_msg {}
    public help_bln {}
    public disp_mode Text {} ;# Text, Icon, Icon&Text.

    # the id of the item in the canvas.
    protected item_id {}
    # name of canvas that the item appears in.
    protected canvas {} ;# Where the icon appears.
}
# puts OpAppGroupApp ; flush stdout

itcl_class OpAppGroupApp {
    inherit OpAppGroupItem
    constructor config {
        if {$iconbitmap == {} && $dflt_app_icon != {}} {set iconbitmap @$dflt_app_icon}
        OpAppGroupItem::constructor
    }
    destructor {}
    method config config {}
    method run {from_win} {
        # Execute the action(s) associate with the Application
        if $confirm_exec_flag {
            # src - name of Application
            # trg - this object for callback
            .appGrpConfirmExec on -src $name -trg $this
        } {
            dorun
        }
    }
    method dorun {} {
        # Execute the action(s) associate with the Application
        if {$dir != {}} {set swd [pwd] ; cd $dir}
        if     {$tcltk_cmd != {}} $tcltk_cmd \
        elseif {$xapp != {}}      "exec $xapp &" \
        elseif {$xterm_cmd != {}} "exec xterm -e $xterm_cmd &"
        if {$dir != {}} {cd $swd}
    }
    method file_save f {
        eval lappend l [$this info class] $this [list_gen]
        puts -nonewline $f " $l"
    }
    method list_gen {} {
        set l [OpAppGroupItem::list_gen] 

        if {$dir != {}} {lappend l  -dir $dir}
        if {$tcltk_cmd != {}} {lappend l  -tcltk_cmd $tcltk_cmd}
        if {$xapp != {}} {lappend l  -xapp $xapp}
        if {$xterm_cmd != {}} {lappend l  -xterm_cmd $xterm_cmd}
        if {$xterm_flags != {}} {lappend l  -xterm_flags $xterm_flags}
        if {$stand_alone_icon != {}} {lappend l  -stand_alone_icon $stand_alone_icon}
        if {$confirm_exec_flag != {}} {lappend l  -confirm_exec_flag $confirm_exec_flag}
        return $l
    }
    method win_edit_specific w {
        opLabeledEntry $w.dir {Home Directory} {} 20
        $w.dir.entry delete 0 end
        $w.dir.entry insert 0 $dir
        opLabeledEntry $w.tcltk_cmd {Tcl/Tk Command} {} 20
        $w.tcltk_cmd.entry delete 0 end
        $w.tcltk_cmd.entry insert 0 $tcltk_cmd
        opLabeledEntry $w.xapp {X application name} {} 20
        $w.xapp.entry delete 0 end
        $w.xapp.entry insert 0 $xapp
        opLabeledEntry $w.xterm_cmd {XTerm Command} {} 20
        $w.xterm_cmd.entry delete 0 end
        $w.xterm_cmd.entry insert 0 $xterm_cmd
        opLabeledEntry $w.xterm_flags {XTerm flags} {} 20
        $w.xterm_flags.entry delete 0 end
        $w.xterm_flags.entry insert 0 $xterm_flags
        opLabeledEntry $w.stand_alone_icon {Stand alone Icon ?} {} 3
        $w.stand_alone_icon.entry delete 0 end
        $w.stand_alone_icon.entry insert 0 $stand_alone_icon
        opLabeledEntry $w.confirm_exec_flag {Confirm Execution} {} 3
        $w.confirm_exec_flag.entry delete 0 end
        $w.confirm_exec_flag.entry insert 0 $confirm_exec_flag
    }
    method win_update_specific w {
        set dir [$w.dir.entry get]
        set tcltk_cmd [$w.tcltk_cmd.entry get]
        set xapp [$w.xapp.entry get]
        set xterm_cmd [$w.xterm_cmd.entry get]
        set xterm_flags [$w.xterm_flags.entry get]
        set stand_alone_icon [$w.stand_alone_icon.entry get]
        set confirm_exec_flag [$w.confirm_exec_flag.entry get]
    }
    method remove {} {
        $this delete
    }
    public dir {} {}                   ; # working directory for application.
    public tcltk_cmd {} {}             ; # tcl script to execute.
    public xapp {} {}                  ; # X application - with its own window.
    public xterm_cmd {} {}             ; # Command to run in xterm.
    public xterm_flags {} {}           ; # warped - inside OpTopLevel, new - create new xterm
    public stand_alone_icon 0 {}       ; # should this application have a stand alone icon in the work space.
    public confirm_exec_flag 0 {}      ; # should the user confirm execution each time, by default no.
    common dflt_app_icon {}            ; # default application icon file name.
    # catch {set dflt_app_icon [opNameToFile app_icon bitmap]}
    proc add_app {} {} ; # create top level with entries for the new application.
}
# puts OpAppGroupGrp ; flush stdout

itcl_class OpAppGroupGrp {
    inherit OpAppGroupItem
    constructor config {
        if {$iconbitmap == {} && $dflt_grp_icon != {}} {set iconbitmap @$dflt_grp_icon}
        OpAppGroupItem::constructor
    }
    destructor {}
    method config config {}
    method run {from_win} { # open a new / raise OpAppGroup window with ...
        # puts "OpAppGroupGrp: $this is running"
        if {$open_win != {}} {
            $open_win raise
        } {
            if $new_win_mode {
                set open_win [OpAppGroup :: create $this]
            } {
                $from_win config -group_name $this
                set open_win $from_win
                $from_win raise
            }
        }
    }
    method list_gen {} {return [OpAppGroupItem::list_gen]}
    method file_save f {
        eval lappend l [$this info class] $this [list_gen]
        puts -nonewline $f " $l"
    }
    method win_edit_specific w {}
    method win_update_specific w {}
    method remove {} {
        if {[itcl_info objects $this/* -isa OpAppGroupItem] != {}} {
            .appGrpGroupNotEmpty on
        } {
            $this delete
        }
    }
    proc new_win_mode_set mode {set new_win_mode $mode}
    proc add_grp {} {} ;# create top level with entries for the new application group
    public open_win  {} {}
    # When group is open, has an open window (OpAppGroup)
    common new_win_mode 1
    # indicate whether to open new window for the group by the run method.
    common dflt_grp_icon {}
    # catch {set dflt_grp_icon [opNameToFile grp_icon bitmap]}
}
# The OpAppGroup is used for displaying of the different Items in canvas.
# the OpAppGroup provides only two public proc, (1) file_set, (2) show_top.
# The rest of the class procs and methods are for private usage.

# puts OpAppGroup ; flush stdout

itcl_class OpAppGroup {
    inherit OpTopLevel

    constructor config {
        # puts "OpAppGroup::constructor $group_name (DBG)"
        set tmp_menus "$tl_menus $ag_menus"
        OpTopLevel::constructor \
            -title "Application Group - $group_name" \
            -name "$group_name" \
            -iconname "$group_name" \
            -iconbitmap app-icon \
            -buttons $ag_buttons \
            -menus $tmp_menus -menu_bar $ag_menubar \
            -active_help "Application Group Display
 $group_name"

        # puts "OpAppGroup::constructor $group_name, after OpTopLevel constructor (DBG)"
        # The difference between the group_name and this is the dot at the start.
        # menus ...
        # Create canvas with scroll bars.

        $work_space config -relief groove
        # Create the canvas with the scrollbars.
        OpSCanvas :: create $work_space
        set canvas $work_space.u.c

        # what are the parameters that are passed to the bind callback in canvas ?
        # was working before changes.
        # <1> - Select the icon and update it's boarder width, to be written.
        # <Double-1> - Activate the icon - worked.
        # the selection is needed to enable menu operations.
        $canvas bind all <ButtonPress-1> "+$this canvas_select %x %y"
        $canvas bind all <Double-1> "+$this run_selected"
        .control add Application-Groups $group_name "$this raise"
        $group_name config -open_win $this
        OpSCanvas :: drag $work_space 1
        # puts "OpAppGroup::constructor $group_name, call update (DBG)"
        set after_constructor 1
        update
    }
    destructor {
        $group_name config -open_win {}
        .control remove Application-Groups $group_name
        # if [llength [itcl_info objects -class OpAppGroup] <= 1] { file_save ; foreach o [itcl_info objects -isa OpAppGroupItem] "$o delete"}
        # file save ?
    }
    method config config {}

    # display items in the canvas.
    # The update function clears the canvas then display each icon.
    method update config {
        # Application Group App/Grp icons buttons.
        $canvas delete all
        if {$display_mode == {} || $display_mode == "Icons"} {
            if {$app_flag == $grp_flag} {
                set to_disp [itcl_info objects $group_name/* -isa OpAppGroupItem]
            } elseif {$grp_flag} {
                set to_disp [itcl_info objects $group_name/* -isa OpAppGroupGrp]
            } elseif {$app_flag} {
                set to_disp [itcl_info objects $group_name/* -isa OpAppGroupApp]
            }
            set new_disp {}
            foreach i $to_disp {
                if [regexp "$group_name/\[^/]*\$"  $i] {
                    lappend new_disp $i
                }
            }
            set to_disp $new_disp
            # puts stderr "todisp :$to_disp:" ; flush stderr
            foreach i $to_disp {
                $i display$display_mode $canvas
            }
            if {$to_disp != {}} {
                # $canvas layout matrix -computeiconsize
                $canvas layout matrix
            }
        } {
            # no support fo the other modes, yet.
        }
        # in all different modes we check the scrollbars.
        OpSCanvas :: update $work_space
    }
    # This function should get one more parameter to distingush between select and activate.
    method canvas_select {x y} {
        # puts "$this select"
        set c_id [$canvas find withtag current]
        if {$c_id == {}} return
        if {$selected_obj != {}} {$canvas itemconfig $selected_obj -bo 1}
        if {[$canvas type $c_id] != "icon"} return
        $canvas dtag current
        set obj [lindex [$canvas itemconfig $c_id -tag] 4]
        $canvas itemconfig $c_id -bo 3
        set selected_obj $obj
        $canvas addtag current withtag $c_id
    }
    method run_selected {} {
        # puts "$this run_selected"
        # run the selected object.
        # if {[$obj info class] == "OpAppGroupGrp" && !$new_window}
        if {$selected_obj == {}} {
            .appGrpSelectFirst on
            return
        }
        if {[$selected_obj info class] == "OpAppGroupGrp" && 0} {
            #; change the view point of $this, else activate the object.
        } {
            # puts "$selected_obj run $this"
            $selected_obj run $this       ; # Activate the related object.
        }
    }
    method edit_selected {} {
        if {$selected_obj == {}} {
            .appGrpSelectFirst on
            return
        }
        $selected_obj win_edit $this
    }
    method delete_selected {} {
        if {$selected_obj == {}} {
            .appGrpSelectFirst on
            return
        }
        # src - the item to be deleted, trg - the display that should be updated
        .appGrpConfirmDeleteItem on -src $selected_obj -update $this
    }
    
    method new_grp {} {}
    method new_app {} {}
    method what_group {} {return $group_name}
    proc display_top {} {
        set all [itcl_info objects -isa OpAppGroupItem]
        if {$all == {}} {
            file_set
            file_load
            create  ;# Create by default open the Top group window.
        }
        if {[itcl_info objects .op_ag_top -class OpAppGroup] != {}} {
            .op_ag_top raise
        } {
            create
        }
    }
    proc file_set {{file {}}} {
        if {$file == {}} {
            global OPInfo
            if [::info exists OPInfo(user_app_list)] {
                set file_name $OPInfo(user_app_list)
            } {
                set file_name [opNameToFile app-list data]
            }
        } {
            set file_name $file
        }
    }
    # dummy list {AppGroupGrp .op_ag_top -name top}
    proc file_load {} {
        # upvar #0 source $file_name
        set o [itcl_info objects -isa OpAppGroupItem]
        foreach i $o {$i delete}
        #
        set f [open $file_name r]
        set all [read $f]
        close $f
        foreach i $all {
            # class-name obj-name configure-parameters
            # *** verify that the new object are global variables, otherwise add 'global [lindex $i 0]'
            # eval [lindex $i 0] [lindex $i 1] [lrange $i 2 end]
            eval $i
        }
        set file_loaded 1
    }
    proc file_save {} {
        # puts {OpAppGroups::file_save Strated ...}
        set f [open $file_name w]
        set all [itcl_info objects -isa OpAppGroupItem]
        if {$all != {}} {set all [lsort $all]}
        foreach i $all {
            puts -nonewline $f "{"
            $i file_save $f
            puts $f "}"
        }
        close $f
        # puts {OpAppGroups::file_save End.}
    }
    proc create {{grp {}}} {
        if {$grp == {}} {set grp op_ag_top}
        if {[itcl_info objects .$grp -class OpAppGroup] != {}} {
            .$grp raise
        } {
            return [OpAppGroup .$grp -group_name $grp]
        }
    }

    # icons, list, cascade tree.
    public display_mode {}
    public new_window 0 {}
    public grp_flag 1 {} ;# should we display the sub groups.
    public app_flag 1 {} ;# should we display the applications in the cureent group.

    # The Group that is displayed in this window.
    public group_name {} {if $after_constructor update}
    # The canvas that we use for display.
    protected canvas {}
    protected selected_obj {}  ;# The 'canvas object id' that the user selected.

    common file_name {}
    common file_loaded 0     ;# set to one after loading
    common open_new_win 0
    # common cmn_app_bm_name app_icon - should be part of their classes.
    # common cmn_grp_bm_name grp_icon
    # {View 0 ag_view}
    common ag_menubar {{File 0 ag_file} {Edit 0 ag_edit} {Windows 0 tl_wins} {Help 0 tl_help right}}
    common ag_menus {
        {ag_file {
            {command -label Run -command {THIS run_selected}}
            {separator}
            {command -label Save -command {OpAppGroup :: file_save}}
            {command -label Reload -command {OpAppGroup :: file_load}}
            {separator}
            {command -label Print -underline 0 -command {opDialogPrint [opSelectionGet]} -state disabled}
            {command -label Quit -command {THIS delete} -underline 0}}
        }
        {ag_edit {
            {command -label New -command {.appGrpNewItem on -src [THIS what_group] -trg THIS}}
            {command -label {Edit Item} -command {THIS edit_selected}}
            {command -label Delete -command {THIS delete_selected}}
            {command -label {Print Item} -underline 0 -command {opDialogPrint [opSelectionGet]} -state disabled}}
        }
        {ag_view {
            {checkbutton -label {New Windows} -onvalue 1 -offvalue 0 -variable OPDirDisplay(THIS,filter) -command {global OPAppGroups ; THIS config -new_window $OPAppGroups(THIS,new_window)}}
            {checkbutton -label {Show Applications} -onvalue 1 -offvalue 0 -variable OPDirDisplay(THIS,app) -command {global OPAppGroups ; THIS config -app_flag $OPAppGroups(THIS,app)}}
            {checkbutton -label {Show Groups} -onvalue 1 -offvalue 0 -variable OPDirDisplay(THIS,grp) -command {global OPAppGroups ; THIS config -grp_flag $OPAppGroups(THIS,grp)}}
            {command -label Icons -command {}}
            {command -label Text -command {}}}
        }
    }
    common ag_buttons {}
}
# End of opAppGroups.tcl.
