project.tcl
README.md

project.tcl

  • project.tcl
  • Variables
  • Common
    • ProjectName project::ProjectName : Gets a project name from its file name.
    • ProjectFileName project::ProjectFileName : Gets a project file name from a project's name.
    • CheckProjectName project::CheckProjectName : Removes spec.characters from a project name (sort of normalizing it). Returns yes, if the name is "correct" (no char replacements made).
    • GetProjects project::GetProjects : Reads settings of all projects.
    • ClockFormat project::ClockFormat : Formats date in seconds. secs - date in seconds
    • ClockScan project::ClockScan : Scans date to get date in seconds. d - date
    • ClockYMD project::ClockYMD : Extracts year, month, day from date. d - date
    • IsOutdated project::IsOutdated : Checks for outdated TODOs of a project. prj - project's name todo - if yes, gets also date and todo Returns 0, if no todo for the project, 1 otherwise; if todo=yes, adds also date and todo outdated (if there is).
    • CheckOutdated project::CheckOutdated : Checks for outdated TODOs of all projects except for the current. Return {} or a name of project with outdated TODOs.
  • Ini
    • SaveData project::SaveData : Saves some data.
    • GetOptVal project::GetOptVal : Gets a name and a value from a line of form "name=value". line - the line
    • SaveSettings project::SaveSettings : Saves project settings to curinfo array.
    • RestoreSettings project::RestoreSettings : Restores project settings from curinfo array.
    • GetProjectOpts project::GetProjectOpts : Reads a project's settings from a project settings file. fname - the project settings file's name
    • PutProjectOpts project::PutProjectOpts : Writes a project's settings to a project settings file. fname - the project settings file's name oldname - old name of the project file dorename - yes, if rename of old -notes/-rems
  • Text fields
    • CurrProject project::CurrProject : Gets a current project name, from a current item of project list.
    • SaveNotes project::SaveNotes : For a current item of project list, saves a file of notes and Commands tab's commands. prj - project's name
    • NotesFile project::NotesFile : Gets a file name of notes. prj - project's name
    • RemsFile project::RemsFile : Gets a file name of reminders.
    • ReadRems project::ReadRems : Reads a file of reminders.
    • SortRems project::SortRems : Sorts reminders by date. rems - list of reminders Returns a list of reminder date before current date, sorted list, flag of outdated reminder.
    • Klnd_save project::Klnd_save : Saves a reminder on a date.
    • KlndBorderText project::KlndBorderText : Highlights/unhighlights a reminder's border. clr - color of border
  • GUI helpers
    • ReadNotes project::ReadNotes : Reads notes of a project and commands for Commands tab. prj - project's name
    • SelectedPrj project::SelectedPrj : Gets a project name of selected item. item - selected item
    • Select project::Select : Handles a selection in a list of projects.
    • Selected project::Selected : Gets a currently selected project's index. what - if "index", selected item's index is returned domsg - if "no", no message displayed if there is no selected project
    • UpdateTree project::UpdateTree : Fills a list of projects.
    • FocusInTab project::FocusInTab : Focuses on a widget in a tab. tab - the tab's path wid - the widget's path
    • CheckNewDir project::CheckNewDir : Checks if the root directory exists. If no, tries to create it. Returns yes, if all is OK.
    • ExistingProject project::ExistingProject : Checks if a project (of entry field) exists. msgOnExist - yes, if message on existing, else on non-existing project Returns a project name if it exists or {} otherwise.
    • ValidProject project::ValidProject : Checks if a project's options are valid.
    • CheckPrjLeaf project::CheckPrjLeaf : Checks "Regexp of a leaf" field.
    • CheckPrjUseLeaf project::CheckPrjUseLeaf : Enables/disables the "Regexp of a leaf" field.
    • StdLeafRE project::StdLeafRE : Sets standard RE for leaf unit.
    • KlndDay project::KlndDay : Selects a date of reminder. dsec - date in seconds to select doblink - if yes, make the month blink
    • KlndDayRem project::KlndDayRem : Selects a date of reminder before a current one. dmin - date in seconds to select
    • Message project::Message : Displays a message in statusbar of projects dialogue. msg - message mode - mode of Message
    • KeyOnTree project::KeyOnTree : Handles key press on the tree. K - key
    • focusTree project::focusTree : Sets focus on the treeview.
    • ValidateIni project::ValidateIni : Validates values of alited.ini. V - %V wildcard of -validatecommand
  • Files tab's procs
    • SelFiles project::SelFiles : Checks for a selection of file listbox. Returns: list of listbox's path and the selection or {}.
    • CurrentFile project::CurrentFile : Gets a current file of file listbox. Returns file name of first selected file or 1st in the list, if no selection.
    • OpenFile project::OpenFile : Opens a file of listbox after double clicking. y - y-coordinate of clicking
    • OpenSelFiles project::OpenSelFiles : Opens selected files of listbox. showmsg - if yes, shows a message Files are open in a currently open project.
    • CloseSelFiles project::CloseSelFiles : Closes selected files of listbox. Files are closed in a currently open project.
    • DetachSelFiles project::DetachSelFiles : Detaches selected files.
    • afterOpenCloseFiles project::afterOpenCloseFiles : Actions after opening/closing files.
    • SelectAllFiles project::SelectAllFiles : Selects all files of listbox.
    • LbxPopup project::LbxPopup : Runs a popup menu on the project files listbox. X - x-coordinate of mouse pointer Y - y-coordinate of mouse pointer
    • ValidateDir project::ValidateDir : Tries to get a project name at choosing root dir.
    • TipOnFile project::TipOnFile : Shows info on a file in the file list as a tooltip. idx - item index
    • PopupMenu project::PopupMenu : Opens a popup menu in the project list. x - x-coordinate to identify an item y - y-coordinate to identify an item X - x-coordinate of the click Y - x-coordinate of the click
    • TabFileHeading project::TabFileHeading : Show Files tab's heading.
    • TabFileFill project::TabFileFill : Fills a listbox of files. flist - file list
    • TabFileInfo project::TabFileInfo : Fills a listbox with a list of project files.
    • TabFileFilter project::TabFileFilter : Fills a listbox with a list of filtered files of all projects.
    • postValidateFilter project::postValidateFilter : Postvalidates the file filter change.
    • preValidateFilter project::preValidateFilter : Prevalidates the file filter change.
  • Buttons for project list
    • Add project::Add : "Add project" button's handler. showtotals - if yes, shows total number of files Returns yes, if the project is successfully added.
    • Change project::Change : "Change project" button's handler.
    • Delete project::Delete : "Delete project" button's handler.
    • Template project::Template : Creates a project by template as set in Template tab. The template can contain directories or files (indented for subdirectories). The files satisfy glob-patterns: changelog*, license*, licence*, readme*. See also: TplDefault
    • UpdateMsg project::UpdateMsg : Displays a message on project updating. msg - message of action (add/change) showtotals - if yes, shows total number of files
  • Buttons
    • Ok project::Ok : 'OK' button handler. args - possible arguments
    • Cancel project::Cancel : 'Cancel' button handler. args - possible arguments
    • Help project::Help : 'Help' button handler.
    • HelpMe project::HelpMe : 'Help' for start.
    • OnProjectEnter project::OnProjectEnter : Actions on entering a project.
    • ProcMessage project::ProcMessage : Handles clicking on message label. Shows the message and if it is about TODO, selects the corresponding project.
    • ProjectEnter project::ProjectEnter : Processes double-clicking and pressing Enter on the project list. Cancels selecting projects if there are old reminders.
    • Klnd_button project::Klnd_button : Fire an event handler (paste/undo/redo) on a reminder. ev - event to fire
    • Klnd_paste project::Klnd_paste : Pastes a text to a reminder.
    • Klnd_undo project::Klnd_undo : Undoes changes of a reminder.
    • Klnd_redo project::Klnd_redo : Redoes changes of a reminder.
    • Klnd_delete project::Klnd_delete : Clears a reminder.
    • Klnd_moveTODO project::Klnd_moveTODO : Moves current TODO to a new date. wrem - text widget of TODO todo - text of TODO date - new date (in seconds)
    • Klnd_next project::Klnd_next : Moves a reminder to *days*. days - days to move to
    • Klnd_next2 project::Klnd_next2 : Moves a reminder to 7 days.
    • Klnd_previous project::Klnd_previous : Moves a reminder back to 1 day.
    • Klnd_previous2 project::Klnd_previous2 : Moves a reminder back to 7 days.
    • KlndPopup project::KlndPopup : Handles a popup menu for the calendar. w - day widget clicked y - year m - month d - day X - X-coordinate of pointer Y - Y-coordinate of pointer
    • ViewDir project::ViewDir : Shows file chooser just to view the project's dir
    • RunComs project::RunComs : Handles running commands of Commands tab.
    • ChecksRun project::ChecksRun : Sets checks of "Commands / Run for project".
    • ChecksCom project::ChecksCom : Sets checks of "Commands / Run for all".
  • Template procs
    • TplDefaultText project::TplDefaultText : Gets default contents of project template.
    • TplDefault project::TplDefault : Sets default contents of project template.
    • UpdateTplLists project::UpdateTplLists : Updates lists of template data, setting the current template on the top.
    • UpdateTplText project::UpdateTplText : Updates the template text.
    • RemoveFromTplList project::RemoveFromTplList : Removes a template name from the lists of template data. val - name of template to be removed
    • DeleteFromTplList project::DeleteFromTplList : Deletes a template name from the list of project templates.
  • Calendar
    • KlndUpdate project::KlndUpdate : Updates calendar data.
    • KlndInDate project::KlndInDate : Gets a date in seconds. y - year m - month d - day If *y* is omitted or y/m/d not valid, gets a current date in seconds.
    • KlndOutDate project::KlndOutDate : Gets a date formatted. y - year m - month d - day If *y* is omitted or y/m/d not valid, gets a current date formatted.
    • KlndDate project::KlndDate : Formats a calendar date by alited's format (Preferences/Templates). date - the date to be formatted
    • KlndText project::KlndText : Gets a reminder text for a date. dt - date
    • KlndClick project::KlndClick : Processes a click on a calendar day. y - year m - month d - day
    • KlndSearch project::KlndSearch : Search a date in calendar data. Returns index of found item or -1 if not found.
    • KlndTextModified project::KlndTextModified : Processes modifications of calendar text. wtxt - text's path args - not used arguments
  • GUI
    • MainFrame project::MainFrame : Creates a main frame of "Project" dialogue.
    • Tab1 project::Tab1 : Creates a main tab of "Project".
    • Tab2 project::Tab2 : Creates Options tab of "Project".
    • Tab3 project::Tab3 : Creates Templates tab of "Project".
    • Tab4 project::Tab4 : Creates Commands tab of "Project".
    • Tab5 project::Tab5 : Creates Files tab of "Project".
  • Main
    • _create project::_create : Creates and opens Projects dialogue.
    • _run project::_run : Runs Projects dialogue. checktodo - if yes, checks for outdated TODOs
  • EOF

alited's source

The alited/src directory contains alited's own source files.

