Widget to display and edit tasks: TaskBoard.oz

Source File

functor 
 
import 
   Tk
 
   Configure(maxJobs: MaxJobs
             maxSpan: MaxSpan
              
             durUnit:     DurUnit
             durFrame:    DurFrame
             jobDistance: JobDistance
 
             type:      Courier
             resColors: ResColors)
 
export 
   'class': TaskBoard
 
prepare 
 
   fun {GetTaskName J T}
      {VirtualString.toAtom j#J#t#T}
   end 
    
   fun {GetResourceName R}
      {VirtualString.toAtom r#R}
   end 
    
   proc {TaskNameTo TN ?J ?T}
      S = {Atom.toString TN}.2
   in 
      J = {String.toInt {List.takeWhile S Char.isDigit}}
      T = {String.toInt {List.dropWhile S Char.isDigit}.2}
   end 
    
define 
 
   OffX = 20
   OffY = JobDistance
 
   class Task 
      from Tk.canvasTag 
      attr 
         X0:0 Y0:0 X1:0
         Duration: 0
         Resource: unit 
         EditMode: true 
       
      meth init(parent:P resource:R duration:D x:X y:Y)
         Task, tkInit(parent:P)
         X0       <- X
         Y0       <- Y
         Duration <- D
         Resource <- R
         {P tk(create rectangle
               X                         Y - DurUnit div 2
               X + D*DurUnit - DurFrame  Y + DurUnit div 2
               fill:ResColors.R tags:self)}
         Task, tkBind(event:  '<1>' 
                      args:   [int(y)]
                      action: P # action(self))
      end 
    
      meth setDuration(D)
         Duration <- D
         Task, tk(coords
                  @X0                      @Y0 - DurUnit div 2
                  @X0 + D*DurUnit-DurFrame @Y0 + DurUnit div 2)
      end 
    
      meth getDuration($)
         @Duration
      end 
    
      meth setResource(R)
         Resource <- R
         Task, tk(itemconfigure fill:ResColors.R)
      end 
    
      meth getResource($)
         @Resource
      end 
    
      meth move(ByX)
         X0 <- @X0 + ByX
         Task,tk(move ByX 0)
      end 
    
      meth setSol(S)
         X = S * DurUnit
      in 
         if @EditMode then Task,tk(move X-@X0 0)
         else Task,tk(move X-@X1 0)
         end 
         EditMode <- false 
         X1       <- X
      end 
    
      meth setEdit 
         if @EditMode then skip else 
            EditMode <- true 
            Task,tk(move @X0-@X1 0)
         end 
      end 
    
   end 
 
 
   class Job 
      feat 
         Number
         Parent
      attr 
         Tasks:  nil
         NextX:  0
          
      meth init(parent:P number:N)
         self.Parent = P
         self.Number = N
         Tasks <- nil
         NextX <- 0
      end 
       
      meth newTask(resource:R duration:D)
         Tasks <- {Append @Tasks
                   [{New Task
                     init(parent:   self.Parent
                          resource: R
                          duration: D
                          x:        @NextX
                          y:        (self.Number - 1) * JobDistance)}]}
         NextX <- @NextX + DurUnit * D
      end 
       
      meth DelTask(Ts D $)
         case Ts of nil then nil
         [] T|Tr then 
            if T==then 
               {ForAll Tr
                proc {$ T}
                   {T move(~{D getDuration($)} * DurUnit)}
                end} Tr
            else T|Job,DelTask(Tr D $)
            end 
         end 
      end 
       
      meth deleteTask(D)
         {D tk(delete)}
         NextX <- @NextX - {D getDuration($)} * DurUnit
         Tasks <- Job,DelTask(@Tasks D $)
      end 
       
      meth SetDur(Ts S D)
         case Ts of nil then skip 
         [] T|Tr then 
            if T==then 
               {ForAll Tr
                proc {$ T}
                   {T move((D-{S getDuration($)}) * DurUnit)}
                end}
            else Job,SetDur(Tr S D)
            end 
         end 
      end 
       
      meth setDuration(T D)
         NextX <- @NextX + (D - {T getDuration($)}) * DurUnit
         Job,SetDur(@Tasks T D)  
         {T setDuration(D)}
      end 
       
      meth setSol(S)
         {Record.forAllInd S
          proc {$ A S}
             if A\=pa andthen A\=pe then J T in 
                {TaskNameTo A ?J ?T}
                if self.Number==then {{Nth @Tasks T} setSol(S)} end 
             end 
          end}
      end 
       
      meth setEdit 
         {ForAll @Tasks proc {$ T} {T setEdit} end}
      end 
       
      meth getLastSpec($)
         case @Tasks of nil then nil else 
            [{GetTaskName self.Number {Length @Tasks}}]
         end 
      end 
       
      meth getSpec($)
         {List.mapInd @Tasks
          fun {$ I T}
             Task={GetTaskName self.Number I}
             Dur ={T getDuration($)}
             Res ={GetResourceName {T getResource($)}}
             Pre = if I==then [pa]
                   else [{GetTaskName self.Number I-1}]
                   end  
          in 
             Task(dur:Dur pre:Pre res:Res)
          end}
      end 
       
   end 
 
    
   class TaskBoard 
      from Tk.canvas 
      feat 
         Jobs Tools BackTag
      attr 
         EditMode: true 
          
      meth tkInit(parent:P tools:T spec:Spec)
         self.Jobs       = {NewArray 1 MaxJobs 1}
         {For 1 MaxJobs 1
          proc {$ J}
             {Put self.Jobs J {New Job init(number:J parent:self)}}
          end}
         self.Tools      = T
         Tk.canvas, tkInit(parent:       P
                           bg:           ivory
                           width:  400
                           height: 220
                           bd:2 relief:sunken
                           scrollregion: q(~OffX
                                           ~OffY
                                           MaxSpan * DurUnit
                                           MaxJobs * JobDistance)
                           xscrollincrement: 1
                           yscrollincrement: 1)
         TaskBoard, tk(xview scroll ~OffX-6 units)
         TaskBoard, tk(yview scroll ~OffY units)
         self.BackTag = {New Tk.canvasTag tkInit(parent:self)}
         {For 1 MaxJobs 1
          proc {$ J}
             Y = (MaxJobs - J) * JobDistance  
          in 
             {self tk(create text ~5 Y font:Courier
                      text:  if J==then 10 else 0#(MaxJobs - J + 1) end 
                      anchor:e)}
          end}
         {For 1 MaxJobs 1
          proc {$ J}
             Y  = (MaxJobs - J) * JobDistance
             Y0 = Y - JobDistance div 2 + 1
             Y1 = Y + JobDistance div 2 - 1
          in 
             {self tk(create rectangle 0 Y0 MaxSpan*DurUnit Y1
                      fill:ivory outline:'' tags:self.BackTag)}
          end}
         {For 1 MaxJobs+1 1
          proc {$ J}
             Y = (MaxJobs - J) * JobDistance + JobDistance div 2
          in 
             {self tk(create line 0 Y MaxSpan*DurUnit Y
                      fill:gray50)}
          end}
         {self.BackTag tkBind(event:  '<1>' 
                              args:   [int(y)]
                              action: self # action(unit))}
         {List.forAllInd Spec
          proc {$ JN Ts}
             J={Get self.Jobs JN}
          in 
             {ForAll Ts proc {$ D#R}
                           {J newTask(resource:R duration:D)}
                        end}
          end}
      end 
          
      meth action(T SY)
         if @EditMode then 
            Y = SY - OffY
            J = {Get self.Jobs
                 {Min {Max 1 (Y + JobDistance div 2 ) div JobDistance + 1}
                  MaxJobs}}
         in 
            case {self.Tools getTool($)}
            of create(R D) then 
               {J newTask(resource:R duration:D)}
            [] delete      then 
               if T\=unit then {J deleteTask(T)} end 
            [] resource(GR) then 
               if T\=unit then {T setResource({GR})} end 
            [] duration(GD) then 
               if T\=unit then {J setDuration(T {GD})} end 
            end 
         end 
      end 
       
      meth getSpec($)
         pa(dur:0) | 
         pe(dur:0 pre:{ForThread 1 MaxJobs 1
                       fun {$ Js J}
                          {Append {{Get self.Jobs J}
                                   getLastSpec($)}
                           Js}
                       end nil}) | 
         {ForThread 1 MaxJobs 1
          fun {$ Ss J}
             {Append {{Get self.Jobs J} getSpec($)} Ss}
          end nil}
      end 
       
      meth setEdit 
         EditMode <- true 
         {For 1 MaxJobs 1
          proc {$ J}
             {{Get self.Jobs J} setEdit}
          end}
      end 
       
      meth setSol(Sol)
         EditMode <- false 
         {For 1 MaxJobs 1
          proc {$ J}
             {{Get self.Jobs J} setSol(Sol)}
          end}
      end 
      meth displaySol(Sol)
         {For 1 MaxJobs 1
          proc {$ J}
             {{Get self.Jobs J} setSol(Sol)}
          end}
      end 
       
   end 
 
end 


Version 1.4.0 (20080702)