Some additional alited's files are also contained in alited/lib/addon directory (alited/lib directory contains all library files).

  • about.tcl - "About alited" dialogue.
  • alited.tcl - The alited's main script to start.
  • bar.tcl - Handles the bar of tabs.
  • check.tcl - "Check Tcl" dialogue and procedures.
  • complete.tcl - Handles auto-completion.
  • edit.tcl - "Edit" menu's procedures.
  • favor.tcl - Handles the favorite and last visited units.
  • favor_ls.tcl - "Saved lists of favorites" dialogue and procedures.
  • file.tcl - "File" menu's procedures.
  • find.tcl - "Find / Replace" dialogue and procedures.
  • format.tcl - "Edit / Formats" menu's procedures.
  • img.tcl - List of images used by alited.
  • indent.tcl - Handles text indentation.
  • info.tcl - Handles the info bar.
  • ini.tcl - Handles initializing alited.
  • keys.tcl - Handles keyboard (mapping etc.).
  • main.tcl - Handles the main form of alited.
  • menu.tcl - Handles alited's menus.
  • msgs.tcl - Localized messages used in several places.
  • paver.tcl - "Tools / Paver" tool.
  • pkgIndex.tcl - Includes README.md text (for Ruff doc generator).
  • pref.tcl - "Preferences" dialogue and procedures.
  • preview.tcl - "Preview (theme, CS)" dialogue called by "Preferences".
  • printer.tcl - "Tools / Project Printer" dialogue and procedures.
  • project.tcl - "Projects" dialogue and procedures.
  • run.tcl - "Tools / Run..." dialogue and procedures.
  • tool.tcl - "Tools" menu's procedures.
  • tree.tcl - Handles the tree of units and files.
  • unit.tcl - Handles the unit tree.
  • unit_tpl.tcl - "Templates" dialogue and procedures.
  • project.tcl
    ###########################################################
    # Name:    project.tcl
    # Author:  Alex Plotnikov  (aplsimple@gmail.com)
    # Date:    04/28/2021
    # Brief:   Handles project settings.
    # License: MIT.
    ###########################################################
    
    # _________________________ Variables ________________________ #
    
    namespace eval project {
    
      # apave object of Projects
      variable obPrj pavedProject
    
      # Projects dialogue's path
      variable win $::alited::al(WIN).diaPrj
    
      # list of projects
      variable prjlist [list]
    
      # initial geometry of Projects dialogue (centered in the main form)
      variable geo root=$::alited::al(WIN)
    
      # saved index of last selected project
      variable ilast -1
    
      # saved tab of Projects dialogue
      variable oldTab {}
    
      # data of projects
      variable prjinfo; array set prjinfo [list]
      foreach _ $::alited::OPTS {
        catch {set prjinfo(*DEFAULT*,$_) $::alited::al(DEFAULT,$_)}  ;# default options
      }
    
      # data of currently open project (to save/restore)
      variable curinfo; array set curinfo [list]
    
      # calendar's data
      variable klnddata; array set klnddata [list]
      set klnddata(dateformat) {%Y/%m/%d}
    
      # todo message and its project
      variable msgtodo {}
      variable itemtodo {}
    
      # flag "projects changed"
      variable updateGUI no
    
      # total of project files
      variable totalfiles 0
    
      # separator for commands in Notes
      variable COMSEP \t
    
      # filter for project files
      variable filefilter {}
      variable casefilter 0
      variable savedfilefilter $filefilter
      variable savedcasefilter $casefilter
    
      # flag "save alited.ini"
      variable saveini no
    }
    
    # ________________________ Common _________________________ #
    
    proc project::ProjectName {fname} {
      # Gets a project name from its file name.
    
      namespace upvar ::alited PRJEXT PRJEXT
      set fname [file tail $fname]
      if {[string match -nocase *$PRJEXT $fname]} {
        set fname [file rootname $fname]
      }
      return $fname
    }
    #_______________________
    
    proc project::ProjectFileName {name} {
      # Gets a project file name from a project's name.
    
      namespace upvar ::alited PRJDIR PRJDIR PRJEXT PRJEXT
      set name [ProjectName [string trim $name]]
      return [file normalize [file join $PRJDIR "$name$PRJEXT"]]
    }
    #_______________________
    
    proc project::CheckProjectName {} {
      # Removes spec.characters from a project name (sort of normalizing it).
      # Returns yes, if the name is "correct" (no char replacements made).
    
      namespace upvar ::alited al al
      set oldname $al(prjname)
      set al(prjname) [::apave::NormalizeFileName $al(prjname)]
      return [expr {$oldname eq $al(prjname)}]
    }
    #_______________________
    
    proc project::GetProjects {} {
      # Reads settings of all projects.
    
      namespace upvar ::alited al al PRJEXT PRJEXT
      variable prjlist
      variable ilast
      set prjlist [list]
      set i [set ilast 0]
      alited::tree::PrepareDirectoryContents
      foreach finfo [alited::tree::GetDirectoryContents $::alited::PRJDIR] {
        set fname [lindex $finfo 2]
        if {[file extension $fname] eq $PRJEXT} {
          if {[GetProjectOpts $fname] eq $al(prjname)} {
            set ilast $i
          }
          incr i
        }
      }
    }
    #_______________________
    
    proc project::ClockFormat {secs} {
      # Formats date in seconds.
      #   secs - date in seconds
    
      variable klnddata
      return [clock format $secs -format $klnddata(dateformat)]
    }
    #_______________________
    
    proc project::ClockScan {d} {
      # Scans date to get date in seconds.
      #   d - date
    
      variable klnddata
      return [clock scan $d -format $klnddata(dateformat)]
    }
    #_______________________
    
    proc project::ClockYMD {d} {
      # Extracts year, month, day from date.
      #   d - date
    
      return [split [ClockFormat $d] /]
    }
    #_______________________
    
    proc project::IsOutdated {prj {todo no}} {
      # Checks for outdated TODOs of a project.
      #   prj - project's name
      #   todo - if yes, gets also date and todo
      # Returns 0, if no todo for the project, 1 otherwise; if todo=yes, adds also date and todo outdated (if there is).
    
      set rems [SortRems [ReadRems $prj]]
      set res [lindex $rems 2]
      if {$todo} {
        lappend res {*}[lindex $rems 1 0]
      }
      return $res
    }
    #_______________________
    
    proc project::CheckOutdated {} {
      # Checks for outdated TODOs of all projects except for the current.
      # Return {} or a name of project with outdated TODOs.
    
      variable prjlist
      foreach prj $prjlist {
        if {$prj ne $::alited::al(prjname)} {
          if {[IsOutdated $prj]} {return $prj}
        }
      }
      return {}
    }
    
    # ________________________ Ini _________________________ #
    
    proc project::SaveData {} {
      # Saves some data.
    
      variable ilast
      set ilast [Selected index no]
    }
    #_______________________
    
    proc project::GetOptVal {line} {
      # Gets a name and a value from a line of form "name=value".
      #   line - the line
    
      if {[set i [string first = $line]]>-1} {
        return [list [string range $line 0 $i-1] [string range $line $i+1 end]]
      }
      return [list]
    }
    #_______________________
    
    proc project::SaveSettings {} {
      # Saves project settings to curinfo array.
    
      namespace upvar ::alited al al OPTS OPTS
      variable curinfo
      foreach v $OPTS {
        set curinfo($v) $al($v)
      }
      set curinfo(prjfile) $al(prjfile)
    }
    #_______________________
    
    proc project::RestoreSettings {} {
      # Restores project settings from curinfo array.
    
      namespace upvar ::alited al al OPTS OPTS
      variable curinfo
      foreach v $OPTS {
        set al($v) $curinfo($v)
      }
      set al(prjfile) $curinfo(prjfile)
      TabFileInfo
    }
    #_______________________
    
    proc project::GetProjectOpts {fname} {
      # Reads a project's settings from a project settings file.
      #   fname - the project settings file's name
    
      namespace upvar ::alited al al OPTS OPTS DIR DIR
      variable prjlist
      variable prjinfo
      variable curinfo
      set pname [ProjectName $fname]
      # save project names to 'prjlist' variable to display it by treeview widget
      lappend prjlist $pname
      # save project files' settings in prjinfo array
      set filecont [readTextFile $fname]
      foreach opt $OPTS {
        catch {set prjinfo($pname,$opt) $prjinfo(*DEFAULT*,$opt)}  ;# defaults
      }
      set prjinfo($pname,tablist) [list]
      if {[set currentprj [expr {$curinfo(prjname) eq $pname}]]} {
        foreach tab [alited::bar::BAR listTab] {
          set tid [lindex $tab 0]
          if {[set val [alited::bar::FileName $tid]] ne {}} {
            lappend prjinfo($pname,tablist) $val
          }
        }
      }
      set prjinfo($pname,prjroot) $DIR
      foreach line [textsplit $filecont] {
        lassign [GetOptVal $line] opt val
        if {[lsearch $OPTS $opt]>-1} {
          set prjinfo($pname,$opt) [alited::ProcEOL $val in]
        } elseif {$opt eq {tab} && !$currentprj && $val ne {}} {
          lappend prjinfo($pname,tablist) $val
        }
      }
      set prjinfo($pname,prjfile) $fname
      set prjinfo($pname,prjname) $pname
      set al(tablist) $prjinfo($pname,tablist)
      return $pname
    }
    #_______________________
    
    proc project::PutProjectOpts {fname oldname dorename} {
      # Writes a project's settings to a project settings file.
      #   fname - the project settings file's name
      #   oldname - old name of the project file
      #   dorename - yes, if rename of old -notes/-rems
    
      namespace upvar ::alited al al OPTS OPTS
      variable prjinfo
      set filecont [readTextFile $oldname]
      if {$filecont eq {}} {
        alited::ini::SaveIniPrj
        set filecont [readTextFile $oldname]
      }
      set newcont {}
      foreach line [textsplit $filecont] {
        lassign [GetOptVal $line] opt val
        if {$line eq {[Tabs]}} {
          foreach tab $al(tablist) {
            append line \n "tab=$tab"
          }
        } elseif {$opt in [list tab rem tablist {*}$OPTS]} {
          continue
        } elseif {$opt in {curtab}} {
          #
        } elseif {$line eq {[Options]}} {
          foreach opt $OPTS {
            if {$opt ni {prjname tablist}} {
              set val [set al($opt)]
              append line \n $opt= $val
              set prjinfo($al(prjname),$opt) [alited::ProcEOL $val in]
            }
          }
        }
        append newcont $line \n
      }
      writeTextFile $fname newcont
      if {$oldname ne $fname} {
        catch {file delete $oldname}
        if {$dorename} {
          foreach ftyp {notes rems} {
            set oldtyp [file rootname $oldname]-$ftyp.txt
            set newtyp [file rootname $fname]-$ftyp.txt
            catch {file rename $oldtyp $newtyp}
          }
        }
      }
    }
    
    # ________________________ Text fields _________________________ #
    
    proc project::CurrProject {} {
      # Gets a current project name, from a current item of project list.
    
      variable obPrj
      variable prjlist
      set prj {}
      catch {
        set tree [$obPrj TreePrj]
        set item [Selected item no]
        set isel [$tree index $item]
        set prj [lindex $prjlist $isel]
      }
      return $prj
    }
    #_______________________
    
    proc project::SaveNotes {{prj ""}} {
      #  For a current item of project list, saves a file of notes and Commands tab's commands.
      #   prj - project's name
    
      namespace upvar ::alited al al
      variable obPrj
      variable klnddata
      variable COMSEP
      if {$prj eq {}} {set prj $klnddata(SAVEPRJ)}
      if {$prj ne {}} {
        set fnotes [NotesFile $prj]
        set fcont [string trimright [[$obPrj TexPrj] get 1.0 end]]
        if {[catch {set indent [string repeat { } $::alited::al(prjindent)]}]} {set indent {    }}
        set fcont [string map [list $COMSEP $indent] $fcont]
        set fcontOrig $fcont
        for {set i 1} {$i<=$al(cmdNum)} {incr i} {
          set com [string trim $al(PTP,run$i)]
          set al(PTP,run$i) {}
          if {$com ne {}} {
            incr irun   ;# starting commands from #1
            append fcont \nrun$irun$COMSEP$al(PTP,runch$i)$COMSEP$com
            set al(PTP,run$i) $com
          }
        }
        for {set i 1} {$i<=$al(cmdNum)} {incr i} {
          set com [string trim $al(PTP,com$i)]
          set al(PTP,com$i) {}
          if {$com ne {}} {
            incr irun   ;# starting commands from #1
            append fcont \nrun$irun$COMSEP$al(PTP,comch$i)$COMSEP$COMSEP$com
            set al(PTP,com$i) $com
          }
        }
        if {$fcontOrig eq {} && $fcont ne {}} {
          set fcont __$al(MC,coms)__$fcont
        }
        writeTextFile $fnotes fcont 0 0
      }
    }
    #_______________________
    
    proc project::NotesFile {prj} {
      # Gets a file name of notes.
      #   prj - project's name
    
      return [file join $::alited::PRJDIR $prj-notes.txt]
    }
    #_______________________
    
    proc project::RemsFile {prj} {
      # Gets a file name of reminders.
    
      return [file join $::alited::PRJDIR $prj-rems.txt]
    }
    #_______________________
    
    proc project::ReadRems {prj} {
      # Reads a file of reminders.
    
      variable klnddata
      set frems [RemsFile $prj]
      if {[file exists $frems]} {
        set res [readTextFile $frems]
      } else {
        set res [list]
      }
      return $res
    }
    #_______________________
    
    proc project::SortRems {rems} {
      # Sorts reminders by date.
      #   rems - list of reminders
      # Returns a list of reminder date before current date, sorted list, flag of outdated reminder.
    
      set tmp [list]
      set dmin [set outdated 0]
      set dcur [clock add [KlndInDate] $::alited::al(todoahead) day]
      foreach it $rems {
        lassign $it d text
        lassign [split $d /] y m d
        if {[catch {set d [KlndInDate $y $m $d]}]} {
          set d [clock seconds]
        }
        if {$d<=$dcur && ($dmin==0 || $d<$dmin)} {
          set dmin $d
        }
        lappend tmp [list $d $text]
      }
      set rems [list]
      foreach it [lsort -index 0 $tmp] {
        lassign $it d text
        lappend rems [list [ClockFormat $d] $text]
      }
      if {$dmin} {
        if {$dmin<[clock seconds]} {
          set outdated 1 ;# current day reached
        } elseif {$dmin<$dcur} {
          set outdated 2 ;# "ahead" day reached
        }
      }
      list $dmin $rems $outdated
    }
    #_______________________
    
    proc project::Klnd_save {} {
      # Saves a reminder on a date.
    
      variable obPrj
      variable prjinfo
      variable klnddata
      set wtxt [$obPrj TexKlnd]
      if {[set prjname $klnddata(SAVEPRJ)] eq {}} return
      set text [string trim [$wtxt get 1.0 end]]
      set date $klnddata(SAVEDATE)
      set info [list $date $text "TODO opt."]  ;# + possible options for future
      set i [KlndSearch $date $prjname]
      if {$text eq {}} {
        if {$i>-1} {
          set prjinfo($prjname,prjrem) [lreplace $prjinfo($prjname,prjrem) $i $i]
        }
        KlndBorderText
      } elseif {$i>-1} {
        set prjinfo($prjname,prjrem) [lreplace $prjinfo($prjname,prjrem) $i $i $info]
      } else {
        lappend prjinfo($prjname,prjrem) $info
      }
      ::klnd::update {} {} {} $prjinfo($prjname,prjrem)
      set fcont $prjinfo($prjname,prjrem)
      writeTextFile [RemsFile $prjname] fcont 0 0
    }
    #_______________________
    
    proc project::KlndBorderText {{clr {}}} {
      # Highlights/unhighlights a reminder's border.
      #   clr - color of border
    
      variable obPrj
      if {$clr eq {}} {set clr [lindex [obj csGet] 8]}
      [$obPrj TexKlnd] configure -highlightbackground $clr
    }
    
    # ________________________ GUI helpers _________________________ #
    
    proc project::ReadNotes {prj} {
      # Reads notes of a project and commands for Commands tab.
      #   prj - project's name
    
      namespace upvar ::alited al al
      variable obPrj
      variable COMSEP
      for {set i 1} {$i<=$al(cmdNum)} {incr i} {
        set al(PTP,run$i) [set al(PTP,com$i) {}]
        set al(PTP,runch$i) [set al(PTP,comch$i) 0]
      }
      set al(PTP,chbClearRun) 0
      set al(PTP,chbClearCom) 0
      set irun [set icom 0]
      set wtxt [$obPrj TexPrj]
      $wtxt delete 1.0 end
      set fnotes [NotesFile $prj]
      if {[file exists $fnotes]} {
        set cont [readTextFile $fnotes]
        if {[set ir [string first run1$COMSEP $cont]]>-1} {
          # get commands for Commands tab (project's and common)
          foreach com [split [string range $cont $i end] \n] {
            lassign [split $com $COMSEP] run ch com1 com2
            set ch [string is true -strict $ch]
            if {$com1 ne {}} {
              set al(PTP,run[incr irun]) $com1
              if {$ch} {set al(PTP,runch$irun) [set al(PTP,chbClearRun) 1]}
            } elseif {$com2 ne {}} {
              set al(PTP,com[incr icom]) $com2
              if {$ch} {set al(PTP,comch$icom) [set al(PTP,chbClearCom) 1]}
            }
          }
          set cont [string range $cont 0 $ir-1]
        }
        set cont [string trim $cont]
        if {$cont ne {}} {$wtxt insert end $cont}
      }
      $wtxt edit reset; $wtxt edit modified no
    }
    #_______________________
    
    proc project::SelectedPrj {item} {
      # Gets a project name of selected item.
      #   item - selected item
    
      variable obPrj
      variable prjlist
      variable prjinfo
      set tree [$obPrj TreePrj]
      if {[string is digit $item]} {  ;# the item is an index
        if {$item<0 || $item>=[llength $prjlist]} {return {}}
        set prj [lindex $prjlist $item]
        set item $prjinfo($prj,ID)
      } elseif {![$tree exists $item]} {
        return {}
      }
      set isel [$tree index $item]
      if {$isel<0 || $isel>=[llength $prjlist]} {return {}}
      list $tree $item [lindex $prjlist $isel]
    }
    #_______________________
    
    proc project::Select {{item ""}} {
      # Handles a selection in a list of projects.
    
      namespace upvar ::alited al al OPTS OPTS
      variable obPrj
      variable prjinfo
      variable klnddata
      if {$item eq {}} {set item [Selected item no]}
      if {$item ne {}} {
        lassign [SelectedPrj $item] tree item prj
        if {$prj eq {}} return
        ReadNotes $prj
        lassign [SortRems [ReadRems $prj]] dmin prjinfo($prj,prjrem)
        foreach opt $OPTS {
          if {[catch {set al($opt) $prjinfo($prj,$opt)} e]} {
            Message $e
            return
          }
        }
        set al(tablist) $prjinfo($prj,tablist)
        TabFileInfo
        if {[$tree selection] ne $item} {
          $tree selection set $item
        }
        if {$dmin>0} {
          KlndDayRem $dmin
        } else {
          KlndDay [clock seconds] no
          KlndBorderText
        }
        ::klnd::blinking no
        set klnddata(SAVEDATE) {}
        catch {after cancel $klnddata(AFTERKLND)}
        set klnddata(AFTERKLND) [after 200 alited::project::KlndUpdate]
        [$obPrj Labprj] configure -text [msgcat::mc {For project}]\ $al(prjname)
        set tip [string map [list %f "$al(MC,prjName) $al(prjname)"] $al(MC,alloffile)]
        ::baltip tip [$obPrj ChbClearRun] $tip
        after 200 "$tree see $item; $tree focus $item"
        CheckPrjUseLeaf
      }
    }
    #_______________________
    
    proc project::Selected {what {domsg yes}} {
      # Gets a currently selected project's index.
      #   what - if "index", selected item's index is returned
      #   domsg - if "no", no message displayed if there is no selected project
    
      variable obPrj
      variable prjlist
      set tree [$obPrj TreePrj]
      if {[set isel [$tree selection]] eq {} && [set isel [$tree focus]] eq {} && $domsg} {
        Message $::alited::al(MC,prjsel) 4
      }
      if {$isel ne {} && $what eq {index}} {
        set isel [$tree index $isel]
      }
      return $isel
    }
    #_______________________
    
    proc project::UpdateTree {} {
      # Fills a list of projects.
    
      variable obPrj
      variable prjlist
      variable prjinfo
      set tree [$obPrj TreePrj]
      $tree delete [$tree children {}]
      foreach prj $prjlist {
        set prjinfo($prj,ID) [$tree insert {} end -values [list $prj]]
      }
    }
    #_______________________
    
    proc project::FocusInTab {tab wid} {
      # Focuses on a widget in a tab.
      #   tab - the tab's path
      #   wid - the widget's path
    
      variable win
      $win.fra.fraR.nbk select $win.fra.fraR.nbk.$tab
      focus $wid
    }
    #_______________________
    
    proc project::CheckNewDir {} {
      # Checks if the root directory exists. If no, tries to create it.
      # Returns yes, if all is OK.
    
      namespace upvar ::alited al al
      variable obPrj
      variable win
      if {![file exists $al(prjroot)]} {
        FocusInTab f1 [$obPrj chooserPath Dir]
        set msg [string map [list %d $al(prjroot)] \
          [msgcat::mc "Directory \"%d\"\ndoesn't exist.\n\nCreate it?"]]
        if {![alited::msg yesno ques $msg YES -geometry root=$win]} {
          return no
        }
        if {[catch {file mkdir $al(prjroot)} err]} {
          set msg [msgcat::mc {Error at creating the directory.}]
          alited::msg ok err [append msg \n\n $err] -geometry root=$win
          return no
        }
      }
      return yes
    }
    
    #_______________________
    
    proc project::ExistingProject {msgOnExist} {
      # Checks if a project (of entry field) exists.
      #   msgOnExist - yes, if message on existing, else on non-existing project
      # Returns a project name if it exists or {} otherwise.
    
      namespace upvar ::alited al al
      variable obPrj
      variable prjlist
      set pname $al(prjname)
      if {[lsearch -exact $prjlist $pname]>-1} {
        if {$msgOnExist} {
          FocusInTab f1 [$obPrj EntName]
          set msg [string map [list %n $pname] $al(MC,prjexists)]
          Message $msg 4
        }
      } else {
        if {!$msgOnExist} {
          FocusInTab f1 [$obPrj EntName]
          set msg [msgcat::mc \
            "A project \"%n\" doesn't exists. Hit \[+\] button to create it."]
          set msg [string map [list %n $pname] $msg]
          Message $msg 4
        }
        set pname {}
      }
      return $pname
    }
    #_______________________
    
    proc project::ValidProject {} {
      # Checks if a project's options are valid.
    
      namespace upvar ::alited al al
      variable obPrj
      variable totalfiles
      set al(prjname) [string trim $al(prjname)]
      if {$al(prjname) eq {} || ![CheckProjectName]} {
        bell
        FocusInTab f1 [$obPrj EntName]
        return no
      }
      if {$al(prjroot) eq {}} {
        bell
        FocusInTab f1 [$obPrj chooserPath Dir]
        return no
      }
      if {$al(prjuseleafRE) && $al(prjleafRE) eq {}} {
        bell
        FocusInTab f2 [$obPrj EntLf]
        return no
      }
      set al(prjroot) [file nativename $al(prjroot)]
      if {![CheckNewDir]} {return no}
      if {$al(prjindent)<0 || $al(prjindent)>8} {set al(prjindent) 2}
      if {$al(prjredunit)<$al(minredunit) || $al(prjredunit)>100} {set al(prjredunit) 20}
      set msg [string map [list %d $al(prjroot)] \
        [msgcat::mc {Checking %d. Wait a little...}]]
      Message $msg 5
      alited::tree::PrepareDirectoryContents
      set totalfiles [llength [alited::tree::GetDirectoryContents $al(prjroot)]]
      if {$totalfiles >= $al(MAXFILES)} {
        set msg [string map [list %n $al(MAXFILES)] \
          [msgcat::mc {Too big directory for a project: %n files or more.}]]
        Message $msg 4
        set res no
      } else {
        Message {}
        set res yes
      }
      return $res
    }
    #_______________________
    
    proc project::CheckPrjLeaf {} {
      # Checks "Regexp of a leaf" field.
    
      namespace upvar ::alited al al
      if {$al(prjleafRE) eq {}} {
        set al(prjleafRE) [string trimright $al(RE,leaf)]
      }
    }
    #_______________________
    
    proc project::CheckPrjUseLeaf {} {
      # Enables/disables the "Regexp of a leaf" field.
    
      namespace upvar ::alited al al
      variable obPrj
      CheckPrjLeaf
      set al(prjuseleafRE) [string is true -strict $al(prjuseleafRE)]
      if {$al(prjuseleafRE)} {set _ normal} {set _ disabled}
      [$obPrj EntLf] configure -state $_
    }
    #_______________________
    
    proc project::StdLeafRE {} {
      # Sets standard RE for leaf unit.
    
      namespace upvar ::alited al al
      set al(prjleafRE) $al(RE,leafDEF)
    }
    #_______________________
    
    proc project::KlndDay {dsec {doblink yes}} {
      # Selects a date of reminder.
      #   dsec - date in seconds to select
      #   doblink - if yes, make the month blink
    
      lassign [ClockYMD $dsec] y m d
      set m [string trimleft $m { 0}]
      set d [string trimleft $d { 0}]
      ::klnd::selectedDay {} $y $m $d $doblink
    }
    #_______________________
    
    proc project::KlndDayRem {dmin} {
      # Selects a date of reminder before a current one.
      #   dmin - date in seconds to select
    
      KlndDay $dmin
      after idle {alited::project::KlndBorderText red}
    }
    #_______________________
    
    proc project::Message {msg {mode 2}} {
      # Displays a message in statusbar of projects dialogue.
      #   msg - message
      #   mode - mode of Message
    
      variable obPrj
      alited::Message $msg $mode [$obPrj LabMess]
    }
    #_______________________
    
    proc project::KeyOnTree {K} {
      # Handles key press on the tree.
      #   K - key
    
      variable prjlist
      Message {}
      set icur [Selected index no]
      if {![string is integer -strict $icur]} return
      foreach i [list [incr icur] 0] {
        foreach pr [lrange $prjlist $i end] {
          if {![string compare -nocase $K [string index $pr 0]]} {
            Select $i
            return
          }
          incr i
        }
      }
    }
    #_______________________
    
    proc project::focusTree {} {
      # Sets focus on the treeview.
    
      variable win
      variable obPrj
      focus $win
      focus [$obPrj TreePrj]
    }
    #_______________________
    
    proc project::ValidateIni {V} {
      # Validates values of alited.ini.
      #   V - %V wildcard of -validatecommand
    
      variable saveini
      set saveini yes
      if {$V eq {focusout}} {
        alited::ini::SaveIni
        set saveini no
      }
      return 1
    }
    
    # ________________________ Files tab's procs _________________________ #
    
    proc project::SelFiles {} {
      # Checks for a selection of file listbox.
      # Returns: list of listbox's path and the selection or {}.
    
      variable obPrj
      set lbx [$obPrj LbxFlist]
      if {![llength [set selidx [$lbx curselection]]]} {
        Message [msgcat::mc {No selected files}] 4
        return {}
      }
      list $lbx $selidx
    }
    #_______________________
    
    proc project::CurrentFile {} {
      # Gets a current file of file listbox.
      # Returns file name of first selected file or 1st in the list, if no selection.
    
      variable obPrj
      set lbx [$obPrj LbxFlist]
      if {[catch {
        if {[llength [set selidx [$lbx curselection]]]} {
          set idx [lindex $selidx 0]
        } else {
          set idx 0
        }
        set res [$lbx get $idx]
      }]} {
        set res {}
      }
      return $res
    }
    #_______________________
    
    proc project::OpenFile {y} {
      # Opens a file of listbox after double clicking.
      #   y - y-coordinate of clicking
    
      variable obPrj
      set lbx [$obPrj LbxFlist]
      set selid [$lbx nearest $y]
      if {$selid != -1} {
        $lbx selection clear 0 end
        $lbx selection set $selid
        OpenSelFiles no
      }
    }
    #_______________________
    
    proc project::OpenSelFiles {{showmsg yes}} {
      # Opens selected files of listbox.
      #   showmsg - if yes, shows a message
      # Files are open in a currently open project.
    
      namespace upvar ::alited al al
      variable prjinfo
      variable curinfo
      set prj $al(prjname)
      set cprj $curinfo(prjname)
      set al(prjname) $curinfo(prjname)
      lassign [SelFiles] lbx selidx
      if {$lbx ne {}} {
        update
        set fnames [list]
        set balloon {}
        foreach idx [lreverse $selidx] {
          set fn [$lbx get $idx]
          if {[file exists $fn]} {
            lappend fnames $fn
            if {[lsearch -index 0 -exact $prjinfo($cprj,tablist) $fn]<0} {
              set prjinfo($cprj,tablist) [linsert $prjinfo($cprj,tablist) 0 $fn]
            }
          } else {
            set balloon $fn
          }
        }
        set llen [llength $fnames]
        if {$showmsg} {
          set msg [string map [list %n $llen] [msgcat::mc {Open files: %n}]]
          Message $msg 3
        }
        if {$llen} {
          alited::file::OpenFile $fnames yes yes alited::info::Put
        }
        if {$balloon ne {}} {
          after idle alited::Balloon1 $balloon
        }
      }
      set al(prjname) $prj
      afterOpenCloseFiles
    }
    #_______________________
    
    proc project::CloseSelFiles {} {
      # Closes selected files of listbox.
      # Files are closed in a currently open project.
    
      namespace upvar ::alited al al
      variable prjinfo
      variable curinfo
      if {[set pname [ExistingProject no]] eq {}} return
      set prj $al(prjname)
      set cprj $curinfo(prjname)
      lassign [SelFiles] lbx selidx
      if {$lbx ne {}} {
        set closecurr no
        set fnamecurr [alited::bar::FileName]
        foreach idx $selidx {
          set fname [$lbx get $idx]
          if {[alited::file::IsNoName $fname]} continue
          if {[set TID [alited::bar::FileTID $fname]] ne {}} {
            if {$fname eq $fnamecurr} {
              set closecurr yes
            } else {
              alited::bar::BAR $TID close no
            }
          }
          if {$prj eq $cprj &&
          [set i [lsearch -index 0 -exact $prjinfo($cprj,tablist) $fname]]>=0} {
            set prjinfo($cprj,tablist) [lreplace $prjinfo($cprj,tablist) $i $i]
          }
        }
        set al(prjname) $cprj
        if {$closecurr && [set TID [alited::bar::FileTID $fnamecurr]] ne {}} {
          alited::bar::BAR $TID close ;# this should be last to check for "No name" tab
        }
        alited::bar::BAR draw
        if {$prj eq $cprj} {
          set al(tablist) $prjinfo($cprj,tablist)
          TabFileInfo
        }
        set al(prjname) $prj
        afterOpenCloseFiles
      }
    }
    #_______________________
    
    proc project::DetachSelFiles {} {
      # Detaches selected files.
    
      lassign [SelFiles] lbx selidx
      set fnames [list]
      if {$lbx ne {}} {
        foreach idx $selidx {lappend fnames [$lbx get $idx]}
      }
      alited::file::Detach $fnames
    }
    #_______________________
    
    proc project::afterOpenCloseFiles {} {
      # Actions after opening/closing files.
    
      variable obPrj
      set flist [$obPrj LbxFlist]
      set fsels [$flist curselection]
      set item [Selected index]
      GetProjects
      UpdateTree
      Select $item
      focusTree
      foreach fsel $fsels {
        after 300 $flist selection set $fsel $fsel
      }
    }
    #_______________________
    
    proc project::SelectAllFiles {} {
      # Selects all files of listbox.
    
      variable obPrj
      [$obPrj LbxFlist] selection set 0 end
    }
    #_______________________
    
    proc project::LbxPopup {X Y} {
      # Runs a popup menu on the project files listbox.
      #   X - x-coordinate of mouse pointer
      #   Y - y-coordinate of mouse pointer
    
      variable obPrj
      variable filefilter
      set popm [$obPrj LbxFlist].popup
      catch {destroy $popm}
      lassign [SelFiles] lbx selidx
      if {$filefilter eq {}} {set state normal} {set state disabled}
      if {[llength $selidx]} {set stateS normal} {set stateS [set state disabled]}
      menu $popm -tearoff 0
      $popm add command -label $::alited::al(MC,openselfile) -command alited::project::OpenSelFiles -state $stateS
      $popm add command -label [msgcat::mc {Close Selected Files}] \
        -command alited::project::CloseSelFiles -state $state
      $popm add separator
      $popm add command -label $::alited::al(MC,detachsel) -command alited::project::DetachSelFiles -state $stateS
      $popm add separator
      $popm add command -label [msgcat::mc {Select All}] -command alited::project::SelectAllFiles -accelerator Ctrl+A
      baltip::sleep 1000
      $obPrj themePopup $popm
      tk_popup $popm $X $Y
    }
    #_______________________
    
    proc project::ValidateDir {} {
      # Tries to get a project name at choosing root dir.
    
      namespace upvar ::alited al al
      update idletasks ;#  update ;# a bug of ttk?
      if {$al(prjname) eq {}} {
        set al(prjname) [file tail $al(prjroot)]
      }
      return yes
    }
    #_______________________
    
    proc project::TipOnFile {idx} {
      # Shows info on a file in the file list as a tooltip.
      #   idx - item index
    
      variable obPrj
      set lbx [$obPrj LbxFlist]
      set item [$lbx get $idx]
      return [alited::file::FileStat $item]
    }
    #_______________________
    
    proc project::PopupMenu {x y X Y} {
      # Opens a popup menu in the project list.
      #   x - x-coordinate to identify an item
      #   y - y-coordinate to identify an item
      #   X - x-coordinate of the click
      #   Y - x-coordinate of the click
    
      namespace upvar ::alited al al
      variable obPrj
      variable win
      set popm $win.popupmenu
      catch {destroy $popm}
      menu $popm -tearoff 0
      $popm add command -label $al(MC,prjadd) \
        -command alited::project::Add {*}[$obPrj iconA add]
      $popm add command -label $al(MC,prjchg) \
        -command alited::project::Change {*}[$obPrj iconA change]
      $popm add command -label $al(MC,prjdel1) \
        -command alited::project::Delete {*}[$obPrj iconA delete]
      $popm add separator
      $popm add command -label $al(MC,CrTemplPrj) \
        -command alited::project::Template {*}[$obPrj iconA plus]
      $popm add command -label $al(MC,ViewDir) \
        -command alited::project::ViewDir {*}[$obPrj iconA OpenFile]
      $obPrj themePopup $popm
      tk_popup $popm $X $Y
    }
    #_______________________
    
    proc project::TabFileHeading {} {
      # Show Files tab's heading.
    
      variable obPrj
      set lbx [$obPrj LbxFlist]
      set txt [msgcat::mc {List of files (%n):}]
      set txt [string map [list %n [$lbx index end]] $txt]
      [$obPrj LabFlist] configure -text $txt
    }
    #_______________________
    
    proc project::TabFileFill {flist} {
      # Fills a listbox of files.
      #   flist - file list
    
      variable obPrj
      set lbx [$obPrj LbxFlist]
      $lbx delete 0 end
      set savfname {}
      foreach tab [lsort -index 0 -dictionary $flist] {
        set fname [lindex [split $tab \t] 0]
        if {$savfname ne $fname} {  ;# no duplicates
          $lbx insert end $fname
          set savfname $fname
        }
      }
    }
    #_______________________
    
    proc project::TabFileInfo {} {
      # Fills a listbox with a list of project files.
    
      variable filefilter
      if {$filefilter ne {}} {
        TabFileFilter
        return
      }
      TabFileFill $::alited::al(tablist)
      TabFileHeading
    }
    #_______________________
    
    proc project::TabFileFilter {} {
      # Fills a listbox with a list of filtered files of all projects.
    
      variable filefilter
      variable casefilter
      variable prjlist
      variable prjinfo
      if {$filefilter eq {}} {
        TabFileInfo
        return
      }
      set alltablist [list]
      if {$casefilter} {set nocase {}} {set nocase -nocase}
      foreach prj $prjlist {
        foreach tab $prjinfo($prj,tablist) {
          set fname [lindex [split $tab \t] 0]
          if {[string match {*}$nocase *$filefilter* $fname]} {
            lappend alltablist $tab
          }
        }
      }
      TabFileFill $alltablist
      TabFileHeading
    }
    #_______________________
    
    proc project::postValidateFilter {} {
      # Postvalidates the file filter change.
    
      variable filefilter
      variable casefilter
      variable savedfilefilter
      variable savedcasefilter
      if {$savedfilefilter ne $filefilter || $savedcasefilter ne $casefilter} {
        if {$filefilter eq {}} {
          TabFileInfo
        } else {
          TabFileFilter
        }
        set savedfilefilter $filefilter
        set savedcasefilter $casefilter
      }
    }
    #_______________________
    
    proc project::preValidateFilter {} {
      # Prevalidates the file filter change.
    
      after idle alited::project::postValidateFilter
      return 1
    }
    
    # ________________________ Buttons for project list _________________________ #
    
    proc project::Add {{showtotals yes}} {
      # "Add project" button's handler.
      #   showtotals - if yes, shows total number of files
      # Returns yes, if the project is successfully added.
    
      namespace upvar ::alited al al OPTS OPTS
      variable prjlist
      variable prjinfo
      SaveNotes
      if {![ValidProject] || [ExistingProject yes] ne {}} {return no}
      set al(tablist) [list]
      TabFileInfo
      set pname $al(prjname)
      set proot $al(prjroot)
      set al(prjfile) [ProjectFileName $pname]
      set al(prjbeforerun) {}
      if {$al(PRJDEFAULT)} {
        # use project defaults from "Setup/Common/Projects", except for prjname & prjroot
        foreach opt $OPTS {
          catch {set al($opt) $al(DEFAULT,$opt)}
        }
        set al(prjname) $pname
        set al(prjroot) $proot
      }
      alited::ini::SaveIni yes  ;# to initialize ini-file
      foreach opt $OPTS {
        set prjinfo($pname,$opt) $al($opt)
      }
      set prjinfo($pname,prjrem) {} ;# reminders
      PutProjectOpts $al(prjfile) $al(prjfile) no
      GetProjects
      UpdateTree
      Select $prjinfo($pname,ID)
      UpdateMsg [string map [list %n $pname] $al(MC,prjnew)] $showtotals
      return yes
    }
    #_______________________
    
    proc project::Change {} {
      # "Change project" button's handler.
    
      namespace upvar ::alited al al
      variable obPrj
      variable curinfo
      variable prjlist
      variable prjinfo
      variable updateGUI
      SaveNotes
      if {[set isel [Selected index]] eq {}} return
      if {![ValidProject]} return
      for {set i 0} {$i<[llength $prjlist]} {incr i} {
        if {$i!=$isel && [lindex $prjlist $i] eq $al(prjname)} {
          set msg [string map [list %n $al(prjname)] $al(MC,prjexists)]
          Message $msg 4
          return
        }
      }
      set oldprj [lindex $prjlist $isel]
      set newprj $al(prjname)
      set prjinfo($newprj,tablist) $prjinfo($oldprj,tablist)
      catch {unset prjinfo($oldprj,tablist)}
      set oldname [ProjectFileName $oldprj]
      set prjlist [lreplace $prjlist $isel $isel $newprj]
      set fname [ProjectFileName $newprj]
      if {$newprj eq $curinfo(prjname)} SaveSettings
      if {$oldprj eq $curinfo(prjname)} {
        set curinfo(prjname) $newprj
        set curinfo(prjfile) $fname
      }
      set prjinfo($newprj,prjrem) $prjinfo($oldprj,prjrem) ;# reminders
      PutProjectOpts $fname $oldname yes
      GetProjects
      UpdateTree
      Select $prjinfo($newprj,ID)
      set updateGUI yes
      UpdateMsg [string map [list %n [lindex $prjlist $isel]] $al(MC,prjupd)]
    }
    #_______________________
    
    proc project::Delete {} {
      # "Delete project" button's handler.
    
      namespace upvar ::alited al al
      variable prjlist
      variable prjinfo
      variable win
      variable curinfo
      if {[set isel [Selected index]] eq {}} return
      set geo "-centerme $win"
      set nametodel [lindex $prjlist $isel]
      if {$nametodel eq $curinfo(prjname)} {
        Message $al(MC,prjcantdel) 4
        return
      }
      set msg [string map [list %n $nametodel] $al(MC,prjdelq)]
      if {![alited::msg yesno ques $msg NO {*}$geo]} {
        return
      }
      if {[catch {file delete [ProjectFileName $nametodel]} err]} {
        alited::msg ok err $err {*}$geo
        return
      }
      catch {file delete [NotesFile $nametodel]}
      catch {file delete [RemsFile $nametodel]}
      if {[set llen [llength $prjlist]] && $isel>=$llen} {
        set isel [incr llen -1]
      }
      GetProjects
      UpdateTree
      Select $isel
      Message [string map [list %n $nametodel] $al(MC,prjdel2)] 3
    }
    #_______________________
    
    proc project::Template {} {
      # Creates a project by template as set in Template tab.
      # The template can contain directories or files (indented for subdirectories).
      # The files satisfy glob-patterns: changelog*, license*, licence*, readme*.
      # See also: TplDefault
    
      namespace upvar ::alited al al
      variable obPrj
      variable curinfo
      # first, update the template list independently on errors
      UpdateTplLists
      # then check the template for correctness
      set wtpl [$obPrj TexTemplate]
      set namelist [set errmess {}]
      set margin [set indent [set spprev -1]]
      foreach name [split [$wtpl get 1.0 end] \n] {
        if {[set name [string trimright $name]] eq {}} continue
        if {[string trim $name] ne [::apave::NormalizeFileName $name]} {
          set errmess [string map [list %n $name] $al(MC,incorrname)]
          break
        }
        set sporig [$obPrj leadingSpaces $name]
        if {$margin<0} {set margin $sporig}
        set sp [$obPrj leadingSpaces [string range $name $margin end]]
        set name [string trimleft $name]
        set lastname {}  ;# root of project dir
        if {$sp || $margin>$sporig} {
          if {$indent<0} {set indent $sp}
          if {$margin>$sporig || $sp % $indent || $sp>($spprev+$indent)} {
            set errmess [string map [list %n $name] \
              [msgcat::mc {Incorrect indentation in Project template: %n}]]
            break
          }
          for {set i [llength $namelist]} {$i} {} {
            incr i -1
            lassign [lindex $namelist $i] n1 s1
            if {$s1<$sp} {
              set lastname $n1/
              break
            }
          }
        }
        lappend namelist [list [file nativename $lastname$name] $sp]
        set spprev $sp
      }
      if {"$errmess$namelist" eq {}} {
        set errmess {The project template is empty!}
      } elseif {[ExistingProject yes] ne {}} {
        return
      } elseif {[file exists $al(prjroot)]} {
        set errmess {The project directory already exists! It isn't created with templates.}
        FocusInTab f1 [$obPrj chooserPath Dir]
        Message [msgcat::mc $errmess] 4
        return
      }
      if {$errmess ne {}} {
        set namelist {}  ;# skip the following foreach
      } elseif {![Add no]} {
        return
      }
      # the template is OK -> create its dir/file tree
      foreach fn $namelist {
        set fn [lindex $fn 0]
        set fname [file join $al(prjroot) $fn]
        switch -glob -nocase -- [file tail $fn] {
          README* - CHANGELOG* {
            set err [catch {writeTextFile $fname {} 1} errmess]
          }
          LICENCE* - LICENSE* {
            set fname0 [file join $curinfo(prjroot) $fn]
            if {[file exists $fname0]} {
              set err [catch {file copy $fname0 $fname} errmess]
            } else {
              set err [catch {writeTextFile $fname {} 1} errmess]
            }
          }
          default {
            set err [catch {file mkdir $fname} errmess]
          }
        }
        if {$err} break
        set errmess {}
      }
      if {$errmess ne {}} {
        FocusInTab f3 $wtpl
        Message [msgcat::mc $errmess] 4
      }
    }
    #_______________________
    
    proc project::UpdateMsg {msg {showtotals yes}} {
      # Displays a message on project updating.
      #   msg - message of action (add/change)
      #   showtotals - if yes, shows total number of files
    
      variable totalfiles
      if {$showtotals} {append msg " ($::alited::al(MC,files) : $totalfiles) "}
      Message $msg 3
    }
    
    # ________________________ Buttons _________________________ #
    
    proc project::Ok {args} {
      # 'OK' button handler.
      #   args - possible arguments
    
      namespace upvar ::alited al al obPav obPav _dirtree _dirtree
      variable obPrj
      variable win
      variable prjlist
      variable prjinfo
      variable curinfo
      variable updateGUI
      alited::CloseDlg
      if {$curinfo(_NO2ENT)} {
        # disables entering twice (at multiple double-clicks)
        return
      }
      if {[set isel [Selected index]] eq {} || ![ValidProject]} {
        focusTree
        return
      }
      if {![ValidProject]} return
      if {[set pname [ExistingProject no]] eq {}} return
      if {[set N [llength [alited::bar::BAR listFlag m]]]} {
        set msg [msgcat::mc "All modified files (%n) will be saved.\n\nDo you agree?"]
        set msg [string map [list %n $N] $msg]
        if {![alited::msg yesno ques $msg YES -centerme $win]} return
      }
      if {![alited::file::SaveAll]} {
        $obPrj res $win 0
        return
      }
      if {[set N [llength [alited::bar::BAR cget -select]]]} {
        set msg [msgcat::mc "All selected files (%n) will remain open\nin the project you are switching to.\n\nDo you agree?"]
        set msg [string map [list %n $N] $msg]
        if {![alited::msg yesno ques $msg YES -centerme $win]} return
      }
      ::apave::withdraw $win
      set curinfo(_NO2ENT) 1
      set fname [ProjectFileName $pname]
      catch {alited::run::Cancel}  ;# possibly open Run dialogue: discard its settings
      RestoreSettings
      alited::ini::SaveIni
      # setting al(project::Ok) to skip "No name" & SaveCurrentIni at closing all
      if {[set al(project::Ok) 1]} {
        set tabs [alited::bar::BAR listTab]
        alited::file::CloseAll 1 -skipsel  ;# the selected tabs aren't closed
        set selfiles [list]                ;# -> get their file names to reopen afterwards
        foreach tid [alited::bar::BAR listFlag s] {
          lappend selfiles [alited::bar::FileName $tid]
        }
        alited::file::CloseAll 1   ;# close all tabs
        set comms [info commands]
        foreach tab $tabs {
          set tab [lindex $tab 0]
          foreach c $comms {
            if {[regexp "\[._\]$tab\(\[.\]internal\$|\$\)" $c]} {
              catch {destroy $c}
              catch {rename $c {}}
            }
          }
        }
        $obPav untouchWidgets clear *btsBar*  ;# remove bartabs references to the untouched
        set al(prjname) $pname
        set al(prjfile) $fname
        alited::ini::ReadIni $fname
        alited::bar::FillBar [$obPav BtsBar]
        set fnames [list]
        for {set i [llength $selfiles]} {$i} {} { ;# reopen selected files of previous project
          incr i -1
          set fname [lindex $selfiles $i]
          if {[alited::bar::FileTID $fname] eq {}} {
            lappend fnames $fname
          }
        }
        set TID [lindex [alited::bar::BAR listTab] $al(curtab) 0]
        catch {alited::bar::BAR $TID show no no}
        if {[llength $fnames]} {alited::file::OpenFile $fnames yes yes}
        alited::main::UpdateProjectInfo
        alited::ini::GetUserDirs
        alited::file::MakeThemHighlighted
        alited::favor::ShowFavVisit
        [$obPav Tree] selection set {}  ;# new project - no group selected
        update
      }
      unset al(project::Ok)
      alited::file::CheckForNew yes
      after 200 {after idle alited::main::FocusText}
      set _dirtree [list] ;# free its memory
      if {!$al(TREE,isunits)} {
        after 200 {after idle {alited::tree::RecreateTree; alited::tree::SeeTreeItem}}
      }
      set updateGUI no ;# GUI will be updating anyway
      $obPrj res $win 1
    }
    #_______________________
    
    proc project::Cancel {args} {
      # 'Cancel' button handler.
      #   args - possible arguments
    
      variable obPrj
      variable win
      alited::CloseDlg
      SaveData
      SaveNotes
      RestoreSettings
      $obPrj res $win 0
    }
    #_______________________
    
    proc project::Help {} {
      # 'Help' button handler.
    
      variable win
      set th {}
      set ts [string range [$win.fra.fraR.nbk select] end-1 end]
      switch $ts {
        f2 - f3 - f4 - f5 {set th [string index $ts end]}
      }
      alited::Help $win $th
    }
    #_______________________
    
    proc project::HelpMe {} {
      # 'Help' for start.
    
      variable win
      alited::HelpMe $win
    }
    #_______________________
    
    proc project::OnProjectEnter {} {
      # Actions on entering a project.
    
      variable win
      variable prjinfo
      variable msgtodo
      variable itemtodo
      lassign [SortRems $prjinfo($::alited::al(prjname),prjrem)] dmin - outdated
      if {$outdated} {
        set tab1 $win.fra.fraR.nbk.f1
        if {[$win.fra.fraR.nbk select] ne $tab1} {
          $win.fra.fraR.nbk select $tab1
        }
        KlndDayRem $dmin
        set msgtodo [msgcat::mc {TODO reminders for the past: %d.}]
        set dmin [ClockFormat $dmin]
        set msgtodo [string map [list %d $dmin] $msgtodo]
        Message $msgtodo 6
        set itemtodo [Selected item no]
      }
    }
    #_______________________
    
    proc project::ProcMessage {} {
      # Handles clicking on message label.
      # Shows the message and if it is about TODO, selects the corresponding project.
    
      variable obPrj
      variable msgtodo
      variable itemtodo
      set msg [baltip cget [$obPrj LabMess] -text]
      if {$msgtodo eq $msg} {
        Message $msg 6
        Select $itemtodo
      } else {
        Message $msg 3
      }
    }
    #_______________________
    
    proc project::ProjectEnter {} {
      # Processes double-clicking and pressing Enter on the project list.
      # Cancels selecting projects if there are old reminders.
    
      OnProjectEnter
      Ok
    }
    #_______________________
    
    proc project::Klnd_button {ev} {
      # Fire an event handler (paste/undo/redo) on a reminder.
      #   ev - event to fire
    
      variable obPrj
      ::apave::eventOnText [$obPrj TexKlnd] $ev
    }
    #_______________________
    
    proc project::Klnd_paste {} {
      # Pastes a text to a reminder.
    
      Klnd_button <<Paste>>
    }
    #_______________________
    
    proc project::Klnd_undo {} {
      # Undoes changes of a reminder.
    
      Klnd_button <<Undo>>
    }
    #_______________________
    
    proc project::Klnd_redo {} {
      # Redoes changes of a reminder.
    
      Klnd_button <<Redo>>
    }
    #_______________________
    
    proc project::Klnd_delete {} {
      # Clears a reminder.
    
      variable obPrj
      [$obPrj TexKlnd] replace 1.0 end {}
    }
    #_______________________
    
    proc project::Klnd_moveTODO {wrem todo date} {
      # Moves current TODO to a new date.
      #   wrem - text widget of TODO
      #   todo - text of TODO
      #   date - new date (in seconds)
    
      variable klnddata
      # get TODO of new date
      lassign [ClockYMD $date] y m d
      KlndClick $y $m $d
      set todonew [string trimright [$wrem get 1.0 end]]
      if {$todonew ne {}} {append todonew \n}
      # add the moved TODO to the new TODO
      append todonew $todo
      # select the new date
      set klnddata(SAVEDATE) [ClockFormat $date]
      KlndDay $date no
      # update the new TODO
      $wrem replace 1.0 end $todonew
    }
    #_______________________
    
    proc project::Klnd_next {{days 1}} {
      # Moves a reminder to *days*.
      #   days - days to move to
    
      variable obPrj
      variable klnddata
      set wrem [$obPrj TexKlnd]
      set todo [string trimright [$wrem get 1.0 end]]
      if {$todo eq {}} {
        bell
        FocusInTab f1 $wrem
        return
      }
      Klnd_delete
      set date [ClockScan $klnddata(SAVEDATE)]
      set date [clock add $date $days days]
      after 100 [list alited::project::Klnd_moveTODO $wrem $todo $date]
    }
    #_______________________
    
    proc project::Klnd_next2 {} {
      # Moves a reminder to 7 days.
    
      Klnd_next 7
    }
    #_______________________
    
    proc project::Klnd_previous {} {
      # Moves a reminder back to 1 day.
    
      Klnd_next -1
    }
    #_______________________
    
    proc project::Klnd_previous2 {} {
      # Moves a reminder back to 7 days.
    
      Klnd_next -7
    }
    #_______________________
    
    proc project::KlndPopup {w y m d X Y} {
      # Handles a popup menu for the calendar.
      #   w - day widget clicked
      #   y - year
      #   m - month
      #   d - day
      #   X - X-coordinate of pointer
      #   Y - Y-coordinate of pointer
    
      variable obPrj
      KlndClick $y $m $d
      ::klnd::selectedDay {} $y $m $d no
      set popm $w.popup
      catch {destroy $popm}
      menu $popm -tearoff 0
      foreach img {delete - previous2 previous - next next2} {
        if {$img eq {-}} {
          $popm add separator
        } else {
          $popm add command -image alimg_$img  -compound left \
            -label $::alited::al(MC,prjT$img) -command alited::project::Klnd_$img
        }
      }
      $obPrj themePopup $popm
      tk_popup $popm $X $Y
      KlndUpdate
    }
    #_______________________
    
    proc project::ViewDir {} {
      # Shows file chooser just to view the project's dir
    
      alited::tool::e_menu EX=4 d=$::alited::al(prjroot)
    }
    #_______________________
    
    proc project::RunComs {} {
      # Handles running commands of Commands tab.
    
      namespace upvar ::alited al al
      variable obPrj
      variable win
      variable prjlist
      variable prjinfo
      SaveNotes [CurrProject]
      set comtorun {}
      set comcnt 0
      # collect commands executed on the current project
      if {[info exists prjinfo($al(prjname),prjroot)]} {
        set dir $prjinfo($al(prjname),prjroot)
        for {set i 1} {$i<=$al(cmdNum)} {incr i} {
          if {$al(PTP,runch$i) && $al(PTP,run$i) ne {}} {
            if {!$comcnt} {
              append comtorun "cd $dir\n"
            }
            append comtorun "$al(PTP,run$i)\n"
            incr comcnt
          }
        }
      }
      # collect general commands executed per project
      foreach prj $prjlist {
        set dir $prjinfo($prj,prjroot)
        set com {}
        for {set i 1} {$i<=$al(cmdNum)} {incr i} {
          if {$al(PTP,comch$i) && $al(PTP,com$i) ne {}} {
            if {$com eq {}} {
              append com "cd $dir\n"
            }
            append com "$al(PTP,com$i)\n"
            incr comcnt
          }
        }
        append comtorun $com
      }
      if {$comtorun eq {}} {
        focus [$obPrj Entrun1]
        bell
      } else {
        set msg [msgcat::mc {%n commands will be executed!}]
        set msg [string map [list %n $comcnt] $msg]
        if {[alited::msg yesno ques $msg YES -centerme $win]} {
          alited::tool::Run_in_e_menu $comtorun
        }
      }
    }
    #_______________________
    
    proc project::ChecksRun {} {
      # Sets checks of "Commands / Run for project".
    
      namespace upvar ::alited al al
      for {set i 1} {$i<=$al(cmdNum)} {incr i} {
        set al(PTP,runch$i) $al(PTP,chbClearRun)
      }
    }
    #_______________________
    
    proc project::ChecksCom {} {
      # Sets checks of "Commands / Run for all".
    
      namespace upvar ::alited al al
      for {set i 1} {$i<=$al(cmdNum)} {incr i} {
        set al(PTP,comch$i) $al(PTP,chbClearCom)
      }
    }
    
    # ________________________ Template procs _________________________ #
    
    proc project::TplDefaultText {} {
      # Gets default contents of project template.
    
      return \
    {doc
    data
      hlp
      img
      msg
    lib
      theme
      utils
        tkcon
    src
    CHANGELOG.md
    LICENSE
    README.md}
    }
    #_______________________
    
    proc project::TplDefault {} {
      # Sets default contents of project template.
    
      variable obPrj
      set cbx [$obPrj CbxTpl]
      $cbx set Default
      $cbx selection clear
      $obPrj displayText [$obPrj TexTemplate] [TplDefaultText]
    }
    #_______________________
    
    proc project::UpdateTplLists {} {
      # Updates lists of template data, setting the current template on the top.
    
      namespace upvar ::alited al al
      variable obPrj
      set wtpl [$obPrj TexTemplate]
      set cbx [$obPrj CbxTpl]
      set al(PTP,name) [string trim [$cbx get]]
      RemoveFromTplList $al(PTP,name)
      if {$al(PTP,name) eq {}} {
        set al(PTP,name) Template\ #[llength $al(PTP,names)]
      }
      set tpltext [string trimright [$wtpl get 1.0 end]]
      set al(PTP,names) [linsert $al(PTP,names) 0 $al(PTP,name)]
      set al(PTP,list) [linsert $al(PTP,list) 0 $al(PTP,name) $tpltext]
      set maxlen 16
      catch {set al(PTP,names) [lreplace $al(PTP,names) $maxlen end]}
      catch {set al(PTP,list) [lreplace $al(PTP,list) $maxlen+$maxlen end]}
      set ltmp [list]
      foreach {n c} $al(PTP,list) {
        set t {}
        foreach l [lrange [split $c \n] 0 200] {append t [string trimright $l] \n}
        lappend ltmp $n [string trimright $t]
      }
      set al(PTP,list) $ltmp
      $cbx set $al(PTP,name)
      $cbx configure -values $al(PTP,names)
    }
    #_______________________
    
    proc project::UpdateTplText {} {
      # Updates the template text.
    
      namespace upvar ::alited al al
      variable obPrj
      set i [lsearch -exact $al(PTP,list) $al(PTP,name)]
      $obPrj displayText [$obPrj TexTemplate] [lindex $al(PTP,list) $i+1]
      UpdateTplLists
    }
    #_______________________
    
    proc project::RemoveFromTplList {val} {
      # Removes a template name from the lists of template data.
      #   val - name of template to be removed
    
      namespace upvar ::alited al al
      if {$val eq {}} return
      # remove from list of template names
      if {[set i [lsearch -exact $al(PTP,names) $val]]>-1} {
        set al(PTP,names) [lreplace $al(PTP,names) $i $i]
      }
      # remove from list of template pairs "name contents"
      if {[set i [lsearch -exact $al(PTP,list) $val]]>-1} {
        set al(PTP,list) [lreplace $al(PTP,list) $i $i+1]
      }
    }
    #_______________________
    
    proc project::DeleteFromTplList {} {
      # Deletes a template name from the list of project templates.
    
      variable obPrj
      set cbx [$obPrj CbxTpl]
      RemoveFromTplList [string trim [$cbx get]]
      $cbx configure -values $::alited::al(PTP,names)
      $cbx set {}
    }
    
    # ________________________ Calendar _________________________ #
    
    proc project::KlndUpdate {} {
      # Updates calendar data.
    
      namespace upvar ::alited al al
      variable prjinfo
      if {![info exists prjinfo($al(prjname),prjrem)]} {
        set prjinfo($al(prjname),prjrem) {}
      }
      ::klnd::update {} {} {} $prjinfo($al(prjname),prjrem)
      lassign [::klnd::selectedDay] y m d
      KlndClick $y $m $d
    }
    #_______________________
    
    proc project::KlndInDate {{y {}} {m {}} {d {}}} {
      # Gets a date in seconds.
      #   y - year
      #   m - month
      #   d - day
      # If *y* is omitted or y/m/d not valid, gets a current date in seconds.
    
      if {$y ne {}} {
        if {[catch {set date [ClockScan $y/$m/$d]}]} {
          set y {}
        }
      }
      if {$y eq {}} {set date [clock seconds]}
      return $date
    }
    #_______________________
    
    proc project::KlndOutDate {{y {}} {m {}} {d {}}} {
      # Gets a date formatted.
      #   y - year
      #   m - month
      #   d - day
      # If *y* is omitted or y/m/d not valid, gets a current date formatted.
    
      return [ClockFormat [KlndInDate $y $m $d]]
    }
    #_______________________
    
    proc project::KlndDate {date} {
      # Formats a calendar date by alited's format (Preferences/Templates).
      #   date - the date to be formatted
    
      set seconds [ClockScan $date]
      return [alited::tool::FormatDate $seconds]
    }
    #_______________________
    
    proc project::KlndText {dt} {
      # Gets a reminder text for a date.
      #   dt - date
    
      namespace upvar ::alited al al
      variable prjinfo
      if {[set i [KlndSearch $dt $al(prjname)]]>-1} {
        return [lindex $prjinfo($al(prjname),prjrem) $i 1]
      }
      return {}
    }
    #_______________________
    
    proc project::KlndClick {y m d} {
      # Processes a click on a calendar day.
      #   y - year
      #   m - month
      #   d - day
    
      variable obPrj
      variable klnddata
      set klnddata(date) [KlndOutDate $y $m $d]
      # first, save a previous reminder at need
      set klnddata(SAVEDATE) $klnddata(date)
      set klnddata(SAVEPRJ) [CurrProject]
      # then display a new reminder's text
      set klndtex [$obPrj TexKlnd]
      $obPrj displayText $klndtex [KlndText $klnddata(date)]
      alited::ini::HighlightFileText $klndtex .md 0 -cmdpos ::apave::None \
        -cmd alited::project::KlndTextModified
      [$obPrj LabKlndDate] configure -text [KlndDate $klnddata(date)]
    }
    #_______________________
    
    proc project::KlndSearch {date prjname} {
      # Search a date in calendar data.
      # Returns index of found item or -1 if not found.
    
      variable prjinfo
      variable klnddata
      set res -1
      catch {
        set res [lsearch -index 0 -exact $prjinfo($prjname,prjrem) $date]
      }
      return $res
    }
    #_______________________
    
    proc project::KlndTextModified {wtxt args} {
      # Processes modifications of calendar text.
      #   wtxt - text's path
      #   args - not used arguments
    
      namespace upvar ::alited al al
      set aft _KLND_TextModified
      catch {after cancel $al($aft)}
      set al($aft) [after idle alited::project::Klnd_save]
    }
    # ________________________ GUI _________________________ #
    
    proc project::MainFrame {} {
      # Creates a main frame of "Project" dialogue.
    
      return {
        {fraTreePrj - - 10 1 {-st nswe -pady 4 -rw 1}}
        {.TreePrj - - - - {pack -side left -expand 1 -fill both}
          {-h 16 -show headings -columns {C1} -displaycolumns {C1}
          -popup {alited::project::PopupMenu %x %y %X %Y}
          -onevent {<<TreeviewSelect>> alited::project::Select
          <Delete> alited::project::Delete
          <Double-Button-1> alited::project::ProjectEnter
          <Return> alited::project::ProjectEnter}}}
        {.sbvPrjs + L - - {pack -side left -fill both}}
        {fraR fraTreePrj L 10 1 {-st nsew -cw 1 -pady 4}}
        {fraR.nbk - - - - {pack -side top -expand 1 -fill both} {
          f1 {-text {$al(MC,info)}}
          f2 {-text {$al(MC,prjOptions)}}
          f3 {-text Templates}
          f4 {-text Commands}
          f5 {-text Files}
          -traverse yes -select f1
        }}
        {fraB1 fraTreePrj T 1 1 {-st nsew}}
        {.btTad - - - - {pack -side left -anchor n}
          {-com alited::project::Add -tip {$::alited::al(MC,prjadd)} -image alimg_add-big}}
        {.btTch - - - - {pack -side left}
          {-com alited::project::Change -tip {$::alited::al(MC,prjchg)} -image alimg_change-big}}
        {.btTdel - - - - {pack -side left}
          {-com alited::project::Delete -tip {$::alited::al(MC,prjdel1)}
          -image alimg_delete-big}}
        {.h_ - - - - {pack -side left -expand 1}}
        {.btTtpl - - - - {pack -side left}
          {-com alited::project::Template -tip {$::alited::al(MC,CrTemplPrj)}
          -image alimg_plus-big}}
        {.btTtview - - - - {pack -side left -padx 4}
          {-image alimg_folder-big -com alited::project::ViewDir -tip {$::alited::al(MC,ViewDir)}}}
        {LabMess fraB1 L 1 1 {-st nsew -pady 0 -padx 3} {-style TLabelFS}}
        {seh fraB1 T 1 2 {-st nsew -pady 2}}
        {fraB2 + T 1 2 {-st nsew} {-padding {2 2}}}
        {.ButHelp - - - - {pack -side left -anchor s -padx 2}
          {-t {$::alited::al(MC,help)} -tip F1 -com alited::project::Help}}
        {.h_ - - - - {pack -side left -expand 1 -fill both -padx 8} {-w 50}}
        {.ButOK - - - - {pack -side left -anchor s -padx 2}
          {-t {$::alited::al(MC,select)} -com alited::project::Ok}}
        {.butCancel - - - - {pack -side left -anchor s} {-t Cancel -com alited::project::Cancel}}
      }
    }
    #_______________________
    
    proc project::Tab1 {} {
      # Creates a main tab of "Project".
    
      namespace upvar ::alited al al
      variable klnddata
      set klnddata(SAVEDATE) [set klnddata(SAVEPRJ) {}]
      set monofont "[font actual apaveFontMono] -size $al(FONTSIZE,small)"
      set klnddata(toobar) "LabKlndDate {{} {} {-font {$monofont}}} sev 6"
      foreach img {delete paste undo redo - previous2 previous next next2} {
        # -method option for possible disable/enable BuT_alimg_delete etc.
        if {$img eq {-}} {
          append klnddata(toobar) " sev 4"
          continue
        }
        append klnddata(toobar) " alimg_$img \{{} \
          -tip {-BALTIP {$al(MC,prjT$img)} -MAXEXP 2@@ -under 4} \
          -com alited::project::Klnd_$img -method yes \}"
      }
      set klnddata(vsbltext) yes
      set klnddata(date) [KlndOutDate]
      after idle alited::project::KlndUpdate
      return {
        {v_ - - 1 1}
        {fra1 v_ T 1 2 {-st nsew -cw 1}}
        {.labName - - 1 1 {-st e -pady 1 -padx 3}
          {-t {$al(MC,prjName)} -foreground $al(FG,DEFopts) -font {$::apave::FONTMAINBOLD}}}
        {.EntName + L 1 1 {-st sw -pady 5} {-tvar ::alited::al(prjname) -w 50}}
        {.labDir .labName T 1 1 {-st e -pady 8 -padx 3} {-t "Root directory:"}}
        {.Dir + L 1 9 {-st sw -pady 5 -padx 3}
          {-tvar ::alited::al(prjroot) -w 50 -validate focus
          -validatecommand alited::project::ValidateDir}}
        {lab fra1 T 1 1 {-st w -pady 4 -padx 3} {-t "Notes:"}}
        {fra2 + T 2 1 {-st nsew -rw 1 -cw 99}}
        {.TexPrj - - - - {pack -side left -expand 1 -fill both -padx 3}
          {-h 20 -w 40 -wrap word -tabnext *.spx -tip {-BALTIP {$al(MC,notes)} -MAXEXP 1}}}
        {.sbv + L - - {pack -side left}}
        {frasp lab L 1 1 {-st nsew}}
        {.lab - - - - {pack -side left} {-t {TODO days ahead:}}}
        {.spx - - - - {pack -side left -padx 8}
          {-tvar ::alited::al(todoahead) -from 0 -to 365 -w 5 -justify center
          -com alited::project::Select -validate all
          -validatecommand {alited::project::ValidateIni %V}}}
        {fra3 fra2 L 2 1 {-st nsew} {-relief groove -borderwidth 2}}
        {.seh - - - - {pack -fill x}}
        {.daT - - - - {pack -fill both}
          {-tvar ::alited::project::klnddata(date)
          -com {alited::project::KlndUpdate; alited::project::KlndBorderText}
          -dateformat "$::alited::al(TPL,%d)"
          -tip {alited::project::KlndText %D} -weeks $::alited::al(klndweeks)
          -popup {alited::project::KlndPopup %W %y %m %d %X %Y} -width 3}}
        {fra3.fra - - - - {pack -fill both -expand 1}}
        {.seh2 - - - - {pack -side top -fill x}}
        {.too - - - - {pack -side top}
          {-relief flat -borderwidth 0 -array {$::alited::project::klnddata(toobar)}}}
        {.TexKlnd - - - - {pack -side left -fill both -expand 1}
          {-wrap none -tabnext {alited::Tnext *.texPrj} -w 4 -h 8
          -tip {-BALTIP {$al(MC,prjTtext)} -MAXEXP 1}}}
    }
    }
    #_______________________
    
    proc project::Tab2 {} {
      # Creates Options tab of "Project".
    
      namespace upvar ::alited al al
      lassign [alited::FgFgBold] fg al(FG,DEFopts)
      if {!$al(PRJDEFAULT)} {
        set al(FG,DEFopts) "$fg -afteridle {grid forget %w}" ;# no heading message
      }
      return {
        {v_ - - 1 10}
        {lab1 + T 1 2 {-st nsew -pady 1 -padx 3}
          {-t {$al(MC,DEFopts)} -foreground $al(FG,DEFopts) -font {$::apave::FONTMAINBOLD}}}
        {fra2 + T 1 2 {-st nsew -cw 1}}
        {.labIgn - - 1 1 {-st e -pady 1 -padx 3} {-t {$al(MC,Ign:)}}}
        {.entIgn + L 1 8 {-st sw -pady 5 -padx 3} {-tvar ::alited::al(prjdirign) -w 50}}
        {.labEOL .labIgn T 1 1 {-st e -pady 1 -padx 3} {-t {$al(MC,EOL:)}}}
        {.cbxEOL + L 1 1 {-st sw -pady 3 -padx 3}
          {-tvar ::alited::al(prjEOL) -values {{} LF CR CRLF} -w 9 -state readonly}}
        {.labIndent .labEOL T 1 1 {-st e -pady 1 -padx 3} {-t {$al(MC,indent:)}}}
        {.spxIndent + L 1 1 {-st sw -pady 3 -padx 3}
          {-tvar ::alited::al(prjindent) -from 0 -to 8 -com {alited::pref::CheckIndent ""}}}
        {.chbIndAuto + L 1 1 {-st sw -pady 3 -padx 3}
          {-var ::alited::al(prjindentAuto) -t {$al(MC,indentAuto)}}}
        {.labRedunit .labIndent T 1 1 {-st e -pady 1 -padx 3} {-t {$al(MC,redunit)}}}
        {.spxRedunit + L 1 1 {-st sw -pady 3 -padx 3}
          {-tvar ::alited::al(prjredunit) -from $al(minredunit) -to 100}}
        {.labmaxcom .labRedunit T 1 1 {-st e -pady 1 -padx 3} {-t {Maximum Run commands}}}
        {.spxMaxcom + L 1 1 {-st sw -pady 3 -padx 3}
          {-tvar ::alited::al(prjmaxcoms) -from 4 -to 99 -tabnext alited::Tnext}}
        {.labMult .labmaxcom T 1 1 {-st e -pady 1 -padx 3}
          {-t {$al(MC,multiline)} -tip {$al(MC,notrecomm)}}}
        {.swiMult + L 1 1 {-st sw -pady 3 -padx 3}
          {-var ::alited::al(prjmultiline) -tip {$al(MC,notrecomm)}}}
        {.labTrWs .labMult T 1 1 {-st e -pady 1 -padx 3} {-t {$al(MC,trailwhite)}}}
        {.swiTrWs + L 1 1 {-st sw -pady 1} {-var ::alited::al(prjtrailwhite)}}
        {.seh .labTrWs T 1 9}
        {.labUself + T 1 1 {-st e -pady 1 -padx 3} {-t {$al(MC,useleafRE)}}}
        {.swiUself + L 1 1 {-st sw -pady 1}
          {-var ::alited::al(prjuseleafRE) -com alited::project::CheckPrjUseLeaf}}
        {.butStd + L 1 7 {-st e} {-t Standard -com alited::project::StdLeafRE}}
        {.labLf .labUself T 1 1 {-st e -pady 1 -padx 3} {-t {$al(MC,leafRE)}}}
        {.EntLf + L 1 8 {-st sew -pady 1} {-tvar ::alited::al(prjleafRE)}}
      }
    }
    #_______________________
    
    proc project::Tab3 {} {
      # Creates Templates tab of "Project".
    
      return {
        {v_ - - 1 9}
        {lab1 + T 1 9 {-st nsew -pady 1 -padx 3} {-t {$al(MC,TemplPrj)}}}
        {lab2 + T 1 1 {-st ew -pady 5 -padx 3}
          {-t Template: -foreground $al(FG,DEFopts) -font {$::apave::FONTMAINBOLD}}}
        {CbxTpl + L 1 3 {-st ew -pady 5}
          {-w 40 -h 12 -cbxsel {$al(PTP,name)} -tvar ::alited::al(PTP,name)
          -values {$al(PTP,names)} -clearcom alited::project::DeleteFromTplList
          -selcombobox alited::project::UpdateTplText}}
        {fraTlist + T 1 8 {-st nswe -padx 3 -cw 1 -rw 1}}
        {.TexTemplate - - - - {pack -side left -fill both -expand 1}
          {-h 20 -w 40 -tabnext "*.butTplDef *.cbxTpl" -wrap none}}
        {.sbv + L - - {pack -side left}}
        {butTplDef fraTlist T 1 1 {-st w -padx 4 -pady 4}
          {-t Standard -com alited::project::TplDefault -tabnext alited::Tnext}}
      }
    }
    #_______________________
    
    proc project::Tab4 {} {
      # Creates Commands tab of "Project".
    
      namespace upvar ::alited al al
      set al(PTP,chbClearRun) 0
      set al(PTP,chbClearCom) 0
      set al(PTP,chbClearTip) [string map [list %f [msgcat::mc General]] $al(MC,alloffile)]
      return {
        {v_ - - 1 3}
        {Labprj + T 1 2 {} {-foreground $al(FG,DEFopts) -font {$::apave::FONTMAINBOLD}}}
        {ChbClearRun labprj L 1 1 {-st w}
          {-var ::alited::al(PTP,chbClearRun) -com alited::project::ChecksRun -takefocus 0}}
        {tcl {
            set prt labprj
            set ent Entrun
            for {set i 1} {$i<=$::alited::al(cmdNum)} {incr i} {
              set lwid "lab$i $prt T 1 1 {-st nse} {-t {$::alited::al(MC,com) $i:}}"
              %C $lwid
              set lwid "$ent$i lab$i L 1 1 {-cw 1 -st ew} {-tvar ::alited::al(PTP,run$i)}"
              %C $lwid
              set lwid "chb$i $ent$i L 1 1 {} {-t {Run it} \
                -var ::alited::al(PTP,runch$i) -takefocus 0}"
              %C $lwid
              set prt lab$i
              set ent ent
            }
            set lwid {seh1 lab6 T 1 3}
            %C $lwid
            set lwid {labcom seh1 T 1 2 {} \
              {-t General -foreground $::alited::al(FG,DEFopts) -font {$::apave::FONTMAINBOLD}}}
            %C $lwid
            set lwid {chbClearCom labcom L 1 1 {-st w} \
              {-var ::alited::al(PTP,chbClearCom) -com alited::project::ChecksCom -takefocus 0 \
              -tip {$al(PTP,chbClearTip)}}}
            %C $lwid
            set prt labcom
            for {set i 1} {$i<=$::alited::al(cmdNum)} {incr i} {
              set lwid "labc$i $prt T 1 1 {-st nse} {-t {$::alited::al(MC,com) $i:}}"
              %C $lwid
              set lwid "entc$i labc$i L 1 1 {-cw 1 -st ew} {-tvar ::alited::al(PTP,com$i)}"
              %C $lwid
              set lwid "chbc$i entc$i L 1 1 {} {-t {Run it} -var ::alited::al(PTP,comch$i) -takefocus 0}"
              %C $lwid
              set prt labc$i
            }
          }
        }
        {seh2 labc6 T 1 3}
        {h_ seh2 T 1 1}
        {butRun h_ L 1 2 {-st ew} {-t Run -com alited::project::RunComs -tip {$al(MC,saving) & $al(MC,run)} -tabnext alited::Tnext}}
      }
    }
    #_______________________
    
    proc project::Tab5 {} {
      # Creates Files tab of "Project".
    
      alited::ini::ProjectsToolbar
      return {
        {frat - - 1 9 {-st ew}}
        {frat.ToolTop + T - - pack {-array {$::alited::al(atools)} -relief groove -borderwidth 1}}
        {labFilter frat T 1 1 {-st se -padx 1} {-t {All files filter:}}}
        {EntFilter + L 1 1 {-st swe -padx 1 -cw 1} {-tvar ::alited::project::filefilter -validate all -validatecommand alited::project::preValidateFilter}}
        {ChbFilter + L 1 1 {-st sw -padx 1} {-var ::alited::project::casefilter -t {Match case} -com alited::project::postValidateFilter}}
        {seh_ labFilter T 1 3}
        {LabFlist + T 1 3 {-pady 3 -padx 3} {-foreground $al(FG,DEFopts) -font {$::apave::FONTMAINBOLD}}}
        {fraFlist + T 1 3 {-st nswe -padx 3 -rw 1}}
        {.LbxFlist - - - - {pack -side left -fill both -expand 1} {-takefocus 0 -selectmode multiple -popup {alited::project::LbxPopup %X %Y}}}
        {.sbvFlist + L - - {pack -side left}}
      }
    }
    
    # ________________________ Main _________________________ #
    
    proc project::_create {} {
      # Creates and opens Projects dialogue.
    
      namespace upvar ::alited al al
      variable obPrj
      variable win
      variable geo
      variable prjlist
      variable oldTab
      variable ilast
      variable curinfo
      set curinfo(_NO2ENT) 0
      set tipson [baltip::cget -on]
      baltip::configure -on $al(TIPS,Projects)
      ::apave::APave create $obPrj $win
      $obPrj makeWindow $win.fra "$al(MC,projects) :: $::alited::PRJDIR"
      set nbk $win.fra.fraR.nbk
      $obPrj paveWindow \
        $win.fra [MainFrame] \
        $nbk.f1 [Tab1] \
        $nbk.f2 [Tab2] \
        $nbk.f3 [Tab3] \
        $nbk.f4 [Tab4] \
        $nbk.f5 [Tab5]
      set tree [$obPrj TreePrj]
      $tree heading C1 -text $al(MC,projects)
      if {$oldTab ne {}} {$nbk select $oldTab}
      UpdateTree
      bind $win <$al(acc_2)> "alited::ini::ToolByKey $nbk ToolPrjEM"
      bind $win <$al(acc_3)> "alited::ini::ToolByKey $nbk ToolPrjRun"
      foreach ev {KeyPress ButtonPress} {
        bind $tree <$ev> {+ alited::project::KeyOnTree %K}
      }
      bind $win <F1> "[$obPrj ButHelp] invoke"
      bind [$obPrj LabMess] <Button-1> alited::project::ProcMessage
      set lbx [$obPrj LbxFlist]
      foreach a {a A} {
        bind $lbx <Control-$a> alited::project::SelectAllFiles
      }
      bind $lbx <Double-Button-1> {alited::project::OpenFile %y}
      ::baltip tip $lbx {alited::project::TipOnFile %i} -shiftX 10
      after 500 alited::project::HelpMe ;# show an introduction after a short pause
      set prjtex [$obPrj TexPrj]
      bind $prjtex <FocusOut> alited::project::SaveNotes
      if {$ilast>-1} {Select $ilast}
      $obPrj displayText [$obPrj TexTemplate] $al(PTP,text)
      alited::ini::HighlightFileText $prjtex .md 0 -cmdpos ::apave::None
      set res [$obPrj showModal $win -geometry $geo -minsize {600 400} -resizable 1 \
        -onclose alited::project::Cancel -focus [$obPrj TreePrj]]
      set oldTab [$nbk select]
      set al(PTP,text) [string trimright [[$obPrj TexTemplate] get 1.0 end]]
      if {[llength $res] < 2} {set res ""}
      # save the new geometry of the dialogue
      set geo [wm geometry $win]
      alited::main::ShowHeader yes
      baltip::configure {*}$tipson
      ::klnd::clearup
      array unset curinfo *
      catch {destroy $win}
      $obPrj destroy
      return $res
    }
    #_______________________
    
    proc project::_run {{checktodo yes}} {
      # Runs Projects dialogue.
      #   checktodo - if yes, checks for outdated TODOs
    
      namespace upvar ::alited al al
      variable win
      variable msgtodo
      variable updateGUI
      variable saveini
      if {[winfo exists $win]} return
      set updateGUI [set saveini no]
      set msgtodo {}
      update  ;# if run from menu: there may be unupdated space under it (in some DE)
      SaveSettings
      GetProjects
      if {$checktodo && ![IsOutdated $al(prjname)]} {
        after 200 {
          if {[set prj [alited::project::CheckOutdated]] ne {}} {
            alited::project::Select $::alited::project::prjinfo($prj,ID)
          }
        }
      }
      after 200 alited::project::OnProjectEnter
      set res [_create]
      if {$updateGUI} alited::main::UpdateAll ;# settings may be changed as for GUI
      if {$saveini} alited::ini::SaveIni
    }
    
    # _________________________________ EOF _________________________________ #
    
    
    project.tcl